fix(telegram): prevent subsequent final payloads from overwriting preview message

When multiple final payloads were dispatched (e.g., model text + tool error
warning), each one tried to edit the draft preview message, causing the last
payload (tool error) to replace the model's text. Guard the preview-edit path
with `!finalizedViaPreviewMessage` so only the first final payload edits the
preview; subsequent payloads are sent as separate messages via deliverReplies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Tarun Sukhani
2026-02-16 12:07:18 +08:00
parent fee43d505d
commit f093be7b3a
2 changed files with 46 additions and 1 deletions

View File

@@ -259,4 +259,44 @@ describe("dispatchTelegramMessage draft streaming", () => {
}),
);
});
it("does not overwrite preview with subsequent final payloads", async () => {
const draftStream = createDraftStream(999);
createTelegramDraftStream.mockReturnValue(draftStream);
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(
async ({ dispatcherOptions, replyOptions }) => {
await replyOptions?.onPartialReply?.({ text: "Checking..." });
// First final payload: model text → edits the preview.
await dispatcherOptions.deliver({ text: "Here are your results" }, { kind: "final" });
// Second final payload: tool error warning → should NOT overwrite the preview.
await dispatcherOptions.deliver(
{ text: "⚠️ 🛠️ Exec: cmd failed: error" },
{ kind: "final" },
);
return { queuedFinal: true };
},
);
deliverReplies.mockResolvedValue({ delivered: true });
editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "999" });
await dispatchWithContext({ context: createContext() });
// Preview should be edited only once with the first final payload.
expect(editMessageTelegram).toHaveBeenCalledTimes(1);
expect(editMessageTelegram).toHaveBeenCalledWith(
123,
999,
"Here are your results",
expect.any(Object),
);
// Second final payload should fall through to deliverReplies as a new message.
expect(deliverReplies).toHaveBeenCalledTimes(1);
expect(deliverReplies).toHaveBeenCalledWith(
expect.objectContaining({
replies: [expect.objectContaining({ text: "⚠️ 🛠️ Exec: cmd failed: error" })],
}),
);
// Preview should NOT be cleared since it was finalized via edit.
expect(draftStream.clear).not.toHaveBeenCalled();
});
});

View File

@@ -287,7 +287,12 @@ export const dispatchTelegramMessage = async ({
| undefined
)?.buttons;
let draftStoppedForPreviewEdit = false;
if (!hasMedia && payload.text && typeof previewMessageId === "number") {
if (
!finalizedViaPreviewMessage &&
!hasMedia &&
payload.text &&
typeof previewMessageId === "number"
) {
const canFinalizeViaPreviewEdit = payload.text.length <= draftMaxChars;
if (canFinalizeViaPreviewEdit) {
draftStream?.stop();