Skip to content

Commit bcc6093

Browse files
committed
feat(telegram): show commentary in progress drafts
1 parent 8ec5d09 commit bcc6093

9 files changed

Lines changed: 104 additions & 10 deletions

docs/channels/telegram.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ curl "https://api.telegram.org/bot<bot_token>/getUpdates"
319319
- `progress` keeps one editable status draft for tool progress, clears it at completion, and sends the final answer as a normal message
320320
- `streaming.preview.toolProgress` controls whether tool/progress updates reuse the same edited preview message (default: `true` when preview streaming is active)
321321
- `streaming.preview.commandText` controls command/exec detail inside those tool-progress lines: `raw` (default, preserves released behavior) or `status` (tool label only)
322+
- `streaming.progress.commentary` (default: `false`) opts into assistant commentary/preamble text in the temporary progress draft
322323
- legacy `channels.telegram.streamMode` and boolean `streaming` values are detected; run `openclaw doctor --fix` to migrate them to `channels.telegram.streaming.mode`
323324

324325
Tool-progress preview updates are the short status lines shown while tools run, for example command execution, file reads, planning updates, patch summaries, or Codex preamble/commentary text in Codex app-server mode. Telegram keeps these enabled by default to match released OpenClaw behavior from `v2026.4.22` and later.

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,6 +2338,60 @@ describe("dispatchTelegramMessage draft streaming", () => {
23382338
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n\n`🛠️ Exec`\n• _Checking files_");
23392339
});
23402340

