Skip to content

fix(compression): prevent compressed history injection on session split#27519

Open
Tranquil-Flow wants to merge 2 commits into
NousResearch:mainfrom
Tranquil-Flow:fix/20293-compression-session-split-flush
Open

fix(compression): prevent compressed history injection on session split#27519
Tranquil-Flow wants to merge 2 commits into
NousResearch:mainfrom
Tranquil-Flow:fix/20293-compression-session-split-flush

Conversation

@Tranquil-Flow

@Tranquil-Flow Tranquil-Flow commented May 17, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Prevents compressed history that was already written into a newly split session from being appended again as live conversation history.

The bug had two related paths:

  1. compress_context() created a new session and reset _last_flushed_db_idx to 0. Later _flush_messages_to_session_db() appended the entire compressed history to the new session as if it were new live output.
  2. Gateway compression paths (session hygiene and /compress) wrote compressed messages with rewrite_transcript(), but the next agent instance still started with a flush cursor of 0, causing the compressed transcript to be appended a second time.

Since transcript replay prefers the source with more messages, the duplicate SQLite transcript could win and make compressed summaries / referenced old turns appear as valid live history.

Related Issue

Fixes #20293

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • agent/conversation_compression.py — set agent._last_flushed_db_idx = len(compressed) after a compression session split.
  • SessionEntry._prewritten_msg_count — add field with serialization/deserialization so gateway compression paths can persist how many messages were already written via rewrite_transcript().
  • gateway/run.py:
    • hygiene compression records session_entry._prewritten_msg_count = len(_compressed)
    • /compress records session_entry._prewritten_msg_count = len(compressed)
    • _run_agent() accepts _prewritten_msg_count and advances the agent flush cursor before the turn runs
  • Regression coverage for the source-level fix points, session-entry serialization compatibility, and flush-cursor behavior.

How to Test

  1. python3 -m pytest -o 'addopts=' tests/compression/test_context_compaction_flush.py -q → 11 passed
  2. Fail-without-fix proof:
    git checkout refs/remotes/upstream/main -- agent/conversation_compression.py gateway/run.py gateway/session.py
    python3 -m pytest -o 'addopts=' tests/compression/test_context_compaction_flush.py -q
    # 5 failed, 6 passed
    
    git checkout HEAD -- agent/conversation_compression.py gateway/run.py gateway/session.py
    python3 -m pytest -o 'addopts=' tests/compression/test_context_compaction_flush.py -q
    # 11 passed

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15 (Darwin 24.6.0)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

$ python3 -m pytest -o 'addopts=' tests/compression/test_context_compaction_flush.py -q
11 passed

@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent loop, run_agent.py, prompt builder comp/gateway Gateway runner, session dispatch, delivery labels May 17, 2026
@Tranquil-Flow Tranquil-Flow force-pushed the fix/20293-compression-session-split-flush branch from e4e8129 to 5acd009 Compare May 25, 2026 09:12
@Tranquil-Flow Tranquil-Flow force-pushed the fix/20293-compression-session-split-flush branch from 5acd009 to f8a2551 Compare May 25, 2026 11:03
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for chasing this compression/session-split bug. I traced the current main persistence path and found one blocking correctness issue in the proposed fix.

Problems

  • In the mid-run compression path, agent/conversation_compression.py:501-551 creates the continuation session but does not write the compressed messages into it. The current loop then deliberately clears conversation_history at agent/conversation_loop.py:2536-2539 so _flush_messages_to_session_db writes the compressed transcript into the new session. Since _flush_messages_to_session_db starts at max(start_idx, _last_flushed_db_idx) (run_agent.py:1562-1568), changing the cursor to len(compressed) would skip that compressed prefix instead of persisting it.
  • The new tests in tests/compression/test_context_compaction_flush.py mostly assert source strings or simulated cursor math from the PR diff, rather than exercising the real AIAgent + session DB persistence path. This is exactly the kind of DB/replay behavior where AGENTS.md:84-87 asks for real-path coverage.

Suggested changes

  • Keep the mid-run compress_context cursor at 0 unless the compressed messages are explicitly written to the new session before the next flush.
  • Add a temp-session-DB regression that fails on current main by inspecting persisted rows after compression/rewrite, rather than checking for exact assignment text.

Automated hermes-sweeper review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder comp/gateway Gateway runner, session dispatch, delivery P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Context Compaction + Session Split: compressed summary injected as valid history into new session

3 participants