#No streaming behavior where text appears between tool calls

1 messages Β· Page 1 of 1 (latest)

modern bear
#

I'm trying to get multiple text blocks when text appears between tools calls on Telegram, like it appears on the macOS app. Intended behavior in macOS screenshot and actual behavior on Telegram screenshot. Tool call shouldn't show on telegram so that part is fine (verbose=off), but interleaving text should be sent as multiple blocks as they appear. Settings below:

{ "blockStreamingDefault": "on",
      "blockStreamingBreak": "text_end",
      "blockStreamingChunk": {
        "minChars": 100,
        "breakPreference": "paragraph"
      },
      "blockStreamingCoalesce": {
        "minChars": 100,
        "idleMs": 500
      },
...,
"blockStreaming": true,
    "blockStreamingCoalesce": {
      "minChars": 300,
      "idleMs": 1500
    },
    "streamMode": "off"
}
midnight kestrelBOT
#

I see the issue clearly now:

macOS (desired): Each text segment appears as a separate bubble with tool call cards between them
Telegram (actual): All text collapsed into one message block, tool calls hidden

The problem is likely in how coalescing interacts with hidden tool blocks. When verbose=off hides the tool calls, the adjacent text blocks are getting merged because there's nothing "visible" breaking them apart.

A few things to try:

  1. Check for config conflicts - You have blockStreamingCoalesce defined twice with different values (100 chars/500ms vs 300 chars/1500ms). The later one might be overriding and causing more aggressive coalescing.

  2. Try blockStreamingBreak: "tool" - This should force a message break when any tool block occurs, even if it's hidden. text_end might not be triggering properly when the following content is a hidden tool.

  3. Reduce coalesce aggressiveness - Lower idleMs or set coalesce to false temporarily to test if that's what's merging things.

  4. Check if this is a Telegram-specific provider setting - If there's a provider-level blockStreamingCoalesce that's more aggressive than the global one, that would explain the difference.

#

Would be worth testing with:

{
  "blockStreamingBreak": "tool",
  "blockStreamingCoalesce": false
}

...to confirm breaks are happening, then dial coalesce back in gradually.

Is this in the global config or the Telegram provider section specifically?

modern bear
#

I'm not sure there are actual allowed options @midnight kestrel

midnight kestrelBOT
#

Fair point, let me check what's actually valid instead of guessing.

#

You're right, I was making stuff up. Here's what the schema actually says:

blockStreamingBreak (agent defaults level only):

  • "text_end" β€” break when text block ends (before tool calls)
  • "message_end" β€” break only at end of full response

blockStreamingCoalesce (both agent defaults and per-provider):

  • minChars β€” minimum chars before sending a block
  • maxChars β€” max chars before forcing a send
  • idleMs β€” idle time before flushing

So "text_end" should already create breaks before tool calls. The fact that it's not working suggests:

  1. Coalesce is merging them afterward β€” if the text between tool calls is short and arrives quickly, coalesce might be gluing them back together
  2. The breaks are happening but the text doesn't meet minChars β€” small interstitial text might be getting buffered

To test: try setting blockStreamingCoalesce.minChars: 1 and idleMs: 0 on the Telegram provider to disable coalescing entirely and see if the breaks show up.

#

If that works, the fix is tuning coalesce thresholds lower for Telegram specifically.

modern bear
#

still happening with minchars:1 and idleMS: 0

midnight kestrelBOT
#

Ah, now I see it clearly. The text_end break is firing, but when the tool block is hidden (verbose=off), the two text segments get concatenated anyway because there's no visible block between them.

This looks like a gap in the block streaming logic β€” the break boundary from the tool block isn't being preserved when the tool itself is filtered out.

Current behavior: text β†’ [hidden tool] β†’ text β†’ renders as one merged message
Desired behavior: text β†’ [hidden tool] β†’ text β†’ renders as two separate messages

This probably needs a code fix rather than config tweaking. The block streamer would need to remember that a break occurred even when the intervening block is suppressed.

Want me to dig into the Clawdbot source to see where this logic lives? Might be a quick fix to preserve break boundaries for hidden blocks.

modern bear
#

@torn warren am I missing something here? Don't know if the documentation is confusing or I am not understand something

midnight kestrelBOT
#

I don't think you're missing anything β€” the config options do what they say, but what you want isn't covered by them.

The docs aren't wrong, there's just no option for "preserve message breaks when tool blocks are hidden." text_end breaks before tool calls, but once the tool is filtered out by verbose=off, those break points disappear.

So it's less "confusing docs" and more "feature doesn't exist yet." You'd need something like blockStreamingBreak: "tool_boundary" that keeps the split even when the tool isn't rendered.

modern bear
#

@torn warren according to Opus, here's the analysis. Not sure if this is a priority though.

The bug:

1. When verbose=off, tool results don't get emitted
2. Block reply coalescing uses an idleMs timeout (default 1000ms) to batch nearby text chunks
3. If text-before-tool and text-after-tool arrive within that window, the coalescer joins them
4. With verbose=on, tool results would naturally create a temporal gap, but the tool output itself doesn't flow through the block reply pipeline β€” it's a separate onToolResult callback
The gap:
The coalescer has no knowledge that a tool executed between text segments. There's no "preserve break on hidden tool" signal.

Looking at pi-embedded-subscribe.ts, tool execution events (tool_execution_start/tool_execution_end) fire regardless of verbose level, but they don't trigger a coalescer flush.

Fix options:

1. In subscribeEmbeddedPiSession, on tool_execution_start or message_start (for new assistant turn after tool), force a coalescer flush
2. Add a new break mode like tool_boundary that flushes on tool boundaries
Want me to draft a patch? The cleanest approach would be flushing the coalescer when an assistant message starts after a tool execution β€” that's when the state reset already happens, so it's a natural boundary.
modern bear
midnight kestrelBOT
#

Nice, fast work! πŸŽ‰

Can't pull up the PR directly (no browser/gh auth here), but based on the analysis β€” flushing the coalescer on message_start after tool execution sounds like the cleanest approach. Natural boundary, minimal invasiveness.

Hopefully Seb can take a look when he's around.

torn warren
#

Thank you

midnight kestrelBOT
#

I see the thread! This is a Clawd-level question about the core streaming/blocking behavior for Telegram. 🦐

The issue: Telegram isn't respecting the blockStreaming config to send text between tool calls as multiple message blocks, even though blockStreaming is enabled and streamMode is off.

A few thoughts:

  1. Telegram provider might have its own handling that differs from the macOS app
  2. The blockStreamingBreak: "text_end" setting tells it to break on tool call boundaries, but maybe Telegram's adapter doesn't implement the multi-block sending for text between calls
  3. This could be a gap between the config schema and what the Telegram provider actually does

Since Peter is the creator, you'd know the codebase better than I do! This might be:

  • A missing implementation in the Telegram provider
  • A bug in how block streaming is detected for Telegram
  • Or intentional behavior if Telegram's API limits multi-message interleaving

Happy to help dig into docs or look at the Telegram provider code if you want me to check something specific! 🦐