Skip to content

fix(gateway): /stop and /new bypass Level 1 active-session guard - #5765

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-b34c1567
Apr 7, 2026
Merged

fix(gateway): /stop and /new bypass Level 1 active-session guard#5765
teknium1 merged 2 commits into
mainfrom
hermes/hermes-b34c1567

Conversation

@teknium1

@teknium1 teknium1 commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes /stop and /new commands being ignored while the agent is running on all gateway platforms (Telegram, Discord, Slack, etc.).

The bug: The base adapter's Level 1 active-session guard intercepted ALL messages while an agent was running. /stop and /new were queued as pending messages. The interrupt mechanism would stop the agent, but then the command text ("/stop") leaked into the conversation as a user message — the model would see "/stop" and respond to it as if the user asked a question.

The flow before this fix:

  1. User sends /stop while agent is running
  2. Base adapter Level 1 queues it in _pending_messages, signals interrupt
  3. monitor_for_interrupt() consumes the pending message, calls agent.interrupt("/stop")
  4. Agent breaks out, returns {interrupted: True, interrupt_message: "/stop"}
  5. Gateway treats "/stop" as a new user message → starts fresh agent run with "/stop" as input
  6. Model responds to "/stop" as user text

The fix (two parts):

  1. gateway/platforms/base.py — Add /stop, /new, /reset to the command bypass set alongside /approve, /deny, /status. Consolidated the three separate bypass blocks into one clean block. These commands are now dispatched inline to the gateway runner's Level 2 handler, which already has proper handling (hard-kill for /stop, session reset for /new).

  2. gateway/run.py — Safety net in _run_agent's pending-message processing: if the pending text resolves to a known slash command via resolve_command(), discard it. This catches edge cases where command text leaks through the interrupt_message fallback.

Test plan

  • E2E verified: /stop, /new, /reset bypass Level 1 and reach Level 2
  • Regular text and unknown commands still queued normally
  • Gateway test suite: 2039 passed, same 23 pre-existing failures (unrelated — matrix, signal, approval, session_race_guard)
  • Zero new test failures introduced

Refs: #5244

teknium1 added 2 commits April 7, 2026 00:06
The base adapter's Level 1 guard intercepted ALL messages while an
agent was running, including /stop and /new. These commands were queued
as pending messages instead of being dispatched to the gateway runner's
Level 2 handler. When the agent eventually stopped (via the interrupt
mechanism), the command text leaked into the conversation as a user
message — the model would receive '/stop' as input and respond to it.

Fix: Add /stop, /new, and /reset to the bypass set in base.py alongside
/approve, /deny, and /status. Consolidate the three separate bypass
blocks into one. Commands in the bypass set are dispatched inline to the
gateway runner, where Level 2 handles them correctly (hard-kill for
/stop, session reset for /new).

Also add a safety net in _run_agent's pending-message processing: if the
pending text resolves to a known slash command, discard it instead of
passing it to the agent. This catches edge cases where command text
leaks through the interrupt_message fallback.

Refs: #5244
17 tests covering:
- /stop, /new, /reset bypass the Level 1 guard when agent is running
- /approve, /deny, /status bypass (existing behavior, now tested)
- Regular text and unknown commands still queued (not bypassed)
- File paths like '/path/to/file' not treated as commands
- Telegram @botName suffix handled correctly
- Safety net command resolution (resolve_command detects known commands)
@teknium1
teknium1 merged commit eb7c408 into main Apr 7, 2026
3 of 4 checks passed
rahimsais pushed a commit to rahimsais/hermes-agent that referenced this pull request Apr 10, 2026
The base adapter's Level 1 guard (handle_message in base.py) intercepts
ALL messages while an agent is running. When /queue arrives during active
processing, it falls through to the default interrupt path which:

1. Triggers an unwanted agent interrupt (the whole point of /queue is
   to NOT interrupt)
