fix(compression): preserve messages across session rotation (flush-before-rotate + /compress rewrite guard)#48584
Merged
Merged
Conversation
compress_context() rotates the session (end_session -> create_session) mid-turn when auto-compress triggers, but never called _flush_messages_to_session_db() first. Messages generated during the current turn that hadn't been persisted to state.db were silently lost. The same bug existed in cli.py:new_session() (/new command). Both paths now flush un-persisted messages before ending the old session.
… skipped The manual /compress handler called rewrite_transcript() unconditionally on the session id returned by _compress_context(). When rotation does not occur (e.g. _session_db unavailable, or the DB split raised), session_id is unchanged and rewrite_transcript() DELETEs the original messages and replaces them with only the compressed summary — permanent data loss (#44794, #39704). Guard the rewrite on actual rotation: only overwrite when _compress_context produced a new session id. Otherwise leave the original transcript intact and log a warning.
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
unresolved-attribute |
2 |
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [unresolved-attribute] unresolved-attribute: Unresolved attribute `_credits_session_start_micros` on type `AIAgent`
run_agent.py:2941: [unresolved-attribute] unresolved-attribute: Object of type `Self@get_credits_spent_micros` has no attribute `_credits_session_start_micros`
✅ Fixed issues (1):
| Rule | Count |
|---|---|
invalid-assignment |
1 |
First entries
tests/run_agent/test_credits_notices_toggle.py:76: [invalid-assignment] invalid-assignment: Object of type `None` is not assignable to attribute `_credits_session_start_micros` of type `int`
Unchanged: 5774 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
This was referenced Jun 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Compression and
/compressno longer lose conversation messages fromstate.dbwhen a session rotates. Two distinct data-loss paths in the same family are closed.Changes
agent/conversation_compression.py+cli.py: flush un-persisted current-turn messages to the old session beforeend_session()rotates the session id. Auto-compression (and/new) can fire mid-turn before_persist_sessionruns at turn-exit, so the turn's messages were never written to SQLite and were lost on rotation. (Salvaged from @kyssta-exe's fix(agent): flush un-persisted messages before session rotation (#47202) #47215, fixes Context compression silently loses unflushed messages (end_session without flush) #47202 and Mid-turn compression loses messages: flush before session rotation #46567.)gateway/slash_commands.py: guard the manual/compressrewrite_transcript()on actual rotation. When_compress_context()can't rotate (e.g._session_dbunavailable or the DB split raised),session_idis unchanged and the unconditionalrewrite_transcript()would DELETE the original messages and replace them with only the compressed summary — permanent data loss. Now the rewrite only runs when rotation produced a new session id; otherwise the original transcript is left intact. (Fixes /compress deletes original messages from state.db when session rotation fails #44794, Bug: Session Hygiene compression overwrites original messages when _session_db is None #39704.)Validation
/compresswith rotation skippedHERMES_HOME, realSessionDB+ real_flush_messages_to_session_db): 3 current-turn messages incl. final answer persist to the old session beforeend_session; 2 history messages correctly skipped by identity tracking.tests/gateway/test_compress_command.py,test_compression_session_id_persistence.py,test_compression_failure_session_sync.py— 9/9 pass.Issues
Fixes #47202, #46567, #44794, #39704.
Authorship: first commit is @kyssta-exe's (cherry-picked, #47215); rewrite-guard commit is ours.
Infographic