fix(desktop): stop background session messages bleeding into the active transcript#37975
Merged
kshitijk4poor merged 1 commit intoJun 3, 2026
Conversation
…ve 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.
davidgut1982
pushed a commit
to davidgut1982/hermes-agent
that referenced
this pull request
Jun 5, 2026
…session-view-bleed fix(desktop): stop background session messages bleeding into the active transcript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On the desktop app, a failed/queued turn from one session renders on top of every other session you toggle to. The screenshot symptom is stale user bubbles (e.g.
the code is in the repo itself in: /apps,test) each followed by a red "session busy" error, persisting into unrelated sessions.Root cause
The messages are real, correctly-keyed transcript entries belonging to one session (verified against
Local Storage+state.db— the data layer is not bleeding). The bleed is in the render path.A still-busy background session (the one the user toggled away from) keeps emitting
updateSessionState()heartbeats — stream deltas, and especially thesession busyprompt.submitrejection errors produced when a queued turn auto-drains while the backend is still busy. EachupdateSessionState()calledsyncSessionStateToView()unconditionally, staging that background session's messages into the shared$messagesview.flushPendingViewState()does guard against the wrong session reaching the view — but only onerequestAnimationFrameis scheduled per frame andpendingViewStateRefholds just the latest writer. So within a single frame a background write can overwrite an already-pending foreground write, and the stale background transcript renders on top of whatever session the user switched to.Fix
Guard at the staging site (
syncSessionStateToView): a session may only stage into$messageswhen it is the currently-active session. Background sessions still update their own cache entry (sessionStateByRuntimeIdRef); they just never touch the view. This mirrors the existingflushPendingViewStateactive-id check, moved earlier so a background write can't displace a pending foreground write within the same RAF window.Pure render fix — no behavior change to queuing, interrupt/Stop (#37948), or drain. The active session's optimistic seeds and updates still reach the view because
activeSessionIdRef.currentis set to the runtime id before itsupdateSessionStatecalls run (create/resume paths).Test plan
tsc -bclean.session busy→ toggle to session B → before: A's rows render over B; after: B shows only its own transcript, A's rows stay in A.Local vitest jsdom suite is environment-broken in this checkout (
window.localStorage.clear is not a functionon node 25, fails identically on clean main), so relying on CI for the suite.