Skip to content

fix(run_agent): notify context engine on commit_memory_session (#22394) - #22431

Closed
briandevans wants to merge 1 commit into
NousResearch:mainfrom
briandevans:fix/commit-memory-session-context-engine-22394
Closed

briandevans wants to merge 1 commit into
NousResearch:mainfrom
briandevans:fix/commit-memory-session-context-engine-22394

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

Summary

commit_memory_session is the session-rotation entry point used by CLI /new, gateway session expiry (Telegram/Discord/Slack), and in-process compression. It called self._memory_manager.on_session_end(...) but never self.context_compressor.on_session_end(...). As a result, plugin context engines like hermes-lcm silently lost the final turns and never finalized the rotating session in their lifecycle store.

The sibling shutdown_memory_provider already calls both. This PR restores parity by mirroring that pattern.

The bug

run_agent.py commit_memory_session (before):

def commit_memory_session(self, messages: list = None) -> None:
    if not self._memory_manager:
        return
    try:
        self._memory_manager.on_session_end(messages or [])
    except Exception:
        pass
    # context_compressor.on_session_end() is NOT called here

Compare with shutdown_memory_provider in the same file, which correctly notifies both:

def shutdown_memory_provider(self, messages: list = None) -> None:
    if self._memory_manager:
        try:
            self._memory_manager.on_session_end(messages or [])
        ...
    if hasattr(self, "context_compressor") and self.context_compressor:
        try:
            self.context_compressor.on_session_end(self.session_id or "", messages or [])
        ...

Concrete impact (per the issue reporter, hermes-lcm v0.9.2):

  • CLI /new: messages that arrived after the last compress() / preflight call are not persisted to lcm.db; the LCM session never gets lifecycle.finalize_session() and stays "active".
  • Gateway session expiry (Telegram/Discord/Slack): same data loss — _finalize_session() calls commit_memory_session().
  • In-process compression: less severe because the new on_session_start fires immediately after, but the old session_id still ends without an on_session_end notification, breaking the engine's session lifecycle pairing.

The fix

Add the missing context_compressor.on_session_end(self.session_id or "", messages or []) block after the memory-manager call, guarded the same way shutdown_memory_provider guards it (hasattr + truthiness + try/except Exception).

Test plan

  • New focused regression suite tests/run_agent/test_commit_memory_session.py (6 tests):
    • primary: with both manager and compressor configured, both receive on_session_end with the same messages
    • compressor receives (session_id, messages) tuple shape
    • messages=None is normalized to [] for both
    • session_id=None is normalized to "" for the compressor (mirrors shutdown_memory_provider)
    • exception in compressor on_session_end does not break session rotation; manager call still happens
    • no context_compressor attribute and context_compressor=None are both safe no-ops
  • Adjacent suites all green locally:
    • tests/agent/test_memory_provider.py (memory manager fan-out tests, including existing TestCommitMemorySessionRouting)
    • tests/run_agent/test_compress_focus_plugin_fallback.py, test_compression_boundary.py, test_compression_persistence.py
    • tests/cli/test_cli_new_session.py, test_cli_shutdown_memory_messages.py
    • tests/gateway/test_shutdown_memory_provider_messages.py, test_compress_command.py
    • 96 passed total, no regressions
  • Regression guard: with the production fix reverted (test branch only), the primary test fails with the expected assert comp.session_end_calls == [(\"sess-1\", msgs)][] == [...]. With the fix restored, all 6 pass.

Test command used:

uv run --with pytest --with pytest-xdist --with pytest-asyncio python3 -m pytest \
  tests/run_agent/test_commit_memory_session.py \
  tests/agent/test_memory_provider.py \
  tests/run_agent/test_compress_focus_plugin_fallback.py \
  tests/run_agent/test_compression_boundary.py \
  tests/run_agent/test_compression_persistence.py \
  tests/cli/test_cli_new_session.py \
  tests/cli/test_cli_shutdown_memory_messages.py \
  tests/gateway/test_shutdown_memory_provider_messages.py \
  tests/gateway/test_compress_command.py

Related

Fixes #22394.

Sibling code paths that may need the same fix: the if not self._memory_manager: return early-return is preserved here to keep the diff minimal and match the reporter's proposed patch. That means an agent configured with a context engine but no memory manager (an unusual but valid configuration) would still skip the compressor's session-end notification. Intentionally left out of this PR's scope — happy to widen by restructuring the guards independently (matching shutdown_memory_provider's shape) if preferred.

…esearch#22394)

`commit_memory_session` is the session-rotation entry point used by
CLI `/new`, gateway session expiry, and in-process compression. It
called `_memory_manager.on_session_end` but skipped
`context_compressor.on_session_end`, so plugin context engines like
hermes-lcm never received the end-of-session flush for the old
session_id.

Concretely, on `/new` and gateway session expiry the LCM session
stays "active" in the lifecycle store and any messages that arrived
after the last `compress()` call are not persisted to lcm.db. The
sibling `shutdown_memory_provider` already calls both — this PR
restores parity by mirroring that pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 9, 2026 09:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes session-rotation lifecycle notifications so plugin context engines (e.g., hermes-lcm) receive on_session_end(...) during commit_memory_session(), aligning behavior with shutdown_memory_provider() and preventing missed final-turn persistence/finalization during /new, gateway expiry, and compression-driven rotation.

Changes:

  • Added context_compressor.on_session_end(session_id, messages) fan-out in AIAgent.commit_memory_session().
  • Added a new regression test suite covering expected commit_memory_session() fan-out behavior and edge cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
run_agent.py Adds missing context-engine on_session_end notification during session commit/rotation.
tests/run_agent/test_commit_memory_session.py Introduces regression tests validating commit_memory_session() behavior for manager/compressor notifications and safety cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread run_agent.py
Comment on lines 5075 to 5080
if not self._memory_manager:
return
try:
self._memory_manager.on_session_end(messages or [])
except Exception:
pass
Comment on lines +29 to +35
def _agent(memory_manager=None, context_compressor=None, session_id="sess-1"):
a = AIAgent.__new__(AIAgent)
a._memory_manager = memory_manager
a.context_compressor = context_compressor
a.session_id = session_id
return a

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/plugins Plugin system and bundled plugins tool/memory Memory tool and memory providers labels May 9, 2026
@briandevans

Copy link
Copy Markdown
Contributor Author

Closing — superseded by @teknium1's #22764, which landed the same fix on main as commit e90aa7f.

The merged version is strictly better: it calls context_compressor.on_session_end even when no memory manager is configured, while this PR's early-return on not self._memory_manager skipped the context-engine notify in that branch. Same root cause, same file, same function — closing as duplicate. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/plugins Plugin system and bundled plugins P2 Medium — degraded but workaround exists tool/memory Memory tool and memory providers type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

commit_memory_session() missing context_compressor.on_session_end() call

3 participants