From c239ad6c9d0189275b2829bcddfa56680eca8c3f Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Wed, 29 Apr 2026 15:13:43 +0200 Subject: [PATCH] fix(headless): use long idle timeout for auto/next/discuss/plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 15s IDLE_TIMEOUT_MS was killing auto-mode prematurely. Symptom: sf headless auto would dispatch a task, the LLM would make 1-2 tool calls, pause to reason about the next step, exceed 15s of "no events", and headless would declare "Status: complete" — exiting at ~35s with the task barely started (123 events but only 2 tool calls). The 120s NEW_MILESTONE_IDLE_TIMEOUT_MS already exists for the same reason ("LLM may pause between tool calls e.g. after mkdir, before writing files"). The same applies to auto/next/discuss/plan — all multi-turn commands where the LLM thinks longer between actions, especially on non-trivial tasks. isMultiTurnCommand was already defined for related logic; this just wires it into the idle-timeout decision. Co-Authored-By: Claude Opus 4.7 --- src/headless.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/headless.ts b/src/headless.ts index 672ec9725..e27de3f91 100644 --- a/src/headless.ts +++ b/src/headless.ts @@ -753,11 +753,17 @@ async function runHeadlessOnce( resolveCompletion = resolve; }); - // Idle timeout — fallback completion detection + // Idle timeout — fallback completion detection. + // Multi-turn commands (auto, next, discuss, plan) involve LLM thinking + // pauses between tool calls that can exceed 15s. Use the longer + // new-milestone timeout for those too — without this, headless killed + // auto-mode after 2 tool calls when the LLM paused to reason about the + // next step (~35s total run, exit "complete" while task barely started). let idleTimer: ReturnType | null = null; - const effectiveIdleTimeout = isNewMilestone - ? NEW_MILESTONE_IDLE_TIMEOUT_MS - : IDLE_TIMEOUT_MS; + const effectiveIdleTimeout = + isNewMilestone || isMultiTurnCommand + ? NEW_MILESTONE_IDLE_TIMEOUT_MS + : IDLE_TIMEOUT_MS; function resetIdleTimer(): void { if (idleTimer) clearTimeout(idleTimer);