hermes - 💡(How to fix) Fix feat(gateway): plugin hook for /stop interrupt — agent_loop_stopped

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…

Fix Action

Fix / Workaround

  • /stop — see gateway/run.py::_interrupt_and_clear_session (called from _handle_stop_command)
  • The /new fast-path inside _dispatch_message (running-agent guard at gateway/run.py:6122) — interrupts the agent before _handle_reset_command runs

Dispatch wrapped in try/except so a misbehaving plugin can't block the interrupt. Symmetric with how on_session_finalize is fired today.

PR: #27208 PR adds the dispatch, registers agent_loop_stopped in VALID_HOOKS with docstring comments, and includes a 3-case pytest covering hook presence, dispatch-on-interrupt, and fault tolerance when a handler raises.

Code Example

return await asyncio.wait_for(future, timeout=TOOL_CALL_TIMEOUT)
RAW_BUFFERClick to expand / collapse

Problem

Plugins that hold per-turn external resources have no way to know when the user interrupts the run with /stop. The existing on_session_finalize hook covers /new (slow path) but is not fired by:

  • /stop — see gateway/run.py::_interrupt_and_clear_session (called from _handle_stop_command)
  • The /new fast-path inside _dispatch_message (running-agent guard at gateway/run.py:6122) — interrupts the agent before _handle_reset_command runs

For these paths the plugin's outbound work just sits there until it times out, while the agent has already moved on.

Concrete use case

hermes-livekit v0.3.0 lets clients connected over a LiveKit data channel register tools that the agent can invoke (notifications, browser actions, robotic-arm controls, etc.). When the LLM picks one of those tools, the plugin's proxy coroutine awaits a result future with a 30s timeout:

return await asyncio.wait_for(future, timeout=TOOL_CALL_TIMEOUT)

If the user runs /stop mid-call, the agent loop's interrupt flag flips but the proxy keeps waiting — the client stays in "we owe a result" mode for the full timeout window, and the agent:tool-call-cancelled notification we'd want to publish to the client never goes out.

The same shape applies to any plugin that proxies a tool call to an external service (MCP-style outbound RPC, remote-fn-call gateways, etc.).

Proposed solution

Add an agent_loop_stopped plugin hook fired from _interrupt_and_clear_session right after running_agent.interrupt(). Kwargs:

  • session_key (str) — which session was interrupted
  • platform (str) — source.platform.value
  • reason (str) — interrupt_reason (e.g. \"user_stop\", \"reset\")
  • invalidation_reason (str) — finer-grained tag (\"stop_command\", \"new_command\", \"stop_command_pending\", \"stop_command_handler\")

Dispatch wrapped in try/except so a misbehaving plugin can't block the interrupt. Symmetric with how on_session_finalize is fired today.

Why a new hook, not a reused one

  • on_session_finalize is per-session; fires only when the session is finalized. /stop doesn't finalize the session, and the /new fast-path interrupts before finalize runs.
  • subagent_stop fires per-delegation, not per-turn.
  • The async gateway hooks (session:end, session:reset) cover /new only, and are file-based hooks, not plugin-registered ones.

No existing hook gives plugins a "the current turn just got abandoned, drop your per-turn external work" signal.

PR

PR: #27208 PR adds the dispatch, registers agent_loop_stopped in VALID_HOOKS with docstring comments, and includes a 3-case pytest covering hook presence, dispatch-on-interrupt, and fault tolerance when a handler raises.

Tested on

  • macOS 25.4.1 (Darwin)
  • Python 3.11
  • Hermes v0.14.0 / commit 3b39096904 (current main as of 2026-05-16)

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