Skip to content

Commit 8cdcdd7

Browse files
authored
Merge pull request #82 from arul28/ade/fix-lanes-tab-374ebbe8
fix lanes tab
2 parents 1639e36 + 15de4ae commit 8cdcdd7

39 files changed

Lines changed: 3038 additions & 453 deletions

apps/desktop/src/main/packagedRuntimeSmoke.ts

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,37 @@ const execFileAsync = promisify(execFile);
1010
const PTY_PROBE_TIMEOUT_MS = 4_000;
1111
const CLAUDE_PROBE_TIMEOUT_MS = 20_000;
1212

13+
function errorMessage(error: unknown): string {
14+
return error instanceof Error ? error.message : String(error);
15+
}
16+
17+
const AUTH_FAILURE_PATTERNS = [
18+
"not authenticated",
19+
"not logged in",
20+
"authentication required",
21+
"authentication error",
22+
"authentication_error",
23+
"login required",
24+
"sign in",
25+
"claude auth login",
26+
"/login",
27+
"authentication_failed",
28+
"invalid authentication credentials",
29+
"invalid api key",
30+
"api error: 401",
31+
"status code: 401",
32+
"status 401",
33+
];
34+
1335
function isClaudeAuthFailureMessage(input: unknown): boolean {
1436
const text = input instanceof Error ? input.message : String(input ?? "");
1537
const lower = text.toLowerCase();
16-
return (
17-
lower.includes("not authenticated")
18-
|| lower.includes("not logged in")
19-
|| lower.includes("authentication required")
20-
|| lower.includes("authentication error")
21-
|| lower.includes("authentication_error")
22-
|| lower.includes("login required")
23-
|| lower.includes("sign in")
24-
|| lower.includes("claude auth login")
25-
|| lower.includes("/login")
26-
|| lower.includes("authentication_failed")
27-
|| lower.includes("invalid authentication credentials")
28-
|| lower.includes("invalid api key")
29-
|| lower.includes("api error: 401")
30-
|| lower.includes("status code: 401")
31-
|| lower.includes("status 401")
32-
);
38+
return AUTH_FAILURE_PATTERNS.some((pattern) => lower.includes(pattern));
3339
}
3440

