Skip to content

Commit d7c623e

Browse files
authored
Merge pull request #2360 from dgageot/board/fix-docker-agent-issue-2333-01589bab
Fix tool call stuck as running when moved out of active reasoning block
2 parents d060de4 + 24f6af2 commit d7c623e

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

pkg/tui/components/messages/messages.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,12 +1339,16 @@ func (m *model) LoadFromSession(sess *session.Session) tea.Cmd {
13391339
}
13401340

13411341
func (m *model) AddOrUpdateToolCall(agentName string, toolCall tools.ToolCall, toolDef tools.Tool, status types.ToolStatus) tea.Cmd {
1342-
// First check if this tool call exists in an active reasoning block
1343-
if block, blockIdx := m.getActiveReasoningBlock(agentName); block != nil {
1344-
if block.HasToolCall(toolCall.ID) {
1345-
block.UpdateToolCall(toolCall.ID, status, toolCall.Function.Arguments)
1346-
m.invalidateItem(blockIdx)
1347-
return nil
1342+
// First check if this tool call exists in any reasoning block
1343+
for i := len(m.messages) - 1; i >= 0; i-- {
1344+
if m.messages[i].Type == types.MessageTypeAssistantReasoningBlock {
1345+
if block, ok := m.views[i].(*reasoningblock.Model); ok {
1346+
if block.HasToolCall(toolCall.ID) {
1347+
block.UpdateToolCall(toolCall.ID, status, toolCall.Function.Arguments)
1348+
m.invalidateItem(i)
1349+
return nil
1350+
}
1351+
}
13481352
}
13491353
}
13501354

pkg/tui/components/messages/messages_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,50 @@ func TestBindingsIncludesEditKeyWhenUserMessageSelected(t *testing.T) {
11141114
assert.True(t, foundE, "Bindings should include 'e' key when user message is selected")
11151115
}
11161116

1117+
func TestAddOrUpdateToolCallFindsToolInNonActiveReasoningBlock(t *testing.T) {
1118+
t.Parallel()
1119+
1120+
sessionState := &service.SessionState{}
1121+
m := NewScrollableView(80, 24, sessionState).(*model)
1122+
m.SetSize(80, 24)
1123+
1124+
agentName := "root"
1125+
toolCall := tools.ToolCall{
1126+
ID: "call_1",
1127+
Function: tools.FunctionCall{Name: "go_workspace", Arguments: `{}`},
1128+
}
1129+
toolDef := tools.Tool{Name: "go_workspace"}
1130+
1131+
// Step 1: Add a reasoning block and a tool call inside it (simulates PartialToolCallEvent)
1132+
m.AppendReasoning(agentName, "Thinking...")
1133+
require.Len(t, m.messages, 1)
1134+
assert.Equal(t, types.MessageTypeAssistantReasoningBlock, m.messages[0].Type)
1135+
1136+
m.AddOrUpdateToolCall(agentName, toolCall, toolDef, types.ToolStatusPending)
1137+
block, ok := m.views[0].(*reasoningblock.Model)
1138+
require.True(t, ok)
1139+
require.True(t, block.HasToolCall("call_1"))
1140+
1141+
// Step 2: Append an assistant message so the reasoning block is no longer the last message
1142+
m.AppendToLastMessage(agentName, "Here is the answer.")
1143+
require.Len(t, m.messages, 2)
1144+
assert.Equal(t, types.MessageTypeAssistant, m.messages[1].Type)
1145+
1146+
// Step 3: Update the tool call to Running (simulates ToolCallEvent)
1147+
// Before the fix, this would not find the tool in the old reasoning block
1148+
// and would create a duplicate standalone entry.
1149+
m.AddOrUpdateToolCall(agentName, toolCall, toolDef, types.ToolStatusRunning)
1150+
1151+
// Verify: still only 2 messages (no duplicate tool call created)
1152+
assert.Len(t, m.messages, 2, "should not create a duplicate tool call message")
1153+
1154+
// Verify the tool call in the reasoning block was updated (not duplicated)
1155+
block, ok = m.views[0].(*reasoningblock.Model)
1156+
require.True(t, ok)
1157+
assert.True(t, block.HasToolCall("call_1"))
1158+
assert.Equal(t, 1, block.ToolCount(), "reasoning block should still have exactly one tool call")
1159+
}
1160+
11171161
func TestBindingsExcludesEditKeyWhenAssistantMessageSelected(t *testing.T) {
11181162
t.Parallel()
11191163

0 commit comments

Comments
 (0)