mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
11b3bf374c
* feat(slack): unify the native progress turn into one streamed message
Native progress mode now streams the whole turn into a single Slack message:
narration flows as markdown_text chunks interleaved with plan/task chunks,
task rows carry file-path details and +N/-N output, the terminal task links
the session via url_source, and the final answer lands through stopStream in
the same message. Media/oversized/error finals keep their normal-delivery
fallbacks.
Deletes the finished-card receipt collapse (the '\u{1F6E0} N tool calls · ⏱ Ns' edit)
outright: the card now stays as its finished self.
Live-verified on a real workspace: exactly one bot message per turn.
* fix(slack): serialize native stream updates and keep append-only rendered text monotonic
Overlapping progress updates (compositor render, narration payload, final)
computed their narration delta before awaiting the network and committed
state only afterwards, so concurrent updates re-appended identical
narration into the streamed message (each status line landed 3x live).
A single ordering chain now makes each update's compute -> append -> commit
atomic.
applyAppendOnlyStreamUpdate also replaced the accumulated rendered text
with the incoming cumulative partial once an appended chunk had diverged
rendered from source, dropping content the sink already displayed; rendered
now only ever extends.
* fix(slack): stop refreshing thread status once a turn has visible output
Slack clears the assistant thread status as soon as the app puts anything
in the thread, and renders its own rotating agent-working row ("Generating
response...", "Finding answers...") for every status write after that -- it
ignores the app-supplied string. The typing keepalive re-set the status
every 3s for up to 60s, so each turn painted a duplicate status row under
the streamed card or progress message.
The status write is now gated on the turn having visible output, which the
dispatcher already tracks (delivered reply, committed preview, or posted
draft message). The first status still fires before any output, so slow
turns keep their indicator, and the typing reaction is tracked separately
so a suppressed status write still cleans up its reaction.
* fix(slack): let the plan card own the status line instead of echoing it
The status headline and plan explanation fed both the streamed narration
markdown and the plan card title, so every headline rendered twice: once as
static text and once in the card that keeps updating it in place. Narration
now carries only authored commentary and reasoning, and a preamble payload
whose text the card title already shows is not streamed again.
* feat(slack): make the native agent card the default progress surface
Slack's native plan/task card was opt-in behind streaming.progress
.nativeTaskCards while the Block Kit session card shipped as the default.
The native surface is the better product on every axis we can measure --
one streamed message instead of three artifacts, live task rows with file
paths and diff counts, and Slack's own agent chrome -- so it becomes the
default and an explicit false selects the Block Kit card instead.
The session link is now emitted only when it can actually work: the
operator set gateway.publicOrigin and left the Control UI enabled.
Installations with no externally reachable Gateway get no link rather than
a dead one.
The progress card still only appears for turns that do real work; the
existing compositor start gate keeps plain question-and-answer turns
card-free.
* fix(slack): finish the final inside a buffered native stream
A short narration leaves the SDK session un-flushed, so `delivered` stays
false until `stop` makes its first network call. Requiring delivery before
finishing in-stream sent the final through normal delivery and then
finalized the stream anyway, producing exactly the second message this path
exists to prevent. Stop-time rejection already falls back via
SlackStreamNotDeliveredError, so a live session is enough.
Addresses the ClawSweeper P1/P2 finding on this PR.
* refactor(slack): collapse duplicate streaming surfaces and drop dead code
Cleanup pass over the progress/streaming neighborhood, all verified unused
by exhaustive reference search:
- Deleted buildSlackProgressStreamStartChunks/UpdateChunks: byte-identical
pass-throughs to the same builder, plus the render-module branch that
chose between them. One exported builder now.
- Collapsed slackStreaming.draftMode, a lossless restatement of the mode it
was derived from, and its outbound mapper; nine comparisons now read the
mode directly. Inbound legacy parsing stays for doctor migration.
- Dropped stopSlackStream's text parameter, the draft stream's stop() member
and onMessageSent hook, a redundant nativeStreaming argument, four dead
members on the progress runtime, and two single-expression wrappers.
- Deduped the native card title, which was computed twice per render.
Production LOC for the whole PR drops from +216 to +114.
* chore(config): regenerate bundled channel metadata for the Slack card default
The generated metadata still carried the old opt-in help text and
default-false description for streaming.progress.nativeTaskCards, so
config UI and diagnostics would publish stale guidance.
* fix(slack): un-export the now-internal legacy draft-mode type
Collapsing draftMode removed the type's only external consumer, so knip
flagged it as an unused export. Doctor migration still parses these legacy
values inbound, so the type stays module-local.
436 lines
23 KiB
Markdown
436 lines
23 KiB
Markdown
---
|
|
summary: "Streaming + chunking behavior (block replies, channel preview streaming, mode mapping)"
|
|
read_when:
|
|
- Explaining how streaming or chunking works on channels
|
|
- Changing block streaming or channel chunking behavior
|
|
- Debugging duplicate/early block replies or channel preview streaming
|
|
title: "Streaming and chunking"
|
|
---
|
|
|
|
OpenClaw has two independent streaming layers, and there is **no true
|
|
token-delta streaming** to channel messages today:
|
|
|
|
- **Block streaming (channels):** emit completed **blocks** as the assistant
|
|
writes. These are normal channel messages, not token deltas.
|
|
- **Preview streaming (Telegram/Discord/Slack/Matrix/Mattermost/MS Teams):**
|
|
update a temporary **preview message** while generating (send + edits/appends).
|
|
|
|
## Control UI startup status
|
|
|
|
After `chat.send` acknowledges an active run, the Gateway can send a typed,
|
|
coarse startup status before assistant text or tool activity is visible. The
|
|
Control UI shows this status beside the working indicator, with stages for
|
|
workspace preparation, environment provisioning, context preparation, and
|
|
model startup.
|
|
|
|
The first assistant delta or tool start permanently replaces startup status for
|
|
that run. Approval status takes precedence while a tool is waiting for operator
|
|
action. Worktree creation and initial cloud dispatch happen before a chat run
|
|
exists, so their pre-run RPC progress is not presented as run startup status;
|
|
environment provisioning appears here only when an active run reprovisions a
|
|
reclaimed worker.
|
|
|
|
## Block streaming (channel messages)
|
|
|
|
Block streaming sends assistant output in coarse chunks as it becomes available.
|
|
|
|
```text
|
|
Model output
|
|
└─ text_delta/events
|
|
├─ (blockStreamingBreak=text_end)
|
|
│ └─ chunker emits blocks as buffer grows
|
|
└─ (blockStreamingBreak=message_end)
|
|
└─ chunker flushes at message_end
|
|
└─ channel send (block replies)
|
|
```
|
|
|
|
- `text_delta/events`: model stream events (may be sparse for non-streaming models).
|
|
- `chunker`: `EmbeddedBlockChunker` applying min/max bounds + break preference.
|
|
- `channel send`: actual outbound messages (block replies).
|
|
|
|
**Controls** (all under `agents.defaults` unless noted):
|
|
|
|
| Key | Values / shape | Default |
|
|
| ------------------------------------------------------------ | ----------------------------------------------------------------------- | ---------- |
|
|
| `blockStreamingDefault` | `"on"` / `"off"` | `"off"` |
|
|
| `blockStreamingBreak` | `"text_end"` / `"message_end"` | - |
|
|
| `blockStreamingChunk` | `{ minChars, maxChars, breakPreference? }` | - |
|
|
| `blockStreamingCoalesce` | `{ minChars?, maxChars?, idleMs? }` (merge streamed blocks before send) | - |
|
|
| `*.streaming.block.enabled` (channel override) | `true` / `false`, forces block streaming per channel (and per account) | - |
|
|
| `*.textChunkLimit` (e.g. `channels.whatsapp.textChunkLimit`) | number, hard cap | 4000 |
|
|
| `*.streaming.chunkMode` | `"length"` / `"newline"` | `"length"` |
|
|
| `channels.discord.maxLinesPerMessage` | number, soft line cap that splits tall replies to avoid UI clipping | 17 |
|
|
|
|
`streaming.chunkMode: "newline"` splits on blank lines (paragraph boundaries),
|
|
not every newline, before falling back to length chunking once the text
|
|
exceeds the limit.
|
|
|
|
Bundled channels spell these overrides as
|
|
`channels.<id>.streaming.{chunkMode,block.enabled,block.coalesce}`. The flat
|
|
`*.chunkMode` / `*.blockStreaming` / `*.blockStreamingCoalesce` spellings are
|
|
rejected everywhere. `openclaw doctor --fix` migrates legacy configs into the
|
|
nested shape.
|
|
|
|
**Boundary semantics** for `blockStreamingBreak`:
|
|
|
|
- `text_end`: stream blocks as soon as the chunker emits; flush on each `text_end`.
|
|
- `message_end`: wait until the assistant message finishes, then flush buffered
|
|
output. Still uses the chunker if the buffered text exceeds `maxChars`, so it
|
|
can emit multiple chunks at the end.
|
|
|
|
### Media delivery with block streaming
|
|
|
|
Streaming media must use structured payload fields such as `mediaUrl` or
|
|
`mediaUrls`; streamed text is not parsed as an attachment command. When block
|
|
streaming sends media early, OpenClaw remembers that delivery for the turn. If
|
|
the final assistant payload repeats the same media URL, final delivery strips
|
|
the duplicate media instead of sending the attachment again.
|
|
|
|
Exact duplicate final payloads are suppressed. If the final payload adds
|
|
distinct text around media that was already streamed, OpenClaw still sends the
|
|
new text while keeping the media single-delivery. This prevents duplicate voice
|
|
notes or files on channels such as Telegram.
|
|
|
|
## Chunking algorithm (low/high bounds)
|
|
|
|
Block chunking is implemented by `EmbeddedBlockChunker`:
|
|
|
|
- **Low bound:** don't emit until buffer >= `minChars` (unless forced).
|
|
- **High bound:** prefer splits before `maxChars`; if forced, split at `maxChars`.
|
|
- **Break preference chain:** `paragraph` -> `newline` -> `sentence` ->
|
|
whitespace -> hard break.
|
|
- **Code fences:** never split inside fences; when forced at `maxChars`, close
|
|
and reopen the fence to keep Markdown valid.
|
|
|
|
`maxChars` is clamped to the channel `textChunkLimit`, so you cannot exceed
|
|
per-channel caps.
|
|
|
|
## Coalescing (merge streamed blocks)
|
|
|
|
When block streaming is enabled, OpenClaw can **merge consecutive block
|
|
chunks** before sending them, reducing single-line spam while still providing
|
|
progressive output.
|
|
|
|
- Coalescing waits for **idle gaps** (`idleMs`) before flushing.
|
|
- Buffers are capped by `maxChars` and flush if they exceed it.
|
|
- `minChars` prevents tiny fragments from sending until enough text accumulates
|
|
(final flush always sends remaining text).
|
|
- Joiner is derived from `blockStreamingChunk.breakPreference`: `paragraph` ->
|
|
`\n\n`, `newline` -> `\n`, `sentence` -> space.
|
|
- Channel overrides are available via `*.streaming.block.coalesce` (including
|
|
per-account configs).
|
|
- Discord, Signal, and Slack default coalesce to `{ minChars: 1500, idleMs: 1000 }`
|
|
unless overridden.
|
|
|
|
## Human-like pacing between blocks
|
|
|
|
When block streaming is enabled, add a **randomized pause** between block
|
|
replies, after the first block, so multi-bubble responses feel more natural.
|
|
|
|
| `agents.defaults.humanDelay.mode` | Behavior |
|
|
| --------------------------------- | ----------------------- |
|
|
| `off` (default) | No pause |
|
|
| `natural` | 800-2500ms random pause |
|
|
| `custom` | `minMs`/`maxMs` |
|
|
|
|
Override per agent via `agents.entries.*.humanDelay`. Applies only to **block
|
|
replies**, not final replies or tool summaries.
|
|
|
|
## "Stream chunks or everything"
|
|
|
|
- **Stream chunks:** `blockStreamingDefault: "on"` + `blockStreamingBreak: "text_end"`
|
|
(emit as you go). Non-Telegram channels also need
|
|
`*.streaming.block.enabled: true`.
|
|
- **Stream everything at end:** `blockStreamingBreak: "message_end"` (flush
|
|
once, possibly multiple chunks if very long).
|
|
- **No block streaming:** `blockStreamingDefault: "off"` (only final reply).
|
|
|
|
Block streaming follows `agents.defaults.blockStreamingDefault` unless a
|
|
channel or account sets `*.streaming.block.enabled` explicitly. QQ Bot has no
|
|
`streaming.block` keys and streams block replies unless
|
|
`channels.qqbot.streaming.mode` is `"off"`. Channels can stream a live preview
|
|
(`channels.<channel>.streaming.mode`) without block replies. The
|
|
`blockStreaming*` defaults live under `agents.defaults`, not the config root.
|
|
|
|
For Discord and Telegram, an explicitly configured non-`off` preview mode
|
|
takes precedence over inherited `agents.defaults.blockStreamingDefault: "on"`.
|
|
Set that channel's `streaming.block.enabled: true` when block replies should
|
|
override its preview. If the preview is unavailable for a turn, inherited block
|
|
delivery still applies.
|
|
|
|
## Preview streaming modes
|
|
|
|
Canonical key: `channels.<channel>.streaming` (nested `{ mode, ... }`; legacy
|
|
top-level boolean/string spellings are rewritten by `openclaw doctor --fix`).
|
|
|
|
| Mode | Behavior |
|
|
| ---------- | --------------------------------------------------------------------- |
|
|
| `off` | Disable preview streaming |
|
|
| `partial` | Single preview replaced with latest text |
|
|
| `block` | Preview updates in chunked/appended steps |
|
|
| `progress` | Progress/status preview during generation, final answer at completion |
|
|
|
|
`streaming.mode: "block"` is a preview-streaming mode for edit-capable
|
|
channels such as Discord and Telegram; it does not by itself enable channel
|
|
block delivery there. Use `streaming.block.enabled` for normal block replies.
|
|
Microsoft Teams is the
|
|
exception: it has no draft-preview block transport, so `streaming.mode:
|
|
"block"` disables native streaming entirely and the reply lands as regular
|
|
block delivery instead of native partial/progress streaming. Mattermost also
|
|
differs: in `block` mode it rotates the preview between completed text and
|
|
tool-activity blocks, so earlier blocks stay visible as separate posts
|
|
instead of being overwritten in one editable draft.
|
|
|
|
### Channel mapping
|
|
|
|
Discord defaults to `off` when `streaming` is unset, Telegram and Slack default
|
|
to `progress`, and Mattermost and MS Teams default to `partial`.
|
|
|
|
| Channel | `off` | `partial` | `block` | `progress` |
|
|
| ---------- | ------------- | --------- | ------- | --------------------------------- |
|
|
| Telegram | Yes | Yes | Yes | editable progress draft (default) |
|
|
| Discord | Yes (default) | Yes | Yes | editable progress draft (opt-in) |
|
|
| Slack | Yes | Yes | Yes | Block Kit session card (default) |
|
|
| Mattermost | Yes | Yes | Yes | Yes |
|
|
| MS Teams | Yes | Yes | Yes | native progress stream |
|
|
|
|
Preview chunk config (`streaming.preview.chunk.*`, e.g. under
|
|
`channels.discord.streaming` or `channels.telegram.streaming`) defaults to
|
|
`minChars: 200`, `maxChars: 800` (clamped to the channel `textChunkLimit`), and
|
|
`breakPreference: "paragraph"`.
|
|
|
|
Slack-only:
|
|
|
|
- `channels.slack.streaming.nativeTransport` toggles Slack native streaming API
|
|
calls (`chat.startStream`/`chat.appendStream`/`chat.stopStream`) when
|
|
`channels.slack.streaming.mode="partial"` (`nativeTransport` defaults to `true`).
|
|
- Slack native streaming and Slack assistant thread status require a reply
|
|
thread target. Top-level DMs do not show that thread-style preview, but can
|
|
still use Slack draft preview posts and edits.
|
|
|
|
### Legacy key migration
|
|
|
|
| Channel | Legacy keys | Status |
|
|
| -------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| Telegram | `streamMode`, scalar/boolean `streaming` | Rewritten to `streaming.mode` by `openclaw doctor --fix`; not read at runtime |
|
|
| Discord | `streamMode`, boolean `streaming` | Rewritten to `streaming.mode` by `openclaw doctor --fix`; not read at runtime |
|
|
| Slack | `streamMode`; boolean `streaming`; legacy `nativeStreaming` | Rewritten to `streaming.mode` (and `streaming.nativeTransport` for the boolean/legacy forms) by `openclaw doctor --fix`; not read at runtime |
|
|
| Matrix | scalar/boolean `streaming` | Rewritten to `streaming.mode` (including Matrix's `"quiet"` mode) by `openclaw doctor --fix`; not read at runtime |
|
|
| Feishu | boolean `streaming` | Rewritten to `streaming.mode` by `openclaw doctor --fix`; not read at runtime |
|
|
| QQ Bot | boolean `streaming`; `streaming.c2cStreamApi` | Rewritten to `streaming.mode` (and `streaming.nativeTransport` for the boolean/`c2cStreamApi` forms) by `openclaw doctor --fix`; not read at runtime |
|
|
|
|
## Runtime behavior
|
|
|
|
### Telegram
|
|
|
|
- Uses `sendMessage` + `editMessageText` preview updates across DMs and
|
|
group/topics; final text edits the active preview in place. Telegram
|
|
ephemeral 30-second "typing" drafts (`sendMessageDraft`) are not used for
|
|
answer streaming.
|
|
- Short initial previews are still debounced for push-notification UX, but
|
|
materialize after a bounded delay so active runs do not stay visually silent.
|
|
- Long finals reuse the preview message for the first chunk and send only the
|
|
remaining chunks.
|
|
- `block` mode rotates the preview into a new message at
|
|
`streaming.preview.chunk.maxChars` (default 800, capped at Telegram's 4096
|
|
edit limit); other modes grow one preview up to 4096 characters.
|
|
- `progress` mode keeps tool progress in an editable status draft, materializes
|
|
the status label when answer streaming is active but no tool line is
|
|
available yet, clears the draft at completion, and sends the final answer
|
|
through normal delivery.
|
|
- If the final edit fails before the completed text is confirmed, OpenClaw uses
|
|
normal final delivery and cleans up the stale preview.
|
|
- Preview streaming is skipped when Telegram block streaming is explicitly
|
|
enabled, to avoid double-streaming.
|
|
- `/reasoning stream` can write reasoning to a transient preview that is
|
|
deleted after final delivery.
|
|
- Telegram selected quote replies are an exception: when `replyToMode` is not
|
|
`"off"` and selected quote text is present, OpenClaw skips the answer preview
|
|
stream for that turn (the final answer must go through the native quote-reply
|
|
path) so tool-progress preview lines cannot render. Current-message replies
|
|
without selected quote text still keep preview streaming. See
|
|
[Telegram channel docs](/channels/telegram) for details.
|
|
|
|
### Discord
|
|
|
|
- Uses send + edit preview messages.
|
|
- `block` mode uses draft chunking (`draftChunk`).
|
|
- Preview streaming is skipped when Discord block streaming is explicitly
|
|
enabled.
|
|
- `progress` mode appends a small `-#` activity receipt (thought/tool-call
|
|
counts and elapsed time) to the final answer and deletes the status draft
|
|
once that answer is delivered, so busy channels keep no orphaned tool log
|
|
above the reply. Error finals keep the draft as the record of the failed
|
|
turn.
|
|
- Final media, error, and explicit-reply payloads cancel pending previews
|
|
without flushing a new draft, then use normal delivery.
|
|
|
|
### Slack
|
|
|
|
- `partial` can use Slack native streaming (`chat.startStream`/`append`/`stop`)
|
|
when available.
|
|
- `block` uses append-style draft previews.
|
|
- `progress` streams Slack's native agent card by default: one message carries
|
|
narration, the live plan/task card, and the final answer. The card appears
|
|
only for turns that do real work, so plain questions are answered without one.
|
|
`streaming.progress.nativeTaskCards: false` falls back to the Block Kit
|
|
session card, which finalizes to success or error and posts the assistant's
|
|
final text as a separate message.
|
|
- Cards include **Open in OpenClaw** only when the session is actually openable:
|
|
`gateway.publicOrigin` is set and `gateway.controlUi.enabled` is not `false`.
|
|
- Top-level DMs without a reply thread use draft preview posts and edits
|
|
instead of Slack native streaming.
|
|
- Native and draft preview streaming suppress block replies for that turn, so a
|
|
Slack reply is streamed by one delivery path only.
|
|
- A successful turn with no visible reply still deletes its draft card. A
|
|
failed no-reply turn retains the card in its error state.
|
|
|
|
### Mattermost
|
|
|
|
- In `partial` mode, streams thinking and partial reply text into a single draft
|
|
preview post that finalizes in place when the final answer is safe to send.
|
|
- In `progress` mode, streams thinking and tool activity into a single status
|
|
preview that finalizes in place when the final answer is safe to send.
|
|
- In `block` mode, rotates between completed text and tool-activity posts;
|
|
parallel and consecutive tool updates share the current tool-activity post.
|
|
- Falls back to sending a fresh final post if the preview post was deleted or
|
|
is otherwise unavailable at finalize time.
|
|
- Final media/error payloads cancel pending preview updates before normal
|
|
delivery instead of flushing a temporary preview post.
|
|
|
|
### Matrix
|
|
|
|
- Draft previews finalize in place when the final text can reuse the preview
|
|
event.
|
|
- Media-only, error, and reply-target-mismatch finals cancel pending preview
|
|
updates before normal delivery; an already-visible stale preview is redacted.
|
|
|
|
## Tool-progress preview updates
|
|
|
|
Preview streaming can also include **tool-progress** updates: short status
|
|
lines like "searching the web", "reading file", or "calling tool" that appear
|
|
in the same preview message while tools are running, ahead of the final reply.
|
|
In Codex app-server mode, Codex preamble/commentary messages use this same
|
|
preview path, so short "I am checking..." progress notes can stream into the
|
|
editable draft without becoming part of the final answer. This keeps
|
|
multi-step tool turns visually alive instead of silent between the first
|
|
thinking preview and the final answer.
|
|
|
|
Long-running tools may emit typed progress before they return. For example,
|
|
`web_fetch` arms a five-second timer when it starts: if the fetch is still
|
|
pending, the preview shows `Fetching page content...`; if the fetch finishes or
|
|
is canceled before then, no progress line is emitted. The later final tool
|
|
result is still delivered normally to the model.
|
|
|
|
Supported surfaces:
|
|
|
|
- **Discord**, **Slack**, **Telegram**, and **Matrix** stream tool-progress and
|
|
Codex preamble updates into the live preview edit by default when preview
|
|
streaming is active. Microsoft Teams uses its native progress stream in
|
|
personal chats.
|
|
- Telegram has shipped with tool-progress preview updates enabled since
|
|
`v2026.4.22`; keeping them enabled preserves that released behavior.
|
|
- **Mattermost** folds tool activity into one preview post in `partial` and
|
|
`progress` modes, or one tool-activity post between text blocks in `block`
|
|
mode (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. On Telegram, `streaming.mode: "off"` is final-only: generic
|
|
progress chatter is also suppressed instead of delivered as standalone status
|
|
messages, while approval prompts, media payloads, and errors still route
|
|
normally.
|
|
- To keep preview streaming but hide tool-progress lines, set
|
|
`streaming.preview.toolProgress` or `streaming.progress.toolProgress` to
|
|
`false` for that channel (both default `true`, and both are honored in every
|
|
mode). To keep tool-progress lines visible while hiding command/exec text,
|
|
set `streaming.preview.commandText` or `streaming.progress.commandText` to
|
|
`"status"` (the default). Set either option to `"raw"` to opt into command
|
|
text. This policy is shared by draft/progress channels
|
|
that use OpenClaw's compact progress renderer, including Discord, Matrix,
|
|
Microsoft Teams, Mattermost, Slack session cards, and Telegram. To disable
|
|
preview edits entirely, set `streaming.mode` to `off`.
|
|
|
|
## Progress draft rendering
|
|
|
|
Progress-mode drafts (`streaming.progress.*`) are bounded and configurable per
|
|
channel:
|
|
|
|
| Key | Default | Behavior |
|
|
| --------------------------------- | ------------- | -------------------------------------------------------------- |
|
|
| `streaming.progress.maxLines` | `8` | Max compact progress lines kept below the draft label |
|
|
| `streaming.progress.maxLineChars` | `120` | Max characters per compact line before truncation (word-aware) |
|
|
| `streaming.progress.label` | `"auto"` | Draft title; a custom string, or `false` to hide it |
|
|
| `streaming.progress.labels` | built-in pool | Candidate labels used when `label: "auto"` |
|
|
|
|
Slack always renders progress mode as its fixed session-card layout; these
|
|
limits still bound the activity rows and plan text inside that card.
|
|
|
|
### Commentary progress lane
|
|
|
|
Beyond tool-progress, the compact progress renderer can surface one more lane
|
|
in the draft:
|
|
|
|
- **`streaming.progress.commentary`** - render the model's pre-tool
|
|
**commentary** (a short "I'll check... then..." narration) interleaved with
|
|
tool lines in the progress draft. On Discord and Telegram in progress mode,
|
|
the same preamble supplies the status headline even when this optional lane
|
|
is off; other channels keep their existing progress behavior. See
|
|
[Progress drafts](/concepts/progress-drafts#status-headline).
|
|
|
|
```json
|
|
{
|
|
"channels": {
|
|
"discord": {
|
|
"streaming": { "mode": "progress", "progress": { "commentary": true } }
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Keep progress lines visible but hide raw command/exec text:
|
|
|
|
```json
|
|
{
|
|
"channels": {
|
|
"telegram": {
|
|
"streaming": {
|
|
"mode": "partial",
|
|
"preview": {
|
|
"toolProgress": true,
|
|
"commandText": "status"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Use the same shape under another compact progress channel key, for example
|
|
`channels.discord`, `channels.matrix`, `channels.msteams`,
|
|
`channels.mattermost`, or Slack draft previews. For progress-draft mode, put
|
|
the same policy under `streaming.progress`:
|
|
|
|
```json
|
|
{
|
|
"channels": {
|
|
"telegram": {
|
|
"streaming": {
|
|
"mode": "progress",
|
|
"progress": {
|
|
"toolProgress": true,
|
|
"commandText": "status"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Related
|
|
|
|
- [Message lifecycle refactor](/concepts/message-lifecycle-refactor) - target shared preview, edit, stream, and finalization design
|
|
- [Progress drafts](/concepts/progress-drafts) - visible work-in-progress messages that update during long turns
|
|
- [Messages](/concepts/messages) - message lifecycle and delivery
|
|
- [Retry](/concepts/retry) - retry behavior on delivery failure
|
|
- [Channels](/channels) - per-channel streaming support
|