diff --git a/internal/tui/components/chat/chat.go b/internal/tui/components/chat/chat.go index 06c9cdba9ba1e8a8475720dd26d432d4378fe2ca..0e6a95937476de9f33b1c5c0dd15e0489c645c43 100644 --- a/internal/tui/components/chat/chat.go +++ b/internal/tui/components/chat/chat.go @@ -118,7 +118,7 @@ func (m *messageListCmp) View() string { // handleChildSession handles messages from child sessions (agent tools). func (m *messageListCmp) handleChildSession(event pubsub.Event[message.Message]) tea.Cmd { var cmds []tea.Cmd - if len(event.Payload.ToolCalls()) == 0 { + if len(event.Payload.ToolCalls()) == 0 && len(event.Payload.ToolResults()) == 0 { return nil } items := m.listCmp.Items() @@ -158,6 +158,15 @@ func (m *messageListCmp) handleChildSession(event pubsub.Event[message.Message]) ) } } + for _, tr := range event.Payload.ToolResults() { + for nestedInx, nestedTC := range nestedToolCalls { + if nestedTC.GetToolCall().ID == tr.ToolCallID { + nestedToolCalls[nestedInx].SetToolResult(tr) + break + } + } + } + toolCall.SetNestedToolCalls(nestedToolCalls) m.listCmp.UpdateItem( toolCallInx, diff --git a/internal/tui/components/chat/messages/renderer.go b/internal/tui/components/chat/messages/renderer.go index 2d86d103bc60f2122c3eaf58883bd6d9ac42a47e..51f3094b1df00db9206e75d582cf2b028d14bc6c 100644 --- a/internal/tui/components/chat/messages/renderer.go +++ b/internal/tui/components/chat/messages/renderer.go @@ -112,10 +112,21 @@ func (br baseRenderer) unmarshalParams(input string, target any) error { } // makeHeader builds the tool call header with status icon and parameters for a nested tool call. -func (br baseRenderer) makeNestedHeader(_ *toolCallCmp, tool string, width int, params ...string) string { +func (br baseRenderer) makeNestedHeader(v *toolCallCmp, tool string, width int, params ...string) string { t := styles.CurrentTheme() + icon := t.S().Base.Foreground(t.GreenDark).Render(styles.ToolPending) + if v.result.ToolCallID != "" { + if v.result.IsError { + icon = t.S().Base.Foreground(t.RedDark).Render(styles.ToolError) + } else { + icon = t.S().Base.Foreground(t.Green).Render(styles.ToolSuccess) + } + } else if v.cancelled { + icon = t.S().Muted.Render(styles.ToolPending) + } tool = t.S().Base.Foreground(t.FgHalfMuted).Render(tool) + " " - return tool + renderParamList(true, width-lipgloss.Width(tool), params...) + prefix := fmt.Sprintf("%s %s ", icon, tool) + return prefix + renderParamList(true, width-lipgloss.Width(tool), params...) } // makeHeader builds ": param (key=value)" and truncates as needed. @@ -542,7 +553,7 @@ func (tr agentRenderer) Render(v *toolCallCmp) string { if v.result.ToolCallID == "" { v.spinning = true - parts = append(parts, v.anim.View()) + parts = append(parts, "", v.anim.View()) } else { v.spinning = false } diff --git a/internal/tui/components/chat/messages/tool.go b/internal/tui/components/chat/messages/tool.go index fe61d44fb77f81d330447beecb9b1a7192a2a0c4..41c3d9656de59bcfae9cae3b5d9a8a07cfaf9afd 100644 --- a/internal/tui/components/chat/messages/tool.go +++ b/internal/tui/components/chat/messages/tool.go @@ -219,11 +219,11 @@ func (m *toolCallCmp) SetIsNested(isNested bool) { // renderPending displays the tool name with a loading animation for pending tool calls func (m *toolCallCmp) renderPending() string { t := styles.CurrentTheme() + icon := t.S().Base.Foreground(t.GreenDark).Render(styles.ToolPending) if m.isNested { tool := t.S().Base.Foreground(t.FgHalfMuted).Render(prettifyToolName(m.call.Name)) - return fmt.Sprintf("%s %s", tool, m.anim.View()) + return fmt.Sprintf("%s %s %s", icon, tool, m.anim.View()) } - icon := t.S().Base.Foreground(t.GreenDark).Render(styles.ToolPending) tool := t.S().Base.Foreground(t.Blue).Render(prettifyToolName(m.call.Name)) return fmt.Sprintf("%s %s %s", icon, tool, m.anim.View()) }