diff --git a/internal/toolcall/toolcalls_parse.go b/internal/toolcall/toolcalls_parse.go index f0cb2ac..e1e934c 100644 --- a/internal/toolcall/toolcalls_parse.go +++ b/internal/toolcall/toolcalls_parse.go @@ -39,6 +39,11 @@ func parseToolCallsDetailedXMLOnly(text string) ToolCallParseResult { return result } result.SawToolCallSyntax = looksLikeToolCallSyntax(trimmed) + trimmed = stripFencedCodeBlocks(trimmed) + trimmed = strings.TrimSpace(trimmed) + if trimmed == "" { + return result + } parsed := parseXMLToolCalls(trimmed) if len(parsed) == 0 { @@ -83,3 +88,55 @@ func looksLikeToolCallSyntax(text string) bool { strings.Contains(lower, "\n```\nDo not execute it." + res := ParseToolCallsDetailed(text, []string{"read_file"}) + if len(res.Calls) != 0 { + t.Fatalf("expected no parsed calls for fenced example, got %#v", res.Calls) + } +} + +func TestParseToolCallsParsesOnlyNonFencedXMLToolCall(t *testing.T) { + text := "```xml\nread_file{\"path\":\"README.md\"}\n```\nsearch{\"q\":\"golang\"}" + res := ParseToolCallsDetailed(text, []string{"read_file", "search"}) + if len(res.Calls) != 1 { + t.Fatalf("expected exactly one parsed call outside fence, got %#v", res.Calls) + } + if res.Calls[0].Name != "search" { + t.Fatalf("expected non-fenced tool call to be parsed, got %#v", res.Calls[0]) + } +}