Release v0.51.291 — Release JG (stage-s2 — preserve live turn content on switch-away #3668) (#3704)
Some checks failed
Release & Docker / release (push) Has been cancelled
Some checks failed
Release & Docker / release (push) Has been cancelled
* fix(#3668): snapshot live turn before stream teardown on session switch The 'stays gone' variant: switching away from a streaming session during a quiet window (mid tool-exec / silent thinking, between content SSE events) left a stale/absent live-turn snapshot, so restoreLiveTurnHtmlForSession() failed on switch-back and loadSession()'s fallback rebuilt with an empty appendThinking(), permanently losing streamed thinking/tool content (only the elapsed clock survived). closeLiveStream() now snapshots the live-turn DOM via snapshotLiveTurnHtmlForSession(sessionId) BEFORE closing the source + tearing down LIVE_STREAMS, so switch-back always restores the exact state shown at switch-away. + regression test asserting snapshot precedes teardown. * docs(changelog): v0.51.291 — Release JG (stage-s2, #3668) --------- Co-authored-by: nesquena-hermes <[email protected]>
This commit is contained in:
@@ -3,6 +3,11 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.51.291] — 2026-06-06 — Release JG (stage-s2 — preserve live turn content when switching away mid-stream)
|
||||
|
||||
### Fixed
|
||||
- **Switching away from a streaming session no longer loses the in-progress thinking/tool content.** When you clicked to another chat while a session was streaming during a quiet window (mid tool-execution or silent reasoning, between content events) and then switched back, the live turn's tool cards and thinking could disappear permanently — only the elapsed-time clock survived — until the response finished and the transcript re-rendered from the server. Cause: the live-turn DOM snapshot was only captured on content/`tool_complete` SSE events, so the switch-away teardown could run with a stale-or-absent snapshot, and the switch-back fallback rebuilt an empty thinking card. `closeLiveStream()` now snapshots the live turn **before** tearing the stream down, so switching back restores the exact state shown at switch-away. (#3668)
|
||||
|
||||
## [v0.51.290] — 2026-06-06 — Release JF (stage-s1 — profile provider/model now respected in session resolution)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -787,6 +787,15 @@ function closeLiveStream(sessionId, streamId, source){
|
||||
if(!live) return;
|
||||
if(streamId&&live.streamId!==streamId) return;
|
||||
if(source&&live.source!==source) return;
|
||||
// Snapshot the current live-turn DOM BEFORE tearing the stream down. The
|
||||
// per-event snapshot (snapshotLiveTurn) only fires on content/tool_complete
|
||||
// SSE events, so switching away during a quiet window (mid tool-exec, silent
|
||||
// thinking) would leave a stale-or-absent snapshot — on switch-back
|
||||
// restoreLiveTurnHtmlForSession() then fails and loadSession()'s fallback
|
||||
// rebuilds with an EMPTY appendThinking(), permanently losing the streamed
|
||||
// thinking/tool content (only the elapsed clock survives). Capturing here
|
||||
// guarantees switch-back restores the exact state shown at switch-away. (#3668)
|
||||
if(typeof snapshotLiveTurnHtmlForSession==='function') snapshotLiveTurnHtmlForSession(sessionId);
|
||||
try{live.source.close();}catch(_){ }
|
||||
delete LIVE_STREAMS[sessionId];
|
||||
// closeLiveStream() is called during session-switch teardown for any session
|
||||
|
||||
@@ -229,3 +229,44 @@ def test_sse_initial_event_reshows_pending_prompt():
|
||||
assert "showApprovalForSession(sid" in MESSAGES_JS
|
||||
# the 'initial' SSE event specifically is wired (not only the live event).
|
||||
assert "addEventListener('initial'" in MESSAGES_JS
|
||||
|
||||
|
||||
def test_close_live_stream_snapshots_turn_before_teardown():
|
||||
"""#3668 'stays gone' variant: switching away from a streaming session during
|
||||
a quiet window (mid tool-exec / silent thinking, between content SSE events)
|
||||
must still preserve the live thinking/tool content on switch-back.
|
||||
|
||||
The per-event snapshot (snapshotLiveTurn) only fires on content/tool_complete
|
||||
events, so closeLiveStream() — the switch-away teardown — must capture a DOM
|
||||
snapshot BEFORE closing the source. Otherwise restoreLiveTurnHtmlForSession()
|
||||
finds no/stale snapshot and loadSession()'s fallback rebuilds with an EMPTY
|
||||
appendThinking(), permanently losing the streamed content (only the elapsed
|
||||
clock survives — the reported signature).
|
||||
"""
|
||||
# Extract the closeLiveStream body and assert the snapshot precedes teardown.
|
||||
start = MESSAGES_JS.index("function closeLiveStream(")
|
||||
brace = MESSAGES_JS.index("{", start)
|
||||
depth = 0
|
||||
body = ""
|
||||
for i in range(brace, len(MESSAGES_JS)):
|
||||
if MESSAGES_JS[i] == "{":
|
||||
depth += 1
|
||||
elif MESSAGES_JS[i] == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
body = MESSAGES_JS[brace + 1 : i]
|
||||
break
|
||||
assert body, "closeLiveStream body not found"
|
||||
snap_idx = body.find("snapshotLiveTurnHtmlForSession(sessionId)")
|
||||
close_idx = body.find("live.source.close()")
|
||||
delete_idx = body.find("delete LIVE_STREAMS[sessionId]")
|
||||
assert snap_idx != -1, (
|
||||
"closeLiveStream() must snapshot the live turn (snapshotLiveTurnHtmlForSession) "
|
||||
"before tearing the stream down, or switch-away during a quiet window loses content (#3668)."
|
||||
)
|
||||
assert close_idx != -1 and snap_idx < close_idx, (
|
||||
"the snapshot must be taken BEFORE live.source.close()."
|
||||
)
|
||||
assert delete_idx != -1 and snap_idx < delete_idx, (
|
||||
"the snapshot must be taken BEFORE LIVE_STREAMS teardown."
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user