Skip to content

Commit 764dbf0

Browse files
committed
fix(discord): keep warning fallback after failed final
1 parent ce4134e commit 764dbf0

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

extensions/discord/src/monitor/message-handler.process.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ vi.mock("openclaw/plugin-sdk/reply-runtime", () => ({
235235
info: { kind: "block" | "final" },
236236
) => Promise<ReplyPayload | null> | ReplyPayload | null;
237237
deliver: (payload: unknown, info: { kind: "block" | "final" }) => Promise<void> | void;
238+
onError?: (err: unknown, info: { kind: "block" | "final" }) => void;
238239
transformReplyPayload?: (payload: ReplyPayload) => ReplyPayload | null;
239240
onSettled?: () => Promise<unknown> | unknown;
240241
};
@@ -258,7 +259,9 @@ vi.mock("openclaw/plugin-sdk/reply-runtime", () => ({
258259
await params.dispatcherOptions.deliver(deliverPayload, info);
259260
};
260261
const queueDelivery = (payload: ReplyPayload, info: { kind: "block" | "final" }) => {
261-
const delivery = Promise.resolve(deliver(payload, info)).catch(() => undefined);
262+
const delivery = Promise.resolve(deliver(payload, info)).catch((err: unknown) => {
263+
params.dispatcherOptions.onError?.(err, info);
264+
});
262265
pendingDeliveries.push(delivery);
263266
return true;
264267
};
@@ -2325,6 +2328,39 @@ describe("processDiscordMessage draft streaming", () => {
23252328
});
23262329
});
23272330

2331+
it("delivers tool warning finals when the recovered reply fails to send", async () => {
2332+
deliverDiscordReply.mockRejectedValueOnce(new Error("send failed"));
2333+
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {
2334+
await params?.dispatcher.sendFinalReply({ text: "delivery failed" });
2335+
await params?.dispatcher.waitForIdle();
2336+
await params?.dispatcher.sendFinalReply(createNonTerminalToolWarningPayload());
2337+
return {
2338+
queuedFinal: true,
2339+
counts: { final: 2, tool: 0, block: 0 },
2340+
failedCounts: { final: 1 },
2341+
};
2342+
});
2343+
2344+
const ctx = await createAutomaticSourceDeliveryContext({
2345+
discordConfig: { streamMode: "off" },
2346+
});
2347+
2348+
await runProcessDiscordMessage(ctx);
2349+
2350+
expect(deliverDiscordReply).toHaveBeenCalledTimes(2);
2351+
expect(firstMockArg(deliverDiscordReply, "deliverDiscordReply")).toMatchObject({
2352+
replies: [{ text: "delivery failed" }],
2353+
});
2354+
expect(deliverDiscordReply.mock.calls[1]?.[0]).toMatchObject({
2355+
replies: [
2356+
{
2357+
text: "⚠️ 🛠️ `run openclaw definitely-not-a-real-subcommand (agent)` failed",
2358+
isError: true,
2359+
},
2360+
],
2361+
});
2362+
});
2363+
23282364
it("keeps mutating tool warning finals after successful-looking replies", async () => {
23292365
const draftStream = createMockDraftStreamForTest();
23302366
dispatchInboundMessage.mockImplementationOnce(async (params?: DispatchInboundParams) => {

extensions/discord/src/monitor/message-handler.process.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,11 +546,13 @@ export async function processDiscordMessage(
546546
observer?.onFinalReplyStart?.();
547547
};
548548
let userFacingFinalDelivered = false;
549+
let userFacingFinalDeliveryFailed = false;
549550
let pendingToolWarningFinal:
550551
| { payload: ReplyPayload; info: { kind: ReplyDispatchKind } }
551552
| undefined;
552553
const markUserFacingFinalDelivered = () => {
553554
userFacingFinalDelivered = true;
555+
userFacingFinalDeliveryFailed = false;
554556
pendingToolWarningFinal = undefined;
555557
draftPreview.markFinalReplyDelivered();
556558
observer?.onFinalReplyDelivered?.();
@@ -630,7 +632,10 @@ export async function processDiscordMessage(
630632
!options?.allowFallbackOnlyToolWarning &&
631633
isFallbackOnlyToolWarningFinal(payload)
632634
) {
633-
if (!userFacingFinalDelivered && !finalReplyStartNotified) {
635+
if (
636+
!userFacingFinalDelivered &&
637+
(!finalReplyStartNotified || userFacingFinalDeliveryFailed)
638+
) {
634639
pendingToolWarningFinal = { payload, info };
635640
}
636641
return { visibleReplySent: false };
@@ -839,6 +844,9 @@ export async function processDiscordMessage(
839844
return { visibleReplySent: true };
840845
};
841846
const onDiscordDeliveryError = (err: unknown, info: { kind: string }) => {
847+
if (info.kind === "final" && finalReplyStartNotified && !userFacingFinalDelivered) {
848+
userFacingFinalDeliveryFailed = true;
849+
}
842850
runtime.error(
843851
danger(
844852
formatDiscordReplyDeliveryFailure({

0 commit comments

Comments
 (0)