Skip to content

fix(gateway): /stop can interrupt a sibling participant's run in a per-user thread - #35959

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-d33f0ceb
May 31, 2026
Merged

fix(gateway): /stop can interrupt a sibling participant's run in a per-user thread#35959
teknium1 merged 1 commit into
mainfrom
hermes/hermes-d33f0ceb

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Authorized users can now /stop a run another user started in the same thread. Previously, in a per-user thread (thread_sessions_per_user=True) /stop only resolved to the caller's own session key, so a run started by a different participant was invisible and /stop replied "no active task to stop".

Root cause: per-user thread keys are ...:{thread_id}:{user_id}, so each participant is isolated. _handle_stop_command looked up _running_agents by the caller's own key only — no channel/thread-wide lookup existed.

Changes

  • gateway/run.py: new _sibling_thread_run_keys(source, own_key) — finds running agents (not the pending sentinel, not the caller's own key) sharing the caller's {chat_id}:{thread_id} prefix. _handle_stop_command falls back to interrupting those when the caller's own key has no run, gated on _is_user_authorized.
  • tests/gateway/test_stop_thread_sibling.py: 7 tests covering the helper + the authorized/unauthorized fallback paths.

Scope guards

  • Thread-only — non-thread channels return [] (the broader whole-channel case is intentionally untouched).
  • Prefix-collision safe — thr1 won't match thr11.
  • Authorized-only — reuses existing _is_user_authorized, no new permission surface.

Validation

Before After
Authorized user /stop on a sibling's per-user-thread run "no active task to stop" run interrupted
Unauthorized user denied denied (no-op, "no active")
Non-thread channel unchanged unchanged

Tests: tests/gateway/test_stop_thread_sibling.py 7/7; test_command_bypass_active_session + test_session_race_guard + test_session_state_cleanup 69/69 (no regression).

Infographic

stop-in-per-user-threads

…r-user thread

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-d33f0ceb vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9534 on HEAD, 9533 on base (🆕 +1)

🆕 New issues (1):

Rule Count
unresolved-import 1
First entries
tests/gateway/test_stop_thread_sibling.py:11: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`

✅ Fixed issues: none

Unchanged: 4946 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1
teknium1 merged commit 1044d9f into main May 31, 2026
22 checks passed
@teknium1
teknium1 deleted the hermes/hermes-d33f0ceb branch May 31, 2026 16:29
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery labels May 31, 2026
KKT-OPT pushed a commit to KKT-OPT/hermes-agent that referenced this pull request May 31, 2026
…r-user thread (NousResearch#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…r-user thread (#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
kossteg pushed a commit to kossteg/hermes-agent that referenced this pull request Jun 16, 2026
…r-user thread (NousResearch#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
T02200059 pushed a commit to T02200059/hermes-agent that referenced this pull request Jun 18, 2026
…r-user thread (NousResearch#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…r-user thread (NousResearch#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…r-user thread (NousResearch#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
donbowman pushed a commit to donbowman/hermes-agent that referenced this pull request Jul 13, 2026
…r-user thread (NousResearch#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…r-user thread (NousResearch#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…r-user thread (NousResearch#35959)

In a per-user thread (thread_sessions_per_user=True), each participant
gets an isolated session key (...:{thread_id}:{user_id}). A run another
user started lives under a different key, so the caller's own /stop found
nothing and replied 'no active task to stop'.

When /stop finds no run under the caller's own key, fall back to
interrupting any running agent(s) sharing the caller's thread prefix
({chat_id}:{thread_id}), gated on _is_user_authorized. Thread-only — the
fallback returns [] for non-thread channels, and a prefix-collision guard
prevents thr1 from matching thr11.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants