hermes - 💡(How to fix) Fix Bug: Session Hygiene compression overwrites original messages when _session_db is None [3 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When Gateway Session Hygiene triggers compression via _compress_context, the original session's messages are permanently deleted from state.db instead of being preserved in a new session. This happens because the session rotation logic in compress_context() is gated on agent._session_db being truthy, but the temporary AIAgent created by the hygiene code often has _session_db = None.

Root Cause

Two code paths interact incorrectly:

Fix Action

Fixed

Code Example

if agent._session_db:     # ← If None, entire session rotation is SKIPPED
    agent._session_db.end_session(agent.session_id, "compression")
    old_session_id = agent.session_id
    agent.session_id = f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:6]}"
    agent._session_db.create_session(...)

---

_hyg_new_sid = _hyg_agent.session_id
if _hyg_new_sid != session_entry.session_id:   # ← False when rotation skipped
    session_entry.session_id = _hyg_new_sid
    self.session_store._save()

# This executes UNCONDITIONALLY:
self.session_store.rewrite_transcript(
    session_entry.session_id, _compressed   # ← Overwrites original session!
)

---

# Lines 9180-9183:
# _compress_context ends the old session and creates
# a new session_id.  Write compressed messages into
# the NEW session so the old transcript stays intact
# and searchable via session_search.

---

_hyg_new_sid = _hyg_agent.session_id
if _hyg_new_sid != session_entry.session_id:
    session_entry.session_id = _hyg_new_sid
    self.session_store._save()
    self._sync_telegram_topic_binding(source, session_entry, reason="hygiene-compression")
    # Only write compressed messages to the NEW session
    self.session_store.rewrite_transcript(session_entry.session_id, _compressed)
else:
    logger.warning(
        "Hygiene compression: session rotation did not occur "
        "(session_db unavailable?) — skipping transcript rewrite "
        "to preserve original messages"
    )
RAW_BUFFERClick to expand / collapse

Bug: Session Hygiene compression overwrites original messages when _session_db is None

Description

When Gateway Session Hygiene triggers compression via _compress_context, the original session's messages are permanently deleted from state.db instead of being preserved in a new session. This happens because the session rotation logic in compress_context() is gated on agent._session_db being truthy, but the temporary AIAgent created by the hygiene code often has _session_db = None.

Root Cause

Two code paths interact incorrectly:

1. agent/conversation_compression.py line 501

if agent._session_db:     # ← If None, entire session rotation is SKIPPED
    agent._session_db.end_session(agent.session_id, "compression")
    old_session_id = agent.session_id
    agent.session_id = f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:6]}"
    agent._session_db.create_session(...)

When _session_db is None, no new session is created, no old session is ended, and agent.session_id remains unchanged.

2. gateway/run.py lines 9184-9195

_hyg_new_sid = _hyg_agent.session_id
if _hyg_new_sid != session_entry.session_id:   # ← False when rotation skipped
    session_entry.session_id = _hyg_new_sid
    self.session_store._save()

# This executes UNCONDITIONALLY:
self.session_store.rewrite_transcript(
    session_entry.session_id, _compressed   # ← Overwrites original session!
)

When session rotation is skipped, _hyg_new_sid == session_entry.session_id evaluates to True, so the if block is skipped. But rewrite_transcript() is called unconditionally on the original session ID, which calls replace_messages()DELETE FROM messages → inserts only the compressed messages.

The comment says the opposite of what happens

# Lines 9180-9183:
# _compress_context ends the old session and creates
# a new session_id.  Write compressed messages into
# the NEW session so the old transcript stays intact
# and searchable via session_search.

In practice, when _session_db is None, the old session's transcript is not kept intact — it's deleted and replaced with the compressed version.

Steps to Reproduce

  1. Use Hermes with a 1M+ token context model (e.g., mimo-v2.5-pro)
  2. Set hygiene_hard_message_limit: 400 (default)
  3. Have a long conversation until message count reaches 400
  4. Gateway Session Hygiene triggers compression
  5. The temporary AIAgent created at gateway/run.py:9159 has _session_db = None
  6. compress_context() runs but skips session rotation (line 501)
  7. rewrite_transcript() overwrites the original session with only ~7 compressed messages
  8. Original messages are permanently deleted from state.db

Expected Behavior

When _session_db is None and session rotation cannot happen, rewrite_transcript() should not overwrite the original session. The compressed messages should either:

  1. Be written to a new session (even without full _session_db support), or
  2. Be kept in memory only (for the current API call) without persisting to the database, or
  3. The compression should be skipped entirely with a warning

Actual Behavior

The original session's messages (potentially hundreds) are permanently deleted from state.db and replaced with ~7 compressed messages. This data loss is irreversible.

Evidence

Observed on 2026-06-05 with mimo-v2.5-pro (1M context):

  • Before compression: session had 271 messages (backup verified)
  • Hygiene triggered at 400 total messages (~189K tokens, only 18% of context)
  • After compression: session had 7 messages
  • 264 messages permanently lost from state.db

Comparison with successful compressions (June 2 and 3) where _session_db was available:

  • Old session was properly end_session()-ed with reason="compression"
  • New session was created with parent_session_id linking to old
  • Old messages preserved, new session got compressed content

Suggested Fix

In gateway/run.py, guard rewrite_transcript on successful session rotation:

_hyg_new_sid = _hyg_agent.session_id
if _hyg_new_sid != session_entry.session_id:
    session_entry.session_id = _hyg_new_sid
    self.session_store._save()
    self._sync_telegram_topic_binding(source, session_entry, reason="hygiene-compression")
    # Only write compressed messages to the NEW session
    self.session_store.rewrite_transcript(session_entry.session_id, _compressed)
else:
    logger.warning(
        "Hygiene compression: session rotation did not occur "
        "(session_db unavailable?) — skipping transcript rewrite "
        "to preserve original messages"
    )

Environment

  • Hermes Agent version: 0.15.1 (commit 64202200a)
  • Model: xiaomi/mimo-v2.5-pro (1,048,576 token context)
  • Config: compression.hygiene_hard_message_limit: 400
  • Platform: Gateway (WeChat/Weixin)
  • OS: Linux 6.8.0-101-generic

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING