feat(webhook): per-route session mode — allow shared persistent sessions - #71570
feat(webhook): per-route session mode — allow shared persistent sessions#71570jzOcb wants to merge 1 commit into
Conversation
Webhook routes derive the agent session key from the delivery id, so every
delivery spawns a fresh zero-context session. Right default for independent
event streams; wrong isolation granularity for conversational/workflow
consumers (e.g. agent-to-agent relays), which cold-start on every message,
lose in-conversation decisions, and re-request approvals.
Adds an opt-in, backward-compatible per-route setting:
"session": "shared" (default: "per-delivery", current behavior)
With "shared" the session key omits the delivery id, so all deliveries on
the route continue one persistent session; deliveries serialize into its
queue rather than running concurrently — the desired semantics for workflow
routes. Delivery-id dedup, HMAC verification, and delivery-info bookkeeping
are unchanged.
|
The adjacent |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the focused opt-in design. The per-delivery premise is present on current main at gateway/platforms/webhook.py:872-874, but this patch needs lifecycle and delivery-routing work before it can provide a persistent shared session.
Problems
gateway/platforms/webhook.py:936-958still closes every completed webhook session. On the next delivery,gateway/session.py:2317-2388detects thewebhook_completerow as stale and recreates it, so the new key alone does not preserve history.- The route-scoped key also aliases
_delivery_info: writes occur atgateway/platforms/webhook.py:885-887, whilesend()resolves the destination from that key atgateway/platforms/webhook.py:375-376. A later delivery can therefore replace the earlier response's rendered delivery target.
Suggested changes
- Separate durable conversation identity from per-delivery response-routing state, and exempt only shared routes from the per-delivery close path while retaining bounded lifecycle handling.
- Add real dispatch-pipeline tests for serial shared deliveries and overlapping deliveries with different rendered
deliver_extravalues; document the new route setting inwebsite/docs/user-guide/messaging/webhooks.md:79-90.
Automated hermes-sweeper review.
| # Right for workflow/relay routes where each event continues | ||
| # the same collaboration. | ||
| session_scope = str(route_config.get("session") or "per-delivery").strip().lower() | ||
| if session_scope == "shared": |
There was a problem hiding this comment.
This changes only the routing key, but on_processing_complete() still unconditionally ends every webhook session at current gateway/platforms/webhook.py:936-958. The next delivery sees that webhook_complete row as stale and receives a fresh session through SessionStore (gateway/session.py:2317-2388), so shared routes will not retain transcript history without a corresponding lifecycle change.
SummaryTwo open PRs address persistent webhook conversations: #57972 adds rendered per-payload session identities, fallback behavior, lifecycle handling, and documentation, while #71570 adds one shared session per route. Neither diff safely resolves the cause because conversation identity remains coupled to mutable response-routing state; #57972 also mishandles fallback lifecycle, and #71570 leaves completion-time auto-close unchanged. Related pull requests
Duplicates#57972 and #71570 substantially duplicate the opt-in persistent-webhook-session capability; #71570 is the constant-per-route variant, while #57972 additionally supports rendered per-payload conversation identities. Suggested consolidationKeep #57972 open with a salvage path: separate stable conversation identity from per-delivery response routing and skip auto-close only when that event successfully resolves a persistent key. Keep #71570 open while that correction is pending; once #57972 demonstrably covers the constant-per-route case and both contributor review blockers, #71570 can be closed as its narrower duplicate. Complex graphflowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
subgraph Dup57972 ["PRs duplicating each other"]
P57972["PR #57972 (open)"]
P71570["PR #71570 (open)"]
end
class P57972 open
class P71570 open
class P71570 target
click P57972 "https://github.com/NousResearch/hermes-agent/pull/57972"
click P71570 "https://github.com/NousResearch/hermes-agent/pull/71570"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label). Cross-PR triage: Reviewed 2 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 7 kB of PR diffs, 11 kB of issue/PR text, 4 kB of discussion (6 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch. |
Problem
Webhook routes derive the agent session key from the delivery id (
gateway/platforms/webhook.py):so every delivery spawns a brand-new, zero-context session. That is the right default for independent event streams (CI pings, monitoring alerts), but the wrong isolation granularity for conversational/workflow webhook consumers — e.g. an agent-to-agent relay where each message continues one ongoing collaboration.
Observed in production use (multi-agent setup, one route, serial messages):
Change
Opt-in, backward-compatible per-route setting in the webhook subscription:
Default
"per-delivery"preserves current behaviour exactly. With"shared", the session key omits the delivery id (webhook:{route_name}), so all deliveries on that route continue one persistent session. Deliveries on a shared route serialize into that session's queue rather than running concurrently — which is the desired semantics for workflow routes.Delivery-id dedup, HMAC verification, and delivery-info bookkeeping are unchanged (delivery info is keyed by the session chat id; on a shared route later deliveries refresh the same entry — same route, same target, no behavioural change to
send()).Related
Filing a separate issue for an adjacent defect this surfaced:
/approveis session-scoped while approval requests are delivered cross-platform, making dangerous-command gates in webhook sessions unapprovable. This PR reduces its blast radius (a shared session keeps pending state reachable across deliveries) but does not fix the routing itself.🤖 Generated with Claude Code