From 8b438a308b82a52b055dd7cceccfd18b98512424 Mon Sep 17 00:00:00 2001 From: ImJarvis by LukeF <92253590+ImLukeF@users.noreply.github.com> Date: Mon, 16 Mar 2026 22:44:10 +1100 Subject: [PATCH] fix(telegram): keep silent error fallback replies quiet --- .../telegram/src/bot-message-dispatch.test.ts | 23 +++++++++++++++++++ .../telegram/src/bot-message-dispatch.ts | 11 +++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/extensions/telegram/src/bot-message-dispatch.test.ts b/extensions/telegram/src/bot-message-dispatch.test.ts index 64fe301658a..ea1c098e7b6 100644 --- a/extensions/telegram/src/bot-message-dispatch.test.ts +++ b/extensions/telegram/src/bot-message-dispatch.test.ts @@ -335,6 +335,29 @@ describe("dispatchTelegramMessage draft streaming", () => { ); }); + it("keeps fallback replies silent after an error reply is skipped", async () => { + dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ dispatcherOptions }) => { + dispatcherOptions.onSkip?.( + { text: "oops", isError: true }, + { kind: "final", reason: "empty" }, + ); + return { queuedFinal: false }; + }); + deliverReplies.mockResolvedValue({ delivered: true }); + + await dispatchWithContext({ + context: createContext(), + telegramCfg: { silentErrorReplies: true }, + }); + + expect(deliverReplies).toHaveBeenLastCalledWith( + expect.objectContaining({ + silent: true, + replies: [expect.objectContaining({ text: expect.any(String) })], + }), + ); + }); + it("keeps block streaming enabled when session reasoning level is on", async () => { loadSessionStore.mockReturnValue({ s1: { reasoningLevel: "on" }, diff --git a/extensions/telegram/src/bot-message-dispatch.ts b/extensions/telegram/src/bot-message-dispatch.ts index 61fc9f92fbf..9b603393450 100644 --- a/extensions/telegram/src/bot-message-dispatch.ts +++ b/extensions/telegram/src/bot-message-dispatch.ts @@ -515,6 +515,7 @@ export const dispatchTelegramMessage = async ({ }); let queuedFinal = false; + let hadErrorReplyFailureOrSkip = false; if (statusReactionController) { void statusReactionController.setThinking(); @@ -541,6 +542,9 @@ export const dispatchTelegramMessage = async ({ ...prefixOptions, typingCallbacks, deliver: async (payload, info) => { + if (payload.isError === true) { + hadErrorReplyFailureOrSkip = true; + } if (info.kind === "final") { // Assistant callbacks are fire-and-forget; ensure queued boundary // rotations/partials are applied before final delivery mapping. @@ -654,7 +658,10 @@ export const dispatchTelegramMessage = async ({ await flushBufferedFinalAnswer(); } }, - onSkip: (_payload, info) => { + onSkip: (payload, info) => { + if (payload.isError === true) { + hadErrorReplyFailureOrSkip = true; + } if (info.reason !== "silent") { deliveryState.markNonSilentSkip(); } @@ -811,7 +818,7 @@ export const dispatchTelegramMessage = async ({ const result = await deliverReplies({ replies: [{ text: fallbackText }], ...deliveryBaseOptions, - silent: silentErrorReplies && dispatchError != null, + silent: silentErrorReplies && (dispatchError != null || hadErrorReplyFailureOrSkip), }); sentFallback = result.delivered; }