--- summary: "Progress drafts: one visible work-in-progress message that updates while an agent runs" read_when: - Configuring visible progress updates for long-running chat turns - Choosing between partial, block, and progress streaming modes - Explaining how OpenClaw updates one channel message while work is in progress - Troubleshooting progress drafts, standalone progress messages, or finalization fallback title: "Progress drafts" --- Progress drafts turn one channel message into a live status line while an agent works, instead of a stack of temporary "still working" replies. Set `channels..streaming.mode: "progress"` and OpenClaw creates the message once real work starts, edits it as the agent reads, plans, calls tools, or waits for approval, then turns it into the final answer. ```text Shelling... ๐Ÿ“– from docs/concepts/progress-drafts.md ๐Ÿ”Ž Web Search: for "discord edit message" ๐Ÿ› ๏ธ Bash: run tests ``` Discord already defaults to `streaming.mode: "progress"` when `channels.discord.streaming.mode`/`streamMode` are unset, so progress drafts show up there without any config. Every other channel defaults to `partial` or `off`; see [Streaming and chunking](/concepts/streaming#channel-mapping) for the full per-channel default table. ## Quick start ```json5 { channels: { discord: { streaming: { mode: "progress", }, }, }, } ``` Defaults from here: an automatic one-word label, a start delay of 5 seconds (or immediately on a second work event), compact progress lines while useful work happens, and suppression of the older standalone progress messages for that turn. This page covers the progress-draft experience and its config knobs. For the full streaming-mode matrix, per-channel runtime notes, and legacy key migration, see [Streaming and chunking](/concepts/streaming). ## What users see | Part | Purpose | | -------------- | --------------------------------------------------------------------------------- | | Label | Short starter/status line such as `Working` or `Shelling`. | | Progress lines | Compact run updates using the same tool icons and detail formatter as `/verbose`. | The label appears once the agent starts meaningful work and stays busy for the initial delay, or a second work event fires immediately. It sits at the top of the rolling progress-line list, so it scrolls away once enough concrete work lines appear. Plain text-only replies never show a progress draft; a line appears only for real work updates, for example `๐Ÿ› ๏ธ Bash: run tests`, `๐Ÿ”Ž Web Search: for "discord edit message"`, or `โœ๏ธ Write: to /tmp/file`. The final answer replaces the draft in place when the channel can safely do that; otherwise OpenClaw sends the final answer through normal delivery and cleans up or stops updating the draft (see [Finalization](#finalization)). ## Choose a mode `channels..streaming.mode` controls the visible in-progress behavior: | Mode | Best for | What appears in chat | | ---------- | -------------------------------- | ------------------------------------------------- | | `off` | Quiet channels | Only the final answer. | | `partial` | Watching answer text appear | One draft edited with the latest answer text. | | `block` | Larger answer-preview chunks | One preview updated or appended in bigger chunks. | | `progress` | Tool-heavy or long-running turns | One status draft, then the final answer. | Pick `progress` when users care more about "what is happening" than watching answer text stream token by token; `partial` when the answer text itself is the progress signal; `block` for larger preview chunks. On Discord and Telegram, `streaming.mode: "block"` is still preview streaming, not normal block-reply delivery โ€” use `streaming.block.enabled` (or legacy `blockStreaming`) for that. ## Configure labels Progress labels live under `channels..streaming.progress`. The default `label` is `"auto"`, which picks from OpenClaw's built-in single-word label pool: ```text Working, Shelling, Scuttling, Clawing, Pinching, Molting, Bubbling, Tiding, Reefing, Cracking, Sifting, Brining, Nautiling, Krilling, Barnacling, Lobstering, Tidepooling, Pearling, Snapping, Surfacing ``` Use a fixed label: ```json5 { channels: { discord: { streaming: { mode: "progress", progress: { label: "Investigating", }, }, }, }, } ``` Use your own label pool (still picked at random/by seed when `label: "auto"`): ```json5 { channels: { discord: { streaming: { mode: "progress", progress: { label: "auto", labels: ["Checking", "Reading", "Testing", "Finishing"], }, }, }, }, } ``` Hide the label and show only progress lines: ```json5 { channels: { discord: { streaming: { mode: "progress", progress: { label: false, }, }, }, }, } ``` ## Control progress lines Progress lines come from real run events: tool starts, item updates, task plans, approvals, command output, patch summaries, and similar agent activity. They are enabled by default (`progress.toolProgress`, default `true`). Tools can also emit typed progress while a single call is still running. That is how a slow fetch or search updates the visible draft before the tool returns its final result. The progress update is a partial tool result with empty model content and explicit public channel metadata: ```json { "content": [], "progress": { "text": "Fetching page content...", "visibility": "channel", "privacy": "public", "id": "web_fetch:fetching" } } ``` OpenClaw renders only `progress.text` in the channel progress UI. The normal tool result still arrives later as `content`/`details` and is the only part returned to the model. When adding progress to a tool, emit a short, generic message and delay it until the operation has been pending long enough to be useful. `web_fetch` does exactly this with a 5-second delay: ```typescript const clearProgressTimer = scheduleToolProgress( onUpdate, { text: "Fetching page content...", id: "web_fetch:fetching" }, 5_000, { signal }, ); try { return await runToolWork(); } finally { clearProgressTimer(); } ``` Fast calls show no progress line; long calls show one while still pending; canceled calls clear the timer before stale progress can appear. Progress text is a public UI side channel, so it must never include secrets, raw arguments, fetched content, command output, or page text. ### Detail mode OpenClaw uses the same formatter for progress drafts and `/verbose`: ```json5 { agents: { defaults: { toolProgressDetail: "explain", // explain | raw }, }, } ``` `"explain"` is the default and keeps drafts stable with concise labels. `"raw"` appends the underlying command when available, which is useful while debugging but noisier in chat. For example, a `node --check /tmp/app.js` call renders differently by mode: | Mode | Progress line | | --------- | --------------------------------------------------------------- | | `explain` | `๐Ÿ› ๏ธ check js syntax for /tmp/app.js` | | `raw` | `๐Ÿ› ๏ธ check js syntax for /tmp/app.js ยท node --check /tmp/app.js` | ### Command/exec text `streaming.progress.commandText` (default `"raw"`) controls how much command detail shows next to exec/bash progress lines, independent of the detail mode above. Set it to `"status"` to keep a tool-progress line visible while hiding the command text entirely: ```json5 { channels: { discord: { streaming: { mode: "progress", progress: { commandText: "status", }, }, }, }, } ``` ### Commentary lane `streaming.progress.commentary` (default `false`) interleaves the model's pre-tool commentary/preamble narration (๐Ÿ’ฌ, for example "I'll check... then ...") with tool lines in the draft. See [Streaming and chunking](/concepts/streaming#commentary-progress-lane) for the shared config shape across channels. ### Line limits Limit how many lines stay visible (default 8): ```json5 { channels: { discord: { streaming: { mode: "progress", progress: { maxLines: 4, }, }, }, }, } ``` Progress lines are compacted automatically to reduce chat-bubble reflow while the draft is edited, and OpenClaw truncates long lines so repeated draft edits do not wrap differently on every update. The default per-line budget is 120 characters; prose cuts at a word boundary, while long details such as paths or raw commands are shortened with a middle ellipsis so the suffix stays visible. Tune the per-line budget: ```json5 { channels: { discord: { streaming: { mode: "progress", progress: { maxLineChars: 160, }, }, }, }, } ``` ### Rich rendering (Slack) Slack can render progress lines as structured Block Kit fields instead of plain text: ```json5 { channels: { slack: { streaming: { mode: "progress", progress: { render: "rich", }, }, }, }, } ``` Rich rendering always sends the same plain-text body alongside the Block Kit fields, so clients that cannot render the richer shape still show the compact progress text. ### Hide tool/task lines Keep the single progress draft but hide tool and task lines: ```json5 { channels: { discord: { streaming: { mode: "progress", progress: { toolProgress: false, }, }, }, }, } ``` With `toolProgress: false`, OpenClaw still suppresses the older standalone tool-progress messages for that turn โ€” the channel stays visually quiet until the final answer, except for the label if one is configured. ## Channel behavior | Channel | Progress transport | Notes | | --------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | Discord | Send one message, then edit it. | Defaults to `progress` mode; final text edits in place when it fits one safe preview message. | | Matrix | Send one event, then edit it. | Account-level streaming config controls account-level drafts. | | Microsoft Teams | Native Teams stream in personal chats. | `streaming.mode: "block"` maps to Teams block delivery instead. | | Slack | Native stream or editable draft post. | Needs a reply thread target; top-level DMs without one still get draft preview posts and edits. | | Telegram | Send one message, then edit it. | If a message lands between the progress draft and the answer, the draft reposts below it (post-new-then-delete-old) instead of scroll-jumping the client. | | Mattermost | Editable draft post. | Tool activity folds into the same draft-style post. | Channels without safe edit support fall back to typing indicators or final-only delivery. See [Streaming and chunking](/concepts/streaming) for the full runtime-behavior breakdown per channel. ## Finalization When the final answer is ready, OpenClaw tries to keep the chat clean: - If the draft can safely become the final answer, OpenClaw edits it in place. - If the channel uses native progress streaming, OpenClaw finalizes that stream when the native transport accepts the final text. - Otherwise (media, an approval prompt, an explicit reply target, too many chunks, or a failed edit/send) OpenClaw sends the final answer through the normal channel delivery path instead of overwriting the draft. The fallback is intentional: sending a fresh final answer beats losing text, mis-threading a reply, or overwriting a draft with a payload the channel cannot represent safely. ## Troubleshooting **I only see the final answer.** Check that `channels..streaming.mode` is `progress` for the account or channel that handled the message. Some group or quote-reply paths disable draft previews for a turn when the channel cannot safely edit the right message. **I see the label but no tool lines.** Check `streaming.progress.toolProgress`. If it is `false`, OpenClaw keeps the single draft behavior but hides tool and task progress lines. **I see a fresh final message instead of an edited draft.** That is the safety fallback described in [Finalization](#finalization). It can happen for media replies, long answers, explicit reply targets, old Telegram drafts, missing Slack thread targets, deleted preview messages, or failed native stream finalization. **I still see standalone progress messages.** Progress mode suppresses default standalone tool-progress messages whenever a draft is active. If standalone messages still appear, confirm the turn is actually using `progress` mode and not `streaming.mode: "off"` or a channel path that cannot create a draft for that message. **Teams behaves differently from Discord or Telegram.** Microsoft Teams uses a native stream in personal chats instead of the generic send-and-edit preview transport, and maps `streaming.mode: "block"` to Teams block delivery because it has no draft-preview block mode like Discord and Telegram. ## Related - [Streaming and chunking](/concepts/streaming) - [Messages](/concepts/messages) - [Channel configuration](/gateway/config-channels) - [Discord](/channels/discord) - [Matrix](/channels/matrix) - [Microsoft Teams](/channels/msteams) - [Slack](/channels/slack) - [Telegram](/channels/telegram) - [Mattermost](/channels/mattermost)