Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

## [Unreleased]

### Fixed

- Prevent compression-summary cards from using ordinary tool output that merely mentions context compression. The streaming path now reuses the shared strict compression-marker predicate, so only synthetic marker prefixes from non-tool messages can seed `compression_anchor_summary`; skill/tool JSON and user discussion about compaction no longer render as misleading `Context compaction` reference cards.

## [v0.51.118] — 2026-05-22 — Release CP (stage-pr2773 — 1-PR hotfix — v0.51.117 brick fix: chat input restored)

### Fixed
Expand Down
7 changes: 6 additions & 1 deletion api/compression_anchor.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _content_has_part_type(content, part_types):
)


def _is_context_compression_marker(message):
def is_context_compression_marker(message):
"""Return true for synthetic compression/reference cards, not user turns."""
if not isinstance(message, dict):
return False
Expand All @@ -71,6 +71,11 @@ def _is_context_compression_marker(message):
)


def _is_context_compression_marker(message):
"""Backward-compatible alias for callers that have not switched yet."""
return is_context_compression_marker(message)


def visible_messages_for_anchor(messages, *, auto_compression: bool = False):
"""Return transcript messages that can anchor compression UI metadata.

Expand Down
12 changes: 2 additions & 10 deletions api/streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
load_settings,
)
from api.helpers import redact_session_data, _redact_text
from api.compression_anchor import visible_messages_for_anchor
from api.compression_anchor import is_context_compression_marker, visible_messages_for_anchor
from api.metering import meter
from api.run_journal import RunJournalWriter
from api.turn_journal import append_turn_journal_event_for_stream
Expand Down Expand Up @@ -2299,15 +2299,7 @@ def _dedupe_replayed_active_context(previous_context, result_messages):


def _is_context_compression_marker(msg):
if not isinstance(msg, dict):
return False
text = _message_text(msg.get('content', '')).lower()
return (
'context compaction' in text
or 'context compression' in text
or 'context was auto-compressed' in text
or 'active task list was preserved across context compression' in text
)
return is_context_compression_marker(msg)


def _compact_summary_text(raw_text: str | None, limit: int = 320) -> str | None:
Expand Down
42 changes: 41 additions & 1 deletion tests/test_issue2028_compression_anchor_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

from pathlib import Path

from api.compression_anchor import visible_messages_for_anchor
from api.compression_anchor import is_context_compression_marker, visible_messages_for_anchor
from api.streaming import _compression_summary_from_messages, _is_context_compression_marker


def test_legacy_duplicate_anchor_helpers_are_removed():
Expand Down Expand Up @@ -57,3 +58,42 @@ def test_visible_messages_for_anchor_keeps_manual_user_messages_simple():
[user_tool_metadata, user_attachment, assistant_tool_metadata],
auto_compression=True,
) == [user_tool_metadata, user_attachment, assistant_tool_metadata]


def test_context_compression_marker_detection_is_prefix_and_role_scoped():
real_marker = {
"role": "assistant",
"content": "[CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted.",
}
preserved_tasks_marker = {
"role": "user",
"content": "[Your active task list was preserved across context compression] - [ ] follow up",
}
tool_noise = {
"role": "tool",
"content": "{\"description\": \"Troubleshoot frequent context compression indicators\"}",
}
user_discussion = {
"role": "user",
"content": "Why do I see context compression after every message?",
}

assert is_context_compression_marker(real_marker)
assert is_context_compression_marker(preserved_tasks_marker)
assert _is_context_compression_marker(real_marker)
assert not is_context_compression_marker(tool_noise)
assert not is_context_compression_marker(user_discussion)


def test_compression_summary_ignores_tool_output_that_mentions_compression():
marker = {
"role": "assistant",
"content": "[CONTEXT COMPACTION — REFERENCE ONLY] Keep this handoff as reference.",
}
skill_tool_output = {
"role": "tool",
"content": "{\"name\": \"hermes-webui-operations\", \"content\": \"Troubleshooting frequent context compression indicators...\"}",
}

assert _compression_summary_from_messages([marker, skill_tool_output]) == marker["content"]
assert _compression_summary_from_messages([skill_tool_output]) is None