From 9a154266cba03d943f409822584620ac3ff7d800 Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Sun, 22 Feb 2026 09:11:26 +0900 Subject: [PATCH] fix: recover XML tool calls as proper tool execution in agent loop Move XML tool call extraction from HTTPProvider-only to the central agent loop so all providers benefit. Plain-text XML tool calls are now parsed and re-executed as real tool calls, with the XML stripped from the response content before sending to users. Co-Authored-By: Claude Opus 4.6 --- pkg/agent/loop.go | 8 ++++++++ pkg/providers/tool_call_extract.go | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 3f65f7e35..380e5e4ca 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -1525,6 +1525,14 @@ func (al *AgentLoop) runLLMIteration( ) } + // Recover XML tool calls emitted as plain text by some providers. + if len(response.ToolCalls) == 0 { + if xmlCalls := providers.ExtractXMLToolCalls(response.Content); len(xmlCalls) > 0 { + response.ToolCalls = xmlCalls + } + } + response.Content = providers.StripXMLToolCalls(response.Content) + // Check if no tool calls - we're done if len(response.ToolCalls) == 0 { // Plan continuation: if unchecked steps remain, nudge the LLM to diff --git a/pkg/providers/tool_call_extract.go b/pkg/providers/tool_call_extract.go index a41d92882..9d48f9945 100644 --- a/pkg/providers/tool_call_extract.go +++ b/pkg/providers/tool_call_extract.go @@ -171,6 +171,11 @@ func findToolCallBlock(text string) (blockStart, blockEnd int, content string, f // value // // +// ExtractXMLToolCalls is the exported version for use by the agent loop. +func ExtractXMLToolCalls(text string) []ToolCall { + return extractXMLToolCalls(text) +} + func extractXMLToolCalls(text string) []ToolCall { var result []ToolCall remaining := text @@ -260,6 +265,11 @@ func extractXMLToolCalls(text string) []ToolCall { return result } +// StripXMLToolCalls is the exported version for use by the agent loop. +func StripXMLToolCalls(text string) string { + return stripXMLToolCalls(text) +} + // stripXMLToolCalls removes XML tool call blocks from response text. // Prevents raw XML tool calls from leaking to users. func stripXMLToolCalls(text string) string {