Skip to content

Commit c1c142e

Browse files
Merge a707989 into bd74a62
2 parents bd74a62 + a707989 commit c1c142e

2 files changed

Lines changed: 60 additions & 6 deletions

File tree

extensions/telegram/src/bot-message-dispatch.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2663,6 +2663,60 @@ describe("dispatchTelegramMessage draft streaming", () => {
26632663
expectDeliveredReply(0, { text: "Branch is up to date" });
26642664
});
26652665

2666+
it("clears progress drafts before durable verbose tool output", async () => {
2667+
const { answerDraftStream } = setupDraftStreams({ answerMessageId: 2001 });
2668+
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(
2669+
async ({ dispatcherOptions, replyOptions }) => {
2670+
await replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
2671+
replyOptions?.onVerboseProgressVisibility?.(() => true);
2672+
await dispatcherOptions.deliver({ text: "Tool output visible to Telegram" }, { kind: "tool" });
2673+
await dispatcherOptions.deliver({ text: "Final answer" }, { kind: "final" });
2674+
return { queuedFinal: true };
2675+
},
2676+
);
2677+
2678+
await dispatchWithContext({
2679+
context: createContext(),
2680+
streamMode: "progress",
2681+
telegramCfg: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
2682+
});
2683+
2684+
expect(answerDraftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`");
2685+
expectDeliveredReply(0, { text: "Tool output visible to Telegram" });
2686+
expectDeliveredReply(0, { text: "Final answer" }, 1);
2687+
expect(answerDraftStream.clear.mock.invocationCallOrder[0]).toBeLessThan(
2688+
deliverReplies.mock.invocationCallOrder[0],
2689+
);
2690+
});
2691+
2692+
it("clears progress drafts before visible tool artifacts", async () => {
2693+
const { answerDraftStream } = setupDraftStreams({ answerMessageId: 2001 });
2694+
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(
2695+
async ({ dispatcherOptions, replyOptions }) => {
2696+
await replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
2697+
await dispatcherOptions.deliver(
2698+
{ mediaUrl: "https://example.com/validation.txt" },
2699+
{ kind: "tool" },
2700+
);
2701+
await dispatcherOptions.deliver({ text: "Final answer" }, { kind: "final" });
2702+
return { queuedFinal: true };
2703+
},
2704+
);
2705+
2706+
await dispatchWithContext({
2707+
context: createContext(),
2708+
streamMode: "progress",
2709+
telegramCfg: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
2710+
});
2711+
2712+
expect(answerDraftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`");
2713+
expectDeliveredReply(0, { mediaUrl: "https://example.com/validation.txt" });
2714+
expectDeliveredReply(0, { text: "Final answer" }, 1);
2715+
expect(answerDraftStream.clear.mock.invocationCallOrder[0]).toBeLessThan(
2716+
deliverReplies.mock.invocationCallOrder[0],
2717+
);
2718+
});
2719+
26662720
it("does not stream text-only tool results into progress drafts", async () => {
26672721
const { answerDraftStream } = setupDraftStreams({ answerMessageId: 2001 });
26682722
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(

extensions/telegram/src/bot-message-dispatch.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2118,8 +2118,9 @@ export const dispatchTelegramMessage = async ({
21182118
}
21192119
if (segment.lane === "answer" && info.kind === "tool") {
21202120
if (verboseProgressActive()) {
2121-
// Durable lane owns tool payloads: send standalone instead
2122-
// of diverting into the draft, which is discarded at final.
2121+
if (streamMode === "progress") {
2122+
await rotateAnswerLaneAfterToolProgress();
2123+
}
21232124
if (
21242125
await sendPayload(
21252126
applyTextToPayload(effectivePayload, segment.update.text),
@@ -2130,10 +2131,6 @@ export const dispatchTelegramMessage = async ({
21302131
continue;
21312132
}
21322133
if (streamMode === "progress" && answerLane.stream) {
2133-
// Progress-mode streams render tool status in the
2134-
// live draft. Do not also emit text-only tool output
2135-
// as answer text, or simple commands duplicate and
2136-
// restart the progress draft.
21372134
continue;
21382135
}
21392136
await prepareAnswerLaneForToolProgress();
@@ -2275,6 +2272,9 @@ export const dispatchTelegramMessage = async ({
22752272
}
22762273
return;
22772274
}
2275+
if (streamMode === "progress" && info.kind === "tool") {
2276+
await rotateAnswerLaneAfterToolProgress();
2277+
}
22782278
const delivered = await sendPayload(effectivePayload, {
22792279
durable: info.kind === "final",
22802280
});

0 commit comments

Comments
 (0)