* fix: live-resume chat stream on session re-entry (#2539) When a session was re-entered after a page refresh or in a new tab while its agent run was still streaming, the UI showed a frozen "Generating response..." spinner, polled stream_status until the run finished, and then did a full reload. The live tokens were never shown. Add resumeStream() in chat.js: it consumes GET /api/chat/resume/{id} (which replays the run's buffer then streams live), renders reply tokens as they arrive, and reloads the session on completion for the canonical final render. sessions.js _checkServerStream now calls it on re-entry and falls back to the previous spinner+poll path if it is unavailable. * Finalize plain-text resume in place instead of reloading On stream completion, resumeStream() called selectSession(), forcing a full history re-fetch and a visible flicker right as the stream finished. For plain text replies (no tool calls, sources, doc streaming, or multi-round output) the live tokens are already rendered, so finalize in place: replace the live bubble with a canonical single message via chatRenderer.addMessage (markdown + footer actions + metrics, the same renderer history uses), captured from the streamed metrics event. No history refetch, no extra round-trip, no flicker. Rich responses still reload, since their canonical render (tool bubbles, sources, multi-bubble) is rebuilt from the saved DB record. * Use a dedicated set for the resume re-attach lock; fix stale docblock resumeStream() marked its re-attach lock in _backgroundStreams, which checkBackgroundStream() also reads. On a second re-entry of the same session while a resume was still live, checkBackgroundStream() mistook that entry for a same-tab POST stream and spawned its own spinner+poll bubble. Move the lock to a dedicated _resumingStreams set (also covered by hasActiveStream) so the two paths no longer collide. Also update the resumeStream docblock to describe the in-place finalize vs reload split.
127 KiB
127 KiB