Progress Drafts: Live Status Updates for Agent Runs

Learn how progress drafts replace temporary status messages with a single live line that updates while an agent works. Configure streaming mode for Discord and Telegram.

Read this 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

Progress drafts replace a stream of temporary "still working" messages with a single live status line that updates while the agent is active. Once channels.<channel>.streaming.mode: "progress" is set, OpenClaw creates the message when real work begins, refreshes it throughout reading, planning, tool calls, and approval waits, and finally converts it into the completed response.

Working...
📖 from docs/concepts/progress-drafts.md
🔎 Web Search: for "discord edit message"
🛠️ Bash: run tests

Note

Discord enables preview streaming by default as off; you must set streaming.mode: "progress" to turn it on. Telegram uses progress as its default without extra setup. On either platform, set mode: "partial" to stream the answer text. Check Streaming and chunking for the complete per-channel defaults table.

Quick start

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
      },
    },
  },
}

The defaults below apply: a 1.5-second start delay, compact progress lines during active work, and removal of the older separate progress messages for that turn. Raw tool-line drafts automatically get a one-word label; a status headline omits that redundant title unless you explicitly configure one.

This document describes the progress-draft experience and its configuration options. For the complete streaming-mode table, per-channel runtime details, and legacy key migration, refer to Streaming and chunking.

What users see

PartPurpose
Status headlineOn Discord and Telegram, the model preamble; Discord adds a utility filler.
LabelOptional starter/status line such as Working.
Progress linesCompact run updates using the same tool icons and detail formatter as /verbose.

The status headline appears above the rolling progress lines, and both remain visible, so a single message conveys what the agent is doing and its progress.

For raw tool progress, the label shows once the agent begins meaningful work and remains active during the initial delay. It sits at the top of the rolling progress-line list, so it scrolls away as concrete work lines accumulate. The implicit label stays hidden when a status headline is present unless you configure one explicitly. Plain text-only replies never display a progress draft; a line appears only for real work updates, such as 🛠️ Bash: run tests, 🔎 Web Search: for "discord edit message", or ✍️ Write: to /tmp/file.

When the channel can safely do so, the final answer replaces the draft in place; otherwise OpenClaw delivers the final answer through normal channels and cleans up or stops updating the draft (see Finalization).

Choose a mode

channels.<channel>.streaming.mode controls the visible in-progress behavior:

ModeBest forWhat appears in chat
offQuiet channelsOnly the final answer.
partialWatching answer text appearOne draft edited with the latest answer text.
blockLarger answer-preview chunksOne preview updated or appended in bigger chunks.
progressTool-heavy or long-running turnsOne status draft, then the final answer.

Choose progress when users prioritize "what is happening" over watching answer text stream token by token; partial when the answer text itself serves as the progress signal; block for larger preview chunks. On Discord and Telegram, streaming.mode: "block" still counts as preview streaming, not normal block-reply delivery, so use streaming.block.enabled for that.

Configure labels

Progress labels are configured under channels.<channel>.streaming.progress. The default raw tool-line label is "auto", which uses the plain built-in Working label. A status headline hides that implicit label; set label: "auto" explicitly if you want a label above it too:

Working

Use a fixed label:

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          label: "Investigating",
        },
      },
    },
  },
}

Use your own label pool (still picked at random/by seed when label: "auto"):

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          label: "auto",
          labels: ["Checking", "Reading", "Testing", "Finishing"],
        },
      },
    },
  },
}

Hide the label and show only progress lines:

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          label: false,
        },
      },
    },
  },
}

Control progress lines

Progress lines derive from real run events: tool starts, item updates, task plans, approvals, command output, patch summaries, and similar agent activity. They are on by default (progress.toolProgress, default true) and remain visible below the status headline. Set progress.toolProgress: false to show only the headline.

Tools can also emit typed progress while a single call is still running. This 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:

{
  "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:

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:

{
  agents: {
    defaults: {
      toolProgressDetail: "explain", // explain | raw
    },
  },
}

"explain" is the default and keeps drafts stable with concise labels. "raw" appends underlying tool detail when available. Command text also requires the explicit streaming.progress.commandText: "raw" opt-in below. With that opt-in, a node --check /tmp/app.js call renders differently by mode:

ModeProgress 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 "status") sets how much command detail appears next to exec/bash progress lines, separate from the detail mode described earlier. Choose "raw" to include command text, or keep "status" to display only the tool-progress status:

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          commandText: "raw",
        },
      },
    },
  },
}

