This looks like a real OpenAI-compatible streaming edge case, and I don’t see an already-indexed issue for that exact pendingPostToolCallDeltas / Telegram-group resend loop.
I checked current main: openai-transport-stream.ts does intentionally buffer visible text/reasoning that arrives while a toolCall block is active, then flushes it after the tool call. There’s even a regression test preserving “visible reasoning text arrives between tool-call chunks”, so the buffering itself is expected behavior. What sounds buggy is the downstream interaction: after message(action=send) succeeds, that flushed trailing assistant text can still become a deliverable block reply / continuation signal, and in group chat that can re-trigger the run. The Ollama adapter avoiding that post-tool text path explains why switching adapters stops it.
Workarounds I’d try, in order:
- Keep using the
ollama adapter for that backend if it’s otherwise compatible — it’s the cleanest workaround right now.
- If your provider is OpenRouter-like or emits
reasoning_details as visible text, set the custom model compat to avoid visible reasoning detail promotion, e.g. compat.visibleReasoningDetailTypes: []. That won’t stop plain delta.content after tool calls, but it can stop the response.output_text/response.text path.
- Tune the upstream model/template to not emit assistant text in the same streamed completion as
tool_calls — OpenAI-compatible tool streams really need “tool call only, then wait for tool result”.
- As a containment measure for Telegram groups, restrict/deny the
message tool for that group or make the group require mentions until fixed, so a loop can’t spam the room.
I’d file this as a GitHub bug with your minimal stream trace, OpenClaw version, and model/provider config: https://github.com/openclaw/openclaw/issues
The key repro artifact is: streamed tool_calls for message.send + later delta.content/visible reasoning in the same assistant turn +