3541
async function probePty(): Promise<{ ok: true; output: string }> {
3642
const pty = await import("node-pty");
37-
return await new Promise((resolve, reject) => {
43+
return new Promise((resolve, reject) => {
3844
let output = "";
3945
const term = pty.spawn("/bin/sh", ["-lc", 'printf "ADE_PTY_OK\\n"'], {
4046
name: "xterm-256color",
@@ -124,12 +130,12 @@ async function probeClaudeStartup(
124130
if (isClaudeAuthFailureMessage(error)) {
125131
return {
126132
state: "auth-failed",
127-
message: error instanceof Error ? error.message : String(error),
133+
message: errorMessage(error),
128134
};
129135
}
130136
return {
131137
state: "runtime-failed",
132-
message: error instanceof Error ? error.message : String(error),
138+
message: errorMessage(error),
133139
};
134140
} finally {
135141
clearTimeout(timeout);
@@ -193,6 +199,6 @@ async function main(): Promise<void> {
193199
}
194200

195201
void main().catch((error) => {
196-
process.stderr.write(error instanceof Error ? error.stack ?? error.message : String(error));
202+
process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));
197203
process.exit(1);
198204
});

apps/desktop/src/main/services/ai/claudeRuntimeProbe.test.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,18 @@ const mockState = vi.hoisted(() => ({
1414
env: { ADE_PROJECT_ROOT: "/tmp/project" },
1515
},
1616
})),
17-
resolveAdeMcpServerLaunch: vi.fn(() => ({
17+
resolveDesktopAdeMcpLaunch: vi.fn(() => ({
18+
mode: "headless_source",
1819
command: "node",
1920
cmdArgs: ["probe.js"],
2021
env: { ADE_PROJECT_ROOT: "/tmp/project" },
22+
entryPath: "probe.js",
23+
runtimeRoot: "/tmp/runtime",
24+
socketPath: "/tmp/project/.ade/mcp.sock",
25+
packaged: false,
26+
resourcesPath: null,
2127
})),
28+
resolveRepoRuntimeRoot: vi.fn(() => "/tmp/runtime"),
2229
}));
2330

2431
vi.mock("@anthropic-ai/claude-agent-sdk", () => ({
@@ -39,8 +46,9 @@ vi.mock("./providerResolver", () => ({
3946
normalizeCliMcpServers: mockState.normalizeCliMcpServers,
4047
}));
4148

42-
vi.mock("../orchestrator/unifiedOrchestratorAdapter", () => ({
43-
resolveAdeMcpServerLaunch: mockState.resolveAdeMcpServerLaunch,
49+
vi.mock("../runtime/adeMcpLaunch", () => ({
50+
resolveDesktopAdeMcpLaunch: mockState.resolveDesktopAdeMcpLaunch,
51+
resolveRepoRuntimeRoot: mockState.resolveRepoRuntimeRoot,
4452
}));
4553

4654
let probeClaudeRuntimeHealth: typeof import("./claudeRuntimeProbe").probeClaudeRuntimeHealth;
@@ -68,7 +76,8 @@ beforeEach(async () => {
6876
mockState.reportProviderRuntimeFailure.mockReset();
6977
mockState.resolveClaudeCodeExecutable.mockClear();
7078
mockState.normalizeCliMcpServers.mockClear();
71-
mockState.resolveAdeMcpServerLaunch.mockClear();
79+
mockState.resolveDesktopAdeMcpLaunch.mockClear();
80+
mockState.resolveRepoRuntimeRoot.mockClear();
7281
const mod = await import("./claudeRuntimeProbe");
7382
probeClaudeRuntimeHealth = mod.probeClaudeRuntimeHealth;
7483
resetClaudeRuntimeProbeCache = mod.resetClaudeRuntimeProbeCache;
@@ -153,4 +162,52 @@ describe("claudeRuntimeProbe", () => {
153162
expect(mockState.reportProviderRuntimeAuthFailure).toHaveBeenCalledTimes(1);
154163
expect(mockState.reportProviderRuntimeFailure).not.toHaveBeenCalled();
155164
});
165+
166+
it("calls resolveDesktopAdeMcpLaunch with defaultRole external and projectRoot", async () => {
167+
const query = makeStream([
168+
{
169+
type: "result",
170+
subtype: "success",
171+
duration_ms: 50,
172+
duration_api_ms: 50,
173+
is_error: false,
174+
num_turns: 1,
175+
result: "ok",
176+
session_id: "session-ok",
177+
total_cost_usd: 0.001,
178+
usage: {
179+
input_tokens: 10,
180+
cache_creation_input_tokens: 0,
181+
cache_read_input_tokens: 0,
182+
output_tokens: 5,
183+
server_tool_use: { web_search_requests: 0 },
184+
service_tier: "standard",
185+
},
186+
},
187+
]);
188+
mockState.query.mockReturnValue(query.stream);
189+
190+
await probeClaudeRuntimeHealth({ projectRoot: "/my/custom/project", force: true });
191+
192+
expect(mockState.resolveDesktopAdeMcpLaunch).toHaveBeenCalledWith(
193+
expect.objectContaining({
194+
projectRoot: "/my/custom/project",
195+
workspaceRoot: "/my/custom/project",
196+
defaultRole: "external",
197+
}),
198+
);
199+
expect(mockState.resolveRepoRuntimeRoot).toHaveBeenCalled();
200+
expect(mockState.reportProviderRuntimeReady).toHaveBeenCalledTimes(1);
201+
});
202+
203+
it("reports runtime-failed when the probe stream throws an error", async () => {
204+
mockState.query.mockImplementation(() => {
205+
throw new Error("spawn ENOENT");
206+
});
207+
208+
await probeClaudeRuntimeHealth({ projectRoot: "/tmp/project", force: true });
209+
210+
expect(mockState.reportProviderRuntimeFailure).toHaveBeenCalledTimes(1);
211+
expect(mockState.reportProviderRuntimeAuthFailure).not.toHaveBeenCalled();
212+
});
156213
});

apps/desktop/src/main/services/chat/agentChatService.ts

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,8 +1083,7 @@ function isLightweightSession(session: Pick<AgentChatSession, "sessionProfile">)
10831083

10841084
let _mcpRuntimeRootCache: string | null = null;
10851085
function resolveMcpRuntimeRoot(): string {
1086-
if (_mcpRuntimeRootCache !== null) return _mcpRuntimeRootCache;
1087-
_mcpRuntimeRootCache = resolveUnifiedRuntimeRoot();
1086+
_mcpRuntimeRootCache ??= resolveUnifiedRuntimeRoot();
10881087
return _mcpRuntimeRootCache;
10891088
}
10901089

@@ -1342,22 +1341,25 @@ export function createAgentChatService(args: {
13421341
ownerId?: string | null;
13431342
computerUsePolicy?: ComputerUsePolicy | null;
13441343
}) => {
1345-
const launch = resolveAdeMcpServerLaunch({
1344+
const { mode, command, entryPath, runtimeRoot, socketPath, packaged, resourcesPath } = resolveAdeMcpServerLaunch({
13461345
workspaceRoot: projectRoot,
13471346
runtimeRoot: resolveMcpRuntimeRoot(),
13481347
defaultRole: args.defaultRole,
13491348
ownerId: args.ownerId ?? undefined,
13501349
computerUsePolicy: normalizeComputerUsePolicy(args.computerUsePolicy, createDefaultComputerUsePolicy()),
13511350
});
1352-
return {
1353-
mode: launch.mode,
1354-
command: launch.command,
1355-
entryPath: launch.entryPath,
1356-
runtimeRoot: launch.runtimeRoot,
1357-
socketPath: launch.socketPath,
1358-
packaged: launch.packaged,
1359-
resourcesPath: launch.resourcesPath,
1360-
};
1351+
return { mode, command, entryPath, runtimeRoot, socketPath, packaged, resourcesPath };
1352+
};
1353+
1354+
/** Best-effort diagnostic: resolve the MCP launch config for a session, returning undefined on failure. */
1355+
const tryDiagnosticMcpLaunch = (managed: ManagedChatSession): ReturnType<typeof summarizeAdeMcpLaunch> | undefined => {
1356+
try {
1357+
return summarizeAdeMcpLaunch({
1358+
defaultRole: managed.session.identityKey === "cto" ? "cto" : "agent",
1359+
ownerId: resolveWorkerIdentityAgentId(managed.session.identityKey),
1360+
computerUsePolicy: managed.session.computerUse,
1361+
});
1362+
} catch { return undefined; }
13611363
};
13621364

13631365
const readTranscriptConversationEntries = (managed: ManagedChatSession): string[] => {
@@ -4692,14 +4694,7 @@ export function createAgentChatService(args: {
46924694
};
46934695

46944696
const startCodexRuntime = async (managed: ManagedChatSession): Promise<CodexRuntime> => {
4695-
let adeMcpLaunch: ReturnType<typeof summarizeAdeMcpLaunch> | undefined;
4696-
try {
4697-
adeMcpLaunch = summarizeAdeMcpLaunch({
4698-
defaultRole: managed.session.identityKey === "cto" ? "cto" : "agent",
4699-
ownerId: resolveWorkerIdentityAgentId(managed.session.identityKey),
4700-
computerUsePolicy: managed.session.computerUse,
4701-
});
4702-
} catch { /* best-effort diagnostic — must not block Codex startup */ }
4697+
const adeMcpLaunch = tryDiagnosticMcpLaunch(managed);
47034698

47044699
logger.info("agent_chat.codex_runtime_start", {
47054700
sessionId: managed.session.id,
@@ -5191,17 +5186,10 @@ export function createAgentChatService(args: {
51915186
);
51925187
}
51935188
let diagClaudePath: string | undefined;
5194-
let diagMcpLaunch: ReturnType<typeof summarizeAdeMcpLaunch> | undefined;
51955189
try {
51965190
diagClaudePath = runtime.v2Session ? undefined : buildClaudeV2SessionOpts(managed, runtime).pathToClaudeCodeExecutable;
51975191
} catch { /* best-effort diagnostic */ }
5198-
try {
5199-
diagMcpLaunch = summarizeAdeMcpLaunch({
5200-
defaultRole: managed.session.identityKey === "cto" ? "cto" : "agent",
5201-
ownerId: resolveWorkerIdentityAgentId(managed.session.identityKey),
5202-
computerUsePolicy: managed.session.computerUse,
5203-
});
5204-
} catch { /* best-effort diagnostic */ }
5192+
const diagMcpLaunch = tryDiagnosticMcpLaunch(managed);
52055193
logger.warn("agent_chat.claude_v2_prewarm_failed", {
52065194
sessionId: managed.session.id,
52075195
error: error instanceof Error ? error.message : String(error),

0 commit comments

Comments
 (0)