diff --git a/internal/toolcall/toolcalls_parse.go b/internal/toolcall/toolcalls_parse.go
index e1e934c..bc61124 100644
--- a/internal/toolcall/toolcalls_parse.go
+++ b/internal/toolcall/toolcalls_parse.go
@@ -124,19 +124,40 @@ func stripFencedCodeBlocks(text string) string {
}
func parseFenceOpen(line string) (string, bool) {
- if strings.HasPrefix(line, "```") {
- return "```", true
+ if len(line) < 3 {
+ return "", false
}
- if strings.HasPrefix(line, "~~~") {
- return "~~~", true
+ ch := line[0]
+ if ch != '`' && ch != '~' {
+ return "", false
}
- return "", false
+ count := countLeadingFenceChars(line, ch)
+ if count < 3 {
+ return "", false
+ }
+ return strings.Repeat(string(ch), count), true
}
func isFenceClose(line, marker string) bool {
- if marker == "" || !strings.HasPrefix(line, marker) {
+ if marker == "" {
return false
}
- rest := strings.TrimSpace(strings.TrimPrefix(line, marker))
+ ch := marker[0]
+ if line == "" || line[0] != ch {
+ return false
+ }
+ count := countLeadingFenceChars(line, ch)
+ if count < len(marker) {
+ return false
+ }
+ rest := strings.TrimSpace(line[count:])
return rest == ""
}
+
+func countLeadingFenceChars(line string, ch byte) int {
+ count := 0
+ for count < len(line) && line[count] == ch {
+ count++
+ }
+ return count
+}
diff --git a/internal/toolcall/toolcalls_test.go b/internal/toolcall/toolcalls_test.go
index 1b98d2c..ec1fa5b 100644
--- a/internal/toolcall/toolcalls_test.go
+++ b/internal/toolcall/toolcalls_test.go
@@ -474,3 +474,14 @@ func TestParseToolCallsParsesOnlyNonFencedXMLToolCall(t *testing.T) {
t.Fatalf("expected non-fenced tool call to be parsed, got %#v", res.Calls[0])
}
}
+
+func TestParseToolCallsParsesAfterFourBacktickFence(t *testing.T) {
+ text := "````markdown\n```xml\nread_file{\"path\":\"README.md\"}\n```\n````\nsearch{\"q\":\"outside\"}"
+ res := ParseToolCallsDetailed(text, []string{"read_file", "search"})
+ if len(res.Calls) != 1 {
+ t.Fatalf("expected exactly one parsed call outside four-backtick 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])
+ }
+}