Skip to content

Commit 215f687

Browse files
committed
fix(tui): tighten reconnect watchdog recovery
1 parent d33b66c commit 215f687

2 files changed

Lines changed: 49 additions & 2 deletions

File tree

src/tui/tui-event-handlers.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,50 @@ describe("tui-event-handlers: streaming watchdog", () => {
963963
handlers.dispose?.();
964964
});
965965

966+
it("reloads history only once when reconnect recovery and deferred history refresh overlap", () => {
967+
const { state, loadHistory, noteLocalRunId, handlers } = createHarness({
968+
streamingWatchdogMs: 5_000,
969+
});
970+
971+
handlers.handleChatEvent({
972+
runId: "run-reconnect",
973+
sessionKey: state.currentSessionKey,
974+
state: "delta",
975+
message: { content: "hello" },
976+
} satisfies ChatEvent);
977+
978+
noteLocalRunId("run-local-empty");
979+
handlers.handleChatEvent({
980+
runId: "run-local-empty",
981+
sessionKey: state.currentSessionKey,
982+
state: "final",
983+
} satisfies ChatEvent);
984+
985+
handlers.pauseStreamingWatchdog();
986+
handlers.reconnectStreamingWatchdog();
987+
vi.advanceTimersByTime(5_001);
988+
989+
expect(loadHistory).toHaveBeenCalledTimes(1);
990+
991+
handlers.dispose?.();
992+
});
993+
994+
it("resets to idle when reconnect drops an active run that is no longer tracked", () => {
995+
const { state, setActivityStatus, handlers } = createHarness({
996+
streamingWatchdogMs: 5_000,
997+
});
998+
state.activeChatRunId = "run-stale";
999+
state.activityStatus = "streaming";
1000+
1001+
handlers.reconnectStreamingWatchdog();
1002+
1003+
expect(state.activeChatRunId).toBeNull();
1004+
expect(state.activityStatus).toBe("idle");
1005+
expect(setActivityStatus).toHaveBeenLastCalledWith("idle");
1006+
1007+
handlers.dispose?.();
1008+
});
1009+
9661010
it("keeps reconnect recovery armed when only terminal lifecycle arrives after reconnect", () => {
9671011
const { state, chatLog, setActivityStatus, loadHistory, handlers } = createHarness({
9681012
streamingWatchdogMs: 5_000,

src/tui/tui-event-handlers.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,14 @@ export function createEventHandlers(context: EventHandlerContext) {
120120
state.activeChatRunId = null;
121121
state.activityStatus = "idle";
122122
setActivityStatus("idle");
123-
flushPendingHistoryRefreshIfIdle();
124123
if (reconnectPendingRunId === runId) {
125124
reconnectPendingRunId = null;
125+
pendingHistoryRefresh = false;
126126
void loadHistory?.();
127127
tui.requestRender();
128128
return;
129129
}
130+
flushPendingHistoryRefreshIfIdle();
130131
chatLog.addSystem(
131132
`streaming watchdog: no stream updates for ${Math.round(
132133
streamingWatchdogMs / 1000,
@@ -236,7 +237,9 @@ export function createEventHandlers(context: EventHandlerContext) {
236237
if (!sessionRuns.has(activeRunId)) {
237238
reconnectPendingRunId = null;
238239
state.activeChatRunId = null;
239-
clearStaleStreamingRunIfNoTrackedRunRemains();
240+
state.activityStatus = "idle";
241+
setActivityStatus("idle");
242+
flushPendingHistoryRefreshIfIdle();
240243
return;
241244
}
242245
reconnectPendingRunId = activeRunId;

0 commit comments

Comments
 (0)