From c397a463cdd4b2d3e36abaa4b252905bcc29962b Mon Sep 17 00:00:00 2001 From: Jason Carreira Date: Sun, 8 Feb 2026 22:16:03 -0500 Subject: [PATCH] fix: headless permission wait deadlock (#865) Co-authored-by: Jason Carreira --- src/headless.ts | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/headless.ts b/src/headless.ts index 91b3b5b..3351568 100644 --- a/src/headless.ts +++ b/src/headless.ts @@ -1941,11 +1941,20 @@ async function runBidirectionalMode( console.log(JSON.stringify(controlRequest)); + const deferredLines: string[] = []; + // Wait for control_response - while (true) { + let result: { + decision: "allow" | "deny"; + reason?: string; + updatedInput?: Record | null; + } | null = null; + + while (result === null) { const line = await getNextLine(); if (line === null) { - return { decision: "deny", reason: "stdin closed" }; + result = { decision: "deny", reason: "stdin closed" }; + break; } if (!line.trim()) continue; @@ -1960,28 +1969,38 @@ async function runBidirectionalMode( | CanUseToolResponse | undefined; if (!response) { - return { decision: "deny", reason: "Invalid response format" }; + result = { decision: "deny", reason: "Invalid response format" }; + break; } if (response.behavior === "allow") { - return { decision: "allow", updatedInput: response.updatedInput }; + result = { + decision: "allow", + updatedInput: response.updatedInput, + }; } else { - return { + result = { decision: "deny", reason: response.message, // TODO: handle interrupt flag }; } + break; } - // Put other messages back in queue for main loop - lineQueue.unshift(line); - // But since we're waiting for permission, we need to wait more - // Actually this causes issues - let's just ignore other messages - // during permission wait (they'll be lost) + + // Defer other messages for the main loop without re-reading them. + deferredLines.push(line); } catch { - // Ignore parse errors + // Defer parse errors so the main loop can surface them. + deferredLines.push(line); } } + + if (deferredLines.length > 0) { + lineQueue.unshift(...deferredLines); + } + + return result; } // Main processing loop