Skip to content

feat(hooks): add message:pre_route event + multi-role-router reference hook - #78269

Closed
raulvidis wants to merge 3 commits into
NousResearch:mainfrom
raulvidis:fix/multi-role-router-blockers
Closed

feat(hooks): add message:pre_route event + multi-role-router reference hook#78269
raulvidis wants to merge 3 commits into
NousResearch:mainfrom
raulvidis:fix/multi-role-router-blockers

Conversation

@raulvidis

@raulvidis raulvidis commented Aug 4, 2026

Copy link
Copy Markdown

Superseded — closed in favor of #74272. This PR builds directly on
@ceverson70's implementation (same message:pre_route hook + reference
router). Closing to keep the original authorship intact; the review-fix
delta (first-route isolation, agent-cache eviction, config-read guard,
doc/attribution corrections) is offered on #74272.

Summary

Adds a message:pre_route gateway hook event that fires after session resolution but before the turn-lease is claimed, letting a hook redirect a turn to a different session based on the inbound message. Ships a reference hook (optional-skills/multi-role-router/) that uses a cheap auxiliary LLM classifier to route each message to the best-suited worker role (code / knowledge / ml / ops), keeping short continuations in the current session via a regex fast-path.

Closes #5143 (Multi-Role Auto-Routing via Gateway Hooks).

What's included

  • gateway/hooks.py — document the message:pre_route event + context contract (platform, user_id, chat_id, thread_id, chat_type, session_id, session_key, message) and the return contract ({"decision":"switch_session","session_id":"<id>"}).
  • gateway/run.py — emit message:pre_route after session resolution, honor a switch_session decision, and evict the cached agent after a switch so the next turn rebinds to the new session (mirrors /resume).
  • optional-skills/multi-role-router/ — reference hook: handler.py, HOOK.yaml, SKILL.md, README.md.
  • Tests — 45 tests for the hook + 13 for the gateway pre_route block.

Design decisions

  • First-route isolation: when the classifier picks a role with no saved session, the hook generates a fresh session id, records it in the state file, and returns a switch decision — so even the first message for a role lands in its own session (not the shared inbound one). The gateway's switch_session creates the SessionEntry for a new target id.
  • Slash-command safety: recognized slash commands dispatch before the agent path is entered, so they never reach the classifier. Only unrecognized /foo text falls through (acceptable — it's just text).
  • Async classifier: calls the auxiliary LLM over non-blocking async HTTP (httpx.AsyncClient) so the hook never blocks the gateway event loop; the triage_specifier auxiliary slot is used for the classifier (matching the config read).

Notes for maintainers

This addresses the reviewer feedback on the earlier proposal: the config-read guard is satisfied (canonical load_config_readonly()), the first-route isolation gap is closed, and dead state-management code was removed rather than shipped.

There is a known routing-contract overlap with #69693 (pre_agent_dispatch plugin hook) and #72942 (pre_gateway_dispatch action) — three proposals at different layers. This PR implements the message:pre_route layer (after session resolution, before turn lease). Happy to align with whichever contract the maintainers decide on.

…e hook

Add a message:pre_route gateway hook event that fires after session
resolution but before the turn-lease is claimed, letting a hook redirect
a turn to a different session based on the inbound message.

Ship a reference hook (optional-skills/multi-role-router/) that uses a
cheap auxiliary LLM classifier to route each message to the best-suited
worker role (code/knowledge/ml/ops), keeping continuations in the current
session via a regex fast-path and a short history window.

Includes:
- gateway/hooks.py: document the message:pre_route event + context contract
- gateway/run.py: emit message:pre_route, handle switch_session decision,
  evict the cached agent after a successful switch (mirrors /resume)
- optional-skills/multi-role-router/: reference hook (handler, HOOK.yaml,
  SKILL.md, README)
- tests: 58 tests for the hook + 13 for the gateway pre_route block

Fixes over the upstream NousResearch#74272:
- config-read guard: use canonical load_config_readonly() instead of raw
  yaml.safe_load of config.yaml (passes tests/hermes_cli/test_config_read_guard.py)
- first-route isolation: a brand-new role now gets its own fresh session on
  the very first message (no shared-session gap)
Three fixes from qwen3.8-max code review of the branch:

1. Async clean: use async_call_llm() instead of the sync call_llm() in the
   async _call_auxiliary_llm(). The sync call blocked the gateway event loop
   for up to 15s per message and defeated the 5s asyncio.wait_for guard
   (the timeout callback couldn't fire while the loop was blocked).

2. Config-slot match: use task="triage_specifier" so the classifier uses the
   model a user configures under auxiliary.triage_specifier (the slot
   _get_auxiliary_config reads), not the compression slot. Read config and
   used config now agree.

3. Remove dead code: _update_meta_session() was never called (handle() only
   handles message:pre_route and has no assistant response at pre-route
   time), so the history list it wrote was never populated. Remove the dead
   function and its 3 tests to avoid shipping unwired state-management in a
   reference hook.
@alt-glitch alt-glitch added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery tool/skills Skills system (list, view, manage) area/sessions Session lifecycle, resume, persistence, history P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state duplicate This issue or pull request already exists labels Aug 4, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #74272: both add the message:pre_route session-switch hook and multi-role-router reference implementation.

- README: remove the /role slash-command table — those commands are not
  implemented in this PR or on main; document the config.yaml controls
  (multi_role_router.auto) that actually work. Fix the troubleshooting
  entry that referenced /role auto off.
- SKILL.md: shorten description to the <=60 char one-sentence standard.
- HOOK.yaml: credit the original implementation (NousResearch#74272, Clark Everson)
  instead of 'community'; bump version to 1.0.1.
- gateway/hooks.py: fix the message:pre_route chat_type contract to the
  real MessageSource values (dm|group|channel|thread|webhook), not the
  Telegram-specific ones.
- handler.py: update the stale /role comment.
@raulvidis

Copy link
Copy Markdown
Author

The triage bot is right — this builds directly on #74272 (same message:pre_route hook + multi-role-router reference implementation; my first commit message even says "fixes over upstream #74272"). Closing in favor of #74272 so @ceverson70's authorship stays intact — per this repo's "preserve contributor credit" rule this should have been a cherry-pick/rebase on top of that branch, not a re-commit.

The only delta here is the reviewer-feedback fixes (first-route session isolation, agent-cache eviction after switch_session, canonical load_config_readonly() guard) plus doc/attribution corrections pushed today — I've posted those over on #74272 for @ceverson70 to fold in, who already has mechanical fixes in progress there. Closing #5143 stays with #74272.

@raulvidis

Copy link
Copy Markdown
Author

Follow-up: this PR couldn't be reopened after the branch rebuild (GitHub pins closed PRs to the old head lineage), so the rebuilt branch is up as #78326 instead — the #74272 base commits are now cherry-picked/rebased with original authorship preserved, with the review-fix delta stacked on top. This PR stays closed as the duplicate it was.

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

Labels

area/sessions Session lifecycle, resume, persistence, history comp/gateway Gateway runner, session dispatch, delivery duplicate This issue or pull request already exists needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state tool/skills Skills system (list, view, manage) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Multi-Role Auto-Routing via Gateway Hooks

2 participants