diff --git a/src/tui/tui-event-handlers.test.ts b/src/tui/tui-event-handlers.test.ts index 2073afe308d..48140fc9a97 100644 --- a/src/tui/tui-event-handlers.test.ts +++ b/src/tui/tui-event-handlers.test.ts @@ -590,4 +590,21 @@ describe("tui-event-handlers: handleAgentEvent", () => { expect(loadHistory).toHaveBeenCalledTimes(1); }); + + it("does not reload history for local run with empty final when another run is active (#53115)", () => { + const { state, loadHistory, noteLocalRunId, handleChatEvent } = createHandlersHarness({ + state: { activeChatRunId: "run-main" }, + }); + + noteLocalRunId("run-local-empty"); + + handleChatEvent({ + runId: "run-local-empty", + sessionKey: state.currentSessionKey, + state: "final", + }); + + expect(state.activeChatRunId).toBe("run-main"); + expect(loadHistory).not.toHaveBeenCalled(); + }); }); diff --git a/src/tui/tui-event-handlers.ts b/src/tui/tui-event-handlers.ts index 6fda2d85163..e0c92608de8 100644 --- a/src/tui/tui-event-handlers.ts +++ b/src/tui/tui-event-handlers.ts @@ -158,9 +158,18 @@ export function createEventHandlers(context: EventHandlerContext) { const isLocalRun = isLocalRunId?.(runId) ?? false; if (isLocalRun) { forgetLocalRunId?.(runId); + // Never reload history for local runs that ended without displayable output. + // This prevents the user's message from disappearing when the backend is slow + // (e.g., Ollama) and sends an empty final event before the response is ready. if (!opts?.allowLocalWithoutDisplayableFinal) { return; } + // Skip history reload if a DIFFERENT run is still active. + // This prevents clearing the user's pending message when a stale/concurrent + // empty final event arrives while a new message is being processed. + if (state.activeChatRunId && state.activeChatRunId !== runId) { + return; + } } if (hasConcurrentActiveRun(runId)) { return;