fix(streaming): scope streaming message rendering per-session to prevent cross-session render leak (#6449) (#6502)

When the user switches from a streaming session (A) to another session (B),
A's streaming text could render in B's window because:

1. The token handler guards against rendering when the session is not active,
   but the rAF/setTimeout window between _scheduleRender() and _doRender()
   can outlive a session switch.
2. _doRender() writes to assistantBody without checking whether the stream's
   session is still the active frontend pane.

Fix: add _isActiveSession() guards to:
- _scheduleRender() — drop scheduled renders when the session changed
- _doRender() — bail before any DOM write if the session switched
- _flushPendingSegmentRender() — defense-in-depth for any future callers

These guard at the same level the token/interim_assistant event handlers
already do, closing the rAF gap that could leak streamed text into a
different session's view.

Co-authored-by: webtecnica <contato@webtecnica.com.br>
Co-authored-by: nesquena-hermes <nesquena+hermes@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-08-13 22:02:33 -07:00
1 parent fe8fc067aa
commit 6261b626a7
1 file changed
+12
+12
View File
@@ -5226,6 +5226,10 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
const force=!!(options&&options.force);
const skipAnchorProcessProse=!!(options&&options.skipAnchorProcessProse);
if(!assistantBody||(!force&&!_renderPending)) return;
// #6449: guard — this stream's session is no longer the active pane.
// Callers already gate on _isActiveSession(), but add the guard here too
// so any future call-site cannot leak rendering into the wrong session.
if(!_isActiveSession()) return;
if(_renderPending) _cancelAnimationFramePendingStreamRender();
const displayText=segmentStart===0
? _parseStreamState().displayText
@@ -5551,6 +5555,11 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
}
if(_renderPending) return;
if(_streamFinalized) return; // Bug A: don't schedule new rAF after stream finalized
// #6449: guard — this stream's session is no longer the active frontend pane.
// Drop the scheduled render instead of writing into a detached or wrong-session DOM.
// Callers (token/interim_assistant handlers) already gate on _isActiveSession(), but
// the rAF/setTimeout window between schedule and execution can outlive a session switch.
if(!_isActiveSession()) return;
_renderPending=true;
// Cap render rate to ~15fps. The browser's rAF fires at 60fps, but each DOM
// update takes 50-150ms on large sessions. During GC pauses, rAF callbacks
@@ -5566,6 +5575,9 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
_renderPending=false;
// Guard: a pending setTimeout+rAF can outlive stream finalization.
if(_streamFinalized) return;
// #6449: guard — the frontend session changed between rAF schedule and execution.
// Writing DOM into this stream's assistantBody would leak text into the wrong pane.
if(!_isActiveSession()) return;
// Mobile scroll-jank guard: temporarily disable overflow-anchor before DOM
// writes to suppress Chromium scroll re-anchoring during streaming growth.
if(typeof window._fixMobileScrollJank==='function') window._fixMobileScrollJank();