From 77248f8cb082e05b37dbfba720dbcb198ae73364 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 14 Aug 2026 18:47:01 -0700 Subject: [PATCH] fix(desktop): clear the stale stream target on the assistant-tail append path too Sibling site of the idle-resume rule from the stale-fold fix: the assistant-tail append exit (user row persisted, no projection row) still carried the journal's streamId onto a not-running resume, which kept the journal entry alive (persistInFlightTurnState only clears when streamId is null) and re-folded the same tail on every open. Apply the same keepPending gate and pin it with a regression assertion. Refs #85308 --- apps/desktop/src/lib/inflight-turn-journal.test.ts | 3 +++ apps/desktop/src/lib/inflight-turn-journal.ts | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/lib/inflight-turn-journal.test.ts b/apps/desktop/src/lib/inflight-turn-journal.test.ts index 552793a0e4..8041db1059 100644 --- a/apps/desktop/src/lib/inflight-turn-journal.test.ts +++ b/apps/desktop/src/lib/inflight-turn-journal.test.ts @@ -577,6 +577,9 @@ describe('recoverInFlightTurnJournal', () => { expect(result.applied).toBe(true) expect(result.messages.map(m => m.id)).toEqual(['db-u1', 'assistant-stream-1']) + // Idle resume: the assistant-tail append path must not resurrect the + // stream target either, or the journal entry re-folds on every open. + expect(result.streamId).toBeNull() const tail = result.messages.at(-1)! expect(tail.pending).toBe(false) expect(tail.parts[0]).toMatchObject({ type: 'tool-call' }) diff --git a/apps/desktop/src/lib/inflight-turn-journal.ts b/apps/desktop/src/lib/inflight-turn-journal.ts index 82397579b0..31a314bb05 100644 --- a/apps/desktop/src/lib/inflight-turn-journal.ts +++ b/apps/desktop/src/lib/inflight-turn-journal.ts @@ -797,7 +797,11 @@ export function mergeInFlightMessages( applied: true, caughtUp: false, messages: [...baseMessages, ...withoutBaseIds(tailAssistants, baseMessages)], - streamId, + // Same idle-resume rule as the other exit paths: only a running turn + // keeps the stream target alive. Carrying the stale streamId here kept + // the journal entry alive (persistInFlightTurnState only clears when + // streamId is null), so the same tail was folded again on every open. + streamId: options.keepPending ? streamId : null, turnStartedAt: null } }