|
5 | 5 | createPluginStateSyncKeyedStoreForTests, |
6 | 6 | resetPluginStateStoreForTests, |
7 | 7 | } from "openclaw/plugin-sdk/plugin-state-test-runtime"; |
| 8 | +import { setReplyPayloadMetadata } from "openclaw/plugin-sdk/reply-payload-testing"; |
8 | 9 | import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; |
9 | 10 | import { resolveAutoTopicLabelConfig as resolveAutoTopicLabelConfigRuntime } from "./auto-topic-label-config.js"; |
10 | 11 | import type { TelegramBotDeps } from "./bot-deps.js"; |
@@ -2612,6 +2613,73 @@ describe("dispatchTelegramMessage draft streaming", () => { |
2612 | 2613 | expectDeliveredReply(0, { text: "Boom" }); |
2613 | 2614 | }); |
2614 | 2615 |
|
| 2616 | + it("suppresses failed tool payloads after the final reply", async () => { |
| 2617 | + dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => { |
| 2618 | + await dispatcherOptions.deliver({ text: "Final answer" }, { kind: "final" }); |
| 2619 | + await dispatcherOptions.deliver( |
| 2620 | + { text: "Tool failed after final", isError: true }, |
| 2621 | + { kind: "tool" }, |
| 2622 | + ); |
| 2623 | + return { queuedFinal: true }; |
| 2624 | + }); |
| 2625 | + |
| 2626 | + await dispatchWithContext({ context: createContext(), streamMode: "off" }); |
| 2627 | + |
| 2628 | + expect(deliverReplies).toHaveBeenCalledTimes(1); |
| 2629 | + expectDeliveredReply(0, { text: "Final answer" }); |
| 2630 | + }); |
| 2631 | + |
| 2632 | + it("preserves final error warnings after the final reply", async () => { |
| 2633 | + dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => { |
| 2634 | + await dispatcherOptions.deliver({ text: "Final answer" }, { kind: "final" }); |
| 2635 | + await dispatcherOptions.deliver({ text: "Write failed", isError: true }, { kind: "final" }); |
| 2636 | + return { queuedFinal: true }; |
| 2637 | + }); |
| 2638 | + |
| 2639 | + await dispatchWithContext({ context: createContext(), streamMode: "off" }); |
| 2640 | + |
| 2641 | + expect(deliverReplies).toHaveBeenCalledTimes(2); |
| 2642 | + expectDeliveredReply(0, { text: "Final answer" }); |
| 2643 | + expectDeliveredReply(0, { text: "Write failed", isError: true }, 1); |
| 2644 | + }); |
| 2645 | + |
| 2646 | + it("suppresses non-terminal final error warnings after the final reply", async () => { |
| 2647 | + dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => { |
| 2648 | + await dispatcherOptions.deliver({ text: "Final answer" }, { kind: "final" }); |
| 2649 | + await dispatcherOptions.deliver( |
| 2650 | + setReplyPayloadMetadata( |
| 2651 | + { text: "Post-processing failed", isError: true }, |
| 2652 | + { nonTerminalToolErrorWarning: true }, |
| 2653 | + ), |
| 2654 | + { kind: "final" }, |
| 2655 | + ); |
| 2656 | + return { queuedFinal: true }; |
| 2657 | + }); |
| 2658 | + |
| 2659 | + await dispatchWithContext({ context: createContext(), streamMode: "off" }); |
| 2660 | + |
| 2661 | + expect(deliverReplies).toHaveBeenCalledTimes(1); |
| 2662 | + expectDeliveredReply(0, { text: "Final answer" }); |
| 2663 | + }); |
| 2664 | + |
| 2665 | + it("preserves non-terminal final error warnings before any final reply is delivered", async () => { |
| 2666 | + dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => { |
| 2667 | + await dispatcherOptions.deliver( |
| 2668 | + setReplyPayloadMetadata( |
| 2669 | + { text: "Post-processing failed", isError: true }, |
| 2670 | + { nonTerminalToolErrorWarning: true }, |
| 2671 | + ), |
| 2672 | + { kind: "final" }, |
| 2673 | + ); |
| 2674 | + return { queuedFinal: true }; |
| 2675 | + }); |
| 2676 | + |
| 2677 | + await dispatchWithContext({ context: createContext(), streamMode: "off" }); |
| 2678 | + |
| 2679 | + expect(deliverReplies).toHaveBeenCalledTimes(1); |
| 2680 | + expectDeliveredReply(0, { text: "Post-processing failed", isError: true }); |
| 2681 | + }); |
| 2682 | + |
2615 | 2683 | it("streams button-bearing text into the same message", async () => { |
2616 | 2684 | const { answerDraftStream } = setupDraftStreams({ answerMessageId: 2001 }); |
2617 | 2685 | const buttons = [[{ text: "OK", callback_data: "ok" }]]; |
|
0 commit comments