feat(plugins): add pre_gateway_dispatch hook for gateway-level message interception - #13445
Closed
keiravoss94 wants to merge 2 commits into
Closed
feat(plugins): add pre_gateway_dispatch hook for gateway-level message interception#13445keiravoss94 wants to merge 2 commits into
keiravoss94 wants to merge 2 commits into
Conversation
keiravoss94
force-pushed
the
feature/pre-gateway-dispatch
branch
from
April 22, 2026 13:57
62a8d84 to
81f98d5
Compare
github-actions
Bot
force-pushed
the
feature/pre-gateway-dispatch
branch
from
April 23, 2026 05:35
81f98d5 to
539b5e4
Compare
keiravoss94
force-pushed
the
feature/pre-gateway-dispatch
branch
from
April 24, 2026 03:58
539b5e4 to
aff2d25
Compare
Introduces a new plugin hook `pre_gateway_dispatch` fired once per
incoming MessageEvent in `_handle_message`, after the internal-event
guard but before the auth / pairing chain. Plugins may return a dict
to influence flow:
{"action": "skip", "reason": "..."} -> drop (no reply)
{"action": "rewrite", "text": "..."} -> replace event.text
{"action": "allow"} / None -> normal dispatch
Motivation: gateway-level message-flow patterns that don't fit cleanly
into any single adapter — e.g. listen-only group-chat windows (buffer
ambient messages, collapse on @mention), or human-handover silent
ingest (record messages while an owner handles the chat manually).
Today these require forking core; with this hook they can live in a
single profile-agnostic plugin.
Hook runs BEFORE auth so plugins can handle unauthorized senders
(e.g. customer-service handover ingest) without triggering the
pairing-code flow. Exceptions in plugin callbacks are caught and
logged; the first non-None action dict wins, remaining results are
ignored.
Includes:
- `VALID_HOOKS` entry + inline doc in `hermes_cli/plugins.py`
- Invocation block in `gateway/run.py::_handle_message`
- 5 new tests in `tests/gateway/test_pre_gateway_dispatch.py`
(skip, rewrite, allow, exception safety, internal-event bypass)
- 2 additional tests in `tests/hermes_cli/test_plugins.py`
- Table entry in `website/docs/user-guide/features/plugins.md`
Made-with: Cursor
… section
Follow-up to aeff6dfe:
- Fix semantic error in VALID_HOOKS inline comment ("after core auth" ->
"before auth"). Hook intentionally runs BEFORE auth so plugins can
handle unauthorized senders without triggering the pairing flow.
- Fix wrong class name in the same comment (HermesGateway ->
GatewayRunner, matching gateway/run.py).
- Add a full ### pre_gateway_dispatch section in
website/docs/user-guide/features/hooks.md (matches the pattern of
every other plugin hook: signature, params table, fires-where,
return-value table, use cases, two worked examples) plus a row in
the quick-reference table.
- Add the anchor link on the plugins.md table row so it matches the
other hook entries.
No code behavior change.
github-actions
Bot
force-pushed
the
feature/pre-gateway-dispatch
branch
from
April 24, 2026 05:01
aff2d25 to
5224051
Compare
Contributor
|
Merged via #15050 — your commits were cherry-picked onto current main with authorship preserved in git log (f33415a, 2f0ccb8). Thanks @keiravoss94! Full PR: #15050 |
github-actions Bot
pushed a commit
to pebble-tech/hermes-agent
that referenced
this pull request
Apr 27, 2026
This is the sole commit on ops-overlay. It carries:
- .github/workflows/sync-upstream.yml: daily rebase + rebuild of main
on top of upstream/main + ops-overlay + each feature branch.
- FORK.md: branch layout, sync model, recovery procedure.
Steady state (2026-04-26 onward): both feature branches we used to
carry have merged upstream (PRs NousResearch#13445 and NousResearch#14904 via NousResearch#15050 and
NousResearch#15191). FEATURE_BRANCHES is now empty; main collapses to
upstream/main + ops-overlay. The fork stays alive so customer VPSes
have a stable deploy target rebuilt on our schedule.
github-actions Bot
pushed a commit
to pebble-tech/hermes-agent
that referenced
this pull request
Apr 30, 2026
This is the sole commit on ops-overlay. It carries:
- .github/workflows/sync-upstream.yml: daily rebase + rebuild of main
on top of upstream/main + ops-overlay + each feature branch.
- FORK.md: branch layout, sync model, recovery procedure.
Steady state (2026-04-26 onward): both feature branches we used to
carry have merged upstream (PRs NousResearch#13445 and NousResearch#14904 via NousResearch#15050 and
NousResearch#15191). FEATURE_BRANCHES is now empty; main collapses to
upstream/main + ops-overlay. The fork stays alive so customer VPSes
have a stable deploy target rebuilt on our schedule.
github-actions Bot
pushed a commit
to pebble-tech/hermes-agent
that referenced
this pull request
May 6, 2026
This is the sole commit on ops-overlay. It carries:
- .github/workflows/sync-upstream.yml: daily rebase + rebuild of main
on top of upstream/main + ops-overlay + each feature branch.
- FORK.md: branch layout, sync model, recovery procedure.
Steady state (2026-04-26 onward): both feature branches we used to
carry have merged upstream (PRs NousResearch#13445 and NousResearch#14904 via NousResearch#15050 and
NousResearch#15191). FEATURE_BRANCHES is now empty; main collapses to
upstream/main + ops-overlay. The fork stays alive so customer VPSes
have a stable deploy target rebuilt on our schedule.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds a new plugin hook
pre_gateway_dispatch, fired once per incomingMessageEventinGatewayRunner._handle_message— after the internal-event guard but before the auth / pairing chain. Plugins may return a dict to influence flow:{"action": "skip", "reason": "..."} # drop (no reply; plugin handled) {"action": "rewrite", "text": "..."} # replace event.text, continue normally {"action": "allow"} / None # normal dispatchMotivation. Several commercially relevant message-flow patterns don't fit cleanly into any single platform adapter:
Today these patterns require modifying
_handle_messagedirectly. With this hook they can live entirely in plugins (e.g. agateway-policyplugin we maintain out-of-tree at https://github.com/pebble-tech/hermes-plugin-gateway-policy), making them profile-agnostic and installable viahermes plugins install.Design choices.
skip | rewrite | allow). Any "side effect" (silent-ingest, handover state, owner notification, etc.) is the plugin's responsibility and results inskipfor the core.event.internal == True) bypass the hook entirely — they're system-generated (background-process completions etc.) and must not be gate-kept by user-facing policy.Related Issue
No existing issue; happy to file one if preferred.
Fixes #
Type of Change
Changes Made
hermes_cli/plugins.py— add"pre_gateway_dispatch"toVALID_HOOKSwith inline documentation of the action contract.gateway/run.py— addimport dataclasses; invoke the hook in_handle_messageafter theis_internalcheck and before thesource.user_id is Noneauth chain; handleskip/rewrite/allowactions and log a structured message on skip.tests/gateway/test_pre_gateway_dispatch.py— new file, 5 tests covering skip, rewrite, allow, exception safety, and internal-event bypass.tests/hermes_cli/test_plugins.py— 2 new tests confirming the hook is registered and that action dicts are collected across multiple plugin callbacks.website/docs/user-guide/features/plugins.md— new row in the "Available hooks" table.Total: ~260 additions, 1 deletion across 5 files. No existing behavior changes when no plugin registers the hook.
How to Test
pip install -e .[dev](or your usual dev setup).pytest tests/gateway/test_pre_gateway_dispatch.py tests/hermes_cli/test_plugins.py -q— 59 tests pass locally.Register a quick plugin to see it in action:
Send a message containing
ignore mevia any connected platform — the gateway logs the skip and returns without replying.Checklist
Code
pytest tests/gateway/test_pre_gateway_dispatch.py tests/hermes_cli/test_plugins.py -qand all tests pass (59 passed)Documentation & Housekeeping
website/docs/user-guide/features/plugins.md)cli-config.yaml.exampleCONTRIBUTING.md/AGENTS.mdScreenshots / Logs
Example log line when a plugin returns
skip:Made with Cursor