Skip to content

Context compression silently loses unflushed messages (end_session without flush) #47202

Description

@Skywind5487

Summary

compress_context() in agent/conversation_compression.py rotates the session_id (end_sessioncreate_session with parent_session_id) without calling _flush_messages_to_session_db() first. Messages generated during the current turn that haven't yet been persisted to state.db are permanently lost.

The same bug exists in cli.py:new_session() (the /new command, line 6442).

Root Cause

_persist_session() (which calls _flush_messages_to_session_db()) only runs at turn end — in conversation_loop.py exit paths. But auto-compression can trigger mid-turn when context exceeds the threshold. At that point:

  1. compress_context() is called with the in-memory messages list
  2. The compressor summarizes old messages → produces a shorter compressed list
  3. end_session() is called on the old session_id — messages never written to DB
  4. A new session_id is created with parent_session_id=old_session_id
  5. _last_flushed_db_idx is reset to 0 for the new session

The result: the old session has message_count from its last clean turn, but ALL messages from the turn that triggered compression are gone.

Impact

Severity: High — permanent data loss, completely silent (no error, no warning).

  • Any conversation turn that triggers auto-compression loses all messages from that turn
  • session_search returns incomplete results for compressed conversations
  • /resume produces a gap in conversation history
  • No offline backup or recovery path for the lost messages
  • Particularly painful for long tool-heavy turns (vision analysis, code execution, multi-step workflows) where 50+ messages can be generated in a single turn

Affected Code

Primary: agent/conversation_compression.pycompress_context()

Around lines 498–533, the agent._session_db block calls end_session() without first calling _flush_messages_to_session_db().

Call sites that trigger this (all in agent/conversation_loop.py):

  • Line 653 — preflight compression
  • Line 2660 — auto-compression during API loop
  • Line 2834 — auto-compression during tool execution loop
  • Line 2990 — auto-compression during tool execution loop
  • Line 3913 — auto-compression during API retry loop

Same bug: cli.py:new_session() (line 6442)

The /new command calls end_session() without _flush_messages_to_session_db() — typing /new mid-turn also loses the current turn's unflushed messages.

Real-World Example

Session 20260601_061248_d68186d8 ("手寫積分筆記數位化"):

  • Started with 70+ turns, 190+ tool turns
  • Heavy work: vision analysis, code execution, P1 iteration (3 rounds), P2 step 1, skill creation
  • At 08:44: max_iterations_reached(16/16) — context compaction triggered
  • Result: DB only has 26 messages (bootstrap + last ~2 turns). All P1/P2 content permanently lost.

Suggested Fix

In compress_context(), before agent._session_db.end_session():

# Flush in-memory messages to DB before ending the old session.
try:
    agent._flush_messages_to_session_db(messages)
except Exception as _flush_err:
    logger.warning("Failed to flush messages before compression: %s", _flush_err)

This is safe because _flush_messages_to_session_db uses _last_flushed_db_idx — it only writes genuinely new messages, never re-writes old ones. No LLM context is affected — pure local SQLite insert.

Apply the same fix to cli.py:new_session().

Discovered by

Hermes Agent (dogfooding session — investigated compaction data loss)
Reported from session: 20260601_061248_d68186d8
Related skill: session-compaction-data-loss (local)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — major feature broken, no workaroundcomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointduplicateThis issue or pull request already existstype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions