v0.12.214 — recover Gemini text-transcript tool calls (#189)
v0.12.214 — June 28, 2026
Recover tool calls that Gemini 3.5 Flash emits as plain-text transcripts instead of structured tool_calls (#189).
Gemini [Called function "…" with args: {…}] transcripts now become real tool calls
- Symptom: through the OpenAI-compatible path, Gemini 3.5 Flash sometimes narrates a tool call as assistant text —
[Called function "terminal" with args: {"command":"whoami"}]— instead of returning structuredtool_callswithfinish_reason: "tool_calls". Downstream chat surfaces (seen in Chermes/Telegram) then displayed the transcript to the user instead of dispatching the tool, stalling agent loops. - Fix: added a third recognizer to
src/textual-tool-calls.ts(which already synthesizes structured tool calls from OpenClaw<tool_call>and Anthropic<function_calls>text shapes). The new extractor locates[Called function "NAME" with args:and parses the JSON args with a balanced-brace scan that honors string literals — so commas, braces, and]inside the JSON can't truncate the match. It only fires when the args parse as a JSON object and the block is closed by], so prose that merely quotes the format doesn't mis-fire. Both call sites — streaming and non-streaming inproxy.ts— share this function, so the recovered call is forwarded as a propertool_callsdelta/message with empty content andfinish_reason: "tool_calls". - Tests: 8 new cases in
src/textual-tool-calls.test.ts(single/multi-arg, empty args, nested JSON with embedded brackets, multiple transcripts with prose stripping, no-mis-fire on missing bracket / non-object args). Full suite 631 passed, lint + typecheck + build clean. - Out of scope: the issue's secondary
[Tool "function" returned]: {…}display line is a downstream chat-surface rendering concern, not a ClawRouter normalization gap — left untouched.