Issue title
gateway/webhook: clarify tool unavailable to agents spawned by webhook routes that have deliver=<messaging-platform> configured
Summary
When a webhook route is configured with deliver: discord (or
telegram, slack, etc.) and deliver_extra.chat_id set, the agent
that runs in response to the webhook can invoke the clarify tool —
but the call returns:
Clarify tool is not available in this execution context.
(In recent versions the agent does block on clarify for the
configured clarify_timeout — but the base text fallback writes a
numbered question to the webhook session, which the gateway's
text-intercept can't resolve because it's keyed on the SOURCE
platform's session_key. The agent times out after 10 minutes and the
user gets nothing actionable.)
Either way, webhook agents can't ask the user clarifying questions
even when the route explicitly delivers to a platform that supports
native buttons.
Reproduction
~/.hermes/config.yaml:
platforms:
webhook:
enabled: true
extra:
port: 8644
routes:
demo-clarify:
secret: "demo-secret"
deliver: discord
deliver_extra:
chat_id: "<your channel id>"
prompt: |
Use the `clarify` tool to ask the user "Which colour?"
with choices ["red", "green", "blue"], then echo their
choice as a one-line response.
POST to https://<host>:8644/webhooks/demo-clarify with the route
secret. Observe the response (no buttons; either an error string or
a 10-minute hang).
Expected behaviour
When deliver is a connected messaging platform and
deliver_extra.chat_id resolves to a real channel, clarify should
render in that channel via the target platform's existing
send_clarify implementation — native buttons on Discord/Telegram/
Slack, text fallback elsewhere — and button-click resolution should
unblock the webhook agent.
Why this matters
Webhook routes are documented as a way for external services to
trigger agent runs that produce rich responses on messaging
platforms (the docs page calls out Discord/Telegram/Slack as
first-class deliver targets). Without clarify access, the agent
can't ask the user follow-up questions — so the use case is limited
to one-shot text responses.
Practical example: a workflow engine (in our case, a personal email
triage tool) pushes "draft ready for approval" events into a Hermes
webhook. The agent needs to ask the user Send / Discard / Edit
and act on the answer. Without clarify, the agent can only emit
bare URLs the user has to tap manually.
Fix sketch (PR forthcoming)
Implement WebhookAdapter.send_clarify to delegate to the route's
target platform adapter:
async def send_clarify(self, chat_id, question, choices, clarify_id,
session_key, metadata=None):
delivery = self._delivery_info.get(chat_id, {})
platform_name = (delivery.get("deliver") or "").strip().lower()
# Fall back to base text behaviour for log / github_comment / unknown.
if not platform_name or platform_name in {"log", "github_comment"}:
return await super().send_clarify(...)
if not self.gateway_runner:
return await super().send_clarify(...)
try:
target_platform = Platform(platform_name)
except ValueError:
return await super().send_clarify(...)
adapter = self.gateway_runner.adapters.get(target_platform)
if not adapter:
return await super().send_clarify(...)
extra = delivery.get("deliver_extra", {}) or {}
target_chat = extra.get("chat_id") or (
self.gateway_runner.config.get_home_channel(target_platform)
and self.gateway_runner.config.get_home_channel(target_platform).chat_id
)
if not target_chat:
return await super().send_clarify(...)
# Forward Telegram thread_id when present.
forwarded = dict(metadata) if metadata else {}
thread_id = extra.get("message_thread_id") or extra.get("thread_id")
if thread_id and "thread_id" not in forwarded:
forwarded["thread_id"] = thread_id
return await adapter.send_clarify(
chat_id=target_chat,
question=question,
choices=choices,
clarify_id=clarify_id,
session_key=session_key,
metadata=forwarded or None,
)
Resolution is already platform-agnostic — clarify_gateway._entries
is module-level, so a Discord button click via the target adapter
correctly unblocks the webhook agent's wait_for_response.
Versions
- Hermes: latest as of 2026-05-24 (commit
bc3f1f4f3 after hermes update --yes)
- macOS host running
hermes gateway run
- Discord adapter connected with native
send_clarify implementation
Happy to send the PR — let me know if you'd prefer a different
approach (e.g. requiring an explicit clarify_target route key
rather than reusing deliver).
Issue title
gateway/webhook:clarifytool unavailable to agents spawned by webhook routes that havedeliver=<messaging-platform>configuredSummary
When a webhook route is configured with
deliver: discord(ortelegram,slack, etc.) anddeliver_extra.chat_idset, the agentthat runs in response to the webhook can invoke the
clarifytool —but the call returns:
(In recent versions the agent does block on
clarifyfor theconfigured
clarify_timeout— but the base text fallback writes anumbered question to the webhook session, which the gateway's
text-intercept can't resolve because it's keyed on the SOURCE
platform's session_key. The agent times out after 10 minutes and the
user gets nothing actionable.)
Either way, webhook agents can't ask the user clarifying questions
even when the route explicitly delivers to a platform that supports
native buttons.
Reproduction
~/.hermes/config.yaml:POST to
https://<host>:8644/webhooks/demo-clarifywith the routesecret. Observe the response (no buttons; either an error string or
a 10-minute hang).
Expected behaviour
When
deliveris a connected messaging platform anddeliver_extra.chat_idresolves to a real channel,clarifyshouldrender in that channel via the target platform's existing
send_clarifyimplementation — native buttons on Discord/Telegram/Slack, text fallback elsewhere — and button-click resolution should
unblock the webhook agent.
Why this matters
Webhook routes are documented as a way for external services to
trigger agent runs that produce rich responses on messaging
platforms (the docs page calls out Discord/Telegram/Slack as
first-class
delivertargets). Withoutclarifyaccess, the agentcan't ask the user follow-up questions — so the use case is limited
to one-shot text responses.
Practical example: a workflow engine (in our case, a personal email
triage tool) pushes "draft ready for approval" events into a Hermes
webhook. The agent needs to ask the user Send / Discard / Edit
and act on the answer. Without
clarify, the agent can only emitbare URLs the user has to tap manually.
Fix sketch (PR forthcoming)
Implement
WebhookAdapter.send_clarifyto delegate to the route'starget platform adapter:
Resolution is already platform-agnostic —
clarify_gateway._entriesis module-level, so a Discord button click via the target adapter
correctly unblocks the webhook agent's
wait_for_response.Versions
bc3f1f4f3afterhermes update --yes)hermes gateway runsend_clarifyimplementationHappy to send the PR — let me know if you'd prefer a different
approach (e.g. requiring an explicit
clarify_targetroute keyrather than reusing
deliver).