2. The pending text '/queue <prompt>' is then caught by the safety net
   in run.py (added in PR NousResearch#5765) which discards it silently:
   'Discarding command from pending queue — commands must not be passed
   as agent input'

The run.py Level 2 handler already correctly handles /queue — it stores
the prompt without interrupting and returns 'Queued for the next turn.'
But it is never reached because base.py intercepts first.

Fix: Add 'queue' and 'q' (its alias) to the active-session bypass set
in base.py, alongside /stop, /new, /reset, /background, /approve,
/deny, and /status. This routes /queue through to the gateway runner's
Level 2 handler which handles it correctly without interrupting.

Introduced by eb7c408 (PR NousResearch#5765) which consolidated the bypass list
and added the safety net that now discards /queue.

Refs: NousResearch#5765
Tommyeds pushed a commit to Tommyeds/hermes-agent that referenced this pull request Apr 12, 2026
…sResearch#5765)

* fix(gateway): /stop and /new bypass Level 1 active-session guard

The base adapter's Level 1 guard intercepted ALL messages while an
agent was running, including /stop and /new. These commands were queued
as pending messages instead of being dispatched to the gateway runner's
Level 2 handler. When the agent eventually stopped (via the interrupt
mechanism), the command text leaked into the conversation as a user
message — the model would receive '/stop' as input and respond to it.

Fix: Add /stop, /new, and /reset to the bypass set in base.py alongside
/approve, /deny, and /status. Consolidate the three separate bypass
blocks into one. Commands in the bypass set are dispatched inline to the
gateway runner, where Level 2 handles them correctly (hard-kill for
/stop, session reset for /new).

Also add a safety net in _run_agent's pending-message processing: if the
pending text resolves to a known slash command, discard it instead of
passing it to the agent. This catches edge cases where command text
leaks through the interrupt_message fallback.

Refs: NousResearch#5244

* test: regression tests for command bypass of active-session guard

17 tests covering:
- /stop, /new, /reset bypass the Level 1 guard when agent is running
- /approve, /deny, /status bypass (existing behavior, now tested)
- Regular text and unknown commands still queued (not bypassed)
- File paths like '/path/to/file' not treated as commands
- Telegram @botName suffix handled correctly
- Safety net command resolution (resolve_command detects known commands)
angelburgosrosado pushed a commit to angelburgosrosado/hermes-agent that referenced this pull request Apr 27, 2026
…sResearch#5765)

* fix(gateway): /stop and /new bypass Level 1 active-session guard

The base adapter's Level 1 guard intercepted ALL messages while an
agent was running, including /stop and /new. These commands were queued
as pending messages instead of being dispatched to the gateway runner's
Level 2 handler. When the agent eventually stopped (via the interrupt
mechanism), the command text leaked into the conversation as a user
message — the model would receive '/stop' as input and respond to it.

Fix: Add /stop, /new, and /reset to the bypass set in base.py alongside
/approve, /deny, and /status. Consolidate the three separate bypass
blocks into one. Commands in the bypass set are dispatched inline to the
gateway runner, where Level 2 handles them correctly (hard-kill for
/stop, session reset for /new).

Also add a safety net in _run_agent's pending-message processing: if the
pending text resolves to a known slash command, discard it instead of
passing it to the agent. This catches edge cases where command text
leaks through the interrupt_message fallback.

Refs: NousResearch#5244

* test: regression tests for command bypass of active-session guard

17 tests covering:
- /stop, /new, /reset bypass the Level 1 guard when agent is running
- /approve, /deny, /status bypass (existing behavior, now tested)
- Regular text and unknown commands still queued (not bypassed)
- File paths like '/path/to/file' not treated as commands
- Telegram @botName suffix handled correctly
- Safety net command resolution (resolve_command detects known commands)
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…sResearch#5765)

* fix(gateway): /stop and /new bypass Level 1 active-session guard

The base adapter's Level 1 guard intercepted ALL messages while an
agent was running, including /stop and /new. These commands were queued
as pending messages instead of being dispatched to the gateway runner's
Level 2 handler. When the agent eventually stopped (via the interrupt
mechanism), the command text leaked into the conversation as a user
message — the model would receive '/stop' as input and respond to it.

Fix: Add /stop, /new, and /reset to the bypass set in base.py alongside
/approve, /deny, and /status. Consolidate the three separate bypass
blocks into one. Commands in the bypass set are dispatched inline to the
gateway runner, where Level 2 handles them correctly (hard-kill for
/stop, session reset for /new).

Also add a safety net in _run_agent's pending-message processing: if the
pending text resolves to a known slash command, discard it instead of
passing it to the agent. This catches edge cases where command text
leaks through the interrupt_message fallback.

Refs: NousResearch#5244

* test: regression tests for command bypass of active-session guard

17 tests covering:
- /stop, /new, /reset bypass the Level 1 guard when agent is running
- /approve, /deny, /status bypass (existing behavior, now tested)
- Regular text and unknown commands still queued (not bypassed)
- File paths like '/path/to/file' not treated as commands
- Telegram @botName suffix handled correctly
- Safety net command resolution (resolve_command detects known commands)
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…sResearch#5765)

* fix(gateway): /stop and /new bypass Level 1 active-session guard

The base adapter's Level 1 guard intercepted ALL messages while an
agent was running, including /stop and /new. These commands were queued
as pending messages instead of being dispatched to the gateway runner's
Level 2 handler. When the agent eventually stopped (via the interrupt
mechanism), the command text leaked into the conversation as a user
message — the model would receive '/stop' as input and respond to it.

Fix: Add /stop, /new, and /reset to the bypass set in base.py alongside
/approve, /deny, and /status. Consolidate the three separate bypass
blocks into one. Commands in the bypass set are dispatched inline to the
gateway runner, where Level 2 handles them correctly (hard-kill for
/stop, session reset for /new).

Also add a safety net in _run_agent's pending-message processing: if the
pending text resolves to a known slash command, discard it instead of
passing it to the agent. This catches edge cases where command text
leaks through the interrupt_message fallback.

Refs: NousResearch#5244

* test: regression tests for command bypass of active-session guard

17 tests covering:
- /stop, /new, /reset bypass the Level 1 guard when agent is running
- /approve, /deny, /status bypass (existing behavior, now tested)
- Regular text and unknown commands still queued (not bypassed)
- File paths like '/path/to/file' not treated as commands
- Telegram @botName suffix handled correctly
- Safety net command resolution (resolve_command detects known commands)
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…sResearch#5765)

* fix(gateway): /stop and /new bypass Level 1 active-session guard

The base adapter's Level 1 guard intercepted ALL messages while an
agent was running, including /stop and /new. These commands were queued
as pending messages instead of being dispatched to the gateway runner's
Level 2 handler. When the agent eventually stopped (via the interrupt
mechanism), the command text leaked into the conversation as a user
message — the model would receive '/stop' as input and respond to it.

Fix: Add /stop, /new, and /reset to the bypass set in base.py alongside
/approve, /deny, and /status. Consolidate the three separate bypass
blocks into one. Commands in the bypass set are dispatched inline to the
gateway runner, where Level 2 handles them correctly (hard-kill for
/stop, session reset for /new).

Also add a safety net in _run_agent's pending-message processing: if the
pending text resolves to a known slash command, discard it instead of
passing it to the agent. This catches edge cases where command text
leaks through the interrupt_message fallback.

Refs: NousResearch#5244

* test: regression tests for command bypass of active-session guard

17 tests covering:
- /stop, /new, /reset bypass the Level 1 guard when agent is running
- /approve, /deny, /status bypass (existing behavior, now tested)
- Regular text and unknown commands still queued (not bypassed)
- File paths like '/path/to/file' not treated as commands
- Telegram @botName suffix handled correctly
- Safety net command resolution (resolve_command detects known commands)
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…sResearch#5765)

* fix(gateway): /stop and /new bypass Level 1 active-session guard

The base adapter's Level 1 guard intercepted ALL messages while an
agent was running, including /stop and /new. These commands were queued
as pending messages instead of being dispatched to the gateway runner's
Level 2 handler. When the agent eventually stopped (via the interrupt
mechanism), the command text leaked into the conversation as a user
message — the model would receive '/stop' as input and respond to it.

Fix: Add /stop, /new, and /reset to the bypass set in base.py alongside
/approve, /deny, and /status. Consolidate the three separate bypass
blocks into one. Commands in the bypass set are dispatched inline to the
gateway runner, where Level 2 handles them correctly (hard-kill for
/stop, session reset for /new).

Also add a safety net in _run_agent's pending-message processing: if the
pending text resolves to a known slash command, discard it instead of
passing it to the agent. This catches edge cases where command text
leaks through the interrupt_message fallback.

Refs: NousResearch#5244

* test: regression tests for command bypass of active-session guard

17 tests covering:
- /stop, /new, /reset bypass the Level 1 guard when agent is running
- /approve, /deny, /status bypass (existing behavior, now tested)
- Regular text and unknown commands still queued (not bypassed)
- File paths like '/path/to/file' not treated as commands
- Telegram @botName suffix handled correctly
- Safety net command resolution (resolve_command detects known commands)
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