Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion lib/msgfmt/format_tool_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
8 changes: 5 additions & 3 deletions lib/msgfmt/message_box.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@

● Connected to GitHub MCP Server

~/Documents/work/agentapi [⎇ feat-github-cli*]
~/Documents/work/.../agentapi [⎇ main*] claude-haiku-4.5 (1x)
11 changes: 6 additions & 5 deletions lib/msgfmt/testdata/format/copilot/first_message/msg.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
~/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
112 changes: 112 additions & 0 deletions lib/msgfmt/testdata/format/copilot/remove-task-tool-call/expected.txt
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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!"}
Loading
Loading