Problem
When the model produces text → calls tools → produces more text → calls more tools → delivers final text, all intermediate text blocks share the same streaming preview message and are overwritten by subsequent blocks. Only the final text is visible to the user. Meaningful intermediate content (summaries, findings, explanations) is silently lost.
This is a regression from PR #32890.
Steps to reproduce
- Ask the agent to browse multiple websites in one turn
- Agent writes "Let me check site A..." → streams to preview
- Agent calls browser tools
- Agent writes "Site A shows X. Now checking site B..." → overwrites preview
- Agent calls more tools
- Agent writes "Done. Sites A and B show X and Y." → overwrites preview again, finalizes
- User sees only: "Let me check site A..." and "Done." — the intermediate summary is lost
Any turn where the model produces multiple meaningful text blocks separated by tool calls will silently lose text.
Root cause
PR #32890 removed the archive + forceNewMessage logic from onAssistantMessageStart in bot-message-dispatch.ts to prevent inter-tool narration from leaking into chat as separate messages. But it removed ALL interleaved delivery — not just the transient narration.
The stream now reuses a single preview across all text blocks in a turn. The streamText function in lane-delivery-text-deliverer.ts updates that preview in place for non-final blocks (isFinal=false) but never commits them as real messages before the final block overwrites them. The onAssistantMessageStart handler only calls rotateLaneForNewMessage when the lane is already finalized — which never happens for intermediate blocks.
Relevant code
extensions/telegram/src/bot-message-dispatch.ts → onAssistantMessageStart handler — only rotates when answerLane.finalized is true
extensions/telegram/src/lane-delivery-text-deliverer.ts → streamText() — updates preview in place for non-final blocks, only commits on isFinal=true
extensions/telegram/src/draft-stream.ts → onSupersededPreview callback — deletes superseded previews when forceNewMessage() is called
Prior art
Expected behavior
Text blocks delivered with info.kind === "block" (non-final) should either be finalized as real messages before a new text block starts, or be cumulatively preserved so the user sees all text the model produced.
Environment
- OpenClaw: 2026.5.26 (10ad3aa) — also reproducible on main as of 2026-05-27
- Channel: Telegram (streaming mode: partial)
Problem
When the model produces text → calls tools → produces more text → calls more tools → delivers final text, all intermediate text blocks share the same streaming preview message and are overwritten by subsequent blocks. Only the final text is visible to the user. Meaningful intermediate content (summaries, findings, explanations) is silently lost.
This is a regression from PR #32890.
Steps to reproduce
Any turn where the model produces multiple meaningful text blocks separated by tool calls will silently lose text.
Root cause
PR #32890 removed the
archive + forceNewMessagelogic fromonAssistantMessageStartinbot-message-dispatch.tsto prevent inter-tool narration from leaking into chat as separate messages. But it removed ALL interleaved delivery — not just the transient narration.The stream now reuses a single preview across all text blocks in a turn. The
streamTextfunction inlane-delivery-text-deliverer.tsupdates that preview in place for non-final blocks (isFinal=false) but never commits them as real messages before the final block overwrites them. TheonAssistantMessageStarthandler only callsrotateLaneForNewMessagewhen the lane is already finalized — which never happens for intermediate blocks.Relevant code
extensions/telegram/src/bot-message-dispatch.ts→onAssistantMessageStarthandler — only rotates whenanswerLane.finalizedis trueextensions/telegram/src/lane-delivery-text-deliverer.ts→streamText()— updates preview in place for non-final blocks, only commits on isFinal=trueextensions/telegram/src/draft-stream.ts→onSupersededPreviewcallback — deletes superseded previews when forceNewMessage() is calledPrior art
Expected behavior
Text blocks delivered with
info.kind === "block"(non-final) should either be finalized as real messages before a new text block starts, or be cumulatively preserved so the user sees all text the model produced.Environment