Bug Description
When acp.stream.deliveryMode is set to "final_only", ACP binding replies in Telegram groups still produce two identical messages (a block reply + a final reply).
Root Cause (from source analysis)
In dispatch-acp.runtime (dist/dispatch-acp.runtime-BgoLNt3T.js), the drainChunker function checks:
const drainChunker = (force) => {
if (settings.deliveryMode === "final_only" && !force) return;
// ...
};
When a turn ends, flush(true) is called with force=true, which bypasses the final_only guard. This causes the accumulated text to be drained into blockReplyPipeline, emitting a deliver("block") call.
Then finalizeAcpTurnOutput (line ~800) checks hasAccumulatedBlockText && !deliveredFinalReply and emits another deliver("final") with the same text.
Result: two identical sendMessage calls to Telegram, ~1 second apart.
Steps to Reproduce
- Configure an ACP binding for a Telegram group topic:
{
"type": "acp",
"agentId": "claude",
"match": {
"channel": "telegram",
"peer": { "kind": "group", "id": "-100xxxxx:topic:1" }
},
"acp": { "mode": "persistent" }
}
- Set
acp.stream.deliveryMode: "final_only"
- Send a message in the bound topic
- Observe two identical bot replies
Expected Behavior
With deliveryMode: "final_only", only the final reply should be sent. The force flush at turn end should respect the delivery mode setting and skip the block pipeline drain.
Suggested Fix
Change drainChunker to always skip when deliveryMode === "final_only":
const drainChunker = (force) => {
if (settings.deliveryMode === "final_only") return; // remove !force condition
// ...
};
Or alternatively, in finalizeAcpTurnOutput, check if a block with the same content was already delivered and skip the final in that case.
Environment
- OpenClaw: 2026.3.24
- acpx: 0.3.1
- ACP backend: claude (Claude Code via OAuth)
- Channel: Telegram (forum group with topics)
- Config:
blockStreamingDefault: "off", acp.stream.deliveryMode: "final_only"
Gateway Log Evidence
Every ACP reply produces paired sendMessage calls ~1s apart:
2026-03-28T11:37:15.888+08:00 [telegram] sendMessage ok chat=-100xxxxx message=458
2026-03-28T11:37:16.663+08:00 [telegram] sendMessage ok chat=-100xxxxx message=459
This persists regardless of blockStreamingDefault, requireMention, or spawnAcpSessions settings.
Bug Description
When
acp.stream.deliveryModeis set to"final_only", ACP binding replies in Telegram groups still produce two identical messages (a block reply + a final reply).Root Cause (from source analysis)
In
dispatch-acp.runtime(dist/dispatch-acp.runtime-BgoLNt3T.js), thedrainChunkerfunction checks:When a turn ends,
flush(true)is called withforce=true, which bypasses thefinal_onlyguard. This causes the accumulated text to be drained intoblockReplyPipeline, emitting adeliver("block")call.Then
finalizeAcpTurnOutput(line ~800) checkshasAccumulatedBlockText && !deliveredFinalReplyand emits anotherdeliver("final")with the same text.Result: two identical
sendMessagecalls to Telegram, ~1 second apart.Steps to Reproduce
{ "type": "acp", "agentId": "claude", "match": { "channel": "telegram", "peer": { "kind": "group", "id": "-100xxxxx:topic:1" } }, "acp": { "mode": "persistent" } }acp.stream.deliveryMode: "final_only"Expected Behavior
With
deliveryMode: "final_only", only the final reply should be sent. The force flush at turn end should respect the delivery mode setting and skip the block pipeline drain.Suggested Fix
Change
drainChunkerto always skip whendeliveryMode === "final_only":Or alternatively, in
finalizeAcpTurnOutput, check if a block with the same content was already delivered and skip the final in that case.Environment
blockStreamingDefault: "off",acp.stream.deliveryMode: "final_only"Gateway Log Evidence
Every ACP reply produces paired sendMessage calls ~1s apart:
This persists regardless of
blockStreamingDefault,requireMention, orspawnAcpSessionssettings.