diff --git a/CHANGELOG.md b/CHANGELOG.md index a7a897a2f37..1de3e3bfd48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ Docs: https://docs.openclaw.ai - Acpx/runtime: validate the runtime session mode at the `AcpxRuntime.ensureSession` wrapper boundary so callers that pass anything other than `persistent` or `oneshot` get a clear `ACP_INVALID_RUNTIME_OPTION` error instead of silently round-tripping through the encoded handle as a default `persistent` mode and later throwing `SessionResumeRequiredError`. Investigation context: #73071. (#73548) Thanks @amknight. - CLI/infer: keep web-search fallback on missing provider API keys, preserve structured validation errors from the selected provider, and let per-request image describe prompts override configured media-entry prompts. (#63263) Thanks @Spolen23. - Chat commands: include configured model-catalog reasoning metadata when building `/think` argument menus so Ollama Cloud and other provider-owned reasoning models show supported levels instead of only `off`. Fixes #73515; supersedes #73568. Thanks @danielzinhu99 and @neeravmakwana. +- Channels/Telegram: suppress generic tool-progress chatter when preview streaming is off, so non-streaming Telegram turns only deliver final replies while approvals, media, and errors still route normally. Refs #72363 and #72482. Thanks @neeravmakwana and @SweetSophia. - CLI/model probes: add repeatable image `--file` inputs to `infer model run` for local and gateway multimodal model smokes, so vision models such as Ollama Qwen VL and Gemini can be tested through the raw model-probe surface. Fixes #63700. Thanks @cedricjanssens. - CLI/image describe: pass `--prompt` and `--timeout-ms` through `infer image describe` and `describe-many`, so custom vision instructions and slow local model budgets reach media-understanding providers such as Ollama, OpenAI, Google, and OpenRouter. Refs #63700. Thanks @cedricjanssens. - WhatsApp/Web: pass explicit Baileys socket timings into every WhatsApp Web socket and expose `web.whatsapp.*` keepalive, connect, and query timeout settings so unstable networks can avoid repeated 408 disconnect and opening-handshake timeout loops. Fixes #56365. (#73580) Thanks @velvet-shark. diff --git a/docs/channels/telegram.md b/docs/channels/telegram.md index 6390e5312fd..fca04800a15 100644 --- a/docs/channels/telegram.md +++ b/docs/channels/telegram.md @@ -295,7 +295,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - Use `streaming.mode: "off"` only when you want to disable Telegram preview edits entirely. Use `streaming.preview.toolProgress: false` when you only want to disable the tool-progress status lines. + Use `streaming.mode: "off"` only when you want final-only delivery: Telegram preview edits are disabled and generic tool/progress chatter is suppressed instead of being sent as standalone "Working..." messages. Approval prompts, media payloads, and errors still route through normal final delivery. Use `streaming.preview.toolProgress: false` when you only want to keep answer preview edits while hiding the tool-progress status lines. For text-only replies: diff --git a/docs/concepts/streaming.md b/docs/concepts/streaming.md index ec55e80b1f1..fe6fbdbbe4f 100644 --- a/docs/concepts/streaming.md +++ b/docs/concepts/streaming.md @@ -191,7 +191,7 @@ Supported surfaces: - **Discord**, **Slack**, **Telegram**, and **Matrix** stream tool-progress into the live preview edit by default when preview streaming is active. - Telegram has shipped with tool-progress preview updates enabled since `v2026.4.22`; keeping them enabled preserves that released behavior. - **Mattermost** already folds tool activity into its single draft preview post (see above). -- Tool-progress edits follow the active preview streaming mode; they are skipped when preview streaming is `off` or when block streaming has taken over the message. +- Tool-progress edits follow the active preview streaming mode; they are skipped when preview streaming is `off` or when block streaming has taken over the message. On Telegram, `streaming.mode: "off"` is final-only: generic progress chatter is also suppressed instead of being delivered as standalone "Working..." messages, while approval prompts, media payloads, and errors still route normally. - To keep preview streaming but hide tool-progress lines, set `streaming.preview.toolProgress` to `false` for that channel. To disable preview edits entirely, set `streaming.mode` to `off`. Example: diff --git a/extensions/telegram/src/bot-message-dispatch.test.ts b/extensions/telegram/src/bot-message-dispatch.test.ts index 246a9d81d94..b5fb8b5e707 100644 --- a/extensions/telegram/src/bot-message-dispatch.test.ts +++ b/extensions/telegram/src/bot-message-dispatch.test.ts @@ -790,7 +790,7 @@ describe("dispatchTelegramMessage draft streaming", () => { ); }); - it("keeps default tool progress messages when answer preview streaming is off", async () => { + it("suppresses default tool progress messages when answer preview streaming is off", async () => { dispatchReplyWithBufferedBlockDispatcher.mockImplementation(async ({ replyOptions }) => { await replyOptions?.onToolStart?.({ name: "exec", phase: "start" }); await replyOptions?.onItemEvent?.({ progressText: "exec ls ~/Desktop" }); @@ -803,7 +803,7 @@ describe("dispatchTelegramMessage draft streaming", () => { expect(dispatchReplyWithBufferedBlockDispatcher).toHaveBeenCalledWith( expect.objectContaining({ replyOptions: expect.objectContaining({ - suppressDefaultToolProgressMessages: false, + suppressDefaultToolProgressMessages: true, }), }), ); diff --git a/extensions/telegram/src/bot-message-dispatch.ts b/extensions/telegram/src/bot-message-dispatch.ts index 8f4d0144f6d..a835aa231cb 100644 --- a/extensions/telegram/src/bot-message-dispatch.ts +++ b/extensions/telegram/src/bot-message-dispatch.ts @@ -1067,7 +1067,8 @@ export const dispatchTelegramMessage = async ({ previewToolProgressLines = []; }) : undefined, - suppressDefaultToolProgressMessages: Boolean(answerLane.stream), + suppressDefaultToolProgressMessages: + !previewStreamingEnabled || Boolean(answerLane.stream), onToolStart: async (payload) => { const toolName = payload.name?.trim(); if (statusReactionController && toolName) {