Skip to content

feat(plugins): add pre_agent_dispatch plugin hook - #69693

Open
luperrypf wants to merge 2 commits into
NousResearch:mainfrom
luperrypf:feat/pre-agent-dispatch-hook
Open

feat(plugins): add pre_agent_dispatch plugin hook#69693
luperrypf wants to merge 2 commits into
NousResearch:mainfrom
luperrypf:feat/pre-agent-dispatch-hook

Conversation

@luperrypf

Copy link
Copy Markdown

Summary

Add a new plugin lifecycle hook pre_agent_dispatch that fires once per turn just before the agent processes a user message. This allows plugins to intercept, rewrite, or bypass normal agent dispatch — useful for routing, content filtering, pre-processing, and multi-agent orchestration.

Hook Protocol

A plugin's pre_agent_dispatch callback receives message, session_key, source, gateway, history, and optionally stream_callback. It returns a dict with an action key:

Action Effect
allow / None Normal agent dispatch (default)
skip Drop the message silently — no reply, no agent run
route Bypass the agent entirely; supply a pre-computed result
rewrite Replace the message text, then dispatch normally

Changes

  • hermes_cli/plugins.py: Register pre_agent_dispatch in VALID_HOOKS (24 total); add dispatch_pre_agent() shared helper with fail-closed error handling
  • gateway/run.py: Integrate hook into gateway dispatch (Telegram, Discord, etc.)
  • tui_gateway/server.py: Integrate hook into TUI/WebUI prompt submission
  • plugins/router/: Reference plugin — pre-turn classifier for Flash→Orchestrator routing
  • tests/test_pre_agent_dispatch_hook.py: 17 tests covering all actions and edge cases

Design Decisions

  1. Fail-closed: Hook exceptions fall back to allow — never blocks the user
  2. First-wins: First non-allow plugin return wins
  3. Streaming passthrough: Optional stream_callback for progressive output during routing
  4. Two call sites: Both tui_gateway and gateway use the shared dispatch_pre_agent() helper

Add a new plugin lifecycle hook 'pre_agent_dispatch' that fires once per turn
just BEFORE the agent processes a user message. Plugins can intercept to:

- skip: drop the message entirely (no reply)
- route: bypass the agent, supply a pre-computed response
- rewrite: replace the message text before normal dispatch
- allow: normal agent dispatch (default / fail-closed)

Core changes:
- hermes_cli/plugins.py: register 'pre_agent_dispatch' in VALID_HOOKS;
  add dispatch_pre_agent() shared helper with fail-closed error handling
- gateway/run.py: integrate hook into gateway message dispatch path,
  passing stream_callback for progressive orchestrator output
- tui_gateway/server.py: integrate hook into TUI prompt submission,
  with deduplication of streamed output via 'streamed' flag

First consumer:
- plugins/router/: pre-turn classifier that routes complex tasks to
  an orchestrator profile via hermes subprocess. Uses the aux LLM
  classifier (configurable via 'router' config section) with regex
  pattern rules for always-simple/always-complex bypass.

Tests: 17 tests covering all actions, error handling, kwarg forwarding,
and the streamed flag passthrough.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/config Config system, migrations, profiles needs-decision Awaiting maintainer decision before any implementation 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 labels Jul 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: #39751 covers lifecycle-hook execution safeguards on hot paths. This PR introduces a distinct pre-dispatch hook with rewrite, skip, and route authority; its intended dispatch contract needs a maintainer decision.

…tch hook and router plugin

- Add 9 new integration/regression unit tests in tests/test_pre_agent_dispatch_hook.py covering priority, multi-hook ordering, and unrecognised actions.
- Add new test suite in tests/plugins/test_router_plugin.py (49 tests) covering pattern matching, noise filtering, response extraction, prompt building, binary resolution, classification caching, and pre-agent dispatch hook integration. Total suite: 75 tests.

@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 the concrete hook protocol and regression coverage. This needs contract and correctness work before it is safe to salvage.

Problems

  • plugins/router/__init__.py:446 caches classification by session_key alone for 300 seconds. After a simple first turn, later complex requests in that session never classify; after a complex first turn, later requests route unconditionally.
  • tui_gateway/server.py:10546 synthesizes result["messages"] without executing AIAgent. Current TUI only copies returned messages into in-memory history at tui_gateway/server.py:9267-9273; the normal agent path owns transcript persistence. Please establish and test durable persistence for routed turns.
  • Main already documents pre_gateway_dispatch as the gateway routing/interception point (website/docs/user-guide/features/hooks.md:1011-1037) and explicitly says a separate UserPromptSubmit event is intentionally not provided (website/docs/user-guide/features/hooks.md:1440). The new route/bypass authority therefore needs a maintainer-approved contract, especially alongside #74272.

Suggested changes

  • Make classification cache inputs message-sensitive, with alternating simple/complex regression coverage.
  • Define the selected routing boundary and persistence/alternation guarantees, then cover all supported surfaces or narrow the claim.

Automated hermes-sweeper review.


# Check cache
if session_key:
cached = _get_cached_classification(session_key)

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.

This cache is keyed only by session_key, so its first result controls every different message in the same session for five minutes. A simple greeting suppresses later complex routing, while a complex first task routes later simple turns. Remove this cache or include the inputs that determine classification, with an alternating-turn regression test.

Comment thread tui_gateway/server.py
"messages": list(history),
"interrupted": False,
}
elif _hook_action == "route":

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.

This bypasses AIAgent and only returns synthetic messages. The current TUI copies returned messages into in-memory history after a turn, but normal transcript persistence is performed through the agent path. Please persist this synthetic user/assistant pair through the established SessionDB path and add a resume/restart test.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins comp/tui Terminal UI (ui-tui/ + tui_gateway/) needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users 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-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.

3 participants