mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 16:24:30 +00:00
feat(hooks): add agentId support to webhook mappings (#13672)
* feat(hooks): add agentId support to webhook mappings
Allow webhook mappings to route hook runs to a specific agent via
the new `agentId` field. This enables lightweight agents with minimal
bootstrap files to handle webhooks, reducing token cost per hook run.
The agentId is threaded through:
- HookMappingConfig (config type + zod schema)
- HookMappingResolved + HookAction (mapping types)
- normalizeHookMapping + buildActionFromMapping (mapping logic)
- mergeAction (transform override support)
- HookAgentPayload + normalizeAgentPayload (direct /hooks/agent endpoint)
- dispatchAgentHook → CronJob.agentId (server dispatch)
The existing runCronIsolatedAgentTurn already supports agentId on
CronJob — this change simply wires it through from webhook mappings.
Usage in config:
hooks.mappings[].agentId = "my-agent"
Usage via POST /hooks/agent:
{ "message": "...", "agentId": "my-agent" }
Includes tests for mapping passthrough and payload normalization.
Includes doc updates for webhook.md.
* fix(hooks): enforce webhook agent routing policy + docs/changelog updates (#13672) (thanks @BillChirico)
* fix(hooks): harden explicit agent allowlist semantics (#13672) (thanks @BillChirico)
---------
Co-authored-by: Pip <pip@openclaw.ai>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
This commit is contained in:
@@ -152,6 +152,53 @@ describe("hooks mapping", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("passes agentId from mapping", async () => {
|
||||
const mappings = resolveHookMappings({
|
||||
mappings: [
|
||||
{
|
||||
id: "hooks-agent",
|
||||
match: { path: "gmail" },
|
||||
action: "agent",
|
||||
messageTemplate: "Subject: {{messages[0].subject}}",
|
||||
agentId: "hooks",
|
||||
},
|
||||
],
|
||||
});
|
||||
const result = await applyHookMappings(mappings, {
|
||||
payload: { messages: [{ subject: "Hello" }] },
|
||||
headers: {},
|
||||
url: baseUrl,
|
||||
path: "gmail",
|
||||
});
|
||||
expect(result?.ok).toBe(true);
|
||||
if (result?.ok && result.action?.kind === "agent") {
|
||||
expect(result.action.agentId).toBe("hooks");
|
||||
}
|
||||
});
|
||||
|
||||
it("agentId is undefined when not set", async () => {
|
||||
const mappings = resolveHookMappings({
|
||||
mappings: [
|
||||
{
|
||||
id: "no-agent",
|
||||
match: { path: "gmail" },
|
||||
action: "agent",
|
||||
messageTemplate: "Subject: {{messages[0].subject}}",
|
||||
},
|
||||
],
|
||||
});
|
||||
const result = await applyHookMappings(mappings, {
|
||||
payload: { messages: [{ subject: "Hello" }] },
|
||||
headers: {},
|
||||
url: baseUrl,
|
||||
path: "gmail",
|
||||
});
|
||||
expect(result?.ok).toBe(true);
|
||||
if (result?.ok && result.action?.kind === "agent") {
|
||||
expect(result.action.agentId).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects missing message", async () => {
|
||||
const mappings = resolveHookMappings({
|
||||
mappings: [{ match: { path: "noop" }, action: "agent" }],
|
||||
|
||||
Reference in New Issue
Block a user