Commentary lane

streaming.progress.commentary (default false) mixes the model's pre-tool commentary and preamble narration (💬, like "I'll check... then...") into the draft alongside tool lines. The shared config structure across channels is covered in Streaming and chunking.

When the commentary lane is active, preambles show up only as those interleaved 💬 lines; the status headline below stays out of the way, preserving the lane's documented format.

Status headline

In progress mode on Discord and Telegram, the model's typed pre-tool preamble becomes the draft's status headline whenever one is available. Other progress-mode channels retain their existing status behavior. The headline is enabled by default and does not bypass the normal activity gate for short turns; setting streaming.progress.commentary routes preambles to the interleaved commentary lane instead.

On Discord, when a utility model resolves for the agent, either through an explicit utilityModel or the primary provider's declared small-model default (OpenAI → gpt-5.6-luna, Anthropic → claude-haiku-4-5), it provides a brief plain-language filler when the model produces no preamble or stays quiet for roughly 20 seconds (Telegram's headline is preamble-only at this time):

Updating the default model in your config, then restarting the gateway to pick
it up. One agent listing call failed and is being retried.

Utility narration is on by default (streaming.progress.narration, default true) and never falls back to the primary model: it operates only with an explicit utilityModel or a provider-declared default for the agent's primary provider. Set utilityModel: "" to turn off utility routing entirely. Tool lines continue accumulating underneath and return if both status sources stop. Draft edits still wait for the normal activity gate and an actual text change, which prevents flashes on fast turns and cuts edit churn in busy channels. Set narration: false to disable only the utility-model filler; model preamble headlines stay enabled:

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          narration: false,
        },
      },
    },
  },
}

Narration input is bounded and redacted: the utility model receives the inbound request text plus the same compact, redacted tool summaries the draft would render, never raw command output or tool results. With commandText: "status", narration input also excludes exec/bash command text, matching what the draft displays.

Line limits

Control how many lines stay visible (default 8):

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          maxLines: 4,
        },
      },
    },
  },
}

Progress lines are automatically compacted to minimize chat-bubble reflow during draft edits, and OpenClaw truncates long lines so repeated draft edits do not wrap differently on each 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 remains visible.

Adjust the per-line budget:

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          maxLineChars: 160,
        },
      },
    },
  },
}

Hide tool/task lines

Keep the single progress draft but hide tool and task lines:

{
  channels: {
    discord: {
      streaming: {
        mode: "progress",
        progress: {
          toolProgress: false,
        },
      },
    },
  },
}

With toolProgress: false, OpenClaw still suppresses the older standalone tool-progress messages for that turn, keeping the channel visually quiet until the final answer, except for the label if one is configured.

Channel behavior

ChannelProgress transportNotes
DiscordSend one message, then edit it.progress is explicit opt-in; the status draft is deleted after the final answer lands.
MatrixSend one event, then edit it.Account-level streaming config controls account-level drafts.
Microsoft TeamsNative Teams stream in personal chats.streaming.mode: "block" maps to Teams block delivery instead.
SlackNative stream or editable draft post.Card style is the default; progress.style: "compact" uses one text draft that an eligible final answer replaces.
TelegramSend 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.
MattermostEditable draft post.block mode rotates between completed text and tool-activity posts; other modes fold tool activity into the same draft-style post.

Channels lacking safe edit support fall back to typing indicators or final-only delivery. The full runtime-behavior breakdown per channel appears in Streaming and chunking.

Finalization

When the final answer is ready, OpenClaw aims to keep the chat tidy:

  • In progress mode on Discord, the final answer goes out as a fresh message and the status draft is deleted once that answer is delivered. Busy channels keep no orphaned tool log above the reply; error finals retain the draft as the visible record of the failed turn.
  • If the draft can safely become the final answer (partial/block modes), OpenClaw edits it in place.
  • Slack's compact progress style also promotes the progress draft into an eligible final text answer by editing that message 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 deliberate: 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.

Verify that channels.<channel>.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. 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.

2,262 words · updated Aug 24, 2026