# NousResearch/hermes-agent · Flush messages before compression/session rotation

## Open now

- Nothing open on GitHub.

Multiple issues and fixes addressing data loss when messages or memories are not flushed before compression session rotation or compaction. The work spans several related flush-before-rotate scenarios across agent, compression, and memory subsystems.

## TL;DR

- 9 member(s): 0 open issue(s), 3 closed issue(s), 0 open PR(s), 1 merged PR(s), 5 PR(s) closed without merging.
- A fix was merged to main in PR #48584. Verify it addresses your case.
- **Recommended:** read PR #48584 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-48584`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.19`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #46567: Mid-turn compression loses messages: flush before session rotation
- #47202: Context compression silently loses unflushed messages (end_session without flush)
- #64315: Compaction resets Hindsight turn buffer without flushing pending memories

## Prior & competing PRs

- **#46617: fix(compression): flush messages before session rotation (#46567)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `partial_fix`; implication `needs_llm`; refs `#46567`, `#46587`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#47204: fix(compression): flush messages before session rotation to prevent data loss**. closed without merge · fix tier `suggested`
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#47215`, `#47215`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#47215: fix(agent): flush un-persisted messages before session rotation (#47202)**. closed without merge · fix tier `suggested`
  - maintainer direction: category `merged_via`; implication `superseded`; refs `81ff916e57`, `#47202`, `#46567`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#48584: fix(compression): preserve messages across session rotation (flush-before-rotate + /compress rewrite guard)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `agent/conversation_compression.py`, `cli.py`, `gateway/slash_commands.py`
  - fix hunks:
  - `agent/conversation_compression.py`

    ```diff
    @@ -512,6 +512,16 @@ def _release_lock() -> None:
                 old_title = agent._session_db.get_session_title(agent.session_id)
                 # Trigger memory extraction on the old session before it rotates.
                 agent.commit_memory_session(messages)
    +            # Flush any un-persisted messages from the current turn to the
    +            # old session *before* rotating.  compress_context() can be
    +            # called mid-turn (auto-compress when context exceeds threshold)
    +            # at a point when _flush_messages_to_session_db() has not yet
    +            # run.  Without this, messages generated during the current turn
    +            # are silently lost on session rotation (#47202).
    +            try:
    +                agent._flush_messages_to_session_db(messages)
    +            except Exception:
    +                pass  # best-effort — don't block compression on a flush error
                 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]}"
    ```
  - `cli.py`

    ```diff
    @@ -5957,6 +5957,18 @@ def new_session(self, silent=False, title=None):
     
             old_session_id = self.session_id
             if self._session_db and old_session_id:
    +            # Flush any un-persisted messages from the current turn to the
    +            # old session *before* rotating.  /new can be called mid-turn
    +            # when _flush_messages_to_session_db() has not yet run — without
    +            # this, messages generated during the current turn are silently
    +            # lost on session rotation (#47202).
    +            if self.agent:
    +                try:
    +                    self.agent._flush_messages_to_session_db(
    +                        self.conversation_history
    +                    )
    +                except Exception:
    +                    pass  # best-effort
                 try:
                     self._session_db.end_session(old_session_id, "new_session")
                 except Exception:
    ```
  - `gateway/slash_commands.py`

    ```diff
    @@ -2588,14 +2588,29 @@ async def _handle_compress_command(self, event: MessageEvent) -> str:
                     # session_id for the continuation.  Write the compressed messages
                     # into the NEW session so the original history stays searchable.
                     new_session_id = tmp_agent.session_id
    -                if new_session_id != session_entry.session_id:
    +                rotated = new_session_id != session_entry.session_id
    +                if rotated:
                         session_entry.session_id = new_session_id
                         self.session_store._save()
                         self._sync_telegram_topic_binding(
                             source, session_entry, reason="compress-command",
                         )
     
    -                self.session_store.rewrite_transcript(new_session_id, compressed)
    +                # Only rewrite the transcript when rotation actually produced a
    +                # NEW session id. If _compress_context could not rotate (e.g.
    +                # _session_db unavailable, or the DB split raised), session_id
    +                # is unchanged and rewrite_transcript() would DELETE the
    +                # original messages and replace them with only the compressed
    +                # summary — permanent data loss (#44794, #39704). In that case
    +                # leave the original transcript intact.
    +                if rotated:
    +                    self.session_store.rewrite_transcript(new_session_id, compressed)
    +                else:
    +                    logger.warning(
    +                        "Manual /compress: session rotation did not occur "
    +                        "(session_id unchanged) — preserving original transcript "
    +                        "instead of overwriting it (#44794)."
    +                    )
                     # Reset stored token count — transcript changed, old value is stale
                     self.session_store.update_session(
                         session_entry.session_key, last_prompt_tokens=0
    ```
- **#46587: fix(agent): flush messages before compression session rotation (#46567)**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#46567`, `#47215`, `#47202`, `#44794`, `#39704`
- **#64400: fix(agent): flush pending memories before compaction resets Hindsight buffer (#64315)**. closed without merge
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `46e87b14f`

## Fix status

- Merged to main in PR #48584 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-18`.
- Subsystem: `agent`
