Bug Description
Summary
The background session expiry watcher in gateway/run.py cleans up expired sessions but does not fire the on_session_finalize hook, despite the documentation stating this hook should fire when "CLI/gateway tears down an active session" hooks.md:613-634 .
Detailed Analysis
Expected Behavior
The on_session_finalize hook is documented to fire when:
CLI exits with an active agent
Gateway tears down an active session (including GC/cleanup scenarios)
User runs /new or /reset commands
Actual Behavior in Idle Timeout
When a session expires due to idle time, the _session_expiry_watcher function in gateway/run.py run.py:2247-2394 performs these actions:
Flushes memories via _async_flush_memories
Cleans up agent resources via _cleanup_agent_resources
Evicts the agent from cache via _evict_cached_agent
Marks the session as flushed
However, it does not call the on_session_finalize hook.
Evidence of the Bug
The expiry watcher code has no invocation of hermes_cli.plugins.invoke_hook for on_session_finalize
Tests in tests/gateway/test_session_boundary_hooks.py verify the hook fires for /new commands and shutdown test_session_boundary_hooks.py:76-156 , but there are no tests for idle timeout scenarios
The CLI implementation properly fires the hook via _notify_session_boundary cli.py:4505-4533
Root Cause
The session expiry watcher was designed to proactively flush memories before the next user message arrives, but the hook firing mechanism was overlooked. This is inconsistent with the documented behavior and with how other session teardown scenarios handle the hook.
Steps to Reproduce
Write plugin with on_session_finalize
LOG_FILE=/tmp/plugin.log
def on_session_finalize(session_id=None, **kwargs):
entry = {
"timestamp": datetime.now().isoformat(),
"session_id": session_id,
"kwargs": kwargs,
}
with open(LOG_FILE, "a") as f:
f.write(json.dumps(entry) + "\n")
f.flush()
def register(ctx):
ctx.register_hook("on_session_finalize", on_session_finalize)
with open(LOG_FILE, "a") as f:
f.write(json.dumps({"register": "on_session_finalize registered"}) + "\n")
f.flush()
Open/close cli session -> notice new entry in plugin.log
Start gateway session
Wait for idle timeout ->notice no new log entry
Expected Behavior
Should have fired
Actual Behavior
didn't
Affected Component
Other
Messaging Platform (if gateway-related)
No response
Debug Report
no logs as nothing happened
Operating System
debian 13
Python Version
No response
Hermes Version
No response
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
No response
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?
Bug Description
Summary
The background session expiry watcher in gateway/run.py cleans up expired sessions but does not fire the on_session_finalize hook, despite the documentation stating this hook should fire when "CLI/gateway tears down an active session" hooks.md:613-634 .
Detailed Analysis
Expected Behavior
The on_session_finalize hook is documented to fire when:
CLI exits with an active agent
Gateway tears down an active session (including GC/cleanup scenarios)
User runs /new or /reset commands
Actual Behavior in Idle Timeout
When a session expires due to idle time, the _session_expiry_watcher function in gateway/run.py run.py:2247-2394 performs these actions:
Flushes memories via _async_flush_memories
Cleans up agent resources via _cleanup_agent_resources
Evicts the agent from cache via _evict_cached_agent
Marks the session as flushed
However, it does not call the on_session_finalize hook.
Evidence of the Bug
The expiry watcher code has no invocation of hermes_cli.plugins.invoke_hook for on_session_finalize
Tests in tests/gateway/test_session_boundary_hooks.py verify the hook fires for /new commands and shutdown test_session_boundary_hooks.py:76-156 , but there are no tests for idle timeout scenarios
The CLI implementation properly fires the hook via _notify_session_boundary cli.py:4505-4533
Root Cause
The session expiry watcher was designed to proactively flush memories before the next user message arrives, but the hook firing mechanism was overlooked. This is inconsistent with the documented behavior and with how other session teardown scenarios handle the hook.
Steps to Reproduce
Write plugin with on_session_finalize
Open/close cli session -> notice new entry in plugin.log
Start gateway session
Wait for idle timeout ->notice no new log entry
Expected Behavior
Should have fired
Actual Behavior
didn't
Affected Component
Other
Messaging Platform (if gateway-related)
No response
Debug Report
Operating System
debian 13
Python Version
No response
Hermes Version
No response
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
No response
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?