Skip to content

fix(kanban): route notifications via owning profile + wake creator agent (salvage #54872) - #56531

Merged
kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:salvage-kanban-profile-54872
Jul 1, 2026
Merged

fix(kanban): route notifications via owning profile + wake creator agent (salvage #54872)#56531
kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:salvage-kanban-profile-54872

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Summary

In a multiplex_profiles gateway, kanban task notifications now deliver via the owning profile's bot adapter (not always the default profile's), and terminal task events wake the creating agent with a synthetic internal message so it can act on the result.

Salvage of #54872 by @zmlgit onto current main (the original branch was ~600 commits behind). Contributor authorship preserved via rebase.

Root cause

_maybe_auto_subscribe stamped notifier_profile from os.environ["HERMES_PROFILE"], which is unset in the gateway main process — multiplex profiles use ContextVars, not env vars. The notifier then filtered out cross-profile subscriptions and always delivered via the default adapter, which may not be in the task's chat ([230002] Bot can NOT be out of the chat).

Changes

  • session_context.py: add HERMES_SESSION_PROFILE ContextVar (profile= kwarg on set_session_vars, backward-compatible default "").
  • run.py: stamp source.profile at dispatch time.
  • kanban_tools.py: _maybe_auto_subscribe reads profile from the ContextVar first, falls back to os.environ (kanban workers).
  • kanban_watchers.py: prefer _profile_adapters[notifier_profile] for delivery; relax the profile skip-filter to only skip when no adapter exists for the owning profile; extend TERMINAL_KINDS; wake the creator agent on completed/gave_up/crashed/timed_out/blocked via a MessageEvent(internal=True) dispatched through the normal handle_message() pipeline.
  • locales/*.yaml: i18n the wake messages (addresses reviewer feedback on the original PR).

Validation

Check Result
Cherry-pick onto current main clean, no conflicts
test_session_context_inheritance.py + test_local_env_session_leak.py + test_kanban_tools.py 115 passed
ruff (changed files) clean
set_session_vars callers backward-compatible (new kwarg defaults "")
Message alternation preserved (internal MessageEvent → handle_message, matches existing precedent)
Prompt cache unaffected (ContextVars not in system prompt; wakeup starts a fresh turn)

Credit: @zmlgit (张满良). Reviewer feedback on hardcoded Chinese text was addressed by the contributor's own follow-up commit (i18n).

Closes #54872

@alt-glitch alt-glitch added type/bug Something isn't working comp/cron Cron scheduler and job management comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have labels Jul 1, 2026
张满良 and others added 3 commits July 2, 2026 00:00
…gent

Three connected changes that fix kanban notifications in multiplex_profile
gateways and enable event-driven agent collaboration:

1. Session profile propagation
   - Add HERMES_SESSION_PROFILE ContextVar (session_context.py)
   - Gateway stamps source.profile at dispatch time (run.py)
   - _maybe_auto_subscribe reads profile from ContextVar instead of
     os.environ which is unset in the gateway main process (kanban_tools.py)

2. Notifier profile-aware routing (kanban_watchers.py)
   - Adapter selection: prefer _profile_adapters[sub.notifier_profile]
     so each profile's bot delivers its own task notifications
   - Relax profile skip-filter: process cross-profile subscriptions when
     the gateway has an adapter for the owning profile
   - Extend TERMINAL_KINDS with status/archived/unblocked

3. Creator agent wakeup on terminal events (kanban_watchers.py)
   - After delivering completed/blocked/gave_up/crashed/timed_out
     notifications, inject a synthetic MessageEvent into the creator's
     session via adapter.handle_message to trigger their agent loop
   - SessionSource built from subscription metadata — no session_store
     lookup needed
…arch#54872

Addresses @tonydwb's review on PR NousResearch#54872 (12:05 UTC, 2026-06-29):

  > the hardcoded Chinese text in the wake messages (lines 118-128 of
  > the diff) should be replaced with English or internationalized.
  > The rest of the codebase uses English for user-facing messages,
  > and hardcoded Chinese will confuse non-Chinese users. Consider
  > using a constants dict or the existing i18n infrastructure.

Used the existing i18n infrastructure (agent/i18n.py::t()) — the same
surface gateway/run.py and slash_commands.py already use for static
user-facing strings.

## Changes

- gateway/kanban_watchers.py: import `t` from agent.i18n; replace the
  hardcoded Chinese strings in the synthetic wake-up message with
  t("gateway.kanban.wake.*") lookups. Behavior unchanged for zh users
  (zh catalog preserves the original Chinese phrasing).

- locales/en.yaml: new `gateway.kanban.wake.*` baseline keys (English):
  completed / gave_up / crashed / timed_out / blocked / status_default
  / status_joiner / message (with {task_id} {status} {title}
  {assignee} {board} placeholders).

- locales/zh.yaml: Chinese translation of the new keys, preserving the
  exact wording the original code used (so existing zh users see no
  visible change).

- locales/{zh-hant,ja,de,es,fr,tr,uk,af,ko,it,ga,pt,ru,hu}.yaml: added
  the same key set with English fallback values. The i18n invariant
  test (tests/agent/test_i18n.py::test_catalog_keys_match_english)
  requires every catalog to carry the same key set as en.yaml; native
  translations can land incrementally without breaking users (the
  loader falls back to en.yaml per-key when a translation is missing,
  but the key must still exist).

## Verification

- scripts/run_tests.sh tests/agent/test_i18n.py
  tests/gateway/test_kanban_watchers_mixin.py
  tests/gateway/test_kanban_notifier.py
  tests/gateway/test_kanban_notifier_watcher_dispatch_gate.py
  → 60 passed, 0 failed (i18n catalog parity + placeholders parity +
  existing kanban notifier behavior).

- Manual: with HERMES_LANGUAGE=en, t("gateway.kanban.wake.completed")
  returns "completed"; with HERMES_LANGUAGE=zh, returns "已完成";
  with HERMES_LANGUAGE=ja (translation pending), falls back to
  "completed" per-key.
…w findings

Follow-up review fixes on the salvage of NousResearch#54872 (原作者 张满良/@zmlgit):

1. [HIGH] Adapter selection now goes through the shared
   _authorization_adapter chokepoint (gateway/authz_mixin.py) instead of a
   local inline lookup that fell back to the DEFAULT profile's same-platform
   adapter when the owning profile had a registry entry but no adapter for
   that platform. That fallback re-introduced the exact cross-profile
   mis-delivery ([230002] Bot can NOT be out of the chat) this change exists
   to fix. Adds a mutation-verified guard test
   (test_notifier_owning_profile_adapter_no_default_fallback).

2. [HIGH→documented] The creator-wake SessionSource cannot faithfully
   reconstruct a DM/thread creator's session key because chat_type is neither
   persisted on the subscription nor carried on the session-context bridge.
   Documented the limitation inline; behavior degrades to a fresh group
   session (never an exception). The end-to-end fix (stamp + persist
   chat_type) is a scoped follow-up, not bundled into this salvage.

3. [MED] Documented that archived/unblocked are intentionally claimed (cursor
   hygiene) but silent, and excluded from wake kinds.

4. [MED] Wake-injection failure now logs at WARNING with exc_info=True (the
   cursor has already advanced, so a broken wake must not be a silent no-op).
@kshitijk4poor
kshitijk4poor force-pushed the salvage-kanban-profile-54872 branch from d380a7a to 53388e7 Compare July 1, 2026 18:30
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) July 1, 2026 18:33
@kshitijk4poor
kshitijk4poor merged commit b225b30 into NousResearch:main Jul 1, 2026
29 checks passed
ianks added a commit to ianks/hermes-agent that referenced this pull request Jul 15, 2026
Wake-ups for kanban notifications and background delegation completions were
injected via handle_message() using a build_session_key()-derived key, which
can never match the raw X-Hermes-Session-Id key that api_server sessions run
under — so the wake landed in a session nobody was reading. On top of that,
ApiServerAdapter.send() reports failure without raising, and that was treated
as a successful delivery, so the notify cursor advanced past events that were
permanently lost; and background delegation was forced synchronous on
api_server since there was no way to wake the session afterward.

Fix: route wake-ups for non-push adapters through a self-post to
/v1/chat/completions with the original session id, treat non-raising send
failures as failures (rewind instead of advancing the cursor), and re-enable
background delegation whenever a session id is available to wake.

The origin session id is captured from the request-scoped api_server chat_id
binding rather than HERMES_SESSION_ID: constructing a child agent calls
set_current_session_id() with the subagent's internal id, clobbering that
variable right before dispatch would read it and misrouting the wake into
the subagent's own session.

Related: NousResearch#56580, NousResearch#64609, NousResearch#53027, NousResearch#63169, NousResearch#56531, NousResearch#50319, NousResearch#64113
teknium1 pushed a commit that referenced this pull request Jul 23, 2026
Wake-ups for kanban notifications and background delegation completions were
injected via handle_message() using a build_session_key()-derived key, which
can never match the raw X-Hermes-Session-Id key that api_server sessions run
under — so the wake landed in a session nobody was reading. On top of that,
ApiServerAdapter.send() reports failure without raising, and that was treated
as a successful delivery, so the notify cursor advanced past events that were
permanently lost; and background delegation was forced synchronous on
api_server since there was no way to wake the session afterward.

Fix: route wake-ups for non-push adapters through a self-post to
/v1/chat/completions with the original session id, treat non-raising send
failures as failures (rewind instead of advancing the cursor), and re-enable
background delegation whenever a session id is available to wake.

The origin session id is captured from the request-scoped api_server chat_id
binding rather than HERMES_SESSION_ID: constructing a child agent calls
set_current_session_id() with the subagent's internal id, clobbering that
variable right before dispatch would read it and misrouting the wake into
the subagent's own session.

Related: #56580, #64609, #53027, #63169, #56531, #50319, #64113
@kshitijk4poor
kshitijk4poor deleted the salvage-kanban-profile-54872 branch August 5, 2026 07:09
randlee pushed a commit to randlee/hermes-agent that referenced this pull request Aug 11, 2026
Wake-ups for kanban notifications and background delegation completions were
injected via handle_message() using a build_session_key()-derived key, which
can never match the raw X-Hermes-Session-Id key that api_server sessions run
under — so the wake landed in a session nobody was reading. On top of that,
ApiServerAdapter.send() reports failure without raising, and that was treated
as a successful delivery, so the notify cursor advanced past events that were
permanently lost; and background delegation was forced synchronous on
api_server since there was no way to wake the session afterward.

Fix: route wake-ups for non-push adapters through a self-post to
/v1/chat/completions with the original session id, treat non-raising send
failures as failures (rewind instead of advancing the cursor), and re-enable
background delegation whenever a session id is available to wake.

The origin session id is captured from the request-scoped api_server chat_id
binding rather than HERMES_SESSION_ID: constructing a child agent calls
set_current_session_id() with the subagent's internal id, clobbering that
variable right before dispatch would read it and misrouting the wake into
the subagent's own session.

Related: NousResearch#56580, NousResearch#64609, NousResearch#53027, NousResearch#63169, NousResearch#56531, NousResearch#50319, NousResearch#64113
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants