-
-
Notifications
You must be signed in to change notification settings - Fork 52.6k
Description
Issue: Inter-tool-call text blocks leak as separate messages to user (Telegram)
Repo: openclaw/openclaw
Problem
When an LLM response involves multiple tool-use rounds, each intermediate text block between tool calls is delivered as a separate Telegram message. Only the final text should be visible.
Example
Model produces: text → tool_use → text → tool_use → text (final reply)
User receives 3 messages instead of just the final reply.
Root Cause
Two paths contribute to the leak:
Path 1: Telegram Streaming Preview (PRIMARY)
In the Telegram reply handler, onAssistantMessageStart archives the current streaming preview message and calls forceNewMessage() to create a new one for each tool-use round. Each round's text becomes an independent Telegram message. The finally block attempts to delete archived previews, but the user has already seen them.
Location: onAssistantMessageStart callback in the Telegram reply path
Search: answerLane.stream?.forceNewMessage()
Fix: Remove the archive + forceNewMessage logic so all tool-use rounds share the same preview message. Intermediate text is overwritten by the next round.
// Before
if (answerLane.hasStreamedMessage) {
const previewMessageId = answerLane.stream?.messageId();
if (...) archivedAnswerPreviews.push({...});
answerLane.stream?.forceNewMessage();
}
// After — just skip the block entirely
/* reuse same preview for all tool-use rounds */Path 2: assistantTexts accumulation (SECONDARY)
finalizeAssistantTexts in normal (non-reasoning) mode pushes each round's text without clearing previous entries. All entries become independent replyItems in the final payload.
Search: else if (!addedDuringMessage && !chunkerHasBuffered && text) pushAssistantText(text);
Fix: Splice out previous entries before pushing.
Versions Affected
Tested on 2026.3.1. The 2026.2.26 narration leak fix addressed streaming-phase duplicate pushes but not the multi-round accumulation.
Search Keywords
forceNewMessage, archivedAnswerPreviews, finalizeAssistantTexts, assistantTextBaseline