diff --git a/pkg/agent/interfaces/interfaces.go b/pkg/agent/interfaces/interfaces.go index bdf483e20..93c8df0f6 100644 --- a/pkg/agent/interfaces/interfaces.go +++ b/pkg/agent/interfaces/interfaces.go @@ -44,4 +44,9 @@ type ChannelManager interface { // SendPlaceholder sends a placeholder message (e.g., for audio transcription). SendPlaceholder(ctx context.Context, channel, chatID string) bool + + // DismissToolFeedback clears any tracked tool feedback animation for the + // given channel/chat. Call this when a turn ends without a final response + // (e.g., ResponseHandled tools) to avoid orphaned animation goroutines. + DismissToolFeedback(ctx context.Context, channel, chatID string) } diff --git a/pkg/agent/pipeline_execute.go b/pkg/agent/pipeline_execute.go index 9935f2c9e..88aebe45d 100644 --- a/pkg/agent/pipeline_execute.go +++ b/pkg/agent/pipeline_execute.go @@ -704,6 +704,9 @@ toolLoop: } ts.setPhase(TurnPhaseCompleted) ts.setFinalContent("") + if al.channelManager != nil && ts.channel != "" { + al.channelManager.DismissToolFeedback(ctx, ts.channel, ts.chatID) + } logger.InfoCF("agent", "Tool output satisfied delivery; ending turn without follow-up LLM", map[string]any{ "agent_id": ts.agent.ID, diff --git a/pkg/channels/manager.go b/pkg/channels/manager.go index d56c4fd9b..b87341c6a 100644 --- a/pkg/channels/manager.go +++ b/pkg/channels/manager.go @@ -192,6 +192,17 @@ func clearTrackedToolFeedbackMessage( } } +// DismissToolFeedback clears any tracked tool feedback animation for the +// given channel/chat. This is called when a turn ends without a final +// response (e.g., ResponseHandled tools) to stop orphaned animation goroutines. +func (m *Manager) DismissToolFeedback(ctx context.Context, channelName, chatID string) { + ch, ok := m.GetChannel(channelName) + if !ok { + return + } + dismissTrackedToolFeedbackMessage(ctx, ch, chatID, nil) +} + func prepareToolFeedbackMessageContent(ch Channel, content string) string { prepared := strings.TrimSpace(content) if prepared == "" {