fix(discord): fail dropped final reply delivery

This commit is contained in:
Patrick Erichsen
2026-05-04 17:13:32 -07:00
committed by Peter Steinberger
parent 1a4c078399
commit 9e97cdb213
8 changed files with 106 additions and 13 deletions

View File

@@ -283,6 +283,36 @@ describe("withReplyDispatcher", () => {
});
});
it("reconciles queuedFinal and counts after dispatcher-side delivery failure", async () => {
const dispatcher = {
sendToolResult: () => true,
sendBlockReply: () => true,
sendFinalReply: () => true,
getQueuedCounts: () => ({ tool: 0, block: 0, final: 0 }),
getCancelledCounts: () => ({ tool: 0, block: 0, final: 0 }),
getFailedCounts: () => ({ tool: 0, block: 0, final: 1 }),
markComplete: () => undefined,
waitForIdle: async () => undefined,
} satisfies ReplyDispatcher;
hoisted.dispatchReplyFromConfigMock.mockResolvedValueOnce({
queuedFinal: true,
counts: { tool: 0, block: 0, final: 1 },
});
const result = await dispatchInboundMessage({
ctx: buildTestCtx(),
cfg: {} as OpenClawConfig,
dispatcher,
replyResolver: async () => ({ text: "ok" }),
});
expect(result).toEqual({
queuedFinal: false,
counts: { tool: 0, block: 0, final: 0 },
failedCounts: { tool: 0, block: 0, final: 1 },
});
});
it("uses CommandTargetSessionKey for silent-reply policy on native command turns", async () => {
hoisted.createReplyDispatcherWithTypingMock.mockReturnValueOnce({
dispatcher: createDispatcher([]),

View File

@@ -103,19 +103,36 @@ function finalizeDispatchResult(
dispatcher: ReplyDispatcher,
): DispatchFromConfigResult {
const cancelledCounts = dispatcher.getCancelledCounts?.();
if (!cancelledCounts) {
const failedCounts = dispatcher.getFailedCounts?.();
if (!cancelledCounts && !failedCounts) {
return result;
}
const counts = {
tool: Math.max(0, result.counts.tool - cancelledCounts.tool),
block: Math.max(0, result.counts.block - cancelledCounts.block),
final: Math.max(0, result.counts.final - cancelledCounts.final),
const resultCounts = {
tool: result.counts?.tool ?? 0,
block: result.counts?.block ?? 0,
final: result.counts?.final ?? 0,
};
const counts = {
tool: Math.max(0, resultCounts.tool - (cancelledCounts?.tool ?? 0) - (failedCounts?.tool ?? 0)),
block: Math.max(
0,
resultCounts.block - (cancelledCounts?.block ?? 0) - (failedCounts?.block ?? 0),
),
final: Math.max(
0,
resultCounts.final - (cancelledCounts?.final ?? 0) - (failedCounts?.final ?? 0),
),
};
const hasFailedCounts =
(failedCounts?.tool ?? 0) > 0 ||
(failedCounts?.block ?? 0) > 0 ||
(failedCounts?.final ?? 0) > 0;
return {
...result,
queuedFinal: result.queuedFinal && counts.final > 0,
counts,
...(hasFailedCounts ? { failedCounts } : {}),
};
}

View File

@@ -8,6 +8,7 @@ import type { ReplyDispatchKind, ReplyDispatcher } from "./reply-dispatcher.type
export type DispatchFromConfigResult = {
queuedFinal: boolean;
counts: Record<ReplyDispatchKind, number>;
failedCounts?: Partial<Record<ReplyDispatchKind, number>>;
sourceReplyDeliveryMode?: SourceReplyDeliveryMode;
};