diff --git a/internal/util/toolcalls_parse_markup.go b/internal/util/toolcalls_parse_markup.go
index e2eff83..85e7138 100644
--- a/internal/util/toolcalls_parse_markup.go
+++ b/internal/util/toolcalls_parse_markup.go
@@ -15,6 +15,7 @@ var antmlArgumentPattern = regexp.MustCompile(`(?is)<(?:[a-z0-9_]+:)?argument\s+
var antmlParametersPattern = regexp.MustCompile(`(?is)<(?:[a-z0-9_]+:)?parameters\s*>\s*(\{.*?\})\s*(?:[a-z0-9_]+:)?parameters>`)
var invokeCallPattern = regexp.MustCompile(`(?is)(.*?)`)
var invokeParamPattern = regexp.MustCompile(`(?is)\s*(.*?)\s*`)
+var toolUseFunctionPattern = regexp.MustCompile(`(?is)\s*(.*?)\s*`)
func parseXMLToolCalls(text string) []ParsedToolCall {
matches := xmlToolCallPattern.FindAllString(text, -1)
@@ -38,6 +39,9 @@ func parseXMLToolCalls(text string) []ParsedToolCall {
if call, ok := parseInvokeFunctionCallStyle(text); ok {
return []ParsedToolCall{call}
}
+ if call, ok := parseToolUseFunctionStyle(text); ok {
+ return []ParsedToolCall{call}
+ }
return nil
}
@@ -229,6 +233,30 @@ func parseInvokeFunctionCallStyle(text string) (ParsedToolCall, bool) {
return ParsedToolCall{Name: name, Input: input}, true
}
+func parseToolUseFunctionStyle(text string) (ParsedToolCall, bool) {
+ m := toolUseFunctionPattern.FindStringSubmatch(text)
+ if len(m) < 3 {
+ return ParsedToolCall{}, false
+ }
+ name := strings.TrimSpace(m[1])
+ if name == "" {
+ return ParsedToolCall{}, false
+ }
+ body := m[2]
+ input := map[string]any{}
+ for _, pm := range invokeParamPattern.FindAllStringSubmatch(body, -1) {
+ if len(pm) < 3 {
+ continue
+ }
+ k := strings.TrimSpace(pm[1])
+ v := strings.TrimSpace(pm[2])
+ if k != "" {
+ input[k] = v
+ }
+ }
+ return ParsedToolCall{Name: name, Input: input}, true
+}
+
func asString(v any) string {
s, _ := v.(string)
return s
diff --git a/internal/util/toolcalls_test.go b/internal/util/toolcalls_test.go
index 9701a46..d66519b 100644
--- a/internal/util/toolcalls_test.go
+++ b/internal/util/toolcalls_test.go
@@ -236,6 +236,20 @@ func TestParseToolCallsSupportsInvokeFunctionCallStyle(t *testing.T) {
}
}
+func TestParseToolCallsSupportsToolUseFunctionParameterStyle(t *testing.T) {
+ text := `test`
+ calls := ParseToolCalls(text, []string{"search_web"})
+ if len(calls) != 1 {
+ t.Fatalf("expected 1 call, got %#v", calls)
+ }
+ if calls[0].Name != "search_web" {
+ t.Fatalf("expected canonical tool name search_web, got %q", calls[0].Name)
+ }
+ if calls[0].Input["query"] != "test" {
+ t.Fatalf("expected query argument, got %#v", calls[0].Input)
+ }
+}
+
func TestParseToolCallsSupportsNestedToolTagStyle(t *testing.T) {
text := `pwdshow cwd`
calls := ParseToolCalls(text, []string{"bash"})