Skip to content

[Bug]: hooks.mappings[].agentId and sessionKey silently ignored for action="wake" #64556

@jaserNo1

Description

@jaserNo1

Summary

For webhook mappings configured with action: "wake", the agentId and sessionKey fields in hooks.mappings[*] are accepted by the config schema but silently ignored at dispatch time. Every wake-mode hook ends up enqueued into the default main agent's heartbeat session, regardless of the configured target.

This is silent — the gateway accepts the request, returns 200 {"ok":true,"mode":"now"}, and the user has no indication that the routing was disregarded.

Reproduction

  1. Configure a wake hook in openclaw.json targeting a non-default agent:
{
  hooks: {
    enabled: true,
    path: "/hooks",
    token: "<token>",
    mappings: [
      {
        id: "example-hook",
        match: { path: "example" },
        action: "wake",
        wakeMode: "now",
        agentId: "integrations",     // ← intended target
        sessionKey: "hook:example",  // ← intended session
        textTemplate: "Hello from {{name}}"
      }
    ]
  }
}
  1. Fire a request:
curl -X POST http://127.0.0.1:18789/hooks/example \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"world"}'
# → HTTP 200 {"ok":true,"mode":"now"}
  1. Inspect where the event landed:
    • Expected: agent:integrations:hook:example queue, integrations agent woken.
    • Actual: the text appears as a System (untrusted): […] line in the agent:main:main heartbeat session of the main agent. The integrations agent never wakes; its hook:example session never receives anything.

Root cause

dispatchWakeHook in the gateway hooks request handler hard-codes the target sessionKey to whatever resolveMainSessionKeyFromConfig() returns, ignoring value.sessionKey (and by extension agentId):

// dist/server.impl-*.js, ~line 26042 in v2026.4.9
const dispatchWakeHook = (value) => {
    const sessionKey = resolveMainSessionKeyFromConfig();   // ← always main
    enqueueSystemEvent(value.text, {
        sessionKey,
        trusted: false
    });
    if (value.mode === "now") requestHeartbeatNow({ reason: "hook:wake" });
};

For comparison, dispatchAgentHook (used when action: "agent") does respect value.sessionKey and the configured agentId because it builds an isolated cron job with explicit delivery — see ~line 26050 in the same file.

Why this matters

  • The schema for hooks.mappings[*] accepts agentId and sessionKey (per the type definitions in plugin-sdk/src/config/types.gateway.d.ts), so users reasonably expect them to take effect for any action.
  • Multi-agent setups silently funnel all wake hooks into the main agent. Users debugging "why doesn't my dedicated agent see the events" find a 200 OK at the gateway, no log entries, and events that appear in a completely different agent's session — making this very hard to diagnose.
  • Related history: Feature: Agent routing in webhook mappings (agentId field) #3432 (feature request for exactly this routing) was closed without action; the fields were later added to the schema, but the implementation was never wired through for wake.

Suggested fix

Either:

(a) Honor the configured sessionKey (and agentId, via session-key resolution) in dispatchWakeHook, falling back to the main session key only when none is provided:

const dispatchWakeHook = (value) => {
    const sessionKey = value.sessionKey
        ?? resolveSessionKeyForAgent(value.agentId)
        ?? resolveMainSessionKeyFromConfig();
    enqueueSystemEvent(value.text, { sessionKey, trusted: false });
    if (value.mode === "now") requestHeartbeatNow({ reason: `hook:wake:${sessionKey}` });
};

(b) If routing wake hooks to non-default agents is not intended, remove agentId/sessionKey from the schema for action: "wake" mappings and surface a config-time warning when they're set, so users aren't misled.

Either path is better than the current state where the config and runtime disagree silently.

Environment

  • openclaw 2026.4.9
  • Reproduced on macOS, but the bug is in dist/server.impl-*.js and is platform-independent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions