





















@@ -3040,6 +3040,61 @@ describe("dispatchTelegramMessage draft streaming", () => {
30403040expect(deliverReplies).not.toHaveBeenCalled();
30413041});
304230423043+it("falls back in forum topics when a queued final was not delivered to Telegram", async () => {
3044+const draftStream = createDraftStream(999);
3045+createTelegramDraftStream.mockReturnValue(draftStream);
3046+dispatchReplyWithBufferedBlockDispatcher.mockResolvedValue({
3047+queuedFinal: true,
3048+counts: { tool: 0, block: 0, final: 1 },
3049+});
3050+deliverReplies.mockResolvedValueOnce({ delivered: true });
3051+3052+await dispatchWithContext({
3053+context: createContext({
3054+isGroup: true,
3055+chatId: -1003752586071,
3056+primaryCtx: {
3057+message: { chat: { id: -1003752586071, type: "supergroup" } },
3058+} as TelegramMessageContext["primaryCtx"],
3059+msg: {
3060+chat: { id: -1003752586071, type: "supergroup" },
3061+message_id: 3,
3062+message_thread_id: 2,
3063+is_topic_message: true,
3064+} as TelegramMessageContext["msg"],
3065+threadSpec: { id: 2, scope: "forum" },
3066+ctxPayload: {
3067+SessionKey: "agent:main:telegram:group:-1003752586071:topic:2",
3068+MessageThreadId: 2,
3069+IsForum: true,
3070+} as unknown as TelegramMessageContext["ctxPayload"],
3071+}),
3072+cfg: {
3073+agents: {
3074+defaults: {
3075+silentReply: {
3076+direct: "disallow",
3077+group: "disallow",
3078+internal: "allow",
3079+},
3080+silentReplyRewrite: {
3081+group: false,
3082+},
3083+},
3084+},
3085+} as unknown as OpenClawConfig,
3086+});
3087+3088+expect(deliverReplies).toHaveBeenCalledTimes(1);
3089+expect(deliverReplies).toHaveBeenCalledWith(
3090+expect.objectContaining({
3091+chatId: "-1003752586071",
3092+thread: { id: 2, scope: "forum" },
3093+replies: [expect.objectContaining({ text: "NO_REPLY" })],
3094+}),
3095+);
3096+});
3097+30433098it("does not add silent-reply fallback after visible block delivery", async () => {
30443099const draftStream = createDraftStream(999);
30453100createTelegramDraftStream.mockReturnValue(draftStream);
@@ -4115,7 +4170,10 @@ describe("dispatchTelegramMessage draft streaming", () => {
41154170vi.useFakeTimers();
41164171const reactionApi = vi.fn(async () => true);
41174172const statusReactionController = createStatusReactionController();
4118-dispatchReplyWithBufferedBlockDispatcher.mockResolvedValue({ queuedFinal: true });
4173+dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => {
4174+await dispatcherOptions.deliver({ text: "Done" }, { kind: "final" });
4175+return { queuedFinal: true };
4176+});
41194177deliverReplies.mockResolvedValue({ delivered: true });
4120417841214179try {
@@ -4154,7 +4212,10 @@ describe("dispatchTelegramMessage draft streaming", () => {
41544212it("restores the initial Telegram status reaction after reply when removeAckAfterReply is disabled", async () => {
41554213const reactionApi = vi.fn(async () => true);
41564214const statusReactionController = createStatusReactionController();
4157-dispatchReplyWithBufferedBlockDispatcher.mockResolvedValue({ queuedFinal: true });
4215+dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => {
4216+await dispatcherOptions.deliver({ text: "Done" }, { kind: "final" });
4217+return { queuedFinal: true };
4218+});
41584219deliverReplies.mockResolvedValue({ delivered: true });
4159422041604221await dispatchWithContext({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。