diff --git a/src/gateway/hooks.ts b/src/gateway/hooks.ts index 957056babcd..16f71eb8718 100644 --- a/src/gateway/hooks.ts +++ b/src/gateway/hooks.ts @@ -223,6 +223,7 @@ export type HookAgentPayload = { message: string; name: string; agentId?: string; + idempotencyKey?: string; wakeMode: "now" | "next-heartbeat"; sessionKey?: string; deliver: boolean; @@ -263,6 +264,21 @@ export function resolveHookDeliver(raw: unknown): boolean { return raw !== false; } +function resolveOptionalHookIdempotencyKey(raw: unknown): string | undefined { + return typeof raw === "string" && raw.trim() ? raw.trim() : undefined; +} + +export function resolveHookIdempotencyKey(params: { + payload: Record; + headers?: Record; +}): string | undefined { + return ( + resolveOptionalHookIdempotencyKey(params.headers?.["idempotency-key"]) || + resolveOptionalHookIdempotencyKey(params.headers?.["x-openclaw-idempotency-key"]) || + resolveOptionalHookIdempotencyKey(params.payload.idempotencyKey) + ); +} + export function resolveHookTargetAgentId( hooksConfig: HooksConfigResolved, agentId: string | undefined, @@ -366,6 +382,7 @@ export function normalizeAgentPayload(payload: Record): const agentIdRaw = payload.agentId; const agentId = typeof agentIdRaw === "string" && agentIdRaw.trim() ? agentIdRaw.trim() : undefined; + const idempotencyKey = resolveOptionalHookIdempotencyKey(payload.idempotencyKey); const wakeMode = payload.wakeMode === "next-heartbeat" ? "next-heartbeat" : "now"; const sessionKeyRaw = payload.sessionKey; const sessionKey = @@ -396,6 +413,7 @@ export function normalizeAgentPayload(payload: Record): message, name, agentId, + idempotencyKey, wakeMode, sessionKey, deliver,