Skip to content

feat(messaging): add explicit active handoff primitives - #37865

Closed
verybigdog wants to merge 5 commits into
NousResearch:mainfrom
verybigdog:contrib/active-handoff-primitives
Closed

feat(messaging): add explicit active handoff primitives#37865
verybigdog wants to merge 5 commits into
NousResearch:mainfrom
verybigdog:contrib/active-handoff-primitives

Conversation

@verybigdog

@verybigdog verybigdog commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add opt-in send_message(trigger_agent=True) active handoff result plumbing while keeping passive sends as the default
  • add Kanban notify-subscribe --trigger-agent persistence and gateway notifier support
  • actively wake the originating gateway agent for gateway-created Kanban task terminal ACKs
  • preserve Kanban origin ACK edges for CLI/script-created graphs:
    • child tasks inherit parent/root terminal notification subscriptions
    • explicit Origin/return_to: Discord ... (<#channel_id>) body prose infers a notify subscription for CLI-created cards
  • document that headless Kanban workers should write board verdicts instead of directly sending Discord/Telegram final ACKs

Safety

  • default behavior remains passive unless trigger_agent=True / --trigger-agent is explicitly requested or a CLI-created task carries a narrow, machine-readable Discord Origin/return_to line
  • active wake is best-effort after a successful visible platform send; it is not an atomic send+wake transaction
  • visible platform send and active wake are reported separately via triggered_agent / trigger_error
  • Kanban subscriptions wake agents only when they carry trigger_agent=1
  • active wake failures are isolated from passive delivery so the notifier does not crash or retry the same terminal event just because wake is unavailable
  • the Origin/return_to parser intentionally recognizes only explicit Discord channel-id shapes such as <#123...> or chat_id=123...; it is not a general natural-language parser

Tests

  • python -m pytest tests/hermes_cli/test_kanban_notify.py tests/gateway/test_kanban_notifier.py tests/gateway/test_kanban_notifier_watcher_dispatch_gate.py tests/tools/test_send_message_tool.py -q → 160 passed, 2 warnings
  • git diff --check
  • python -m compileall gateway/kanban_watchers.py gateway/slash_commands.py hermes_cli/kanban.py hermes_cli/kanban_db.py tools/send_message_tool.py tests/hermes_cli/test_kanban_notify.py -q

Refresh notes

  • rebased onto current origin/main (4829f8d2c at refresh time)
  • force-pushed with --force-with-lease from old head f3c9a6453 to new head 9829f4ee0

Local operator policy and AgentFlow supervisor hooks remain intentionally out of scope.

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery labels Jun 3, 2026
@verybigdog
verybigdog force-pushed the contrib/active-handoff-primitives branch from 0cbd4cc to 6a48465 Compare June 3, 2026 08:21
@verybigdog
verybigdog marked this pull request as ready for review June 3, 2026 08:25
@verybigdog

Copy link
Copy Markdown
Contributor Author

Marked ready for review.

This PR intentionally keeps the primitive small/default-off:

  • normal sends and Kanban notifications remain passive by default
  • active wake is explicit (trigger_agent=True / notify-subscribe --trigger-agent)
  • visible platform delivery and active wake are reported separately (triggered_agent / trigger_error)
  • active wake is best-effort after a successful visible send, not an atomic send+wake transaction

One implementation detail I'm happy to adjust: _trigger_gateway_agent currently lives near send_message_tool because send_message is the first direct caller. If maintainers prefer, I can move it into a gateway-owned handoff/helper module so the Kanban notifier does not import from tools.

@verybigdog

Copy link
Copy Markdown
Contributor Author

Refreshed this PR onto current origin/main and removed local-only scope.

Current branch contents are only the generic active handoff primitive:

  • explicit trigger_agent=True send_message wake path
  • visible delivery and active wake reported separately
  • Kanban notify active-wake opt-in
  • passive send/notify behavior remains default

Intentionally excluded from this upstream PR:

Verification after refresh:

  • git diff --check origin/main..HEAD
  • python3 -m py_compile gateway/run.py hermes_cli/kanban.py hermes_cli/kanban_db.py tools/send_message_tool.py
  • python3 -m pytest tests/hermes_cli/test_kanban_notify.py tests/tools/test_send_message_tool.py -q -o 'addopts=' → 148 passed

@verybigdog
verybigdog marked this pull request as draft June 8, 2026 13:10
@verybigdog
verybigdog marked this pull request as ready for review June 8, 2026 13:19
@verybigdog
verybigdog force-pushed the contrib/active-handoff-primitives branch from bd01012 to f3c9a64 Compare June 8, 2026 13:19
@verybigdog
verybigdog force-pushed the contrib/active-handoff-primitives branch from f3c9a64 to 9829f4e Compare June 11, 2026 06:38
@verybigdog

Copy link
Copy Markdown
Contributor Author

Refreshed this PR onto current origin/main and added the ACK-edge regression fix that came out of live Kanban routing.

Changes since the previous head:

  • rebased branch onto origin/main (4829f8d2c at refresh time)
  • added commit 9829f4ee0 fix(kanban): preserve origin ACK edges for CLI-created graphs
  • child tasks now inherit parent/root terminal notify subscriptions, so child BLOCK/DONE states keep the same return path as the graph root
  • CLI/script-created cards with explicit Origin/return_to: Discord ... (<#channel_id>) prose now infer a notify subscription and set trigger_agent=True
  • kept AgentFlow/local operator policy out of this PR

Verification on the refreshed branch:

python -m pytest \
  tests/hermes_cli/test_kanban_notify.py \
  tests/gateway/test_kanban_notifier.py \
  tests/gateway/test_kanban_notifier_watcher_dispatch_gate.py \
  tests/tools/test_send_message_tool.py \
  -q

160 passed, 2 warnings in 4.43s

git diff --check
python -m compileall \
  gateway/kanban_watchers.py \
  gateway/slash_commands.py \
  hermes_cli/kanban.py \
  hermes_cli/kanban_db.py \
  tools/send_message_tool.py \
  tests/hermes_cli/test_kanban_notify.py \
  -q

New head: 9829f4ee03cf30b79e37b7d87c92bf7e7618158b.

@verybigdog
verybigdog force-pushed the contrib/active-handoff-primitives branch from 9829f4e to 3e79286 Compare June 11, 2026 09:11
@verybigdog

Copy link
Copy Markdown
Contributor Author

Refreshed against latest origin/main and re-verified.

Evidence:

  • old fork head: 9829f4ee03cf30b79e37b7d87c92bf7e7618158b
  • new fork head: 3e792866f
  • base: origin/main 955fa4006
  • verification: python -m compileall -q tools/send_message_tool.py gateway/kanban_watchers.py gateway/slash_commands.py hermes_cli/kanban.py hermes_cli/kanban_db.py
  • verification: python -m pytest tests/tools/test_send_message_tool.py tests/hermes_cli/test_kanban_notify.py tests/gateway/test_kanban_notifier.py -q => 158 passed, 2 warnings
  • independent post-remediation review: GO / no blocking security or logic findings

Scope note from review hardening:

  • send_message no longer exposes or honors model-supplied trigger_agent; active wake remains private to verified Kanban notification subscriptions.
  • Origin/return_to body inference now creates passive terminal ACK subscriptions only. Active wake requires explicit control-plane intent via --ack-trigger-agent / notify-subscribe --trigger-agent or inherited verified subscriptions.
  • ContextOps/local dogfood hooks and lane policy are intentionally excluded.

@verybigdog

Copy link
Copy Markdown
Contributor Author

Refreshed again against current origin/main after upstream advanced.

Evidence:

  • old fork head: 3e792866f90b393c01c0f5f8adf6f89b4658470c
  • new fork head: e8714861d
  • current base: origin/main c6b0eb4de
  • conflict resolved in tools/send_message_tool.py by preserving upstream react/unreact schema and keeping trigger_agent out of the model-exposed send_message schema.
  • verification: python -m compileall -q tools/send_message_tool.py gateway/kanban_watchers.py gateway/slash_commands.py hermes_cli/kanban.py hermes_cli/kanban_db.py
  • verification: python -m pytest tests/tools/test_send_message_tool.py tests/hermes_cli/test_kanban_notify.py tests/gateway/test_kanban_notifier.py -q => 158 passed, 2 warnings

@verybigdog
verybigdog force-pushed the contrib/active-handoff-primitives branch from 3e79286 to e871486 Compare June 16, 2026 11:34
@nikitaBarkov

nikitaBarkov commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Hi @verybigdog 👋

  1. Is this PR still active and planned for merge? It currently has conflicts with main, so I wanted to check its status before making any changes on my side.

  2. I need essentially the same functionality, but with a few extensions on top of your work:

  • a delivery_mode enum (notify / notify+wake / wake) instead of the on/off trigger_agent, adding a pure wake mode (wake the agent without a passive message);
  • a persisted chat_type, so the woken turn resolves the operator's actual session via build_session_key instead of creating a context-less parallel session;
  • keeping the wake primitive out of the model-exposed send_message schema (internal gateway/CLI/notifier only);
  • retaining the background wake task so it can't be garbage-collected and silently dropped.
  1. Since we'd be touching many of the same files, I'd rather coordinate than open a competing PR. A couple of options, whichever you prefer:
  • I open my own branch/PR and credit you as a co-author (Co-authored-by:), effectively superseding this one; or
  • I fold my changes into your branch so it ships as a single feature, with you as the primary author.

Let me know what works best for you.

If useful, here's the diff of my version: main...nikitaBarkov:nikita.barkov/wake_notify_kanban

@verybigdog

Copy link
Copy Markdown
Contributor Author

Thanks @nikitaBarkov — yes, this is still active, and I agree it needs a refresh against current main before it can realistically move forward.

Your direction looks like the right abstraction to me. In particular:

  • delivery_mode (notify / notify+wake / wake) is cleaner than the old boolean-style trigger_agent gate;
  • persisting chat_type so the wake resolves through build_session_key fixes the context-less parallel-session issue;
  • keeping the wake primitive out of the model-exposed send_message schema is the right safety boundary;
  • retaining the background wake task is important so wake delivery cannot be silently GC-dropped.

Since both branches touch the same gateway/Kanban/CLI surface and both are stale/conflicting against current main, I think the safest path is a refreshed superseding branch from current main that folds in these semantics, with you credited as co-author for the design/diff. I’ll avoid opening a competing implementation that ignores your work.

I’ll use this as the target shape for the refresh:

  • delivery_mode enum persisted on Kanban notify subscriptions;
  • persisted chat_type for wake session-key correctness;
  • internal-only wake primitive, not model-exposed through send_message;
  • retained background wake task;
  • passive delivery and active wake reported separately;
  • fail-closed tests around notify/wake behavior and session routing.

If you strongly prefer this to land through your branch instead, say so and I can adapt, but a clean current-main branch will probably be easier to review.

@verybigdog
verybigdog force-pushed the contrib/active-handoff-primitives branch from e871486 to 1f5101e Compare July 2, 2026 11:01
@verybigdog

Copy link
Copy Markdown
Contributor Author

Refreshed this PR on current origin/main with Nikita Barkov's delivery_mode / chat_type active-wake design folded in and credited in the commit trailer.

Head: 1f5101efd8ea805e0349bccf1560302724d9fffb

What changed:

  • kanban_notify_subs now persists delivery_mode (notify, notify+wake, wake) and chat_type.
  • Plain/default notify remains passive and never wakes, including legacy rows with a session_id.
  • notify+wake and wake use an internal-only _trigger_gateway_agent helper; no trigger_agent / wake field is exposed through the model-facing send_message schema.
  • Wakes replay persisted chat_type, user_id, and notifier profile routing so build_session_key lands in the real operator session.
  • The scheduled wake task is retained in the gateway background task set.
  • Status/dashboard churn is passive only; active wake fires for terminal outcome kinds.

Verification run locally from the refreshed worktree:

  • scripts/run_tests.sh tests/hermes_cli/test_kanban_notify.py tests/tools/test_send_message_tool.py -> 176 passed, 0 failed.
  • git diff --cached --check -> clean.
  • python3 -m py_compile gateway/kanban_watchers.py gateway/slash_commands.py hermes_cli/kanban.py hermes_cli/kanban_db.py tools/send_message_tool.py tests/hermes_cli/test_kanban_notify.py tests/tools/test_send_message_tool.py -> clean.
  • Independent Claude review after fix -> PASS.

PR is no longer conflicting: mergeable=MERGEABLE (GitHub currently reports mergeStateStatus=BLOCKED, likely because branch protection/checks have not completed or are required externally).

@verybigdog

Copy link
Copy Markdown
Contributor Author

Follow-up for review BLOCK on gateway auto-subscribe delivery_mode/chat_type:

Fixed in new head dafc0e4b38aa1f660dcc4381bd24ac50f7574530.

Changes:

  • gateway/session_context.py now carries HERMES_SESSION_CHAT_TYPE through the task-local session context/env bridge.
  • gateway/run.py binds context.source.chat_type into that session context.
  • tools/kanban_tools.py::_maybe_auto_subscribe now writes gateway-created task subscriptions with the source chat_type and delivery_mode='notify+wake'.
  • TUI/desktop fallback remains passive/default (delivery_mode='notify', chat_type='dm'), and CLI/no-channel paths still do not auto-subscribe.
  • Regression assertions added in tests/tools/test_kanban_tools.py.

Verification:

  • scripts/run_tests.sh tests/tools/test_kanban_tools.py tests/hermes_cli/test_kanban_notify.py tests/tools/test_send_message_tool.py => 273 passed.
  • python3 -m py_compile gateway/session_context.py gateway/run.py tools/kanban_tools.py tests/tools/test_kanban_tools.py => passed.
  • git diff --check => passed.
  • Manual contextvar adversarial probe: Discord/group tool-created child row recorded delivery_mode='notify+wake', chat_type='group', thread/user/profile preserved.

Credit/history: this is a narrow follow-up commit on top of the existing PR branch; Nikita’s existing contribution remains intact.

@verybigdog

Copy link
Copy Markdown
Contributor Author

Final fan-in after the gateway auto-subscribe remediation:

  • Current PR head verified on GitHub: dafc0e4b38aa1f660dcc4381bd24ac50f7574530 (contrib/active-handoff-primitives -> main), PR open, mergeable=MERGEABLE, mergeStateStatus=BLOCKED, no status checks currently reported by GitHub.
  • Remediation review verdict: GO. The follow-up commit carries gateway-created kanban_create auto-subscriptions through delivery_mode='notify+wake' and persisted chat_type, while CLI/no-channel and TUI/desktop fallback paths remain passive/default.
  • Local re-verification from a clean review worktree at the same head:
    • git diff --check origin/main...HEAD -> passed.
    • python3 -m py_compile gateway/session_context.py gateway/run.py gateway/kanban_watchers.py gateway/slash_commands.py hermes_cli/kanban.py hermes_cli/kanban_db.py tools/kanban_tools.py tools/send_message_tool.py tests/tools/test_kanban_tools.py tests/hermes_cli/test_kanban_notify.py tests/tools/test_send_message_tool.py -> passed.
    • scripts/run_tests.sh tests/tools/test_kanban_tools.py tests/hermes_cli/test_kanban_notify.py tests/tools/test_send_message_tool.py -> 273 passed, 0 failed.
    • send_message schema probe -> exposed properties are only action, emoji, message, message_id, target; no model-exposed trigger_agent, wake, delivery_mode, or chat_type field.
  • Scope check: diff is limited to gateway/Kanban notify delivery-mode/session-context plumbing, tests, and Kanban docs; main workspace only has pre-existing untracked .kanban-t_4623baae/ review artifacts.

Credit/coordination: Nikita Barkov's design/diff remains credited in commit 1f5101efd8 via Co-authored-by: Nikita Barkov <nikita.barkov@users.noreply.github.com>, and this refresh intentionally treats Nikita as a co-worker/co-designer on the delivery_mode + chat_type shape rather than a competing implementation.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for carrying the delivery-mode and persisted-routing design through the notifier path. The current-main premise is real: gateway/kanban_watchers.py:517-548 still hardcodes wake sources to chat_type="group".

Problems

  • tools/send_message_tool.py:534-535 falls back to runner.adapters after _authorization_adapter() returns None. That reintroduces the cross-profile wrong-bot delivery that current main deliberately fixed in b225b30d0; _authorization_adapter fails closed for unavailable stamped profiles at gateway/authz_mixin.py:50-57.
  • The routing data is still incomplete. The PR persists user_id, but build_session_key prefers user_id_alt for group/channel isolation (gateway/session.py:936-957). A source carrying a distinct alternate identifier can still wake a parallel session.

Suggested changes

  • Preserve the profile chokepoint: return a trigger error rather than falling back to the default adapter for an unavailable non-default profile, with a regression test.
  • Persist and replay user_id_alt as well, and test equality between the original and woken build_session_key when the two identifiers differ.

Automated hermes-sweeper review.

Comment thread tools/send_message_tool.py Outdated
adapter = authz_adapter(platform, profile or None)
except Exception:
adapter = None
if adapter is None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: do not fall back to runner.adapters after _authorization_adapter(platform, profile) returns None for a stamped secondary profile. Current main deliberately fails closed here (gateway/authz_mixin.py:50-57; b225b30d0) because this fallback sends via the default profile's bot. Return a trigger error for that case and add the same no-default-fallback regression.

Comment thread tools/kanban_tools.py
@@ -1038,7 +1041,9 @@ def _maybe_auto_subscribe(conn: Any, task_id: str) -> bool:
conn, task_id=task_id,
platform=platform, chat_id=chat_id,
thread_id=thread_id, user_id=user_id,
chat_type=chat_type,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: this routing row records user_id but not user_id_alt. build_session_key() prefers user_id_alt for non-DM participant isolation (gateway/session.py:936-957), so the wake can still create a parallel session. Persist and replay the alternate identifier, with a differing-ID session-key regression.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 14, 2026
@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Jul 14, 2026
Hermes Agent and others added 3 commits July 15, 2026 07:37
Persist delivery_mode and chat_type on kanban notification subscriptions so terminal events can either send passively, wake the destination agent, or do both without changing default notify behavior.

Keep the wake primitive internal to the gateway/notifier path, retain scheduled wake tasks, and add regression coverage for passive notify, wake-only, session routing, and schema exposure.

Co-authored-by: Nikita Barkov <nikita.barkov@users.noreply.github.com>

Co-authored-by: Joosung Lee <10740155+verybigdog@users.noreply.github.com>
Fail closed when an active wake is stamped for a non-default profile whose live adapter is unavailable, instead of falling back to the default profile adapter.

Persist and replay user_id_alt alongside the existing kanban notify routing tuple so woken turns resolve through the same build_session_key as the originating event when user_id and user_id_alt differ.
@verybigdog
verybigdog force-pushed the contrib/active-handoff-primitives branch from dafc0e4 to e5718e6 Compare July 15, 2026 07:56
@verybigdog

Copy link
Copy Markdown
Contributor Author

Refreshed #37865 on current origin/main and addressed the 2026-07-13 sweeper findings.

Head: e5718e67f6bddc632db7b5d35a1cfc3c50b80f52 (rebased on origin/main 569b912d7d0931c7256e9f5fb326609e9deda377).

Fixes in this refresh:

  • Active wake now preserves the profile authorization chokepoint: when _authorization_adapter() cannot resolve a stamped non-default profile adapter, _trigger_gateway_agent() returns trigger_error and does not fall back to the default/wrong bot adapter. Regression: test_profile_stamped_wake_fails_closed_without_profile_adapter.
  • kanban_notify_subs now persists/inherits user_id_alt; gateway-created auto-subscriptions capture HERMES_SESSION_USER_ID_ALT; notifier wake replay passes user_id_alt into SessionSource.
  • Routing coverage now proves the original and woken build_session_key stay equal when user_id and user_id_alt differ for DM, isolated group, isolated channel, shared group, and thread cases.
  • Preserved the existing internal-only wake boundary: send_message model schema still exposes only action, emoji, message, message_id, target; no trigger_agent, wake, delivery_mode, chat_type, or user_id_alt field.
  • Preserved retained background wake tasks, passive default delivery, and separate visible-delivery / active-wake receipts.
  • Nikita Barkov's credited delivery_mode/chat_type design commit remains in history with its Co-authored-by: trailer.

Verification:

  • git diff --check origin/main...HEAD -> passed.
  • python3 -m py_compile gateway/session_context.py gateway/run.py gateway/kanban_watchers.py gateway/slash_commands.py hermes_cli/kanban.py hermes_cli/kanban_db.py tools/kanban_tools.py tools/send_message_tool.py tests/tools/test_send_message_tool.py tests/tools/test_kanban_tools.py tests/hermes_cli/test_kanban_notify.py -> passed.
  • Focused wrapper: scripts/run_tests.sh tests/tools/test_send_message_tool.py tests/hermes_cli/test_kanban_notify.py tests/tools/test_kanban_tools.py tests/gateway/test_kanban_notifier.py tests/gateway/test_kanban_notifier_watcher_dispatch_gate.py tests/gateway/test_session_env.py -q -> 305 passed, 0 failed.
  • Full wrapper: scripts/run_tests.sh -q -> 41,234 passed, 16 failed. The failures are in existing unrelated areas not touched by this PR diff (tests/agent/test_codex_responses_adapter.py, tests/agent/test_skill_utils.py, tests/gateway/test_agent_cache.py, tests/hermes_cli/test_gateway_runtime_health.py, tests/honcho_plugin/test_pin_peer_name.py, tests/tools/test_execute_code_approval_cluster.py, tests/tools/test_skills_tool_discovery_cache.py).
  • GitHub after push: mergeable=MERGEABLE, mergeStateStatus=BLOCKED, no status checks currently reported.

@verybigdog

Copy link
Copy Markdown
Contributor Author

Verdict: BLOCK

I re-reviewed current head e5718e67f6bddc632db7b5d35a1cfc3c50b80f52 against origin/main 569b912d7d0931c7256e9f5fb326609e9deda377.

Blocking finding:

  • gateway/slash_commands.py:466-480 still drops source.user_id_alt when /kanban create auto-subscribes a gateway-created task. The row is created with delivery_mode='notify+wake', but only user_id and chat_type are persisted. For isolated group/channel sessions where build_session_key() prefers user_id_alt, the later active wake replays a different source and lands in a parallel context-less session.

Repro probe on this PR checkout:

original           agent:main:discord:group:chat-1:union-id
replay_missing_alt agent:main:discord:group:chat-1:open-id
equal              False

The model-tool auto-subscribe path in tools/kanban_tools.py does persist HERMES_SESSION_USER_ID_ALT, but the gateway slash-command auto-subscribe path does not, so the fail-closed/routing fix is incomplete.

Suggested fix:

  • In gateway/slash_commands.py, read source.user_id_alt and pass user_id_alt=... into kb.add_notify_sub(...) alongside user_id.
  • Add a regression test for /kanban create gateway auto-subscribe with user_id != user_id_alt, asserting the persisted subscription includes user_id_alt and the original/wake build_session_key() are equal.

Checks run from clean worktree /tmp/hermes-pr37865-review:

  • git diff --check origin/main...HEAD -> pass
  • python3 -m py_compile gateway/session_context.py gateway/run.py gateway/kanban_watchers.py gateway/slash_commands.py hermes_cli/kanban.py hermes_cli/kanban_db.py tools/kanban_tools.py tools/send_message_tool.py tests/tools/test_send_message_tool.py tests/tools/test_kanban_tools.py tests/hermes_cli/test_kanban_notify.py -> pass
  • scripts/run_tests.sh tests/tools/test_send_message_tool.py tests/hermes_cli/test_kanban_notify.py tests/tools/test_kanban_tools.py tests/gateway/test_session_env.py -q -> 295 passed

Because the current PR acceptance criteria explicitly require user_id_alt persistence/replay and exact session-key equality across group/thread variants, this remains BLOCK until the gateway slash auto-subscribe path is covered too.

@verybigdog

Copy link
Copy Markdown
Contributor Author

Remediation for reviewer BLOCK #37865 (comment) pushed at a6ee9a2.

What changed:

  • gateway /kanban create auto-subscribe now persists source.user_id_alt beside user_id, chat_type, thread_id, notifier_profile, and delivery_mode="notify+wake".
  • Added a real slash-command-path regression that creates the subscription through GatewayRunner._handle_kanban_command, reloads the persisted row, reconstructs the wake SessionSource, and proves build_session_key(original) == build_session_key(replayed) for user_id != user_id_alt in channel and thread-isolated shapes.

Verification:

  • scripts/run_tests.sh tests/hermes_cli/test_kanban_notify.py → 24 passed.
  • python3 -m py_compile gateway/slash_commands.py tests/hermes_cli/test_kanban_notify.py → passed.
  • git diff --check → passed.

Live PR state after push: mergeable=MERGEABLE, mergeStateStatus=BLOCKED, checks=[] (no checks reported yet).

@verybigdog

Copy link
Copy Markdown
Contributor Author

Keeping this as the canonical active-wake PR for the M28 consolidation. HEAD (a6ee9a2) already resolves the sweeper review: it fails closed for an unavailable non-default stamped profile adapter (no default-bot fallback) and persists+replays user_id_alt so a woken turn keys to the same build_session_key as the originating event. delivery_mode/chat_type/user_id_alt, parent->child notify-sub inheritance (including delivery_mode/chat_type/user_id_alt), DM/group/thread routing, and 'no model-exposed wake / no body-prose inference' all land here. Targeted tests: 281 passed via scripts/run_tests.sh; merges cleanly against current main. The distinct follow-on surface is delivery-boundary failure receipts (#49752); #49751 is duplicate because #37865 already carries the task-graph subscription inheritance.

@AlexanderPrendota

Copy link
Copy Markdown

@teknium1 can u have a look please? :)

@alt-glitch alt-glitch added comp/cron Cron scheduler and job management blocked Waiting on external dependency or decision comp/tools Tool registry, model_tools, toolsets labels Jul 27, 2026
@AlexanderPrendota

Copy link
Copy Markdown

@verybigdog, hey, could u please resolve the conflicts?

@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

Four PRs address the active-wake and durable Kanban ACK path: #37865 implements opt-in delivery modes with source-faithful wake routing and graph subscription inheritance; #49593 adds receipt/ledger and diagnostics machinery around an overlapping wake implementation; #49749 isolates wake-text sanitization; and #49751 isolates graph subscription inheritance plus origin wiring.

Related pull requests

  • feat(messaging): add explicit active handoff primitives #37865 related — (+1223/-114) — n/a: Implements passive-by-default notify/notify+wake/wake delivery, persists and replays chat_type, user_id, user_id_alt, and profile ownership, and carries subscriptions into child tasks. The diff addresses the contributor keep_open review's wrong-profile and parallel-session findings by failing closed for unavailable stamped profiles and round-tripping user_id_alt; the PR remains relevant as the open canonical implementation but currently needs conflict resolution.
  • feat(gateway): add active-wake operator receipts #49593 [closed] related — (+3249/-25) — n/a: Adds scheduled/accepted/started wake receipts, an ACK ledger, relay diagnostics, and an overlapping active-wake path. Despite the contributor keep_open review, this closed PR should remain a reference rather than be reopened: its diff hard-codes chat_type="group" and records operator observed on session acceptance/start, while feat(messaging): add explicit active handoff primitives #37865 provides source-faithful routing; any independently useful receipt or diagnostic pieces require a narrow follow-up that preserves the review's operator-observation distinction.
  • fix(send_message): sanitize active-wake snippets #49749 [closed] duplicate — (+561/-2) — n/a: Adds redaction for token-, path-, and media-like content but also duplicates the active-wake transport with a model-facing trigger and a synthetic group participant. Despite the contributor keep_open review, it is appropriately closed as superseded by feat(messaging): add explicit active handoff primitives #37865 because the diff neither truncates the wake text as claimed nor routes to the originating session; sanitization alone can be salvaged separately if still needed.
  • fix(kanban): preserve ACK subscriptions across task graphs #49751 [closed] duplicate — (+279/-17) — n/a: Copies parent notification subscriptions into child tasks and adds explicit origin options, but persists an unconsumed trigger_agent flag and infers external targets from task-body prose. Despite the contributor keep_open review, it is appropriately closed as a duplicate of feat(messaging): add explicit active handoff primitives #37865, whose diff preserves the same graph edge together with the routing fields and watcher consumption that this PR lacks.

Duplicates

#49593 and #49749 substantially duplicate #37865's active-wake mechanism; #49751 duplicates #37865's Kanban graph-subscription inheritance. #49593 also incorporates the narrower surfaces represented by #49749 and #49751.

Suggested consolidation

Author action on #37865: rebase onto current main and resolve the reported conflicts, then retain its source-faithful, profile-fail-closed delivery-mode and graph-inheritance path while addressing any newly exposed integration failures. Keep #49593, #49749, and #49751 closed as superseded/duplicate implementations; salvage only narrow receipt/diagnostic or sanitization pieces in separate follow-ups that preserve the contributor reviews' routing and operator-observation constraints.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    subgraph Dup37865 ["PRs duplicating each other"]
        P37865["PR #37865 (open)"]
        P49749["PR #49749 (closed)"]
        P49751["PR #49751 (closed)"]
    end
    class P37865 open
    class P49749 closed
    class P49751 closed
    class P37865 target
    click P37865 "https://github.com/NousResearch/hermes-agent/pull/37865"
    click P49749 "https://github.com/NousResearch/hermes-agent/pull/49749"
    click P49751 "https://github.com/NousResearch/hermes-agent/pull/49751"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 4 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 279 kB of PR diffs, 5 kB of issue/PR text, 18 kB of discussion (23 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@verybigdog

Copy link
Copy Markdown
Contributor Author

Resolved the conflicts against current main (3dcbe9001f30de749971911041c02916437b5bff) in 224a85b5ca1cfabc7c73fa93c430ca2b1988a30e and pushed the updated branch.

The resolution keeps the PR's explicit notify / notify+wake / wake modes, source-faithful chat_type / user_id_alt routing, and inherited return-path metadata, while using the current gateway.wake.deliver_wake path rather than restoring the older duplicate wake implementation.

Validation:

  • PR is now reported MERGEABLE by GitHub.
  • Targeted notifier/routing/session tests: 112 passed.
  • ruff check on changed Python files: passed.
  • Full local suite was also attempted; it was not clean in this checkout because optional test dependencies (acp, Daytona, Modal, fal-client, etc.) are absent and the run timed out at 87%. The PR-targeted suite had no failures.

teknium1 pushed a commit that referenced this pull request Aug 13, 2026
…session routing

Salvage of #37865 by @verybigdog. Adds delivery_mode (notify / notify+wake / wake)
on kanban notify subscriptions, persists chat_type + user_id_alt so a woken turn
reconstructs the creator's real session key, inherits the return path to child
tasks, and keeps wake out of the model-exposed send_message schema.

Original commits were authored under a local placeholder identity
(hermes-agent@users.noreply.local); re-attributed to the contributor's
public email.
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #85487 with your work applied onto current main. Your original commits were authored under a local placeholder identity (hermes-agent@users.noreply.local), so the salvage carries the change as a single commit re-attributed to your public email — credit preserved in git log and the PR body.

We added two follow-up fixes on top: (1) the migration now backfills pre-existing gateway subscriptions to notify+wake on first-add of the column (before delivery_mode existed, wake was unconditional — defaulting legacy rows to notify would have silently disabled it on upgrade), and (2) api_server subscriptions default to notify+wake since that platform has no push send() and the wake self-post is the delivery.

Thanks for the design and the thorough test coverage — the delivery-mode gate and the user_id_alt round-trip were the right call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked Waiting on external dependency or decision comp/cron Cron scheduler and job management comp/gateway Gateway runner, session dispatch, delivery comp/tools Tool registry, model_tools, toolsets P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants