From 8f9eb5eec50d476f5f3cce82215d9e59268386fd Mon Sep 17 00:00:00 2001 From: Charles Packer Date: Wed, 31 Dec 2025 17:39:26 -0800 Subject: [PATCH] fix: patch server-side tools showing up as interrupted (#441) --- src/cli/App.tsx | 9 ++++++++- src/cli/helpers/accumulator.ts | 2 ++ src/cli/helpers/stream.ts | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/cli/App.tsx b/src/cli/App.tsx index d665073..75b24e3 100644 --- a/src/cli/App.tsx +++ b/src/cli/App.tsx @@ -807,11 +807,18 @@ export default function App({ // Use a ref to track pending refresh if (!buffersRef.current.pendingRefresh) { buffersRef.current.pendingRefresh = true; + // Capture the current generation to detect if resume invalidates this refresh + const capturedGeneration = buffersRef.current.commitGeneration || 0; setTimeout(() => { buffersRef.current.pendingRefresh = false; // Skip refresh if stream was interrupted - prevents stale updates appearing // after user cancels. Normal stream completion still renders (interrupted=false). - if (!buffersRef.current.interrupted) { + // Also skip if commitGeneration changed - this means a resume is in progress and + // committing now would lock in the stale "Interrupted by user" state. + if ( + !buffersRef.current.interrupted && + (buffersRef.current.commitGeneration || 0) === capturedGeneration + ) { refreshDerived(); } }, 16); // ~60fps diff --git a/src/cli/helpers/accumulator.ts b/src/cli/helpers/accumulator.ts index a199eee..18f38ed 100644 --- a/src/cli/helpers/accumulator.ts +++ b/src/cli/helpers/accumulator.ts @@ -72,6 +72,7 @@ export type Buffers = { lastOtid: string | null; // Track the last otid to detect transitions pendingRefresh?: boolean; // Track throttled refresh state interrupted?: boolean; // Track if stream was interrupted by user (skip stale refreshes) + commitGeneration?: number; // Incremented when resuming from error to invalidate pending refreshes usage: { promptTokens: number; completionTokens: number; @@ -90,6 +91,7 @@ export function createBuffers(): Buffers { pendingToolByRun: new Map(), toolCallIdToLineId: new Map(), lastOtid: null, + commitGeneration: 0, usage: { promptTokens: 0, completionTokens: 0, diff --git a/src/cli/helpers/stream.ts b/src/cli/helpers/stream.ts index 3034b97..28e54f9 100644 --- a/src/cli/helpers/stream.ts +++ b/src/cli/helpers/stream.ts @@ -336,6 +336,9 @@ export async function drainStreamWithResume( // Reset interrupted flag so resumed chunks can be processed by onChunk. // Without this, tool_return_message for server-side tools (web_search, fetch_webpage) // would be silently ignored, showing "Interrupted by user" even on successful resume. + // Increment commitGeneration to invalidate any pending setTimeout refreshes that would + // commit the stale "Interrupted by user" state before the resume stream completes. + buffers.commitGeneration = (buffers.commitGeneration || 0) + 1; buffers.interrupted = false; // Resume from Redis where we left off