fix(hooks): respect agentId and sessionKey for wake-mode hook mappings#64567
fix(hooks): respect agentId and sessionKey for wake-mode hook mappings#64567hclsys wants to merge 2 commits into
Conversation
dispatchWakeHook() hard-coded the session target to
resolveMainSessionKeyFromConfig(), ignoring any agentId or sessionKey
configured in hooks.mappings[]. Every wake-mode hook ended up in the
default main agent's heartbeat session regardless of the mapping config.
This is silent: the gateway returns 200 {ok:true,mode:"now"} and the
user has no indication that routing was disregarded. Multi-agent setups
silently funnel all wake hooks into the wrong agent — events appear in
a completely different agent's session from the configured target.
For comparison, the "agent" action path (dispatchAgentHook) already
resolves the hook's sessionKey and agentId from the mapping config via
resolveHookSessionKey + resolveHookTargetAgentId. The wake path was
simply never wired.
Fix:
- Add agentId/sessionKey to the wake variant of HookAction type
(hooks-mapping.ts)
- Thread them through buildActionFromMapping + mergeAction
(hooks-mapping.ts)
- Update dispatchWakeHook to accept + use them, falling back to
resolveMainSessionKeyFromConfig when neither is provided
(server/hooks.ts)
- Update the HooksHandlerDeps type to match (server-http.ts)
- Pass the fields through at the mapping dispatch call site
(server-http.ts:631)
When agentId is provided without sessionKey, the session key is
derived as `agent:${agentId}:main` — the agent's default main
session, matching the convention used elsewhere.
Fixes openclaw#64556
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 90ed1c8a36
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| dispatchWakeHook({ | ||
| text: mapped.action.text, | ||
| mode: mapped.action.mode, | ||
| agentId: mapped.action.agentId, | ||
| sessionKey: mapped.action.sessionKey, |
There was a problem hiding this comment.
Enforce session-key policy before mapped wake dispatch
This mapped-wake path now forwards mapped.action.sessionKey directly to dispatchWakeHook without the resolveHookSessionKey/isSessionKeyAllowedByPrefix checks used by mapped agent actions. In mappings that template or transform sessionKey from webhook payload data, callers can now inject wake events into arbitrary sessions even when hooks.allowedSessionKeyPrefixes is configured, which is a policy bypass introduced by this routing change.
Useful? React with 👍 / 👎.
| (value.agentId | ||
| ? `agent:${value.agentId}:main` | ||
| : resolveMainSessionKeyFromConfig()); |
There was a problem hiding this comment.
Resolve wake agent IDs through hook agent policy
The wake dispatcher derives agent:<agentId>:main from raw agentId and never applies resolveHookTargetAgentId/isHookAgentAllowed, so mapped wake hooks can target disallowed agents and no longer get unknown-agent fallback behavior. Any mapping or transform that sets agentId now bypasses the same agent policy gates enforced for mapped agent actions, causing inconsistent and potentially unauthorized routing.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR correctly wires
Confidence Score: 4/5Safe to merge after addressing the agentId normalization gap in dispatchWakeHook. The core fix (threading agentId/sessionKey through the wake mapping path) is structurally correct and the plumbing in hooks-mapping.ts and server-http.ts looks good. One P1 defect remains: the session key derived from agentId skips normalizeAgentId, creating a silent misroute for mixed-case or unknown agent IDs — the same class of failure the PR is meant to close. src/gateway/server/hooks.ts — the agentId-to-session-key derivation needs normalizeAgentId applied before embedding the ID in the key string. Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/gateway/server/hooks.ts
Line: 41-46
Comment:
**`agentId` not normalized before building session key**
The derived session key uses the raw (trimmed-only) agent ID. `buildMainSessionKey` and `normalizeHookDispatchSessionKey` both call `normalizeAgentId` which lowercases the ID before embedding it in the key. If a wake mapping has a mixed-case `agentId`, the resulting session key won't match the real session — silently routing the event nowhere, with a `200 ok` response.
The agent-action mapping path avoids this by going through `resolveHookTargetAgentId` (which normalizes and also falls back to the config default agent for unknown IDs). The wake path bypasses both normalization and that fallback. The fix is to call `normalizeAgentId` on `value.agentId` before embedding it in the session key string, consistent with how `buildMainSessionKey` in `main-session.ts` does it.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(hooks): respect agentId and sessionK..." | Re-trigger Greptile |
| const sessionKey = | ||
| value.sessionKey ?? | ||
| (value.agentId | ||
| ? `agent:${value.agentId}:main` | ||
| : resolveMainSessionKeyFromConfig()); | ||
| enqueueSystemEvent(value.text, { sessionKey, trusted: false }); |
There was a problem hiding this comment.
agentId not normalized before building session key
The derived session key uses the raw (trimmed-only) agent ID. buildMainSessionKey and normalizeHookDispatchSessionKey both call normalizeAgentId which lowercases the ID before embedding it in the key. If a wake mapping has a mixed-case agentId, the resulting session key won't match the real session — silently routing the event nowhere, with a 200 ok response.
The agent-action mapping path avoids this by going through resolveHookTargetAgentId (which normalizes and also falls back to the config default agent for unknown IDs). The wake path bypasses both normalization and that fallback. The fix is to call normalizeAgentId on value.agentId before embedding it in the session key string, consistent with how buildMainSessionKey in main-session.ts does it.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server/hooks.ts
Line: 41-46
Comment:
**`agentId` not normalized before building session key**
The derived session key uses the raw (trimmed-only) agent ID. `buildMainSessionKey` and `normalizeHookDispatchSessionKey` both call `normalizeAgentId` which lowercases the ID before embedding it in the key. If a wake mapping has a mixed-case `agentId`, the resulting session key won't match the real session — silently routing the event nowhere, with a `200 ok` response.
The agent-action mapping path avoids this by going through `resolveHookTargetAgentId` (which normalizes and also falls back to the config default agent for unknown IDs). The wake path bypasses both normalization and that fallback. The fix is to call `normalizeAgentId` on `value.agentId` before embedding it in the session key string, consistent with how `buildMainSessionKey` in `main-session.ts` does it.
How can I resolve this? If you propose a fix, please make it concise.The agentId-to-session-key derivation used the raw value without normalizeAgentId(), so mixed-case agent IDs produced keys that didn't match any real session — silently routing the event nowhere while returning 200 ok. The agent-action path avoids this by going through resolveHookTargetAgentId (which normalizes). Apply the same normalization to the wake path for consistency. Addresses greptile 4/5 P1 feedback on openclaw#64567.
This comment was marked as low quality.
This comment was marked as low quality.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4185d84d02
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| dispatchWakeHook({ | ||
| text: mapped.action.text, | ||
| mode: mapped.action.mode, | ||
| agentId: mapped.action.agentId, | ||
| sessionKey: mapped.action.sessionKey, |
There was a problem hiding this comment.
Enforce hook routing policy for mapped wake actions
This wake-mapping branch dispatches immediately with agentId/sessionKey and returns before running the policy checks used for mapped agent actions (isHookAgentAllowed, resolveHookSessionKey, and prefix enforcement). As a result, a hooks.mappings wake rule (especially one using transform/payload-derived routing fields) can route to disallowed agents or session-key prefixes even when hooks.allowedAgentIds / hooks.allowedSessionKeyPrefixes are configured, which breaks the documented hook safety constraints and can silently route events into unauthorized sessions.
Useful? React with 👍 / 👎.
Summary
dispatchWakeHook()hard-codes the session target toresolveMainSessionKeyFromConfig(), ignoring anyagentIdorsessionKeyconfigured inhooks.mappings[]. Every wake-mode hook ends up in the defaultmainagent's heartbeat session regardless of the mapping config — silently.The gateway returns
200 {"ok":true,"mode":"now"}and the user has no indication that routing was disregarded. Multi-agent setups silently funnel all wake hooks into the wrong agent.Fixes #64556
Root cause
For comparison,
dispatchAgentHook(theaction: "agent"path) already resolves the hook'ssessionKeyandagentIdfrom the mapping config viaresolveHookSessionKey+resolveHookTargetAgentId. The wake path was simply never wired.The mapping dispatch call at
server-http.ts:631passed onlytextandmode, discarding theagentId/sessionKeythatbuildActionFromMappingcould have propagated from the hook mapping config.Fix
Three files, ~30 LOC:
hooks-mapping.ts:agentId?: stringandsessionKey?: stringto thewakevariant ofHookActionbuildActionFromMapping(frommapping.agentId/mapping.sessionKey)mergeAction(for transform override support)server/hooks.ts:dispatchWakeHooksignature to accept optionalagentId/sessionKeyvalue.sessionKey ?? agent:${value.agentId}:main ?? resolveMainSessionKeyFromConfig()server-http.ts:HooksHandlerDeps.dispatchWakeHooktype to matchmapped.action.agentId/mapped.action.sessionKeyat the mapping dispatch siteFallback behavior
sessionKeyandagentIdsessionKey(explicit)agentId: "integrations"agent:integrations:mainresolveMainSessionKeyFromConfig()(existing behavior)What about the direct
/hooks/wakepath?The direct
POST /hooks/wakepath atserver-http.ts:533-541sendsnormalizeWakePayload(payload)which only extractstextandmodefrom the request body. This path continues to use the main session key — which is correct because direct wake requests don't have a mapping config to draw routing from. Only mapped wake hooks (hooks.mappings[].action: "wake") gain the new routing.Related work
fix(hooks): gateway hook event routing respects target agent session— fixes hook completion/error system event routing (different function, different code path). Complementary, not conflicting: AI-assisted: Gateway hook event routing respects target agent session #39046 fixesbuildCompletionSystemEvent, this PR fixesdispatchWakeHook. Both touchserver/hooks.tsbut different functions.wake.Scope
hooks-mapping.ts(+10/-1),server/hooks.ts(+14/-2),server-http.ts(+7/-1)agentaction path, no changes toresolveHookSessionKey, no changes to the direct/hooks/wakeendpointcc @steipete — gateway hooks routing. Credit to @jaserNo1 for the complete RCA and clear reproduction in #64556.