diff --git a/packages/adapter-utils/src/types.ts b/packages/adapter-utils/src/types.ts index 65176754..6d5442aa 100644 --- a/packages/adapter-utils/src/types.ts +++ b/packages/adapter-utils/src/types.ts @@ -160,6 +160,7 @@ export interface CreateConfigValues { promptTemplate: string; model: string; thinkingEffort: string; + chrome: boolean; dangerouslySkipPermissions: boolean; search: boolean; dangerouslyBypassSandbox: boolean; diff --git a/packages/adapters/claude-local/src/index.ts b/packages/adapters/claude-local/src/index.ts index 57a9b0c7..b9f09555 100644 --- a/packages/adapters/claude-local/src/index.ts +++ b/packages/adapters/claude-local/src/index.ts @@ -15,6 +15,7 @@ Core fields: - cwd (string, optional): default absolute working directory fallback for the agent process - model (string, optional): Claude model id - effort (string, optional): reasoning effort passed via --effort (low|medium|high) +- chrome (boolean, optional): pass --chrome when running Claude - promptTemplate (string, optional): run prompt template - bootstrapPromptTemplate (string, optional): first-run prompt template - maxTurnsPerRun (number, optional): max turns for one run diff --git a/packages/adapters/claude-local/src/server/execute.ts b/packages/adapters/claude-local/src/server/execute.ts index 0b1b7922..d260018a 100644 --- a/packages/adapters/claude-local/src/server/execute.ts +++ b/packages/adapters/claude-local/src/server/execute.ts @@ -23,6 +23,7 @@ import { parseClaudeStreamJson, describeClaudeFailure, detectClaudeLoginRequired, + isClaudeMaxTurnsResult, isClaudeUnknownSessionError, } from "./parse.js"; @@ -263,6 +264,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise 0) args.push("--max-turns", String(maxTurns)); @@ -439,6 +442,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise) : null; + const clearSessionForMaxTurns = isClaudeMaxTurnsResult(parsed); return { exitCode: proc.exitCode, @@ -460,7 +464,7 @@ export async function execute(ctx: AdapterExecutionContext): Promise): string | return parts.length > 1 ? parts.join(": ") : null; } +export function isClaudeMaxTurnsResult(parsed: Record | null | undefined): boolean { + if (!parsed) return false; + + const subtype = asString(parsed.subtype, "").trim().toLowerCase(); + if (subtype === "error_max_turns") return true; + + const stopReason = asString(parsed.stop_reason, "").trim().toLowerCase(); + if (stopReason === "max_turns") return true; + + const resultText = asString(parsed.result, "").trim(); + return /max(?:imum)?\s+turns?/i.test(resultText); +} + export function isClaudeUnknownSessionError(parsed: Record): boolean { const resultText = asString(parsed.result, "").trim(); const allMessages = [resultText, ...extractClaudeErrorMessages(parsed)] diff --git a/packages/adapters/claude-local/src/ui/build-config.ts b/packages/adapters/claude-local/src/ui/build-config.ts index 0dcb0533..41c4f036 100644 --- a/packages/adapters/claude-local/src/ui/build-config.ts +++ b/packages/adapters/claude-local/src/ui/build-config.ts @@ -57,6 +57,7 @@ export function buildClaudeLocalConfig(v: CreateConfigValues): Record { + it("detects max-turn exhaustion by subtype", () => { + expect( + isClaudeMaxTurnsResult({ + subtype: "error_max_turns", + result: "Reached max turns", + }), + ).toBe(true); + }); + + it("detects max-turn exhaustion by stop_reason", () => { + expect( + isClaudeMaxTurnsResult({ + stop_reason: "max_turns", + }), + ).toBe(true); + }); + + it("returns false for non-max-turn results", () => { + expect( + isClaudeMaxTurnsResult({ + subtype: "success", + stop_reason: "end_turn", + }), + ).toBe(false); + }); +}); diff --git a/ui/src/adapters/claude-local/config-fields.tsx b/ui/src/adapters/claude-local/config-fields.tsx index 603f0b71..f7887279 100644 --- a/ui/src/adapters/claude-local/config-fields.tsx +++ b/ui/src/adapters/claude-local/config-fields.tsx @@ -24,6 +24,20 @@ export function ClaudeLocalAdvancedFields({ }: AdapterConfigFieldsProps) { return ( <> + + isCreate + ? set!({ chrome: v }) + : mark("adapterConfig", "chrome", v) + } + />