fix(webhook): honor per-route enabled_toolsets in gateway-triggered sessions - #66953
fix(webhook): honor per-route enabled_toolsets in gateway-triggered sessions#66953mclbills12gmailcom wants to merge 1 commit into
Conversation
…essions
Webhook-triggered agent sessions previously ignored any toolset
configuration and always received only the platform-default web tools
(web_search, web_extract, vision_analyze, clarify), regardless of what
the webhook route intended or what the profile config specified.
Cron jobs already have an enabled_toolsets field that gates what tools
the agent receives for that run. This change adds the same capability to
webhook routes, giving them parity with cron jobs.
Changes
-------
gateway/session.py
Add enabled_toolsets: Optional[List[str]] = None field to SessionSource.
Serialized in to_dict/from_dict so a resumed webhook session keeps its
toolset scope. None is the backward-compat sentinel (use platform
default) and is excluded from the wire dict.
gateway/run.py
Gate in _run_agent_inner at toolset resolution: when
source.enabled_toolsets is not None, use it verbatim instead of
calling _get_platform_tools. Matches cron job replace semantics.
The is-not-None guard preserves the distinction between [] (explicitly
no tools) and None (unset, fall back to platform default).
gateway/platforms/webhook.py
Stamp route_config.get(enabled_toolsets) onto source.enabled_toolsets
after source construction in _handle_webhook. isinstance(list) guard
means a malformed or absent route value leaves the field as None
(safe fail-open to platform default).
Usage
-----
Add enabled_toolsets to a webhook subscription in
webhook_subscriptions.json:
{
"my-route": {
"prompt": "...",
"enabled_toolsets": ["file", "terminal"]
}
}
Tests
-----
16 new tests in tests/gateway/test_webhook_enabled_toolsets.py cover:
- SessionSource field defaults and round-trips (None, list, empty list)
- to_dict backward compat (None absent from wire)
- Route config wiring logic (list stamped, non-list left as None)
- run.py gate semantics (override vs fallback, empty list not collapsed)
249 passed, 1 pre-existing failure (TestDualStackBind IPv6 bind test,
confirmed failing on upstream main before this change).
|
|
Thanks for tracing the missing local gateway propagation; the premise is confirmed on current main: webhook sources do not carry the route setting, and Problems
Suggested changes
Automated hermes-sweeper review. |
|
Thanks @mclbills12gmailcom — you identified this gap and built a working fix three weeks before anyone else, and your PR was the first submission for per-route webhook toolsets. Crediting you as the original submitter for this feature. The capability just landed on
Docs: https://hermes-agent.nousresearch.com/docs/user-guide/messaging/webhooks#per-route-toolsets Closing as superseded by #85154. Appreciate the contribution — sorry we didn't catch the overlap before building. |
Summary
When a webhook route subscription includes
enabled_toolsets, the gateway-triggered agent session now receives exactly those tools. Without this fix, all webhook-triggered sessions silently fell back to the platform default toolset regardless of the route config.Root cause
SessionSourcehad no field forenabled_toolsets, so the value set in a route subscription was never propagated to the agent runner._run_agent_inneronly checked the platform-level toolset override, never the per-source override.Changes
gateway/session.pyenabled_toolsets: Optional[List[str]] = NonetoSessionSource; wired intoto_dict/from_dictgateway/run.py_run_agent_inner: ifsource.enabled_toolsets is not None, use it; else fall back to platform defaultgateway/platforms/webhook.pySessionSource, copyroute_config.enabled_toolsetsonto ittests/gateway/test_webhook_enabled_toolsets.pyVerification
mainconfirmed pre-existing before this patch)enabled_toolsets=["file","terminal"]receivedread_filewhere previously only web tools were availablemain; conflict with Discord auto-thread fields insession.pyresolved with no overlap