Skip to content

fix(cli): invoke on_session_end hook on Ctrl+C exit - #3930

Closed
kelsia14 wants to merge 1 commit into
NousResearch:mainfrom
kelsia14:fix-on-session-end-ctrlc
Closed

fix(cli): invoke on_session_end hook on Ctrl+C exit#3930
kelsia14 wants to merge 1 commit into
NousResearch:mainfrom
kelsia14:fix-on-session-end-ctrlc

Conversation

@kelsia14

Copy link
Copy Markdown
Contributor

PR Description

Summary

Fix: Invoke on_session_end plugin hook on Ctrl+C exit

Problem

The on_session_end plugin hook was only called when run_conversation() completed naturally, but not when the user pressed Ctrl+C to exit the CLI. This caused plugins that rely on session lifecycle events (cleanup, flushing, indexing, session verification) to miss the session termination signal on interrupt.

Specifically affected scenarios:

  • Session index verification plugins wouldn't index sessions on Ctrl+C
  • Memory flushing plugins wouldn't flush on interrupt
  • Any cleanup logic in on_session_end was skipped for abrupt exits

Root Cause

In cli.py, the finally block after KeyboardInterrupt performed several cleanup actions:

  • Flush memories
  • Shut down voice recorder
  • Close session in SQLite via _session_db.end_session()

But it did not invoke the on_session_end plugin hook, which is only called at the end of run_conversation() in run_agent.py.

Solution

Add explicit invocation of on_session_end in the CLI's KeyboardInterrupt handler (cli.py:7478-7492), with:

  • interrupted=True - signals the session was aborted by user
  • completed=False - signals the conversation did not finish naturally
  • Same parameter signature as the existing hook in run_agent.py

Changes

# cli.py - in the finally block after KeyboardInterrupt (line ~7478)
# Plugin hook: on_session_end
# Fired when CLI exits (including Ctrl+C) so plugins can cleanup.
if self.agent:
    try:
        from hermes_cli.plugins import invoke_hook as _invoke_hook
        _invoke_hook(
            "on_session_end",
            session_id=self.agent.session_id,
            completed=False,
            interrupted=True,
            model=getattr(self.agent, 'model', None),
            platform=getattr(self.agent, 'platform', None) or "cli",
        )
    except Exception as exc:
        logger.debug("on_session_end hook failed during CLI exit: %s", exc)

Testing

  1. Create a test plugin that registers for on_session_end:
# ~/.hermes/plugins/test_plugin/__init__.py
def register(ctx):
    ctx.register_hook("on_session_end", on_session_end)

def on_session_end(session_id, completed, interrupted, model, platform):
    print(f"[TEST] Session {session_id} ended: interrupted={interrupted}, completed={completed}")
    # Verify this prints when pressing Ctrl+C
  1. Start Hermes CLI with the plugin enabled
  2. Have a conversation, then press Ctrl+C to exit
  3. Before fix: Hook is NOT called
  4. After fix: Hook is called with interrupted=True, completed=False

Impact

Hook Before (Ctrl+C) After (Ctrl+C)
on_session_end ❌ Not invoked ✅ Invoked with interrupted=True
Other hooks ✅ Work normally ✅ No change
  • Backwards Compatible: Hook signature unchanged
  • No Breaking Changes: Only adds missing invocation
  • Consistent Behavior: Exit method (Ctrl+C vs normal exit) no longer affects plugin lifecycle notifications

Related

  • Similar hook invocation exists in run_agent.py:8069-8083 at the end of run_conversation()
  • This fix ensures parity between normal completion and interrupt exit paths
  • Complements the upstream fix d6b78362 (update session_log_file during compression)

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Changes are minimal and focused
  • Comments added for clarity
  • No breaking changes introduced
  • Tested locally with Ctrl+C exit

The on_session_end plugin hook was only called when run_conversation()
completed naturally, but NOT when the user pressed Ctrl+C to exit.
This meant plugins doing cleanup, flushing, or indexing would miss
the session end event on interrupt.

Now we explicitly invoke on_session_end in the finally block of the
CLI's KeyboardInterrupt handler, with interrupted=True and completed=False
to signal the abrupt exit.

Closes NousResearch#3929 (reopened as clean PR)
@kelsia14

Copy link
Copy Markdown
Contributor Author

Superseded by #4038 which includes this fix plus additional signal handling improvements (SIGHUP, SIGTERM, BrokenPipeError).

@kelsia14 kelsia14 closed this Mar 30, 2026
@kelsia14
kelsia14 deleted the fix-on-session-end-ctrlc branch April 2, 2026 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant