- Block streaming (channels): emit completed blocks as the assistant writes. These are normal channel messages (not token deltas).
- Preview streaming (Telegram/Discord/Slack): update a temporary preview message while generating.
Block streaming (channel messages)
Block streaming sends assistant output in coarse chunks as it becomes available.text_delta/events: model stream events (may be sparse for non-streaming models).chunker:EmbeddedBlockChunkerapplying min/max bounds + break preference.channel send: actual outbound messages (block replies).
agents.defaults.blockStreamingDefault:"on"/"off"(default off).- Channel overrides:
*.blockStreaming(and per-account variants) to force"on"/"off"per channel. agents.defaults.blockStreamingBreak:"text_end"or"message_end".agents.defaults.blockStreamingChunk:{ minChars, maxChars, breakPreference? }.agents.defaults.blockStreamingCoalesce:{ minChars?, maxChars?, idleMs? }(merge streamed blocks before send).- Channel hard cap:
*.textChunkLimit(e.g.,channels.whatsapp.textChunkLimit). - Channel chunk mode:
*.chunkMode(lengthdefault,newlinesplits on blank lines (paragraph boundaries) before length chunking). - Discord soft cap:
channels.discord.maxLinesPerMessage(default 17) splits tall replies to avoid UI clipping.
text_end: stream blocks as soon as chunker emits; flush on eachtext_end.message_end: wait until assistant message finishes, then flush buffered output.
message_end still uses the chunker if the buffered text exceeds maxChars, so it can emit multiple chunks at the end.
Media delivery with block streaming
MEDIA: directives are normal delivery metadata. When block streaming sends a
media block early, OpenClaw remembers that delivery for the turn. If the final
assistant payload repeats the same media URL, the 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 when an agent emits MEDIA: during
streaming and the provider also includes it in the completed reply.
Chunking algorithm (low/high bounds)
Block chunking is implemented byEmbeddedBlockChunker:
- Low bound: don’t emit until buffer >=
minChars(unless forced). - High bound: prefer splits before
maxChars; if forced, split atmaxChars. - Break preference:
paragraph→newline→sentence→whitespace→ hard break. - Code fences: never split inside fences; when forced at
maxChars, close + reopen the fence to keep Markdown valid.
maxChars is clamped to the channel textChunkLimit, so you can’t exceed per-channel caps.
Coalescing (merge streamed blocks)
When block streaming is enabled, OpenClaw can merge consecutive block chunks before sending them out. This reduces “single-line spam” while still providing progressive output.- Coalescing waits for idle gaps (
idleMs) before flushing. - Buffers are capped by
maxCharsand will flush if they exceed it. minCharsprevents 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
*.blockStreamingCoalesce(including per-account configs). - Default coalesce
minCharsis bumped to 1500 for Signal/Slack/Discord unless overridden.
Human-like pacing between blocks
When block streaming is enabled, you can add a randomized pause between block replies (after the first block). This makes multi-bubble responses feel more natural.- Config:
agents.defaults.humanDelay(override per agent viaagents.list[].humanDelay). - Modes:
off(default),natural(800–2500ms),custom(minMs/maxMs). - Applies only to block replies, not final replies or tool summaries.
”Stream chunks or everything”
This maps to:- Stream chunks:
blockStreamingDefault: "on"+blockStreamingBreak: "text_end"(emit as you go). Non-Telegram channels also need*.blockStreaming: true. - Stream everything at end:
blockStreamingBreak: "message_end"(flush once, possibly multiple chunks if very long). - No block streaming:
blockStreamingDefault: "off"(only final reply).
*.blockStreaming is explicitly set to true. Channels can stream a live preview
(channels.<channel>.streaming) without block replies.
Config location reminder: the blockStreaming* defaults live under
agents.defaults, not the root config.
Preview streaming modes
Canonical key:channels.<channel>.streaming
Modes:
off: disable preview streaming.partial: single preview that is replaced with latest text.block: preview updates in chunked/appended steps.progress: progress/status preview during generation, final answer at completion.
Channel mapping
| Channel | off | partial | block | progress |
|---|---|---|---|---|
| Telegram | ✅ | ✅ | ✅ | maps to partial |
| Discord | ✅ | ✅ | ✅ | maps to partial |
| Slack | ✅ | ✅ | ✅ | ✅ |
| Mattermost | ✅ | ✅ | ✅ | ✅ |
channels.slack.streaming.nativeTransporttoggles Slack native streaming API calls whenchannels.slack.streaming.mode="partial"(default:true).- Slack native streaming and Slack assistant thread status require a reply thread target; top-level DMs do not show that thread-style preview.
- Telegram: legacy
streamModeand scalar/booleanstreamingvalues are detected and migrated by doctor/config compatibility paths tostreaming.mode. - Discord:
streamMode+ booleanstreamingauto-migrate tostreamingenum. - Slack:
streamModeauto-migrates tostreaming.mode; booleanstreamingauto-migrates tostreaming.modeplusstreaming.nativeTransport; legacynativeStreamingauto-migrates tostreaming.nativeTransport.
Runtime behavior
Telegram:- Uses
sendMessage+editMessageTextpreview updates across DMs and group/topics. - Preview streaming is skipped when Telegram block streaming is explicitly enabled (to avoid double-streaming).
/reasoning streamcan write reasoning to preview.
- Uses send + edit preview messages.
blockmode uses draft chunking (draftChunk).- Preview streaming is skipped when Discord block streaming is explicitly enabled.
- Final media, error, and explicit-reply payloads cancel pending previews without flushing a new draft, then use normal delivery.
partialcan use Slack native streaming (chat.startStream/append/stop) when available.blockuses append-style draft previews.progressuses status preview text, then final answer.- Native and draft preview streaming suppress block replies for that turn, so a Slack reply is streamed by one delivery path only.
- Final media/error payloads and progress finals do not create throwaway draft messages; only text/block finals that can edit the preview flush pending draft text.
- Streams thinking, tool activity, and partial reply text into a single draft preview post that finalizes in place when the final answer is safe to send.
- 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.
- 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. This keeps multi-step tool turns visually alive rather than silent between the first thinking preview and the final answer. Supported surfaces:- Discord, Slack, and Telegram 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
offor when block streaming has taken over the message. - To keep preview streaming but hide tool-progress lines, set
streaming.preview.toolProgresstofalsefor that channel. To disable preview edits entirely, setstreaming.modetooff.