Bug
When an ACP agent streams output via block replies on Discord, the final message gets sent twice — once as streamed blocks, and again as a full duplicate at the end.
Root Cause
In dispatch-acp.runtime-nq9T9sRU.js, the function shouldTreatDeliveredTextAsVisible only marks a delivery as visible when:
kind === "final", or
channel === "telegram"
Block deliveries (kind === "block") on non-Telegram channels (e.g. Discord) always return false. This means state.deliveredVisibleText stays false after blocks are streamed.
Then in finalizeAcpTurnOutput, the guard condition:
!params.delivery.hasDeliveredVisibleText()
evaluates to true even after all blocks were posted, so the full accumulatedBlockText is sent again as a final reply — duplicating the entire response.
Telegram works correctly because the channel check catches it. Discord does not.
Fix
Add kind === "block" to the visible-text check:
function shouldTreatDeliveredTextAsVisible(params) {
if (!params.text?.trim()) return false;
if (params.kind === "final") return true;
if (params.kind === "block") return true; // ← add this
return normalizeDeliveryChannel(params.channel) === "telegram";
}
Steps to Reproduce
- Set up an ACP session (e.g. cursor) bound to a Discord thread
- Send a prompt that produces a multi-chunk streamed response
- Observe the agent's reply appears twice in the thread (identical content)
Environment
- Channel: Discord
- OpenClaw version: current (post-0.4.0 acpx update)
- Delivery mode: live streaming (block replies)
Bug
When an ACP agent streams output via block replies on Discord, the final message gets sent twice — once as streamed blocks, and again as a full duplicate at the end.
Root Cause
In
dispatch-acp.runtime-nq9T9sRU.js, the functionshouldTreatDeliveredTextAsVisibleonly marks a delivery as visible when:kind === "final", orchannel === "telegram"Block deliveries (
kind === "block") on non-Telegram channels (e.g. Discord) always returnfalse. This meansstate.deliveredVisibleTextstaysfalseafter blocks are streamed.Then in
finalizeAcpTurnOutput, the guard condition:evaluates to
trueeven after all blocks were posted, so the fullaccumulatedBlockTextis sent again as a final reply — duplicating the entire response.Telegram works correctly because the channel check catches it. Discord does not.
Fix
Add
kind === "block"to the visible-text check:Steps to Reproduce
Environment