diff --git a/static/messages.js b/static/messages.js index 6842bbb9..7fffdc63 100644 --- a/static/messages.js +++ b/static/messages.js @@ -1829,12 +1829,15 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){ // Context was auto-compressed during this turn. Render it through the // same transient compression-card path as manual /compress, without // inserting a fake assistant message into history or model context. - if(!S.session||S.session.session_id!==activeSid) return; + if(!S.session) return; + const currentSid=S.session.session_id; let d={}; try{ d=JSON.parse(e.data||'{}')||{}; }catch(_){ d={}; } const eventSid=d.old_session_id||d.session_id||activeSid; - if(eventSid!==activeSid && d.new_session_id!==activeSid && d.continuation_session_id!==activeSid) return; const continuationSid=d.new_session_id||d.continuation_session_id||''; + const eventMatchesCurrent=!!(currentSid&&(eventSid===currentSid||d.new_session_id===currentSid||d.continuation_session_id===currentSid)); + if(!eventMatchesCurrent) return; + const displaySid=currentSid; const message=String(d.message||'Context auto-compressed to continue the conversation').trim(); if(d.usage&&typeof _syncCtxIndicator==='function'){ S.lastUsage={...(S.lastUsage||{}),...d.usage}; @@ -1842,7 +1845,7 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){ } if(typeof setCompressionUi==='function'){ const state={ - sessionId:activeSid, + sessionId:displaySid, phase:'done', automatic:true, message, diff --git a/tests/test_auto_compression_card.py b/tests/test_auto_compression_card.py index 23576ff2..d57a7956 100644 --- a/tests/test_auto_compression_card.py +++ b/tests/test_auto_compression_card.py @@ -217,16 +217,15 @@ def test_auto_compression_sse_uses_transient_card_not_fake_message(): def test_auto_compression_sse_keeps_inactive_and_malformed_paths_safe(): block = _compressed_listener_block() - guard = "if(!S.session||S.session.session_id!==activeSid) return;" + guard = "if(!S.session) return;" assert guard in block assert block.index(guard) < block.index("setCompressionUi") assert "try{ d=JSON.parse(e.data||'{}')||{}; }catch(_){ d={}; }" in block assert "const eventSid=d.old_session_id||d.session_id||activeSid;" in block - # The listener also accepts a rotated continuation session id so journal- - # replay reconnects after compression rotate land the done card. - # See Opus advisor followup on stage-385 (v0.51.92). - event_guard = "if(eventSid!==activeSid && d.new_session_id!==activeSid && d.continuation_session_id!==activeSid) return;" + assert "const eventMatchesCurrent=" in block + event_guard = "if(!eventMatchesCurrent) return;" assert event_guard in block + assert block.index("const eventMatchesCurrent=") < block.index(event_guard) def test_auto_compression_done_accepts_rotated_continuation_session_event(): @@ -238,12 +237,29 @@ def test_auto_compression_done_accepts_rotated_continuation_session_event(): # continuation id as display metadata instead of dropping the event. assert "const eventSid=d.old_session_id||d.session_id||activeSid;" in block assert "const continuationSid=d.new_session_id||d.continuation_session_id||'';" in block - event_guard = "if(eventSid!==activeSid && d.new_session_id!==activeSid && d.continuation_session_id!==activeSid) return;" + event_guard = "if(!eventMatchesCurrent) return;" assert event_guard in block - assert block.index("const eventSid=") < block.index(event_guard) + assert block.index("const eventSid=") < block.index("const eventMatchesCurrent=") assert "continuationSessionId:continuationSid" in block +def test_auto_compression_done_accepts_event_after_current_session_rotates(): + block = _compressed_listener_block() + + # The final compressed event can arrive/replay after another event has already + # updated S.session to the continuation session id. Do not drop it just + # because the active browser session no longer equals the original activeSid. + strict_active_guard = "if(!S.session||S.session.session_id!==activeSid) return;" + assert strict_active_guard not in block + assert "if(!S.session) return;" in block + assert "const currentSid=S.session.session_id;" in block + assert "const eventMatchesCurrent=" in block + assert "const displaySid=currentSid;" in block + assert "sessionId:displaySid" in block + assert block.index("const eventSid=") < block.index("const eventMatchesCurrent=") + assert block.index("const displaySid=") < block.index("setCompressionUi(state)") + + def test_auto_compression_done_sse_refreshes_context_indicator_usage(): block = _compressed_listener_block()