fix(desktop): stop background session messages bleeding into the active transcript

A still-busy background session (one the user toggled away from) keeps
emitting updateSessionState() heartbeats — stream deltas, and especially
the 'session busy' prompt-rejection errors from auto-drained queued turns.
Each call invoked syncSessionStateToView() unconditionally, staging that
session's messages into the shared $messages view.

flushPendingViewState() guarded against the wrong session reaching the
view, but only one requestAnimationFrame is scheduled per frame and
pendingViewStateRef holds just the latest writer. So within a single
frame a background write could overwrite an already-pending foreground
write, and the stale background transcript (e.g. the red 'session busy'
rows) would render on top of whatever session the user switched to —
appearing to 'bleed' into every session.

Guard at the staging site: a session may only stage into the view when
it is the currently-active session. Background sessions still update
their own cache entry; they just never touch $messages. Pure render
fix, no behavior change to queuing, interrupt, or drain.
This commit is contained in:
kshitijk4poor
2026-06-03 12:09:18 +05:30
parent ada04573a9
commit 28f1590b7a

View File

@ -95,6 +95,19 @@ export function useSessionStateCache({
const syncSessionStateToView = useCallback(
(sessionId: string, state: ClientSessionState) => {
// Only the currently-viewed session may stage into the shared `$messages`
// view. A background session (e.g. one still busy and emitting stream /
// error updates after the user toggled away) must update its own cache
// entry but never the view — otherwise its messages clobber the
// foreground transcript and appear to "bleed" into every other session.
// The flush below also re-checks the active id, but staging here is what
// prevents a background write from overwriting an already-pending
// foreground write within the same animation frame (only one RAF is
// scheduled, so the last `pendingViewStateRef` writer would otherwise win).
if (sessionId !== activeSessionIdRef.current) {
return
}
pendingViewStateRef.current = { sessionId, state }
if (viewSyncRafRef.current !== null) {