hermes - 💡(How to fix) Fix [bug] /compress and gateway hygiene compression destroy original transcript — tmp_agent missing session_db kwarg [1 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…

Error Message

Additional Logs / Traceback (optional)

Root Cause

Root Cause Analysis (optional)

Fix Action

Fixed

Code Example

SELECT id, source, parent_session_id, end_reason, message_count
   FROM sessions WHERE source = 'telegram' ORDER BY started_at DESC LIMIT 5;

---

Debug report available on request (contains PII in default output, withheld from public issue).

---



---

tmp_agent = AIAgent(
    **runtime_kwargs,
    model=model,
    max_iterations=4,
    quiet_mode=True,
    skip_memory=True,
    enabled_toolsets=["memory"],
    session_id=session_entry.session_id,
    # ← session_db= is missing
)

---

new_session_id = tmp_agent.session_id
if new_session_id != session_entry.session_id:
    session_entry.session_id = new_session_id
    self.session_store._save()
self.session_store.rewrite_transcript(new_session_id, compressed)

---

tmp_agent = AIAgent(
     **runtime_kwargs,
     model=model,
     max_iterations=4,
     quiet_mode=True,
     skip_memory=True,
     enabled_toolsets=["memory"],
     session_id=session_entry.session_id,
+    session_db=self._session_db,
 )
RAW_BUFFERClick to expand / collapse

Bug Description

The Gateway's _handle_compress_command (manual /compress) and the Gateway Session Hygiene auto-compress paths both create a temporary AIAgent (tmp_agent / _hyg_agent) without passing session_db= to the constructor. This causes tmp_agent._session_db = None, which makes _compress_context()'s session-rotate block (if self._session_db:) silently no-op.

The downstream session_store.rewrite_transcript(session_entry.session_id, compressed) then overwrites the original session's transcript with the compressed message list, destroying searchable history — exactly the data-loss scenario commits 1544638f4 and cd2e180ef intended to prevent.

Those two earlier commits added the if new_session_id != session_entry.session_id: swap after _compress_context(), but they assume _compress_context() actually rotated the session. When _session_db is None, it doesn't, so new_session_id == session_entry.session_id and the rewrite still hits the original session_id.

This is the same class of bug as PR #20021 (ACP adapter missing session_db) and PR #4802 (API server adapter missing session_db). The Gateway compress/hygiene paths have the same omission.

Steps to Reproduce

  1. Run gateway with Telegram or Discord bot.
  2. Have a long enough conversation (60+ messages).
  3. Send /compress in Telegram or Discord.
  4. Inspect state.db:
    SELECT id, source, parent_session_id, end_reason, message_count
    FROM sessions WHERE source = 'telegram' ORDER BY started_at DESC LIMIT 5;
  5. Observe:
    • No new session row with parent_session_id pointing to the original.
    • The original session's end_reason is NULL (not 'compression').
    • The original session's message_count reflects the compressed count (e.g., 10) rather than the original (e.g., 69) — original messages are physically gone.

Expected Behavior

Per the documented compression behavior (sessions.md "Auto-Lineage on Compression"):

  • The original session should be closed with end_reason='compression' and its messages preserved intact.
  • A new continuation session should be created with parent_session_id pointing to the original.
  • The compressed message list should be written to the new session, not the original.
  • session_search should still find the original transcript.

Actual Behavior

  • The original session's messages are overwritten with the compressed (head + summary + tail) list.
  • end_reason stays NULL, parent_session_id stays NULL.
  • No new continuation session is created.
  • The original transcript (e.g., 69 messages of conversation history) is physically destroyed and unrecoverable from state.db.
  • No context compression started: info log is emitted (the session-rotate block at run_agent.py:9048 is entirely skipped).

Affected Component

Gateway (Telegram/Discord/Slack/WhatsApp)

Messaging Platform (if gateway-related)

Telegram

Debug Report

Debug report available on request (contains PII in default output, withheld from public issue).

Operating System

macOS 14.8.5

Python Version

3.11.15

Hermes Version

0.12.0 (2026.4.30)

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

Affected Locations

gateway/run.py:_handle_compress_command (around L8780):

tmp_agent = AIAgent(
    **runtime_kwargs,
    model=model,
    max_iterations=4,
    quiet_mode=True,
    skip_memory=True,
    enabled_toolsets=["memory"],
    session_id=session_entry.session_id,
    # ← session_db= is missing
)

Same pattern in Gateway Session Hygiene path (around L5688) with _hyg_agent = AIAgent(...).

Why _session_db is None matters

run_agent.py:_compress_context() session rotate is guarded by if self._session_db: (L9048). When _session_db is None, the entire rotate block is skipped and tmp_agent.session_id is never changed.

Why existing safeguards don't help

Commits 1544638f4 and cd2e180ef added:

new_session_id = tmp_agent.session_id
if new_session_id != session_entry.session_id:
    session_entry.session_id = new_session_id
    self.session_store._save()
self.session_store.rewrite_transcript(new_session_id, compressed)

When _session_db is None, new_session_id == session_entry.session_id (no rotation happened), so rewrite_transcript overwrites the original session.

Proposed Fix (optional)

Pass session_db=self._session_db to both tmp_agent and _hyg_agent constructors:

 tmp_agent = AIAgent(
     **runtime_kwargs,
     model=model,
     max_iterations=4,
     quiet_mode=True,
     skip_memory=True,
     enabled_toolsets=["memory"],
     session_id=session_entry.session_id,
+    session_db=self._session_db,
 )

Same for _hyg_agent. With this, _compress_context()'s session-rotate block runs, the original transcript is preserved (closed with end_reason='compression'), and the new continuation session gets a fresh session_id with parent_session_id pointing to the original.

We've verified this fix works in production (Telegram /compress with 112-message session — original preserved, new session created with correct parent linkage).

Until upstream merges the fix, ContextEngine plugins can work around this by detecting parent_agent._session_db is None and instantiating SessionDB() directly to perform the rotate themselves.

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

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