Skip to content

Commit 663fabb

Browse files
committed
fix(telegram): render progress drafts as rich previews
1 parent c847db5 commit 663fabb

8 files changed

Lines changed: 329 additions & 99 deletions

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

Lines changed: 105 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,9 +1812,9 @@ describe("dispatchTelegramMessage draft streaming", () => {
18121812
telegramCfg: { streaming: { mode: "partial" } },
18131813
});
18141814

1815-
expect(mockCallArg(answerDraftStream.update)).toContain("Exec");
1816-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(2, "Done ");
1817-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(3, "Done answer");
1815+
expect(mockCallArg(answerDraftStream.updatePreview).text).toContain("Exec");
1816+
expect(answerDraftStream.update).toHaveBeenNthCalledWith(1, "Done ");
1817+
expect(answerDraftStream.update).toHaveBeenNthCalledWith(2, "Done answer");
18181818
expect(answerDraftStream.update).toHaveBeenLastCalledWith("Done answer.");
18191819
expect(deliverReplies).not.toHaveBeenCalled();
18201820
});
@@ -2219,15 +2219,14 @@ describe("dispatchTelegramMessage draft streaming", () => {
22192219

22202220
expect(answerDraftStream.update).toHaveBeenNthCalledWith(1, "Site A shows X.");
22212221
expect(answerDraftStream.update).toHaveBeenNthCalledWith(2, "Site A shows X.");
2222-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(
2223-
3,
2224-
expect.stringMatching(/`🛠 Exec`$/),
2222+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith(
2223+
expect.objectContaining({ text: expect.stringMatching(/`🛠 Exec`$/) }),
22252224
);
2226-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(4, "Final answer");
2225+
expect(answerDraftStream.update).toHaveBeenNthCalledWith(3, "Final answer");
22272226
expect(answerDraftStream.clear).toHaveBeenCalledTimes(1);
22282227
expect(answerDraftStream.forceNewMessage).toHaveBeenCalledTimes(2);
22292228
const progressResetOrder = answerDraftStream.forceNewMessage.mock.invocationCallOrder[0];
2230-
const progressUpdateOrder = answerDraftStream.update.mock.invocationCallOrder[2];
2229+
const progressUpdateOrder = answerDraftStream.updatePreview.mock.invocationCallOrder[0];
22312230
expect(progressResetOrder).toBeLessThan(progressUpdateOrder);
22322231
expect(deliverReplies).not.toHaveBeenCalled();
22332232
});
@@ -2247,12 +2246,11 @@ describe("dispatchTelegramMessage draft streaming", () => {
22472246
await dispatchWithContext({ context: createContext() });
22482247

22492248
expect(answerDraftStream.update).toHaveBeenNthCalledWith(1, "Site A shows X.");
2250-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(
2251-
2,
2252-
expect.stringMatching(/`🛠 Exec`$/),
2249+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith(
2250+
expect.objectContaining({ text: expect.stringMatching(/`🛠 Exec`$/) }),
22532251
);
2254-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(3, "Site B shows Y.");
2255-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(4, "Final answer");
2252+
expect(answerDraftStream.update).toHaveBeenNthCalledWith(2, "Site B shows Y.");
2253+
expect(answerDraftStream.update).toHaveBeenNthCalledWith(3, "Final answer");
22562254
expect(answerDraftStream.forceNewMessage).toHaveBeenCalledTimes(2);
22572255
expect(answerDraftStream.clear).toHaveBeenCalledTimes(1);
22582256
expect(deliverReplies).not.toHaveBeenCalled();
@@ -2290,16 +2288,15 @@ describe("dispatchTelegramMessage draft streaming", () => {
22902288

22912289
await dispatchWithContext({ context: createContext() });
22922290

2293-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(
2294-
1,
2295-
expect.stringMatching(/`🛠 Exec`$/),
2291+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith(
2292+
expect.objectContaining({ text: expect.stringMatching(/`🛠 Exec`$/) }),
22962293
);
2297-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(2, "Branch is up to date");
2294+
expect(answerDraftStream.update).toHaveBeenNthCalledWith(1, "Branch is up to date");
22982295
expect(answerDraftStream.forceNewMessage).toHaveBeenCalledTimes(1);
22992296
expect(answerDraftStream.clear).toHaveBeenCalledTimes(1);
23002297
const clearOrder = answerDraftStream.clear.mock.invocationCallOrder[0];
23012298
const rotationOrder = answerDraftStream.forceNewMessage.mock.invocationCallOrder[0];
2302-
const finalUpdateOrder = answerDraftStream.update.mock.invocationCallOrder[1];
2299+
const finalUpdateOrder = answerDraftStream.update.mock.invocationCallOrder[0];
23032300
expect(clearOrder).toBeLessThan(rotationOrder);
23042301
expect(rotationOrder).toBeLessThan(finalUpdateOrder);
23052302
});
@@ -2317,16 +2314,15 @@ describe("dispatchTelegramMessage draft streaming", () => {
23172314

23182315
await dispatchWithContext({ context: createContext() });
23192316

2320-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(
2321-
1,
2322-
expect.stringMatching(/`🛠 Exec`$/),
2317+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith(
2318+
expect.objectContaining({ text: expect.stringMatching(/`🛠 Exec`$/) }),
23232319
);
2324-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(2, "Branch is up to date");
2320+
expect(answerDraftStream.update).toHaveBeenNthCalledWith(1, "Branch is up to date");
23252321
expect(answerDraftStream.forceNewMessage).toHaveBeenCalledTimes(1);
23262322
expect(answerDraftStream.clear).toHaveBeenCalledTimes(1);
23272323
const clearOrder = answerDraftStream.clear.mock.invocationCallOrder[0];
23282324
const rotationOrder = answerDraftStream.forceNewMessage.mock.invocationCallOrder[0];
2329-
const finalUpdateOrder = answerDraftStream.update.mock.invocationCallOrder[1];
2325+
const finalUpdateOrder = answerDraftStream.update.mock.invocationCallOrder[0];
23302326
expect(clearOrder).toBeLessThan(rotationOrder);
23312327
expect(rotationOrder).toBeLessThan(finalUpdateOrder);
23322328
});
@@ -2373,9 +2369,13 @@ describe("dispatchTelegramMessage draft streaming", () => {
23732369
telegramCfg: { streaming: { mode: "progress" } },
23742370
});
23752371

2376-
expect(answerDraftStream.update).toHaveBeenCalledWith(
2377-
"Cracking\n\n`🛠️ Exec`\n\n`🛠️ git rev-parse --abbrev-ref HEAD`",
2378-
);
2372+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith({
2373+
text: "Cracking\n\n`🛠️ Exec`\n`🛠️ git rev-parse --abbrev-ref HEAD`",
2374+
richMessage: {
2375+
html: "<b>Cracking</b><br><b>🛠️ Exec</b><br><b>🛠️ Exec</b> <code>git rev-parse --abbrev-ref HEAD</code>",
2376+
skip_entity_detection: true,
2377+
},
2378+
});
23792379
expect(answerDraftStream.update).not.toHaveBeenCalledWith("Branch is up to date");
23802380
expect(answerDraftStream.forceNewMessage).toHaveBeenCalledTimes(1);
23812381
expect(answerDraftStream.clear).toHaveBeenCalledTimes(1);
@@ -2400,12 +2400,18 @@ describe("dispatchTelegramMessage draft streaming", () => {
24002400
telegramCfg: { streaming: { mode: "progress" } },
24012401
});
24022402

2403-
expect(answerDraftStream.update).toHaveBeenCalledTimes(2);
2404-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(1, "Cracking\n\n`🛠️ Exec`");
2405-
expect(answerDraftStream.update).toHaveBeenNthCalledWith(2, trailingFinalStatusText);
2403+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith({
2404+
text: "Cracking\n\n`🛠️ Exec`",
2405+
richMessage: {
2406+
html: "<b>Cracking</b><br><b>🛠️ Exec</b>",
2407+
skip_entity_detection: true,
2408+
},
2409+
});
2410+
expect(answerDraftStream.update).toHaveBeenCalledTimes(1);
2411+
expect(answerDraftStream.update).toHaveBeenNthCalledWith(1, trailingFinalStatusText);
24062412
expect(answerDraftStream.forceNewMessage).toHaveBeenCalledTimes(2);
24072413
expect(answerDraftStream.forceNewMessage.mock.invocationCallOrder[1]).toBeLessThan(
2408-
answerDraftStream.update.mock.invocationCallOrder[1],
2414+
answerDraftStream.update.mock.invocationCallOrder[0],
24092415
);
24102416
expectDeliveredReply(0, { text: "Branch is up to date" });
24112417
});
@@ -2430,11 +2436,13 @@ describe("dispatchTelegramMessage draft streaming", () => {
24302436
telegramCfg: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
24312437
});
24322438

2433-
expect(answerDraftStream.update).not.toHaveBeenCalledWith(
2439+
expect(answerDraftStream.updatePreview).not.toHaveBeenCalledWith(
24342440
expect.stringContaining("stdout line one"),
24352441
);
2436-
expect(answerDraftStream.update).toHaveBeenLastCalledWith(
2437-
"Shelling\n\n`🛠️ Exec`\n\n`🔎 Web Search: docs lookup`",
2442+
expect(answerDraftStream.updatePreview).toHaveBeenLastCalledWith(
2443+
expect.objectContaining({
2444+
text: "Shelling\n\n`🛠️ Exec`\n`🔎 Web Search: docs lookup`",
2445+
}),
24382446
);
24392447
expect(deliverReplies).not.toHaveBeenCalled();
24402448
});
@@ -2456,8 +2464,10 @@ describe("dispatchTelegramMessage draft streaming", () => {
24562464
telegramCfg: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
24572465
});
24582466

2459-
expect(answerDraftStream.update).toHaveBeenCalledTimes(1);
2460-
expect(answerDraftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`");
2467+
expect(answerDraftStream.updatePreview).toHaveBeenCalledTimes(1);
2468+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith(
2469+
expect.objectContaining({ text: "Shelling\n\n`🛠️ Exec`" }),
2470+
);
24612471
expectDeliveredReply(0, { text: "Branch is up to date" });
24622472
});
24632473

@@ -2484,8 +2494,10 @@ describe("dispatchTelegramMessage draft streaming", () => {
24842494
telegramCfg: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
24852495
});
24862496

2487-
expect(answerDraftStream.update).toHaveBeenCalledTimes(1);
2488-
expect(answerDraftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`");
2497+
expect(answerDraftStream.updatePreview).toHaveBeenCalledTimes(1);
2498+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith(
2499+
expect.objectContaining({ text: "Shelling\n\n`🛠️ Exec`" }),
2500+
);
24892501
expectDeliveredReply(0, { text: "Branch is up to date" });
24902502
});
24912503

@@ -2516,8 +2528,10 @@ describe("dispatchTelegramMessage draft streaming", () => {
25162528
telegramCfg: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
25172529
});
25182530

2519-
expect(answerDraftStream.update).toHaveBeenCalledTimes(1);
2520-
expect(answerDraftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`");
2531+
expect(answerDraftStream.updatePreview).toHaveBeenCalledTimes(1);
2532+
expect(answerDraftStream.updatePreview).toHaveBeenCalledWith(
2533+
expect.objectContaining({ text: "Shelling\n\n`🛠️ Exec`" }),
2534+
);
25212535
expectDeliveredReply(0, { text: "Branch is up to date" });
25222536
});
25232537

@@ -2648,7 +2662,13 @@ describe("dispatchTelegramMessage draft streaming", () => {
26482662
telegramCfg: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
26492663
});
26502664

2651-
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`");
2665+
expect(draftStream.updatePreview).toHaveBeenCalledWith({
2666+
text: "Shelling\n\n`🛠️ Exec`",
2667+
richMessage: {
2668+
html: "<b>Shelling</b><br><b>🛠️ Exec</b>",
2669+
skip_entity_detection: true,
2670+
},
2671+
});
26522672
expect(draftStream.flush).toHaveBeenCalled();
26532673
});
26542674

@@ -2670,7 +2690,13 @@ describe("dispatchTelegramMessage draft streaming", () => {
26702690
});
26712691

26722692
expect(createTelegramDraftStream).toHaveBeenCalledTimes(1);
2673-
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`\n\n• _Checking files_");
2693+
expect(draftStream.updatePreview).toHaveBeenCalledWith({
2694+
text: "Shelling\n\n`🛠️ Exec`\n• _Checking files_",
2695+
richMessage: {
2696+
html: "<b>Shelling</b><br><b>🛠️ Exec</b><br><i>Checking files</i>",
2697+
skip_entity_detection: true,
2698+
},
2699+
});
26742700
});
26752701

26762702
it("renders configured Telegram commentary progress from preamble item events", async () => {
@@ -2697,7 +2723,13 @@ describe("dispatchTelegramMessage draft streaming", () => {
26972723
},
26982724
});
26992725

2700-
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n\n_Checking recent context_");
2726+
expect(draftStream.updatePreview).toHaveBeenCalledWith({
2727+
text: "Shelling\n\n_Checking recent context_",
2728+
richMessage: {
2729+
html: "<b>Shelling</b><br><i>Checking recent context</i>",
2730+
skip_entity_detection: true,
2731+
},
2732+
});
27012733
});
27022734

27032735
it("suppresses Telegram preamble progress when commentary is disabled", async () => {
@@ -2724,7 +2756,9 @@ describe("dispatchTelegramMessage draft streaming", () => {
27242756
},
27252757
});
27262758

2727-
expect(draftStream.update).not.toHaveBeenCalledWith(expect.stringContaining("Checking recent"));
2759+
expect(draftStream.updatePreview).not.toHaveBeenCalledWith(
2760+
expect.objectContaining({ text: expect.stringContaining("Checking recent") }),
2761+
);
27282762
});
27292763

27302764
it("keeps the progress draft label when tool progress lines are hidden", async () => {
@@ -2748,7 +2782,10 @@ describe("dispatchTelegramMessage draft streaming", () => {
27482782
},
27492783
});
27502784

2751-
expect(draftStream.update).toHaveBeenCalledWith("Shelling");
2785+
expect(draftStream.updatePreview).toHaveBeenCalledWith({
2786+
text: "Shelling",
2787+
richMessage: { html: "<b>Shelling</b>", skip_entity_detection: true },
2788+
});
27522789
expect(draftStream.flush).toHaveBeenCalled();
27532790
});
27542791

@@ -2777,10 +2814,20 @@ describe("dispatchTelegramMessage draft streaming", () => {
27772814
},
27782815
});
27792816

2780-
await vi.waitFor(() => expect(draftStream.update).toHaveBeenCalledWith("Working"));
2781-
expect(draftStream.update).not.toHaveBeenCalledWith("Working.");
2782-
expect(draftStream.update).not.toHaveBeenCalledWith("Working..");
2783-
expect(draftStream.update).not.toHaveBeenCalledWith("Working...");
2817+
await vi.waitFor(() =>
2818+
expect(draftStream.updatePreview).toHaveBeenCalledWith(
2819+
expect.objectContaining({ text: "Working" }),
2820+
),
2821+
);
2822+
expect(draftStream.updatePreview).not.toHaveBeenCalledWith(
2823+
expect.objectContaining({ text: "Working." }),
2824+
);
2825+
expect(draftStream.updatePreview).not.toHaveBeenCalledWith(
2826+
expect.objectContaining({ text: "Working.." }),
2827+
);
2828+
expect(draftStream.updatePreview).not.toHaveBeenCalledWith(
2829+
expect.objectContaining({ text: "Working..." }),
2830+
);
27842831
finishRun?.();
27852832
await run;
27862833
});
@@ -2800,10 +2847,10 @@ describe("dispatchTelegramMessage draft streaming", () => {
28002847
const pendingToolStart = replyOptions?.onToolStart?.({ name: "exec", phase: "start" });
28012848
await Promise.resolve();
28022849
await Promise.resolve();
2803-
const updateBeforeStatusReaction = draftStream.update.mock.calls.at(-1)?.[0];
2850+
const updateBeforeStatusReaction = draftStream.updatePreview.mock.calls.at(-1)?.[0]?.text;
28042851
releaseSetTool?.();
28052852
await pendingToolStart;
2806-
expect(updateBeforeStatusReaction).toMatch(/^Shelling\n`🛠 Exec`$/);
2853+
expect(updateBeforeStatusReaction).toBe("Shelling\n\n`🛠️ Exec`");
28072854
return { queuedFinal: false };
28082855
});
28092856

@@ -2839,9 +2886,13 @@ describe("dispatchTelegramMessage draft streaming", () => {
28392886
telegramCfg: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
28402887
});
28412888

2842-
expect(draftStream.update).toHaveBeenCalledWith(
2843-
"Shelling\n\n`🔎 Web Search: docs lookup`\n\n• `tests passed`",
2844-
);
2889+
expect(draftStream.updatePreview).toHaveBeenCalledWith({
2890+
text: "Shelling\n\n`🔎 Web Search: docs lookup`\n• `tests passed`",
2891+
richMessage: {
2892+
html: "<b>Shelling</b><br><b>🔎 Web Search</b> <code>docs lookup</code><br><b>Update</b> <code>tests passed</code>",
2893+
skip_entity_detection: true,
2894+
},
2895+
});
28452896
expect(draftStream.forceNewMessage).toHaveBeenCalledTimes(1);
28462897
expect(draftStream.materialize).not.toHaveBeenCalled();
28472898
expect(draftStream.clear).toHaveBeenCalledTimes(1);
@@ -3984,7 +4035,7 @@ describe("dispatchTelegramMessage draft streaming", () => {
39844035
telegramCfg: { streaming: { mode: "partial" } },
39854036
});
39864037

3987-
expect(mockCallArg(answerDraftStream.update)).toContain("Exec");
4038+
expect(mockCallArg(answerDraftStream.updatePreview).text).toContain("Exec");
39884039
expect(answerDraftStream.update).toHaveBeenLastCalledWith("block after progress");
39894040
});
39904041

0 commit comments

Comments
 (0)