From d13a174fe176fb6ef1dbde463379e9a6ce027b41 Mon Sep 17 00:00:00 2001 From: Lumen Yang Date: Wed, 13 May 2026 08:49:25 +0200 Subject: [PATCH 1/3] fix: keep compression anchor stable in windowed transcript --- static/ui.js | 14 +++++++++++--- tests/test_auto_compression_card.py | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/static/ui.js b/static/ui.js index d071d93ed93..0fa474bb5c3 100644 --- a/static/ui.js +++ b/static/ui.js @@ -4472,9 +4472,11 @@ function _compressionAnchorIndex(visWithIdx, anchorKey, fallbackIdx=null){ for(let i=visWithIdx.length-1;i>=0;i--){ const candidate=_compressionMessageAnchorKey(visWithIdx[i].m); if(!candidate) continue; + const anchorTs=String(anchorKey.ts??''); + const candidateTs=String(candidate.ts??''); if( candidate.role===String(anchorKey.role||'') && - String(candidate.ts??'')===String(anchorKey.ts??'') && + (!anchorTs||!candidateTs||candidateTs===anchorTs) && String(candidate.text||'')===String(anchorKey.text||'') && Number(candidate.attachments||0)===Number(anchorKey.attachments||0) ){ @@ -4938,13 +4940,19 @@ function renderMessages(options){ break; } } - const insertionAnchor=_compressionAnchorIndex( - renderVisWithIdx, + const insertionAnchorFull=_compressionAnchorIndex( + visWithIdx, compressionState ? compressionState.anchorMessageKey : sessionCompressionAnchorKey, compressionState ? (typeof compressionState.anchorVisibleIdx==='number' ? compressionState.anchorVisibleIdx : compressionState.anchorRawIdx) : sessionCompressionAnchor ); + let insertionAnchor=null; + if(typeof insertionAnchorFull==='number'){ + if(insertionAnchorFull Date: Wed, 13 May 2026 10:59:36 +0200 Subject: [PATCH 2/3] fix: place compression banner at persisted marker --- static/ui.js | 4 +++- tests/test_auto_compression_card.py | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/static/ui.js b/static/ui.js index 0fa474bb5c3..ec2076bb756 100644 --- a/static/ui.js +++ b/static/ui.js @@ -4890,6 +4890,7 @@ function renderMessages(options){ inner.innerHTML=''; const compressionNode=compressionState?_compressionCardsNode(compressionState):null; const referenceMessage=S.messages.find(m=>_isContextCompactionMessage(m)); + const referenceMessageRawIdx=referenceMessage?S.messages.findIndex(m=>m===referenceMessage):-1; const referenceText=referenceMessage ? msgContent(referenceMessage)||String(referenceMessage.content||'') : sessionCompressionSummary; @@ -5171,7 +5172,8 @@ function renderMessages(options){ const handoffSummaryStates=_collectHandoffSummaryStates(S.messages); _insertCompressionLikeNode(compressionNode); - _insertCompressionLikeNode(referenceNode); + if(referenceNode&&referenceMessageRawIdx>=0) _insertCompressionLikeNodeByRawIdx(referenceNode, referenceMessageRawIdx); + else _insertCompressionLikeNode(referenceNode); _insertCompressionLikeNode(preservedOnlyNode, preservedOnlyAnchor); _insertCompressionLikeNode(handoffState?_handoffCardsNode(handoffState):null, renderVisWithIdx.length?renderVisWithIdx.length-1:null); for(const entry of handoffSummaryStates){ diff --git a/tests/test_auto_compression_card.py b/tests/test_auto_compression_card.py index 22a8e84b03e..16afbe2edc7 100644 --- a/tests/test_auto_compression_card.py +++ b/tests/test_auto_compression_card.py @@ -244,6 +244,14 @@ def test_compression_anchor_index_is_translated_into_render_window(): assert "windowStart+renderVisWithIdx.length" in block +def test_reference_message_uses_raw_transcript_position_before_anchor_fallback(): + src = _read("static/ui.js") + + assert "const referenceMessageRawIdx=referenceMessage?S.messages.findIndex(m=>m===referenceMessage):-1;" in src + assert "if(referenceNode&&referenceMessageRawIdx>=0) _insertCompressionLikeNodeByRawIdx(referenceNode, referenceMessageRawIdx);" in src + assert "else _insertCompressionLikeNode(referenceNode);" in src + + def test_preserved_task_list_attaches_once_per_render(): src = _read("static/ui.js") From 7f01abf931d082ff7f44020af1dca2f51e78d697 Mon Sep 17 00:00:00 2001 From: Lumen Yang Date: Wed, 13 May 2026 13:27:51 +0200 Subject: [PATCH 3/3] fix: ignore stale compaction markers when placing banner --- static/ui.js | 24 ++++++++++++++++++++++-- tests/test_auto_compression_card.py | 27 ++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/static/ui.js b/static/ui.js index ec2076bb756..38334691adb 100644 --- a/static/ui.js +++ b/static/ui.js @@ -4486,6 +4486,24 @@ function _compressionAnchorIndex(visWithIdx, anchorKey, fallbackIdx=null){ } return typeof fallbackIdx==='number' ? fallbackIdx : null; } +function _latestCompressionReferenceMessage(messages, summaryText=''){ + if(!Array.isArray(messages)||!messages.length) return {message:null, rawIdx:-1}; + const summaryNorm=String(summaryText||'').replace(/\s+/g,' ').trim(); + for(let i=messages.length-1;i>=0;i--){ + const m=messages[i]; + if(!_isContextCompactionMessage(m)) continue; + if(!summaryNorm) return {message:m, rawIdx:i}; + let content=''; + try{ + content=String(msgContent(m)||''); + }catch(_){ + content=String((m&&m.content)||''); + } + const contentNorm=content.replace(/\s+/g,' ').trim(); + if(contentNorm.includes(summaryNorm)) return {message:m, rawIdx:i}; + } + return {message:null, rawIdx:-1}; +} function _compressionReferenceCardHtml(text, open=false){ const preview=text.split(/\n+/).filter(Boolean).slice(0,2).join(' '); return ` @@ -4889,8 +4907,10 @@ function renderMessages(options){ $('emptyState').style.display=(vis.length||preservedCompressionTaskMessages.length)?'none':''; inner.innerHTML=''; const compressionNode=compressionState?_compressionCardsNode(compressionState):null; - const referenceMessage=S.messages.find(m=>_isContextCompactionMessage(m)); - const referenceMessageRawIdx=referenceMessage?S.messages.findIndex(m=>m===referenceMessage):-1; + const {message:referenceMessage, rawIdx:referenceMessageRawIdx}=_latestCompressionReferenceMessage( + S.messages, + sessionCompressionSummary + ); const referenceText=referenceMessage ? msgContent(referenceMessage)||String(referenceMessage.content||'') : sessionCompressionSummary; diff --git a/tests/test_auto_compression_card.py b/tests/test_auto_compression_card.py index 16afbe2edc7..ff50bb33b4f 100644 --- a/tests/test_auto_compression_card.py +++ b/tests/test_auto_compression_card.py @@ -247,11 +247,36 @@ def test_compression_anchor_index_is_translated_into_render_window(): def test_reference_message_uses_raw_transcript_position_before_anchor_fallback(): src = _read("static/ui.js") - assert "const referenceMessageRawIdx=referenceMessage?S.messages.findIndex(m=>m===referenceMessage):-1;" in src + assert "const {message:referenceMessage, rawIdx:referenceMessageRawIdx}=_latestCompressionReferenceMessage(" in src assert "if(referenceNode&&referenceMessageRawIdx>=0) _insertCompressionLikeNodeByRawIdx(referenceNode, referenceMessageRawIdx);" in src assert "else _insertCompressionLikeNode(referenceNode);" in src +def test_reference_message_selection_prefers_latest_matching_marker(): + src = _read("static/ui.js") + start = src.find("function _latestCompressionReferenceMessage") + assert start != -1, "compression reference selection helper not found" + end = src.find("function _compressionReferenceCardHtml", start) + assert end != -1, "compression reference renderer not found after selection helper" + helper = src[start:end] + + assert "for(let i=messages.length-1;i>=0;i--)" in helper + assert "if(!summaryNorm) return {message:m, rawIdx:i};" in helper + assert "if(contentNorm.includes(summaryNorm)) return {message:m, rawIdx:i};" in helper + + +def test_reference_message_falls_back_to_current_summary_when_only_stale_markers_exist(): + src = _read("static/ui.js") + start = src.find("function _latestCompressionReferenceMessage") + assert start != -1, "compression reference selection helper not found" + end = src.find("function _compressionReferenceCardHtml", start) + assert end != -1, "compression reference renderer not found after selection helper" + helper = src[start:end] + + assert "const summaryNorm=String(summaryText||'').replace(/\\s+/g,' ').trim();" in helper + assert "return {message:null, rawIdx:-1};" in helper + + def test_preserved_task_list_attaches_once_per_render(): src = _read("static/ui.js")