diff --git a/packages/adapter-cli/src/generated/adapter.ts b/packages/adapter-cli/src/generated/adapter.ts index 3d3807c44..0771a1751 100644 --- a/packages/adapter-cli/src/generated/adapter.ts +++ b/packages/adapter-cli/src/generated/adapter.ts @@ -24,7 +24,17 @@ export class CliAdapter implements EngineAdapter { this.getVersion = this.getVersion.bind(this); } + private withEngineTimeout(options: DispatchOptions): DispatchOptions { + // Honor the engine's declared timeout (slow coding-plan engines like zai set timeout=300) over a lower generic command default (orchestration uses 120s), so a slow engine isn't cut off mid-answer and lose its slot. Returns a NEW options object — never mutates the caller's, since callers may reuse/retry the same DispatchOptions. + const declared = Number((options.engine as any)?.timeout ?? 0); + if (declared > Number(options.timeout ?? 0)) { + return { ...options, timeout: declared }; + } + return options; + } + async dispatch(options: DispatchOptions): Promise { + options = this.withEngineTimeout(options); // Prefer CLI binary when available — API is fallback for binary-less engines const binaryPath = options.engine.binary ? this.registry.findBinary(options.engine) : null; if (!binaryPath) { @@ -119,6 +129,7 @@ export class CliAdapter implements EngineAdapter { } async *dispatchStream(options: DispatchOptions): AsyncGenerator { + options = this.withEngineTimeout(options); // workspace-pure isolation, resolved once for every dispatch path below. const iso = computeEngineIsolation(options.engine, { isolation: options.isolation, cwd: options.cwd }); // Subscription pty path for claude — yields response deltas, returns final result. @@ -215,6 +226,7 @@ export class CliAdapter implements EngineAdapter { } async dispatchAgent(options: DispatchOptions): Promise { + options = this.withEngineTimeout(options); // workspace-pure isolation, resolved once for every agent dispatch path below. const iso = computeEngineIsolation(options.engine, { isolation: options.isolation, cwd: options.cwd }); // Subscription pty path for claude (agent mode: tools + bypassed perms). @@ -371,6 +383,7 @@ export class CliAdapter implements EngineAdapter { } async *dispatchAgentStream(options: DispatchOptions): AsyncGenerator { + options = this.withEngineTimeout(options); // workspace-pure isolation, resolved once for every agent-stream path below. const iso = computeEngineIsolation(options.engine, { isolation: options.isolation, cwd: options.cwd }); // Subscription pty path for claude (agent mode). Same diff capture as dispatchAgent. diff --git a/packages/adapter-cli/src/kern/adapter.kern b/packages/adapter-cli/src/kern/adapter.kern index 29eff2d11..3176da2d9 100644 --- a/packages/adapter-cli/src/kern/adapter.kern +++ b/packages/adapter-cli/src/kern/adapter.kern @@ -17,8 +17,17 @@ service name=CliAdapter implements=EngineAdapter assign target="this.isAvailable" value="this.isAvailable.bind(this)" assign target="this.getVersion" value="this.getVersion.bind(this)" + method name=withEngineTimeout params="options:DispatchOptions" returns="DispatchOptions" private=true + handler lang="kern" + comment raw="// Honor the engine's declared timeout (slow coding-plan engines like zai set timeout=300) over a lower generic command default (orchestration uses 120s), so a slow engine isn't cut off mid-answer and lose its slot. Returns a NEW options object — never mutates the caller's, since callers may reuse/retry the same DispatchOptions." + let name=declared value="Number((options.engine as any)?.timeout ?? 0)" + if cond="declared > Number(options.timeout ?? 0)" + return value="{ ...options, timeout: declared }" + return value="options" + method name=dispatch params="options:DispatchOptions" returns="Promise" async=true handler lang="kern" + assign target="options" value="this.withEngineTimeout(options)" comment raw="// Prefer CLI binary when available — API is fallback for binary-less engines" let name=binaryPath value="options.engine.binary ? this.registry.findBinary(options.engine) : null" if cond="!binaryPath" @@ -101,6 +110,7 @@ service name=CliAdapter implements=EngineAdapter method name=dispatchStream params="options:DispatchOptions" returns="string, DispatchResult, void" async=true stream=true handler <<< + options = this.withEngineTimeout(options); // workspace-pure isolation, resolved once for every dispatch path below. const iso = computeEngineIsolation(options.engine, { isolation: options.isolation, cwd: options.cwd }); // Subscription pty path for claude — yields response deltas, returns final result. @@ -198,6 +208,7 @@ service name=CliAdapter implements=EngineAdapter method name=dispatchAgent params="options:DispatchOptions" returns="Promise" async=true handler lang="kern" + assign target="options" value="this.withEngineTimeout(options)" comment raw="// workspace-pure isolation, resolved once for every agent dispatch path below." let name=iso value="computeEngineIsolation(options.engine, { isolation: options.isolation, cwd: options.cwd })" comment raw="// Subscription pty path for claude (agent mode: tools + bypassed perms)." @@ -333,6 +344,7 @@ service name=CliAdapter implements=EngineAdapter method name=dispatchAgentStream params="options:DispatchOptions" returns="string, AgentDispatchResult, void" async=true stream=true handler <<< + options = this.withEngineTimeout(options); // workspace-pure isolation, resolved once for every agent-stream path below. const iso = computeEngineIsolation(options.engine, { isolation: options.isolation, cwd: options.cwd }); // Subscription pty path for claude (agent mode). Same diff capture as dispatchAgent.