feat(plugins): add pre_agent_dispatch plugin hook - #69693
Conversation
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.
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
left a comment
There was a problem hiding this comment.
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:446caches classification bysession_keyalone 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:10546synthesizesresult["messages"]without executingAIAgent. Current TUI only copies returned messages into in-memory history attui_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_dispatchas 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) |
There was a problem hiding this comment.
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.
| "messages": list(history), | ||
| "interrupted": False, | ||
| } | ||
| elif _hook_action == "route": |
There was a problem hiding this comment.
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.
Summary
Add a new plugin lifecycle hook
pre_agent_dispatchthat 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_dispatchcallback receivesmessage,session_key,source,gateway,history, and optionallystream_callback. It returns a dict with anactionkey:allow/Noneskiprouteresultrewritetext, then dispatch normallyChanges
hermes_cli/plugins.py: Registerpre_agent_dispatchinVALID_HOOKS(24 total); adddispatch_pre_agent()shared helper with fail-closed error handlinggateway/run.py: Integrate hook into gateway dispatch (Telegram, Discord, etc.)tui_gateway/server.py: Integrate hook into TUI/WebUI prompt submissionplugins/router/: Reference plugin — pre-turn classifier for Flash→Orchestrator routingtests/test_pre_agent_dispatch_hook.py: 17 tests covering all actions and edge casesDesign Decisions
allow— never blocks the userallowplugin return winsstream_callbackfor progressive output during routingdispatch_pre_agent()helper