mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 08:01:23 +00:00
fix(announce): use deterministic idempotency keys to prevent duplicate subagent announces (#17150)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 54bba3cea1
Co-authored-by: widingmarcus-cyber <245375637+widingmarcus-cyber@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import crypto from "node:crypto";
|
||||
import { resolveQueueSettings } from "../auto-reply/reply/queue.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import {
|
||||
@@ -16,6 +15,11 @@ import {
|
||||
mergeDeliveryContext,
|
||||
normalizeDeliveryContext,
|
||||
} from "../utils/delivery-context.js";
|
||||
import {
|
||||
buildAnnounceIdFromChildRun,
|
||||
buildAnnounceIdempotencyKey,
|
||||
resolveQueueAnnounceId,
|
||||
} from "./announce-idempotency.js";
|
||||
import {
|
||||
isEmbeddedPiRunActive,
|
||||
queueEmbeddedPiMessage,
|
||||
@@ -113,6 +117,15 @@ async function sendAnnounce(item: AnnounceQueueItem) {
|
||||
const origin = item.origin;
|
||||
const threadId =
|
||||
origin?.threadId != null && origin.threadId !== "" ? String(origin.threadId) : undefined;
|
||||
// Share one announce identity across direct and queued delivery paths so
|
||||
// gateway dedupe suppresses true retries without collapsing distinct events.
|
||||
const idempotencyKey = buildAnnounceIdempotencyKey(
|
||||
resolveQueueAnnounceId({
|
||||
announceId: item.announceId,
|
||||
sessionKey: item.sessionKey,
|
||||
enqueuedAt: item.enqueuedAt,
|
||||
}),
|
||||
);
|
||||
await callGateway({
|
||||
method: "agent",
|
||||
params: {
|
||||
@@ -123,7 +136,7 @@ async function sendAnnounce(item: AnnounceQueueItem) {
|
||||
to: requesterIsSubagent ? undefined : origin?.to,
|
||||
threadId: requesterIsSubagent ? undefined : threadId,
|
||||
deliver: !requesterIsSubagent,
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
idempotencyKey,
|
||||
},
|
||||
timeoutMs: 15_000,
|
||||
});
|
||||
@@ -163,6 +176,7 @@ function loadRequesterSessionEntry(requesterSessionKey: string) {
|
||||
|
||||
async function maybeQueueSubagentAnnounce(params: {
|
||||
requesterSessionKey: string;
|
||||
announceId?: string;
|
||||
triggerMessage: string;
|
||||
summaryLine?: string;
|
||||
requesterOrigin?: DeliveryContext;
|
||||
@@ -199,6 +213,7 @@ async function maybeQueueSubagentAnnounce(params: {
|
||||
enqueueAnnounce({
|
||||
key: canonicalKey,
|
||||
item: {
|
||||
announceId: params.announceId,
|
||||
prompt: params.triggerMessage,
|
||||
summaryLine: params.summaryLine,
|
||||
enqueuedAt: Date.now(),
|
||||
@@ -543,8 +558,13 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
replyInstruction,
|
||||
].join("\n");
|
||||
|
||||
const announceId = buildAnnounceIdFromChildRun({
|
||||
childSessionKey: params.childSessionKey,
|
||||
childRunId: params.childRunId,
|
||||
});
|
||||
const queued = await maybeQueueSubagentAnnounce({
|
||||
requesterSessionKey: targetRequesterSessionKey,
|
||||
announceId,
|
||||
triggerMessage,
|
||||
summaryLine: taskLabel,
|
||||
requesterOrigin: targetRequesterOrigin,
|
||||
@@ -565,6 +585,10 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
const { entry } = loadRequesterSessionEntry(targetRequesterSessionKey);
|
||||
directOrigin = deliveryContextFromSession(entry);
|
||||
}
|
||||
// Use a deterministic idempotency key so the gateway dedup cache
|
||||
// catches duplicates if this announce is also queued by the gateway-
|
||||
// level message queue while the main session is busy (#17122).
|
||||
const directIdempotencyKey = buildAnnounceIdempotencyKey(announceId);
|
||||
await callGateway({
|
||||
method: "agent",
|
||||
params: {
|
||||
@@ -578,7 +602,7 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
!requesterIsSubagent && directOrigin?.threadId != null && directOrigin.threadId !== ""
|
||||
? String(directOrigin.threadId)
|
||||
: undefined,
|
||||
idempotencyKey: crypto.randomUUID(),
|
||||
idempotencyKey: directIdempotencyKey,
|
||||
},
|
||||
expectFinal: true,
|
||||
timeoutMs: 15_000,
|
||||
|
||||
Reference in New Issue
Block a user