2341+
it("renders configured Telegram commentary progress from preamble item events", async () => {
2342+
const draftStream = createSequencedDraftStream(2001);
2343+
createTelegramDraftStream.mockReturnValue(draftStream);
2344+
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
2345+
await replyOptions?.onReplyStart?.();
2346+
await replyOptions?.onItemEvent?.({
2347+
kind: "preamble",
2348+
itemId: "preamble-1",
2349+
progressText: "Checking recent context",
2350+
});
2351+
return { queuedFinal: false };
2352+
});
2353+
2354+
await dispatchWithContext({
2355+
context: createContext(),
2356+
streamMode: "progress",
2357+
telegramCfg: {
2358+
streaming: {
2359+
mode: "progress",
2360+
progress: { label: "Shelling", commentary: true },
2361+
},
2362+
},
2363+
});
2364+
2365+
expect(draftStream.update).toHaveBeenCalledWith("Shelling\n\n_Checking recent context_");
2366+
});
2367+
2368+
it("suppresses Telegram preamble progress when commentary is disabled", async () => {
2369+
const draftStream = createSequencedDraftStream(2001);
2370+
createTelegramDraftStream.mockReturnValue(draftStream);
2371+
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => {
2372+
await replyOptions?.onReplyStart?.();
2373+
await replyOptions?.onItemEvent?.({
2374+
kind: "preamble",
2375+
itemId: "preamble-1",
2376+
progressText: "Checking recent context",
2377+
});
2378+
return { queuedFinal: false };
2379+
});
2380+
2381+
await dispatchWithContext({
2382+
context: createContext(),
2383+
streamMode: "progress",
2384+
telegramCfg: {
2385+
streaming: {
2386+
mode: "progress",
2387+
progress: { label: "Shelling" },
2388+
},
2389+
},
2390+
});
2391+
2392+
expect(draftStream.update).not.toHaveBeenCalledWith(expect.stringContaining("Checking recent"));
2393+
});
2394+
23412395
it("keeps the progress draft label when tool progress lines are hidden", async () => {
23422396
const draftStream = createSequencedDraftStream(2001);
23432397
createTelegramDraftStream.mockReturnValue(draftStream);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,12 @@ export const dispatchTelegramMessage = async ({
19581958
await progressPromise;
19591959
},
19601960
onItemEvent: async (payload) => {
1961+
if (payload.kind === "preamble") {
1962+
await progressDraft.pushCommentaryProgress(payload.progressText, {
1963+
itemId: payload.itemId,
1964+
});
1965+
return;
1966+
}
19611967
await pushStreamToolProgress(
19621968
buildChannelProgressDraftLineForEntry(telegramCfg, {
19631969
event: "item",

extensions/telegram/src/config-schema.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,22 @@ describe("telegram custom commands schema", () => {
106106
});
107107
});
108108

109+
it("accepts Telegram progress commentary config", () => {
110+
expectTelegramConfigValid({
111+
streaming: {
112+
mode: "progress",
113+
progress: { commentary: true },
114+
},
115+
accounts: {
116+
ops: {
117+
streaming: {
118+
progress: { commentary: true },
119+
},
120+
},
121+
},
122+
});
123+
});
124+
109125
it("rejects removed DM thread reply policy keys", () => {
110126
expectTelegramConfigIssue({ dm: { threadReplies: "off" } }, "");
111127
expectTelegramConfigIssue(

extensions/telegram/src/config-ui-hints.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ export const telegramChannelConfigUiHints = {
109109
label: "Telegram Progress Command Text",
110110
help: 'Command/exec detail in progress draft lines: "raw" preserves released behavior; "status" shows only the tool label.',
111111
},
112+
"streaming.progress.commentary": {
113+
label: "Telegram Progress Commentary",
114+
help: "Show assistant commentary/preamble text in the temporary progress draft. Final answer delivery is unchanged.",
115+
},
112116
"retry.attempts": {
113117
label: "Telegram Retry Attempts",
114118
help: "Max retry attempts for outbound Telegram API calls (default: 3).",

src/config/bundled-channel-config-metadata.generated.ts

Lines changed: 4 additions & 4 deletions
Large diffs are not rendered by default.

src/config/schema.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ describe("config schema", () => {
284284
expect(progressPropsFor("discord")).not.toHaveProperty("nativeTaskCards");
285285
expect(progressPropsFor("telegram")).not.toHaveProperty("nativeTaskCards");
286286
expect(progressPropsFor("discord")).toHaveProperty("commentary");
287-
expect(progressPropsFor("telegram")).not.toHaveProperty("commentary");
287+
expect(progressPropsFor("telegram")).toHaveProperty("commentary");
288288
expect(res.uiHints["channels.matrix"]?.label).toBe("Matrix");
289289
expect(res.uiHints["channels.matrix.accessToken"]?.sensitive).toBe(true);
290290
expect(res.uiHints["channels.matrix.streaming.progress.label"]?.label).toBe(
@@ -298,7 +298,9 @@ describe("config schema", () => {
298298
expect(res.uiHints["channels.discord.streaming.progress.toolProgress"]?.label).toBe(
299299
"Discord Progress Tool Lines",
300300
);
301-
expect(res.uiHints["channels.telegram.streaming.progress.commentary"]).toBeUndefined();
301+
expect(res.uiHints["channels.telegram.streaming.progress.commentary"]?.label).toBe(
302+
"Telegram Progress Commentary",
303+
);
302304
expect(res.uiHints["channels.mattermost.streaming.progress.label"]?.label).toBe(
303305
"Mattermost Progress Label",
304306
);
@@ -456,7 +458,7 @@ describe("config schema", () => {
456458
).toBe(false);
457459
});
458460

459-
it("accepts progress commentary only for Discord streaming config", () => {
461+
it("accepts progress commentary for Discord and Telegram streaming config", () => {
460462
expect(
461463
DiscordConfigSchema.safeParse({
462464
streaming: {
@@ -473,7 +475,7 @@ describe("config schema", () => {
473475
progress: { commentary: true },
474476
},
475477
}).success,
476-
).toBe(false);
478+
).toBe(true);
477479
});
478480

479481
it("keeps per-agent model overrides limited to model selection", () => {

src/config/types.telegram.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {
22
ChannelPreviewStreamingConfig,
3+
ChannelStreamingProgressConfig,
34
ChannelStreamingPreviewConfig,
45
ContextVisibilityMode,
56
DmPolicy,
@@ -76,6 +77,12 @@ export type TelegramStreamingPreviewConfig = ChannelStreamingPreviewConfig & {
7677

7778
export type TelegramPreviewStreamingConfig = Omit<ChannelPreviewStreamingConfig, "preview"> & {
7879
preview?: TelegramStreamingPreviewConfig;
80+
progress?: TelegramStreamingProgressConfig;
81+
};
82+
83+
export type TelegramStreamingProgressConfig = ChannelStreamingProgressConfig & {
84+
/** Include assistant commentary/preamble text in the progress draft. Default: false. */
85+
commentary?: boolean;
7986
};
8087

8188
export type TelegramExecApprovalConfig = {

src/config/zod-schema.providers-core.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const ChannelStreamingProgressSchema = z
103103
commandText: z.enum(["raw", "status"]).optional(),
104104
})
105105
.strict();
106-
const DiscordStreamingProgressSchema = ChannelStreamingProgressSchema.extend({
106+
const ChannelCommentaryStreamingProgressSchema = ChannelStreamingProgressSchema.extend({
107107
commentary: z.boolean().optional(),
108108
}).strict();
109109
const SlackStreamingProgressSchema = ChannelStreamingProgressSchema.extend({
@@ -122,7 +122,7 @@ const TelegramPreviewStreamingConfigSchema = ChannelPreviewStreamingConfigSchema
122122
preview: TelegramStreamingPreviewSchema.optional(),
123123
}).strict();
124124
const DiscordPreviewStreamingConfigSchema = ChannelPreviewStreamingConfigSchema.extend({
125-
progress: DiscordStreamingProgressSchema.optional(),
125+
progress: ChannelCommentaryStreamingProgressSchema.optional(),
126126
}).strict();
127127
const SlackStreamingConfigSchema = ChannelPreviewStreamingConfigSchema.extend({
128128
nativeTransport: z.boolean().optional(),
@@ -1704,3 +1704,7 @@ export const MSTeamsConfigSchema = z
17041704
// so we cannot require them in the config object itself.
17051705
// Runtime validation happens in resolveMSTeamsCredentials().
17061706
});
1707+
1708+
// Keep this runtime-only widening out of exported schema declarations.
1709+
TelegramPreviewStreamingConfigSchema.shape.progress =
1710+
ChannelCommentaryStreamingProgressSchema.optional();

0 commit comments

Comments
 (0)