diff --git a/lib/msgfmt/format_tool_call.go b/lib/msgfmt/format_tool_call.go index 5befafa5..dddb0d39 100644 --- a/lib/msgfmt/format_tool_call.go +++ b/lib/msgfmt/format_tool_call.go @@ -125,6 +125,68 @@ func removeCodexReportTaskToolCall(msg string) (string, []string) { return strings.TrimRight(strings.Join(lines, "\n"), "\n"), toolCallMessages } +func removeCopilotReportTaskToolCall(msg string) (string, []string) { + msg = "\n" + msg // This handles the case where the message starts with a tool call + + // Remove all tool calls that start with `● coder_report_task:` + lines := strings.Split(msg, "\n") + + toolCallStartIdx := -1 + newLineAfterToolCall := -1 + + // Store all tool call start and end indices [[start, end], ...] + var toolCallIdxs []toolCallRange + + for i := 0; i < len(lines); i++ { + line := strings.Trim(strings.TrimSpace(lines[i]), "\n") + + if strings.Contains(line, "● coder_report_task:") { + toolCallStartIdx = i + } else if toolCallStartIdx != -1 { + if strings.Contains(line, "{\"message\":\"Thanks for reporting!\"}") { + // 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 + } + } + } + + // Handle the malformed/partially rendered tool_calls + // Note: This case has not yet been observed in Copilot + if toolCallStartIdx != -1 { + if newLineAfterToolCall != -1 { + toolCallIdxs = append(toolCallIdxs, toolCallRange{toolCallStartIdx, newLineAfterToolCall, true}) + } else { + toolCallIdxs = append(toolCallIdxs, toolCallRange{toolCallStartIdx, len(lines), true}) + } + } + + // If no tool calls found, return original message + if len(toolCallIdxs) == 0 { + return strings.TrimLeft(msg, "\n"), []string{} + } + + toolCallMessages := make([]string, 0) + + // Remove tool calls from the message + for i := len(toolCallIdxs) - 1; i >= 0; i-- { + start, end := toolCallIdxs[i].start, toolCallIdxs[i].end + + // 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")) + } + + lines = append(lines[:start], lines[end:]...) + } + return strings.TrimLeft(strings.Join(lines, "\n"), "\n"), toolCallMessages +} + func FormatToolCall(agentType AgentType, message string) (string, []string) { switch agentType { case AgentTypeClaude: @@ -138,7 +200,7 @@ func FormatToolCall(agentType AgentType, message string) (string, []string) { case AgentTypeGemini: return message, []string{} case AgentTypeCopilot: - return message, []string{} + return removeCopilotReportTaskToolCall(message) case AgentTypeAmp: return message, []string{} case AgentTypeCursor: diff --git a/lib/msgfmt/message_box.go b/lib/msgfmt/message_box.go index 1ac75c9e..8e4b731c 100644 --- a/lib/msgfmt/message_box.go +++ b/lib/msgfmt/message_box.go @@ -28,9 +28,11 @@ func findGreaterThanMessageBox(lines []string) int { func findGenericSlimMessageBox(lines []string) int { for i := len(lines) - 3; i >= max(len(lines)-9, 0); i-- { if strings.Contains(lines[i], "───────────────") && - (strings.Contains(lines[i+1], "|") || strings.Contains(lines[i+1], "│") || strings.Contains(lines[i+1], "❯")) && - strings.Contains(lines[i+2], "───────────────") { - return i + (strings.Contains(lines[i+1], "|") || strings.Contains(lines[i+1], "│") || strings.Contains(lines[i+1], "❯")) { + if (i+2 < len(lines) && strings.Contains(lines[i+2], "───────────────")) || + (i+3 < len(lines) && strings.Contains(lines[i+3], "───────────────")) { + return i + } } } return -1 diff --git a/lib/msgfmt/testdata/format/copilot/first_message/expected.txt b/lib/msgfmt/testdata/format/copilot/first_message/expected.txt index db1217c5..183de5ff 100644 --- a/lib/msgfmt/testdata/format/copilot/first_message/expected.txt +++ b/lib/msgfmt/testdata/format/copilot/first_message/expected.txt @@ -8,4 +8,4 @@ ● Connected to GitHub MCP Server - ~/Documents/work/agentapi [⎇ feat-github-cli*] \ No newline at end of file + ~/Documents/work/.../agentapi [⎇ main*] claude-haiku-4.5 (1x) \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/copilot/first_message/msg.txt b/lib/msgfmt/testdata/format/copilot/first_message/msg.txt index dee0c4da..580d1d9f 100644 --- a/lib/msgfmt/testdata/format/copilot/first_message/msg.txt +++ b/lib/msgfmt/testdata/format/copilot/first_message/msg.txt @@ -8,8 +8,9 @@ ● Connected to GitHub MCP Server - ~/Documents/work/agentapi [⎇ feat-github-cli*] - ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ - │ > Enter @ to mention files or / for commands │ - ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ - Ctrl+c Exit · Ctrl+r Expand all \ No newline at end of file + ~/Documents/work/.../agentapi [⎇ main*] claude-haiku-4.5 (1x) +─────────────────────────────────────────────────────────────────── +❯ Type @ to mention files, # for issues/PRs, / for commands, or ? + for shortcuts +─────────────────────────────────────────────────────────────────── + shift+tab switch mode \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/expected.txt b/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/expected.txt new file mode 100644 index 00000000..87798c63 --- /dev/null +++ b/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/expected.txt @@ -0,0 +1,112 @@ +◐ The user wants me to build a snake game in Python. This is a + complete project that I should plan out first. Let me: + + 1. Create a plan for building the snake game + 2. Set up the structure + 3. Implement the game + 4. Test it + + I should start by reporting task status and creating a plan. + Since this is a new task that requires planning, I should: + 1. Report the task as "working" + 2. Create a plan.md file + 3. Set up todos in SQL + 4. Then implement + + Let me start by reporting and exploring the current directory + structure, then creating a plan. + +● List directory . + └ 1 file found + +● Let me check if snake_game already exists: + +● List directory snake_game + └ 6 files found + +● I see there's already a snake_game folder. Let me check the + existing files: + +● Read snake_game/README.md + └ 97 lines read + +● Read snake_game/main.py + └ 229 lines read + +● Great! A snake game already exists. Let me check the test file + and game summary: + +● Read snake_game/test_game.py + └ 113 lines read + +● Read snake_game/GAME_SUMMARY.txt + └ 89 lines read + +● Perfect! The snake game is already built and complete. Let me + verify it's working by running the tests: + +● Run snake game unit tests + $ cd /home/coder/projects/snake_game && python3 test_game.py + └ 7 lines... + +● The virtual environment needs pygame installed. Let me set it + up: + +● Install pygame and run tests + $ cd /home/coder/projects/snake_game && source venv/bin/activate + && pip install pygame -q && python3 test_game.py + └ 16 lines... + +● Excellent! All tests pass. Now let me verify the game can run: + +● Verify game starts successfully + $ cd /home/coder/projects/snake_game && source venv/bin/activate + && timeout 3 python3 main.py 2>&1 || true + └ 3 lines... + +● Perfect! The game initializes successfully. Let me update you + on what's been completed: + +● ✅ Snake Game Complete! + + Your Python snake game is fully built and tested. Here's what + you have: + + 🎮 Game Features + + - Classic snake gameplay with grid-based movement + - Collision detection (walls & self) + - Scoring system (10 points per food) + - Game over screen with restart option + - Keyboard controls (arrow keys) + - 10 FPS consistent gameplay speed + + 📂 Project Structure + + snake_game/ + ├── main.py - Full game implementation + ├── test_game.py - 8 passing unit tests + ├── README.md - Complete documentation + └── venv/ - Virtual environment with pygame + + 🚀 Quick Start + + cd /home/coder/projects/snake_game + source venv/bin/activate + python3 main.py + + ⌨️ Controls + + - Arrow Keys - Move snake + - R - Restart (when game over) + - ESC - Quit + + ✅ Verification Status + + - ✓ All 8 unit tests passing + - ✓ Game initializes and runs successfully + - ✓ Pygame installed and configured + + Ready to play! 🐍 + + ~/projects claude-haiku-4.5 (1x) \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/expected_tool_calls.txt b/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/expected_tool_calls.txt new file mode 100644 index 00000000..c79f4ab5 --- /dev/null +++ b/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/expected_tool_calls.txt @@ -0,0 +1,16 @@ +● coder_report_task: Last Call + └ {"message":"Thanks for reporting!"} + +--- +● coder_report_task: Snake game built and verified - all tests +passing, game runs successfully + └ {"message":"Thanks for reporting!"} + +--- +● coder_report_task: Starting snake game project planning and +implementation + └ {"message":"Thanks for reporting!"} + +--- +● coder_report_task: First Call + └ {"message":"Thanks for reporting!"} diff --git a/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/msg.txt b/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/msg.txt new file mode 100644 index 00000000..89b24c48 --- /dev/null +++ b/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/msg.txt @@ -0,0 +1,126 @@ +● coder_report_task: First Call + └ {"message":"Thanks for reporting!"} + +◐ The user wants me to build a snake game in Python. This is a + complete project that I should plan out first. Let me: + + 1. Create a plan for building the snake game + 2. Set up the structure + 3. Implement the game + 4. Test it + + I should start by reporting task status and creating a plan. + Since this is a new task that requires planning, I should: + 1. Report the task as "working" + 2. Create a plan.md file + 3. Set up todos in SQL + 4. Then implement + + Let me start by reporting and exploring the current directory + structure, then creating a plan. + +● coder_report_task: Starting snake game project planning and +implementation + └ {"message":"Thanks for reporting!"} + +● List directory . + └ 1 file found + +● Let me check if snake_game already exists: + +● List directory snake_game + └ 6 files found + +● I see there's already a snake_game folder. Let me check the + existing files: + +● Read snake_game/README.md + └ 97 lines read + +● Read snake_game/main.py + └ 229 lines read + +● Great! A snake game already exists. Let me check the test file + and game summary: + +● Read snake_game/test_game.py + └ 113 lines read + +● Read snake_game/GAME_SUMMARY.txt + └ 89 lines read + +● Perfect! The snake game is already built and complete. Let me + verify it's working by running the tests: + +● Run snake game unit tests + $ cd /home/coder/projects/snake_game && python3 test_game.py + └ 7 lines... + +● The virtual environment needs pygame installed. Let me set it + up: + +● Install pygame and run tests + $ cd /home/coder/projects/snake_game && source venv/bin/activate + && pip install pygame -q && python3 test_game.py + └ 16 lines... + +● Excellent! All tests pass. Now let me verify the game can run: + +● Verify game starts successfully + $ cd /home/coder/projects/snake_game && source venv/bin/activate + && timeout 3 python3 main.py 2>&1 || true + └ 3 lines... + +● Perfect! The game initializes successfully. Let me update you + on what's been completed: + +● coder_report_task: Snake game built and verified - all tests +passing, game runs successfully + └ {"message":"Thanks for reporting!"} + +● ✅ Snake Game Complete! + + Your Python snake game is fully built and tested. Here's what + you have: + + 🎮 Game Features + + - Classic snake gameplay with grid-based movement + - Collision detection (walls & self) + - Scoring system (10 points per food) + - Game over screen with restart option + - Keyboard controls (arrow keys) + - 10 FPS consistent gameplay speed + + 📂 Project Structure + + snake_game/ + ├── main.py - Full game implementation + ├── test_game.py - 8 passing unit tests + ├── README.md - Complete documentation + └── venv/ - Virtual environment with pygame + + 🚀 Quick Start + + cd /home/coder/projects/snake_game + source venv/bin/activate + python3 main.py + + ⌨️ Controls + + - Arrow Keys - Move snake + - R - Restart (when game over) + - ESC - Quit + + ✅ Verification Status + + - ✓ All 8 unit tests passing + - ✓ Game initializes and runs successfully + - ✓ Pygame installed and configured + + Ready to play! 🐍 + +● coder_report_task: Last Call + └ {"message":"Thanks for reporting!"} + + ~/projects claude-haiku-4.5 (1x) \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/user.txt b/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/user.txt new file mode 100644 index 00000000..abeefff7 --- /dev/null +++ b/lib/msgfmt/testdata/format/copilot/remove-task-tool-call/user.txt @@ -0,0 +1 @@ +Build a snake game \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/copilot/thinking/expected.txt b/lib/msgfmt/testdata/format/copilot/thinking/expected.txt index 05930b50..ef6309f9 100644 --- a/lib/msgfmt/testdata/format/copilot/thinking/expected.txt +++ b/lib/msgfmt/testdata/format/copilot/thinking/expected.txt @@ -6,4 +6,4 @@ ◉ Thinking (Esc to cancel) - ~/Documents/work/agentapi [⎇ feat-github-cli*] \ No newline at end of file + ~/Documents/work/.../agentapi [⎇ main*] claude-haiku-4.5 (1x) \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/copilot/thinking/msg.txt b/lib/msgfmt/testdata/format/copilot/thinking/msg.txt index 3a878269..bf10760b 100644 --- a/lib/msgfmt/testdata/format/copilot/thinking/msg.txt +++ b/lib/msgfmt/testdata/format/copilot/thinking/msg.txt @@ -28,8 +28,9 @@ ◉ Thinking (Esc to cancel) - ~/Documents/work/agentapi [⎇ feat-github-cli*] - ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ - │ > Enter @ to mention files or / for commands │ - ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ - Ctrl+c Exit · Ctrl+r Expand all Remaining requests: 0% + ~/Documents/work/.../agentapi [⎇ main*] claude-haiku-4.5 (1x) +─────────────────────────────────────────────────────────────────── +❯ Type @ to mention files, # for issues/PRs, / for commands, or ? + for shortcuts +─────────────────────────────────────────────────────────────────── + shift+tab switch mode Remaining requests: 0%