From 96bda53567a60c048672985e9f8520c045283e57 Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Thu, 12 Feb 2026 17:43:14 +0530 Subject: [PATCH 1/2] feat: handle partial/malformed tool call detection for claude-code --- lib/msgfmt/format_tool_call.go | 46 ++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/lib/msgfmt/format_tool_call.go b/lib/msgfmt/format_tool_call.go index 826e872..fd8ac50 100644 --- a/lib/msgfmt/format_tool_call.go +++ b/lib/msgfmt/format_tool_call.go @@ -1,9 +1,16 @@ package msgfmt import ( + "fmt" "strings" ) +type toolCallRange struct { + start int + end int + malformed bool +} + func removeClaudeReportTaskToolCall(msg string) (string, []string) { msg = "\n" + msg // This handles the case where the message starts with a tool call @@ -11,23 +18,38 @@ func removeClaudeReportTaskToolCall(msg string) (string, []string) { lines := strings.Split(msg, "\n") toolCallStartIdx := -1 + newLineAfterToolCall := -1 // Store all tool call start and end indices [[start, end], ...] - var toolCallIdxs [][]int + var toolCallIdxs []toolCallRange for i := 1; i < len(lines)-1; i++ { prevLine := strings.TrimSpace(lines[i-1]) - line := strings.TrimSpace(lines[i]) + line := strings.Trim(strings.TrimSpace(lines[i]), "\n") nextLine := strings.TrimSpace(lines[i+1]) if strings.Contains(line, "coder - coder_report_task (MCP)") { toolCallStartIdx = i - } else if toolCallStartIdx != -1 && line == "\"message\": \"Thanks for reporting!\"" && nextLine == "}" && strings.HasSuffix(prevLine, "{") { - // Store [start, end] pair - toolCallIdxs = append(toolCallIdxs, []int{toolCallStartIdx, min(len(lines), i+2)}) + } else if toolCallStartIdx != -1 { + if line == "\"message\": \"Thanks for reporting!\"" && nextLine == "}" && strings.HasSuffix(prevLine, "{") { + // Store [start, end] pair + toolCallIdxs = append(toolCallIdxs, toolCallRange{toolCallStartIdx, min(len(lines), i+2), false}) + + // Reset to find the next tool call + toolCallStartIdx = -1 + newLineAfterToolCall = -1 + } else if len(line) == 0 { + newLineAfterToolCall = i + 1 + } + } + } - // Reset to find the next tool call - toolCallStartIdx = -1 + // Handle the malformed/partially rendered tool_calls + if toolCallStartIdx != -1 { + if newLineAfterToolCall != -1 { + toolCallIdxs = append(toolCallIdxs, toolCallRange{toolCallStartIdx, newLineAfterToolCall, true}) + } else { + toolCallIdxs = append(toolCallIdxs, toolCallRange{toolCallStartIdx, len(lines), true}) } } @@ -40,10 +62,14 @@ func removeClaudeReportTaskToolCall(msg string) (string, []string) { // Remove tool calls from the message for i := len(toolCallIdxs) - 1; i >= 0; i-- { - idxPair := toolCallIdxs[i] - start, end := idxPair[0], idxPair[1] + start, end := toolCallIdxs[i].start, toolCallIdxs[i].end - toolCallMessages = append(toolCallMessages, strings.Join(lines[start:end], "\n")) + // If the toolCall is malformed, we don't want to log it + if !toolCallIdxs[i].malformed { + // [DEBUG] print, will remove before merge + fmt.Printf("Found malformed toolCall with newLineAfterToolCall, start: %d, end: %d, toolcall: %s", start, end, strings.Join(lines[start:end], "\n")) + toolCallMessages = append(toolCallMessages, strings.Join(lines[start:end], "\n")) + } lines = append(lines[:start], lines[end:]...) } From 751310f8bc40d7031fb8cf2e0d0addfba435a437 Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Thu, 12 Feb 2026 17:52:30 +0530 Subject: [PATCH 2/2] feat: add test for malformed tool_call --- lib/msgfmt/format_tool_call.go | 3 ++- .../format/claude/remove-task-tool-call/expected.txt | 3 +++ .../testdata/format/claude/remove-task-tool-call/msg.txt | 7 +++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/msgfmt/format_tool_call.go b/lib/msgfmt/format_tool_call.go index fd8ac50..6e140c1 100644 --- a/lib/msgfmt/format_tool_call.go +++ b/lib/msgfmt/format_tool_call.go @@ -66,9 +66,10 @@ func removeClaudeReportTaskToolCall(msg string) (string, []string) { // If the toolCall is malformed, we don't want to log it if !toolCallIdxs[i].malformed { + toolCallMessages = append(toolCallMessages, strings.Join(lines[start:end], "\n")) + } else { // [DEBUG] print, will remove before merge fmt.Printf("Found malformed toolCall with newLineAfterToolCall, start: %d, end: %d, toolcall: %s", start, end, strings.Join(lines[start:end], "\n")) - toolCallMessages = append(toolCallMessages, strings.Join(lines[start:end], "\n")) } lines = append(lines[:start], lines[end:]...) diff --git a/lib/msgfmt/testdata/format/claude/remove-task-tool-call/expected.txt b/lib/msgfmt/testdata/format/claude/remove-task-tool-call/expected.txt index caef066..8dc6fbd 100644 --- a/lib/msgfmt/testdata/format/claude/remove-task-tool-call/expected.txt +++ b/lib/msgfmt/testdata/format/claude/remove-task-tool-call/expected.txt @@ -40,3 +40,6 @@ Open the HTML file in your web browser and use the arrow keys to move the snake. Collect the red food to grow and increase your score! + + + Above is a malformed tool call message \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/claude/remove-task-tool-call/msg.txt b/lib/msgfmt/testdata/format/claude/remove-task-tool-call/msg.txt index cc64341..d8d5fe9 100644 --- a/lib/msgfmt/testdata/format/claude/remove-task-tool-call/msg.txt +++ b/lib/msgfmt/testdata/format/claude/remove-task-tool-call/msg.txt @@ -72,6 +72,13 @@ "message": "Thanks for reporting!" } + coder - coder_report_task (MCP)(summary: "Snake game created + successfully at snake-game.html", + link: "file:///home/coder/snake-ga + me.html", state: "working") + + Above is a malformed tool call message + ─────────────────────────────────────────────────────────────────── ❯