diff --git a/crates/agent_ui/src/conversation_view.rs b/crates/agent_ui/src/conversation_view.rs index 20f5212dbce530c1f898a1a411bca5a72757f4bd..5c16f8fe8fe4bd005b8f6306291ae536817a4fee 100644 --- a/crates/agent_ui/src/conversation_view.rs +++ b/crates/agent_ui/src/conversation_view.rs @@ -1507,18 +1507,6 @@ impl ConversationView { return; } - let used_tools = thread.read(cx).used_tools_since_last_user_message(); - self.notify_with_sound( - if used_tools { - "Finished running tools" - } else { - "New message" - }, - IconName::ZedAssistant, - window, - cx, - ); - let should_send_queued = if let Some(active) = self.root_thread_view() { active.update(cx, |active, cx| { if active.skip_queue_processing_count > 0 { @@ -1542,7 +1530,23 @@ impl ConversationView { } else { false }; - if should_send_queued { + + // Skip notifying when a queued message is about to be auto-sent: the agent + // is not actually idle and a notification here would fire just before the + // next turn starts. + if !should_send_queued { + let used_tools = thread.read(cx).used_tools_since_last_user_message(); + self.notify_with_sound( + if used_tools { + "Finished running tools" + } else { + "New message" + }, + IconName::ZedAssistant, + window, + cx, + ); + } else { self.send_queued_message_at_index(0, false, window, cx); } } @@ -3072,6 +3076,71 @@ pub(crate) mod tests { ); } + #[gpui::test] + async fn test_no_notification_when_queued_message_will_be_auto_sent(cx: &mut TestAppContext) { + init_test(cx); + + let connection = StubAgentConnection::new(); + let (conversation_view, cx) = + setup_conversation_view(StubAgentServer::new(connection.clone()), cx).await; + add_to_workspace(conversation_view.clone(), cx); + + let message_editor = message_editor(&conversation_view, cx); + message_editor.update_in(cx, |editor, window, cx| { + editor.set_text("first", window, cx); + }); + + active_thread(&conversation_view, cx) + .update_in(cx, |view, window, cx| view.send(window, cx)); + + cx.run_until_parked(); + + let session_id = conversation_view.read_with(cx, |view, cx| { + view.active_thread() + .unwrap() + .read(cx) + .thread + .read(cx) + .session_id() + .clone() + }); + + active_thread(&conversation_view, cx).update_in(cx, |thread, _window, cx| { + thread.add_to_queue( + vec![acp::ContentBlock::Text(acp::TextContent::new( + "queued".to_string(), + ))], + vec![], + cx, + ); + }); + + cx.deactivate_window(); + cx.run_until_parked(); + + cx.update(|_, cx| { + connection.send_update( + session_id.clone(), + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk::new( + "first response".into(), + )), + cx, + ); + connection.end_turn(session_id, acp::StopReason::EndTurn); + }); + + cx.run_until_parked(); + + assert_eq!( + cx.windows() + .iter() + .filter(|window| window.downcast::().is_some()) + .count(), + 0, + "No notification should fire when a queued message will be auto-sent on Stopped" + ); + } + #[gpui::test] async fn test_notification_for_error(cx: &mut TestAppContext) { init_test(cx);