agent_ui: Rename `ConnectionView` to `ConversationView` (#51684)

Bennet Bo Fenner created

We now share connections across multiple "ConnectionView"s the naming
does not make sense anymore

Release Notes:

- N/A

Change summary

crates/agent_ui/src/agent_panel.rs                   | 211 ++++---
crates/agent_ui/src/agent_ui.rs                      |   4 
crates/agent_ui/src/conversation_view.rs             | 355 +++++++------
crates/agent_ui/src/conversation_view/thread_view.rs |  10 
crates/agent_ui/src/message_editor.rs                |   2 
crates/agent_ui/src/sidebar.rs                       |   2 
crates/agent_ui/src/test_support.rs                  |   2 
crates/agent_ui/src/thread_history_view.rs           |  20 
crates/zed/src/visual_test_runner.rs                 |   4 
9 files changed, 333 insertions(+), 277 deletions(-)

Detailed changes

crates/agent_ui/src/agent_panel.rs πŸ”—

@@ -31,12 +31,12 @@ use zed_actions::agent::{
 
 use crate::ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal, HoldForDefault};
 use crate::{
-    AddContextServer, AgentDiffPane, ConnectionView, CopyThreadToClipboard, CycleStartThreadIn,
+    AddContextServer, AgentDiffPane, ConversationView, CopyThreadToClipboard, CycleStartThreadIn,
     Follow, InlineAssistant, LoadThreadFromClipboard, NewTextThread, NewThread,
     OpenActiveThreadAsMarkdown, OpenAgentDiff, OpenHistory, ResetTrialEndUpsell, ResetTrialUpsell,
     StartThreadIn, ToggleNavigationMenu, ToggleNewThreadMenu, ToggleOptionsMenu,
     agent_configuration::{AgentConfiguration, AssistantConfigurationEvent},
-    connection_view::{AcpThreadViewEvent, ThreadView},
+    conversation_view::{AcpThreadViewEvent, ThreadView},
     slash_command::SlashCommandCompletionProvider,
     text_thread_editor::{AgentPanelDelegate, TextThreadEditor, make_lsp_adapter_delegate},
     ui::EndTrialUpsell,
@@ -275,9 +275,9 @@ pub fn init(cx: &mut App) {
                 .register_action(|workspace, _: &OpenAgentDiff, window, cx| {
                     let thread = workspace
                         .panel::<AgentPanel>(cx)
-                        .and_then(|panel| panel.read(cx).active_connection_view().cloned())
-                        .and_then(|thread_view| {
-                            thread_view
+                        .and_then(|panel| panel.read(cx).active_conversation().cloned())
+                        .and_then(|conversation| {
+                            conversation
                                 .read(cx)
                                 .active_thread()
                                 .map(|r| r.read(cx).thread.clone())
@@ -624,7 +624,7 @@ enum History {
 enum ActiveView {
     Uninitialized,
     AgentThread {
-        server_view: Entity<ConnectionView>,
+        conversation_view: Entity<ConversationView>,
     },
     TextThread {
         text_thread_editor: Entity<TextThreadEditor>,
@@ -878,7 +878,7 @@ pub struct AgentPanel {
     focus_handle: FocusHandle,
     active_view: ActiveView,
     previous_view: Option<ActiveView>,
-    background_threads: HashMap<acp::SessionId, Entity<ConnectionView>>,
+    background_threads: HashMap<acp::SessionId, Entity<ConversationView>>,
     new_thread_menu_handle: PopoverMenuHandle<ContextMenu>,
     start_thread_in_menu_handle: PopoverMenuHandle<ContextMenu>,
     agent_panel_menu_handle: PopoverMenuHandle<ContextMenu>,
@@ -1334,9 +1334,11 @@ impl AgentPanel {
             .unwrap_or(false)
     }
 
-    pub fn active_connection_view(&self) -> Option<&Entity<ConnectionView>> {
+    pub fn active_conversation(&self) -> Option<&Entity<ConversationView>> {
         match &self.active_view {
-            ActiveView::AgentThread { server_view, .. } => Some(server_view),
+            ActiveView::AgentThread {
+                conversation_view, ..
+            } => Some(conversation_view),
             ActiveView::Uninitialized
             | ActiveView::TextThread { .. }
             | ActiveView::History { .. }
@@ -1556,11 +1558,11 @@ impl AgentPanel {
     }
 
     fn expand_message_editor(&mut self, window: &mut Window, cx: &mut Context<Self>) {
-        let Some(thread_view) = self.active_connection_view() else {
+        let Some(conversation_view) = self.active_conversation() else {
             return;
         };
 
-        let Some(active_thread) = thread_view.read(cx).active_thread().cloned() else {
+        let Some(active_thread) = conversation_view.read(cx).active_thread().cloned() else {
             return;
         };
 
@@ -1886,8 +1888,8 @@ impl AgentPanel {
         cx: &mut Context<Self>,
     ) {
         if let Some(workspace) = self.workspace.upgrade()
-            && let Some(thread_view) = self.active_connection_view()
-            && let Some(active_thread) = thread_view.read(cx).active_thread().cloned()
+            && let Some(conversation_view) = self.active_conversation()
+            && let Some(active_thread) = conversation_view.read(cx).active_thread().cloned()
         {
             active_thread.update(cx, |thread, cx| {
                 thread
@@ -2077,21 +2079,23 @@ impl AgentPanel {
         }
     }
 
-    pub fn as_active_server_view(&self) -> Option<&Entity<ConnectionView>> {
+    pub fn active_conversation_view(&self) -> Option<&Entity<ConversationView>> {
         match &self.active_view {
-            ActiveView::AgentThread { server_view } => Some(server_view),
+            ActiveView::AgentThread { conversation_view } => Some(conversation_view),
             _ => None,
         }
     }
 
-    pub fn as_active_thread_view(&self, cx: &App) -> Option<Entity<ThreadView>> {
-        let server_view = self.as_active_server_view()?;
+    pub fn active_thread_view(&self, cx: &App) -> Option<Entity<ThreadView>> {
+        let server_view = self.active_conversation_view()?;
         server_view.read(cx).active_thread().cloned()
     }
 
     pub fn active_agent_thread(&self, cx: &App) -> Option<Entity<AcpThread>> {
         match &self.active_view {
-            ActiveView::AgentThread { server_view, .. } => server_view
+            ActiveView::AgentThread {
+                conversation_view, ..
+            } => conversation_view
                 .read(cx)
                 .active_thread()
                 .map(|r| r.read(cx).thread.clone()),
@@ -2109,7 +2113,7 @@ impl AgentPanel {
     pub fn parent_threads(&self, cx: &App) -> Vec<Entity<ThreadView>> {
         let mut views = Vec::new();
 
-        if let Some(server_view) = self.as_active_server_view() {
+        if let Some(server_view) = self.active_conversation_view() {
             if let Some(thread_view) = server_view.read(cx).parent_thread(cx) {
                 views.push(thread_view);
             }
@@ -2125,11 +2129,11 @@ impl AgentPanel {
     }
 
     fn retain_running_thread(&mut self, old_view: ActiveView, cx: &mut Context<Self>) {
-        let ActiveView::AgentThread { server_view } = old_view else {
+        let ActiveView::AgentThread { conversation_view } = old_view else {
             return;
         };
 
-        let Some(thread_view) = server_view.read(cx).parent_thread(cx) else {
+        let Some(thread_view) = conversation_view.read(cx).parent_thread(cx) else {
             return;
         };
 
@@ -2143,14 +2147,15 @@ impl AgentPanel {
             return;
         }
 
-        self.background_threads.insert(session_id, server_view);
+        self.background_threads
+            .insert(session_id, conversation_view);
     }
 
     pub(crate) fn active_native_agent_thread(&self, cx: &App) -> Option<Entity<agent::Thread>> {
         match &self.active_view {
-            ActiveView::AgentThread { server_view, .. } => {
-                server_view.read(cx).as_native_thread(cx)
-            }
+            ActiveView::AgentThread {
+                conversation_view, ..
+            } => conversation_view.read(cx).as_native_thread(cx),
             _ => None,
         }
     }
@@ -2206,24 +2211,26 @@ impl AgentPanel {
         // Re-subscribe whenever the ConnectionView changes, since the inner
         // ThreadView may have been replaced (e.g. navigating between threads).
         self._active_view_observation = match &self.active_view {
-            ActiveView::AgentThread { server_view } => {
+            ActiveView::AgentThread { conversation_view } => {
                 self._thread_view_subscription =
-                    Self::subscribe_to_active_thread_view(server_view, window, cx);
-                let focus_handle = server_view.focus_handle(cx);
+                    Self::subscribe_to_active_thread_view(conversation_view, window, cx);
+                let focus_handle = conversation_view.focus_handle(cx);
                 self._active_thread_focus_subscription =
                     Some(cx.on_focus_in(&focus_handle, window, |_this, _window, cx| {
                         cx.emit(AgentPanelEvent::ThreadFocused);
                         cx.notify();
                     }));
-                Some(
-                    cx.observe_in(server_view, window, |this, server_view, window, cx| {
+                Some(cx.observe_in(
+                    conversation_view,
+                    window,
+                    |this, server_view, window, cx| {
                         this._thread_view_subscription =
                             Self::subscribe_to_active_thread_view(&server_view, window, cx);
                         cx.emit(AgentPanelEvent::ActiveViewChanged);
                         this.serialize(cx);
                         cx.notify();
-                    }),
-                )
+                    },
+                ))
             }
             _ => {
                 self._thread_view_subscription = None;
@@ -2347,7 +2354,7 @@ impl AgentPanel {
     }
 
     fn subscribe_to_active_thread_view(
-        server_view: &Entity<ConnectionView>,
+        server_view: &Entity<ConversationView>,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Option<Subscription> {
@@ -2521,13 +2528,18 @@ impl AgentPanel {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        if let Some(server_view) = self.background_threads.remove(&session_id) {
-            self.set_active_view(ActiveView::AgentThread { server_view }, focus, window, cx);
+        if let Some(conversation_view) = self.background_threads.remove(&session_id) {
+            self.set_active_view(
+                ActiveView::AgentThread { conversation_view },
+                focus,
+                window,
+                cx,
+            );
             return;
         }
 
-        if let ActiveView::AgentThread { server_view } = &self.active_view {
-            if server_view
+        if let ActiveView::AgentThread { conversation_view } = &self.active_view {
+            if conversation_view
                 .read(cx)
                 .active_thread()
                 .map(|t| t.read(cx).id.clone())
@@ -2538,8 +2550,8 @@ impl AgentPanel {
             }
         }
 
-        if let Some(ActiveView::AgentThread { server_view }) = &self.previous_view {
-            if server_view
+        if let Some(ActiveView::AgentThread { conversation_view }) = &self.previous_view {
+            if conversation_view
                 .read(cx)
                 .active_thread()
                 .map(|t| t.read(cx).id.clone())
@@ -2590,8 +2602,8 @@ impl AgentPanel {
 
         let connection_store = self.connection_store.clone();
 
-        let server_view = cx.new(|cx| {
-            crate::ConnectionView::new(
+        let conversation_view = cx.new(|cx| {
+            crate::ConversationView::new(
                 server,
                 connection_store,
                 ext_agent,
@@ -2608,9 +2620,9 @@ impl AgentPanel {
             )
         });
 
-        cx.observe(&server_view, |this, server_view, cx| {
+        cx.observe(&conversation_view, |this, server_view, cx| {
             let is_active = this
-                .as_active_server_view()
+                .active_conversation_view()
                 .is_some_and(|active| active.entity_id() == server_view.entity_id());
             if is_active {
                 cx.emit(AgentPanelEvent::ActiveViewChanged);
@@ -2622,7 +2634,12 @@ impl AgentPanel {
         })
         .detach();
 
-        self.set_active_view(ActiveView::AgentThread { server_view }, focus, window, cx);
+        self.set_active_view(
+            ActiveView::AgentThread { conversation_view },
+            focus,
+            window,
+            cx,
+        );
     }
 
     fn active_thread_has_messages(&self, cx: &App) -> bool {
@@ -3140,7 +3157,9 @@ impl Focusable for AgentPanel {
     fn focus_handle(&self, cx: &App) -> FocusHandle {
         match &self.active_view {
             ActiveView::Uninitialized => self.focus_handle.clone(),
-            ActiveView::AgentThread { server_view, .. } => server_view.focus_handle(cx),
+            ActiveView::AgentThread {
+                conversation_view, ..
+            } => conversation_view.focus_handle(cx),
             ActiveView::History { history: kind } => match kind {
                 History::AgentThreads { view } => view.read(cx).focus_handle(cx),
                 History::TextThreads => self.text_thread_history.focus_handle(cx),
@@ -3269,8 +3288,8 @@ impl AgentPanel {
         const LOADING_SUMMARY_PLACEHOLDER: &str = "Loading Summary…";
 
         let content = match &self.active_view {
-            ActiveView::AgentThread { server_view } => {
-                let server_view_ref = server_view.read(cx);
+            ActiveView::AgentThread { conversation_view } => {
+                let server_view_ref = conversation_view.read(cx);
                 let is_generating_title = server_view_ref.as_native_thread(cx).is_some()
                     && server_view_ref.parent_thread(cx).map_or(false, |tv| {
                         tv.read(cx).thread.read(cx).has_provisional_title()
@@ -3296,18 +3315,18 @@ impl AgentPanel {
                         div()
                             .w_full()
                             .on_action({
-                                let thread_view = server_view.downgrade();
+                                let conversation_view = conversation_view.downgrade();
                                 move |_: &menu::Confirm, window, cx| {
-                                    if let Some(thread_view) = thread_view.upgrade() {
-                                        thread_view.focus_handle(cx).focus(window, cx);
+                                    if let Some(conversation_view) = conversation_view.upgrade() {
+                                        conversation_view.focus_handle(cx).focus(window, cx);
                                     }
                                 }
                             })
                             .on_action({
-                                let thread_view = server_view.downgrade();
+                                let conversation_view = conversation_view.downgrade();
                                 move |_: &editor::actions::Cancel, window, cx| {
-                                    if let Some(thread_view) = thread_view.upgrade() {
-                                        thread_view.focus_handle(cx).focus(window, cx);
+                                    if let Some(conversation_view) = conversation_view.upgrade() {
+                                        conversation_view.focus_handle(cx).focus(window, cx);
                                     }
                                 }
                             })
@@ -3315,7 +3334,7 @@ impl AgentPanel {
                             .into_any_element()
                     }
                 } else {
-                    Label::new(server_view.read(cx).title(cx))
+                    Label::new(conversation_view.read(cx).title(cx))
                         .color(Color::Muted)
                         .truncate()
                         .into_any_element()
@@ -3400,9 +3419,9 @@ impl AgentPanel {
             .into_any()
     }
 
-    fn handle_regenerate_thread_title(thread_view: Entity<ConnectionView>, cx: &mut App) {
-        thread_view.update(cx, |thread_view, cx| {
-            if let Some(thread) = thread_view.as_native_thread(cx) {
+    fn handle_regenerate_thread_title(conversation_view: Entity<ConversationView>, cx: &mut App) {
+        conversation_view.update(cx, |conversation_view, cx| {
+            if let Some(thread) = conversation_view.as_native_thread(cx) {
                 thread.update(cx, |thread, cx| {
                     thread.generate_title(cx);
                 });
@@ -3450,18 +3469,20 @@ impl AgentPanel {
             _ => false,
         };
 
-        let thread_view = match &self.active_view {
-            ActiveView::AgentThread { server_view } => Some(server_view.clone()),
+        let conversation_view = match &self.active_view {
+            ActiveView::AgentThread { conversation_view } => Some(conversation_view.clone()),
             _ => None,
         };
         let thread_with_messages = match &self.active_view {
-            ActiveView::AgentThread { server_view } => {
-                server_view.read(cx).has_user_submitted_prompt(cx)
+            ActiveView::AgentThread { conversation_view } => {
+                conversation_view.read(cx).has_user_submitted_prompt(cx)
             }
             _ => false,
         };
         let has_auth_methods = match &self.active_view {
-            ActiveView::AgentThread { server_view } => server_view.read(cx).has_auth_methods(),
+            ActiveView::AgentThread { conversation_view } => {
+                conversation_view.read(cx).has_auth_methods()
+            }
             _ => false,
         };
 
@@ -3505,13 +3526,13 @@ impl AgentPanel {
                                     .separator();
                             }
 
-                            if let Some(thread_view) = thread_view.as_ref() {
+                            if let Some(conversation_view) = conversation_view.as_ref() {
                                 menu = menu
                                     .entry("Regenerate Thread Title", None, {
-                                        let thread_view = thread_view.clone();
+                                        let conversation_view = conversation_view.clone();
                                         move |_, cx| {
                                             Self::handle_regenerate_thread_title(
-                                                thread_view.clone(),
+                                                conversation_view.clone(),
                                                 cx,
                                             );
                                         }
@@ -3888,7 +3909,9 @@ impl AgentPanel {
             };
 
         let active_thread = match &self.active_view {
-            ActiveView::AgentThread { server_view } => server_view.read(cx).as_native_thread(cx),
+            ActiveView::AgentThread { conversation_view } => {
+                conversation_view.read(cx).as_native_thread(cx)
+            }
             ActiveView::Uninitialized
             | ActiveView::TextThread { .. }
             | ActiveView::History { .. }
@@ -4208,7 +4231,7 @@ impl AgentPanel {
         };
 
         let is_thread_loading = self
-            .active_connection_view()
+            .active_conversation()
             .map(|thread| thread.read(cx).is_loading())
             .unwrap_or(false);
 
@@ -4533,13 +4556,11 @@ impl AgentPanel {
             ActiveView::Uninitialized | ActiveView::History { .. } | ActiveView::Configuration => {
                 false
             }
-            ActiveView::AgentThread { server_view, .. }
-                if server_view.read(cx).as_native_thread(cx).is_none() =>
-            {
-                false
-            }
-            ActiveView::AgentThread { server_view } => {
-                let history_is_empty = server_view
+            ActiveView::AgentThread {
+                conversation_view, ..
+            } if conversation_view.read(cx).as_native_thread(cx).is_none() => false,
+            ActiveView::AgentThread { conversation_view } => {
+                let history_is_empty = conversation_view
                     .read(cx)
                     .history()
                     .is_none_or(|h| h.read(cx).is_empty());
@@ -4804,9 +4825,9 @@ impl AgentPanel {
         cx: &mut Context<Self>,
     ) {
         match &self.active_view {
-            ActiveView::AgentThread { server_view } => {
-                server_view.update(cx, |thread_view, cx| {
-                    thread_view.insert_dragged_files(paths, added_worktrees, window, cx);
+            ActiveView::AgentThread { conversation_view } => {
+                conversation_view.update(cx, |conversation_view, cx| {
+                    conversation_view.insert_dragged_files(paths, added_worktrees, window, cx);
                 });
             }
             ActiveView::TextThread {
@@ -4905,8 +4926,10 @@ impl Render for AgentPanel {
             .on_action(cx.listener(Self::reset_font_size))
             .on_action(cx.listener(Self::toggle_zoom))
             .on_action(cx.listener(|this, _: &ReauthenticateAgent, window, cx| {
-                if let Some(thread_view) = this.active_connection_view() {
-                    thread_view.update(cx, |thread_view, cx| thread_view.reauthenticate(window, cx))
+                if let Some(conversation_view) = this.active_conversation() {
+                    conversation_view.update(cx, |conversation_view, cx| {
+                        conversation_view.reauthenticate(window, cx)
+                    })
                 }
             }))
             .child(self.render_toolbar(window, cx))
@@ -4924,8 +4947,10 @@ impl Render for AgentPanel {
 
                 match &self.active_view {
                     ActiveView::Uninitialized => parent,
-                    ActiveView::AgentThread { server_view, .. } => parent
-                        .child(server_view.clone())
+                    ActiveView::AgentThread {
+                        conversation_view, ..
+                    } => parent
+                        .child(conversation_view.clone())
                         .child(self.render_drag_target(cx)),
                     ActiveView::History { history: kind } => match kind {
                         History::AgentThreads { view } => parent.child(view.clone()),
@@ -5130,9 +5155,9 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate {
             // Wait to create a new context until the workspace is no longer
             // being updated.
             cx.defer_in(window, move |panel, window, cx| {
-                if let Some(thread_view) = panel.active_connection_view() {
-                    thread_view.update(cx, |thread_view, cx| {
-                        thread_view.insert_selections(window, cx);
+                if let Some(conversation_view) = panel.active_conversation() {
+                    conversation_view.update(cx, |conversation_view, cx| {
+                        conversation_view.insert_selections(window, cx);
                     });
                 } else if let Some(text_thread_editor) = panel.active_text_thread_editor() {
                     let snapshot = buffer.read(cx).snapshot(cx);
@@ -5168,9 +5193,9 @@ impl AgentPanelDelegate for ConcreteAssistantPanelDelegate {
             // Wait to create a new context until the workspace is no longer
             // being updated.
             cx.defer_in(window, move |panel, window, cx| {
-                if let Some(thread_view) = panel.active_connection_view() {
-                    thread_view.update(cx, |thread_view, cx| {
-                        thread_view.insert_terminal_text(text, window, cx);
+                if let Some(conversation_view) = panel.active_conversation() {
+                    conversation_view.update(cx, |conversation_view, cx| {
+                        conversation_view.insert_terminal_text(text, window, cx);
                     });
                 } else if let Some(text_thread_editor) = panel.active_text_thread_editor() {
                     text_thread_editor.update(cx, |text_thread_editor, cx| {
@@ -5233,8 +5258,8 @@ impl AgentPanel {
     ///
     /// This is a test-only accessor that exposes the private `active_thread_view()`
     /// method for test assertions. Not compiled into production builds.
-    pub fn active_thread_view_for_tests(&self) -> Option<&Entity<ConnectionView>> {
-        self.active_connection_view()
+    pub fn active_thread_view_for_tests(&self) -> Option<&Entity<ConversationView>> {
+        self.active_conversation()
     }
 
     /// Sets the start_thread_in value directly, bypassing validation.
@@ -5296,7 +5321,7 @@ impl AgentPanel {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::connection_view::tests::{StubAgentServer, init_test};
+    use crate::conversation_view::tests::{StubAgentServer, init_test};
     use crate::test_support::{active_session_id, open_thread_with_connection, send_message};
     use acp_thread::{StubAgentConnection, ThreadStatus};
     use assistant_text_thread::TextThreadStore;
@@ -5419,7 +5444,7 @@ mod tests {
                 "workspace A agent type should be restored"
             );
             assert!(
-                panel.active_connection_view().is_some(),
+                panel.active_conversation().is_some(),
                 "workspace A should have its active thread restored"
             );
         });
@@ -5439,7 +5464,7 @@ mod tests {
                 "workspace B agent type should be restored"
             );
             assert!(
-                panel.active_connection_view().is_none(),
+                panel.active_conversation().is_none(),
                 "workspace B should have no active thread"
             );
         });
@@ -5856,7 +5881,7 @@ mod tests {
         send_message(&panel, &mut cx);
 
         let weak_view_a = panel.read_with(&cx, |panel, _cx| {
-            panel.active_connection_view().unwrap().downgrade()
+            panel.active_conversation().unwrap().downgrade()
         });
 
         // Thread A should be idle (auto-completed via set_next_prompt_updates).

crates/agent_ui/src/agent_ui.rs πŸ”—

@@ -8,9 +8,9 @@ mod branch_names;
 mod buffer_codegen;
 mod completion_provider;
 mod config_options;
-pub(crate) mod connection_view;
 mod context;
 mod context_server_configuration;
+pub(crate) mod conversation_view;
 mod entry_view_state;
 mod external_source_prompt;
 mod favorite_models;
@@ -71,7 +71,7 @@ pub use crate::agent_panel::{
 use crate::agent_registry_ui::AgentRegistryPage;
 pub use crate::inline_assistant::InlineAssistant;
 pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
-pub(crate) use connection_view::ConnectionView;
+pub(crate) use conversation_view::ConversationView;
 pub use external_source_prompt::ExternalSourcePrompt;
 pub(crate) use mode_selector::ModeSelector;
 pub(crate) use model_selector::ModelSelector;

crates/agent_ui/src/connection_view.rs β†’ crates/agent_ui/src/conversation_view.rs πŸ”—

@@ -306,9 +306,9 @@ pub enum AcpServerViewEvent {
     ActiveThreadChanged,
 }
 
-impl EventEmitter<AcpServerViewEvent> for ConnectionView {}
+impl EventEmitter<AcpServerViewEvent> for ConversationView {}
 
-pub struct ConnectionView {
+pub struct ConversationView {
     agent: Rc<dyn AgentServer>,
     connection_store: Entity<AgentConnectionStore>,
     connection_key: Agent,
@@ -325,7 +325,7 @@ pub struct ConnectionView {
     _subscriptions: Vec<Subscription>,
 }
 
-impl ConnectionView {
+impl ConversationView {
     pub fn has_auth_methods(&self) -> bool {
         self.as_connected().map_or(false, |connected| {
             !connected.connection.auth_methods().is_empty()
@@ -478,7 +478,7 @@ impl ConnectedServerState {
     }
 }
 
-impl ConnectionView {
+impl ConversationView {
     pub fn new(
         agent: Rc<dyn AgentServer>,
         connection_store: Entity<AgentConnectionStore>,
@@ -2313,7 +2313,7 @@ impl ConnectionView {
     fn render_markdown(&self, markdown: Entity<Markdown>, style: MarkdownStyle) -> MarkdownElement {
         let workspace = self.workspace.clone();
         MarkdownElement::new(markdown, style).on_url_click(move |text, window, cx| {
-            crate::connection_view::thread_view::open_link(text, &workspace, window, cx);
+            crate::conversation_view::thread_view::open_link(text, &workspace, window, cx);
         })
     }
 
@@ -2643,7 +2643,7 @@ fn placeholder_text(agent_name: &str, has_commands: bool) -> String {
     }
 }
 
-impl Focusable for ConnectionView {
+impl Focusable for ConversationView {
     fn focus_handle(&self, cx: &App) -> FocusHandle {
         match self.active_thread() {
             Some(thread) => thread.read(cx).focus_handle(cx),
@@ -2653,7 +2653,7 @@ impl Focusable for ConnectionView {
 }
 
 #[cfg(any(test, feature = "test-support"))]
-impl ConnectionView {
+impl ConversationView {
     /// Expands a tool call so its content is visible.
     /// This is primarily useful for visual testing.
     pub fn expand_tool_call(&mut self, tool_call_id: acp::ToolCallId, cx: &mut Context<Self>) {
@@ -2666,7 +2666,7 @@ impl ConnectionView {
     }
 }
 
-impl Render for ConnectionView {
+impl Render for ConversationView {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         self.sync_queued_message_editors(window, cx);
         let v2_flag = cx.has_flag::<AgentV2FeatureFlag>();
@@ -2786,9 +2786,10 @@ pub(crate) mod tests {
     async fn test_drop(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, _cx) = setup_thread_view(StubAgentServer::default_response(), cx).await;
-        let weak_view = thread_view.downgrade();
-        drop(thread_view);
+        let (conversation_view, _cx) =
+            setup_conversation_view(StubAgentServer::default_response(), cx).await;
+        let weak_view = conversation_view.downgrade();
+        drop(conversation_view);
         assert!(!weak_view.is_upgradable());
     }
 
@@ -2801,14 +2802,14 @@ pub(crate) mod tests {
         };
         let initial_content = AgentInitialContent::FromExternalSource(prompt);
 
-        let (thread_view, cx) = setup_thread_view_with_initial_content(
+        let (conversation_view, cx) = setup_conversation_view_with_initial_content(
             StubAgentServer::default_response(),
             initial_content,
             cx,
         )
         .await;
 
-        active_thread(&thread_view, cx).read_with(cx, |view, cx| {
+        active_thread(&conversation_view, cx).read_with(cx, |view, cx| {
             assert!(view.show_external_source_prompt_warning);
             assert_eq!(view.thread.read(cx).entries().len(), 0);
             assert_eq!(view.message_editor.read(cx).text(cx), "Write me a script");
@@ -2824,17 +2825,18 @@ pub(crate) mod tests {
         };
         let initial_content = AgentInitialContent::FromExternalSource(prompt);
 
-        let (thread_view, cx) = setup_thread_view_with_initial_content(
+        let (conversation_view, cx) = setup_conversation_view_with_initial_content(
             StubAgentServer::default_response(),
             initial_content,
             cx,
         )
         .await;
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
         cx.run_until_parked();
 
-        active_thread(&thread_view, cx).read_with(cx, |view, cx| {
+        active_thread(&conversation_view, cx).read_with(cx, |view, cx| {
             assert!(!view.show_external_source_prompt_warning);
             assert_eq!(view.message_editor.read(cx).text(cx), "");
             assert_eq!(view.thread.read(cx).entries().len(), 2);
@@ -2845,16 +2847,18 @@ pub(crate) mod tests {
     async fn test_notification_for_stop_event(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::default_response(), cx).await;
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
         });
 
         cx.deactivate_window();
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
@@ -2869,17 +2873,18 @@ pub(crate) mod tests {
     async fn test_notification_for_error(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, cx) =
-            setup_thread_view(StubAgentServer::new(SaboteurAgentConnection), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::new(SaboteurAgentConnection), cx).await;
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
         });
 
         cx.deactivate_window();
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
@@ -2907,9 +2912,9 @@ pub(crate) mod tests {
         let connection_store =
             cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
 
-        let thread_view = cx.update(|window, cx| {
+        let conversation_view = cx.update(|window, cx| {
             cx.new(|cx| {
-                ConnectionView::new(
+                ConversationView::new(
                     Rc::new(StubAgentServer::default_response()),
                     connection_store,
                     Agent::Custom { id: "Test".into() },
@@ -2931,7 +2936,7 @@ pub(crate) mod tests {
         cx.run_until_parked();
 
         let history = cx.update(|_window, cx| {
-            thread_view
+            conversation_view
                 .read(cx)
                 .history()
                 .expect("Missing history")
@@ -2939,7 +2944,7 @@ pub(crate) mod tests {
         });
 
         // Initially empty because StubAgentConnection.session_list() returns None
-        active_thread(&thread_view, cx).read_with(cx, |view, _cx| {
+        active_thread(&conversation_view, cx).read_with(cx, |view, _cx| {
             assert_eq!(view.recent_history_entries.len(), 0);
         });
 
@@ -2951,7 +2956,7 @@ pub(crate) mod tests {
         });
         cx.run_until_parked();
 
-        active_thread(&thread_view, cx).read_with(cx, |view, _cx| {
+        active_thread(&conversation_view, cx).read_with(cx, |view, _cx| {
             assert_eq!(view.recent_history_entries.len(), 1);
             assert_eq!(
                 view.recent_history_entries[0].session_id,
@@ -2967,7 +2972,7 @@ pub(crate) mod tests {
         });
         cx.run_until_parked();
 
-        active_thread(&thread_view, cx).read_with(cx, |view, _cx| {
+        active_thread(&conversation_view, cx).read_with(cx, |view, _cx| {
             assert_eq!(view.recent_history_entries.len(), 1);
             assert_eq!(
                 view.recent_history_entries[0].session_id,
@@ -2981,7 +2986,7 @@ pub(crate) mod tests {
         init_test(cx);
 
         let session = AgentSessionInfo::new(SessionId::new("history-session"));
-        let (thread_view, history, cx) = setup_thread_view_with_history(
+        let (conversation_view, history, cx) = setup_thread_view_with_history(
             StubAgentServer::new(SessionHistoryConnection::new(vec![session.clone()])),
             cx,
         )
@@ -2994,7 +2999,7 @@ pub(crate) mod tests {
             );
         });
 
-        active_thread(&thread_view, cx).read_with(cx, |view, _cx| {
+        active_thread(&conversation_view, cx).read_with(cx, |view, _cx| {
             assert_eq!(view.recent_history_entries.len(), 1);
             assert_eq!(
                 view.recent_history_entries[0].session_id,
@@ -3017,9 +3022,9 @@ pub(crate) mod tests {
         let connection_store =
             cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
 
-        let thread_view = cx.update(|window, cx| {
+        let conversation_view = cx.update(|window, cx| {
             cx.new(|cx| {
-                ConnectionView::new(
+                ConversationView::new(
                     Rc::new(StubAgentServer::new(ResumeOnlyAgentConnection)),
                     connection_store,
                     Agent::Custom { id: "Test".into() },
@@ -3039,7 +3044,7 @@ pub(crate) mod tests {
 
         cx.run_until_parked();
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             let state = view.active_thread().unwrap();
             assert!(state.read(cx).resumed_without_history);
             assert_eq!(state.read(cx).list_state.item_count(), 0);
@@ -3072,9 +3077,9 @@ pub(crate) mod tests {
         let connection_store =
             cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
 
-        let _thread_view = cx.update(|window, cx| {
+        let _conversation_view = cx.update(|window, cx| {
             cx.new(|cx| {
-                ConnectionView::new(
+                ConversationView::new(
                     Rc::new(StubAgentServer::new(connection)),
                     connection_store,
                     Agent::Custom { id: "Test".into() },
@@ -3105,20 +3110,21 @@ pub(crate) mod tests {
     async fn test_refusal_handling(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, cx) =
-            setup_thread_view(StubAgentServer::new(RefusalAgentConnection), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::new(RefusalAgentConnection), cx).await;
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Do something harmful", window, cx);
         });
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
         // Check that the refusal error is set
-        thread_view.read_with(cx, |thread_view, cx| {
+        conversation_view.read_with(cx, |thread_view, cx| {
             let state = thread_view.active_thread().unwrap();
             assert!(
                 matches!(state.read(cx).thread_error, Some(ThreadError::Refusal)),
@@ -3131,9 +3137,9 @@ pub(crate) mod tests {
     async fn test_connect_failure_transitions_to_load_error(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, cx) = setup_thread_view(FailingAgentServer, cx).await;
+        let (conversation_view, cx) = setup_conversation_view(FailingAgentServer, cx).await;
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             let title = view.title(cx);
             assert_eq!(
                 title.as_ref(),
@@ -3167,11 +3173,12 @@ pub(crate) mod tests {
         init_test(cx);
 
         let connection = AuthGatedAgentConnection::new();
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::new(connection), cx).await;
 
         // When new_session returns AuthRequired, the server should transition
         // to Connected + Unauthenticated rather than getting stuck in Loading.
-        thread_view.read_with(cx, |view, _cx| {
+        conversation_view.read_with(cx, |view, _cx| {
             let connected = view
                 .as_connected()
                 .expect("Should be in Connected state even though auth is required");
@@ -3189,7 +3196,7 @@ pub(crate) mod tests {
             );
         });
 
-        thread_view.read_with(cx, |view, _cx| {
+        conversation_view.read_with(cx, |view, _cx| {
             assert!(
                 view.active_thread().is_none(),
                 "active_thread() should be None when unauthenticated without a session"
@@ -3199,7 +3206,7 @@ pub(crate) mod tests {
         // Authenticate using the real authenticate flow on ConnectionView.
         // This calls connection.authenticate(), which flips the internal flag,
         // then on success triggers reset() -> new_session() which now succeeds.
-        thread_view.update_in(cx, |view, window, cx| {
+        conversation_view.update_in(cx, |view, window, cx| {
             view.authenticate(
                 acp::AuthMethodId::new(AuthGatedAgentConnection::AUTH_METHOD_ID),
                 window,
@@ -3209,7 +3216,7 @@ pub(crate) mod tests {
         cx.run_until_parked();
 
         // After auth, the server should have an active thread in the Ok state.
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             let connected = view
                 .as_connected()
                 .expect("Should still be in Connected state after auth");
@@ -3254,16 +3261,18 @@ pub(crate) mod tests {
 
         connection.set_next_prompt_updates(vec![acp::SessionUpdate::ToolCall(tool_call)]);
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::new(connection), cx).await;
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
         });
 
         cx.deactivate_window();
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
@@ -3278,11 +3287,12 @@ pub(crate) mod tests {
     async fn test_notification_when_panel_hidden(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::default_response(), cx).await;
 
-        add_to_workspace(thread_view.clone(), cx);
+        add_to_workspace(conversation_view.clone(), cx);
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
 
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
@@ -3292,7 +3302,8 @@ pub(crate) mod tests {
         // Note: In the test environment, the panel is not actually added to the dock,
         // so is_agent_panel_hidden will return true
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
@@ -3309,9 +3320,10 @@ pub(crate) mod tests {
     async fn test_notification_still_works_when_window_inactive(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::default_response(), cx).await;
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
         });
@@ -3319,7 +3331,8 @@ pub(crate) mod tests {
         // Deactivate window - should show notification regardless of setting
         cx.deactivate_window();
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
@@ -3387,9 +3400,9 @@ pub(crate) mod tests {
             cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project1.clone(), cx)));
 
         let agent = StubAgentServer::default_response();
-        let thread_view = cx.update(|window, cx| {
+        let conversation_view = cx.update(|window, cx| {
             cx.new(|cx| {
-                ConnectionView::new(
+                ConversationView::new(
                     Rc::new(agent),
                     connection_store,
                     Agent::Custom { id: "Test".into() },
@@ -3408,7 +3421,7 @@ pub(crate) mod tests {
         });
         cx.run_until_parked();
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
         });
@@ -3435,7 +3448,8 @@ pub(crate) mod tests {
         // Window is active, agent panel is visible in workspace1, but workspace1
         // is in the background. The notification should show because the user
         // can't actually see the agent panel.
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
@@ -3482,16 +3496,18 @@ pub(crate) mod tests {
             );
         });
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::default_response(), cx).await;
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
         });
 
         // Window is active
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
@@ -3508,18 +3524,20 @@ pub(crate) mod tests {
     async fn test_notification_closed_when_thread_view_dropped(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::default_response(), cx).await;
 
-        let weak_view = thread_view.downgrade();
+        let weak_view = conversation_view.downgrade();
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
         });
 
         cx.deactivate_window();
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
@@ -3532,7 +3550,7 @@ pub(crate) mod tests {
         );
 
         // Drop the thread view (simulating navigation to a new thread)
-        drop(thread_view);
+        drop(conversation_view);
         drop(message_editor);
         // Trigger an update to flush effects, which will call release_dropped_entities
         cx.update(|_window, _cx| {});
@@ -3553,45 +3571,49 @@ pub(crate) mod tests {
         );
     }
 
-    async fn setup_thread_view(
+    async fn setup_conversation_view(
         agent: impl AgentServer + 'static,
         cx: &mut TestAppContext,
-    ) -> (Entity<ConnectionView>, &mut VisualTestContext) {
-        let (thread_view, _history, cx) =
-            setup_thread_view_with_history_and_initial_content(agent, None, cx).await;
-        (thread_view, cx)
+    ) -> (Entity<ConversationView>, &mut VisualTestContext) {
+        let (conversation_view, _history, cx) =
+            setup_conversation_view_with_history_and_initial_content(agent, None, cx).await;
+        (conversation_view, cx)
     }
 
     async fn setup_thread_view_with_history(
         agent: impl AgentServer + 'static,
         cx: &mut TestAppContext,
     ) -> (
-        Entity<ConnectionView>,
+        Entity<ConversationView>,
         Entity<ThreadHistory>,
         &mut VisualTestContext,
     ) {
-        let (thread_view, history, cx) =
-            setup_thread_view_with_history_and_initial_content(agent, None, cx).await;
-        (thread_view, history.expect("Missing history"), cx)
+        let (conversation_view, history, cx) =
+            setup_conversation_view_with_history_and_initial_content(agent, None, cx).await;
+        (conversation_view, history.expect("Missing history"), cx)
     }
 
-    async fn setup_thread_view_with_initial_content(
+    async fn setup_conversation_view_with_initial_content(
         agent: impl AgentServer + 'static,
         initial_content: AgentInitialContent,
         cx: &mut TestAppContext,
-    ) -> (Entity<ConnectionView>, &mut VisualTestContext) {
-        let (thread_view, _history, cx) =
-            setup_thread_view_with_history_and_initial_content(agent, Some(initial_content), cx)
-                .await;
-        (thread_view, cx)
+    ) -> (Entity<ConversationView>, &mut VisualTestContext) {
+        let (conversation_view, _history, cx) =
+            setup_conversation_view_with_history_and_initial_content(
+                agent,
+                Some(initial_content),
+                cx,
+            )
+            .await;
+        (conversation_view, cx)
     }
 
-    async fn setup_thread_view_with_history_and_initial_content(
+    async fn setup_conversation_view_with_history_and_initial_content(
         agent: impl AgentServer + 'static,
         initial_content: Option<AgentInitialContent>,
         cx: &mut TestAppContext,
     ) -> (
-        Entity<ConnectionView>,
+        Entity<ConversationView>,
         Option<Entity<ThreadHistory>>,
         &mut VisualTestContext,
     ) {
@@ -3607,9 +3629,9 @@ pub(crate) mod tests {
 
         let agent_key = Agent::Custom { id: "Test".into() };
 
-        let thread_view = cx.update(|window, cx| {
+        let conversation_view = cx.update(|window, cx| {
             cx.new(|cx| {
-                ConnectionView::new(
+                ConversationView::new(
                     Rc::new(agent),
                     connection_store.clone(),
                     agent_key.clone(),
@@ -3635,16 +3657,17 @@ pub(crate) mod tests {
                 .and_then(|e| e.read(cx).history().cloned())
         });
 
-        (thread_view, history, cx)
+        (conversation_view, history, cx)
     }
 
-    fn add_to_workspace(thread_view: Entity<ConnectionView>, cx: &mut VisualTestContext) {
-        let workspace = thread_view.read_with(cx, |thread_view, _cx| thread_view.workspace.clone());
+    fn add_to_workspace(conversation_view: Entity<ConversationView>, cx: &mut VisualTestContext) {
+        let workspace =
+            conversation_view.read_with(cx, |thread_view, _cx| thread_view.workspace.clone());
 
         workspace
             .update_in(cx, |workspace, window, cx| {
                 workspace.add_item_to_active_pane(
-                    Box::new(cx.new(|_| ThreadViewItem(thread_view.clone()))),
+                    Box::new(cx.new(|_| ThreadViewItem(conversation_view.clone()))),
                     None,
                     true,
                     window,
@@ -3654,7 +3677,7 @@ pub(crate) mod tests {
             .unwrap();
     }
 
-    struct ThreadViewItem(Entity<ConnectionView>);
+    struct ThreadViewItem(Entity<ConversationView>);
 
     impl Item for ThreadViewItem {
         type Event = ();
@@ -4330,11 +4353,11 @@ pub(crate) mod tests {
     }
 
     fn active_thread(
-        thread_view: &Entity<ConnectionView>,
+        conversation_view: &Entity<ConversationView>,
         cx: &TestAppContext,
     ) -> Entity<ThreadView> {
         cx.read(|cx| {
-            thread_view
+            conversation_view
                 .read(cx)
                 .active_thread()
                 .expect("No active thread")
@@ -4343,10 +4366,10 @@ pub(crate) mod tests {
     }
 
     fn message_editor(
-        thread_view: &Entity<ConnectionView>,
+        conversation_view: &Entity<ConversationView>,
         cx: &TestAppContext,
     ) -> Entity<MessageEditor> {
-        let thread = active_thread(thread_view, cx);
+        let thread = active_thread(conversation_view, cx);
         cx.read(|cx| thread.read(cx).message_editor.clone())
     }
 
@@ -4373,9 +4396,9 @@ pub(crate) mod tests {
             cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
 
         let connection = Rc::new(StubAgentConnection::new());
-        let thread_view = cx.update(|window, cx| {
+        let conversation_view = cx.update(|window, cx| {
             cx.new(|cx| {
-                ConnectionView::new(
+                ConversationView::new(
                     Rc::new(StubAgentServer::new(connection.as_ref().clone())),
                     connection_store,
                     Agent::Custom { id: "Test".into() },
@@ -4395,7 +4418,7 @@ pub(crate) mod tests {
 
         cx.run_until_parked();
 
-        let thread = thread_view
+        let thread = conversation_view
             .read_with(cx, |view, cx| {
                 view.active_thread().map(|r| r.read(cx).thread.clone())
             })
@@ -4421,7 +4444,7 @@ pub(crate) mod tests {
             assert_eq!(thread.entries().len(), 2);
         });
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             let entry_view_state = view
                 .active_thread()
                 .map(|active| active.read(cx).entry_view_state.clone())
@@ -4462,7 +4485,7 @@ pub(crate) mod tests {
             user_message.id.clone().unwrap()
         });
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             let entry_view_state = view
                 .active_thread()
                 .unwrap()
@@ -4501,7 +4524,7 @@ pub(crate) mod tests {
             assert_eq!(thread.entries().len(), 2);
         });
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             let active = view.active_thread().unwrap();
             active
                 .read(cx)
@@ -4534,10 +4557,10 @@ pub(crate) mod tests {
             acp::ContentChunk::new("Response 1".into()),
         )]);
 
-        let (thread_view, cx) =
-            setup_thread_view(StubAgentServer::new(connection.clone()), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::new(connection.clone()), cx).await;
 
-        let thread = thread_view
+        let thread = conversation_view
             .read_with(cx, |view, cx| {
                 view.active_thread().map(|r| r.read(cx).thread.clone())
             })
@@ -4560,12 +4583,12 @@ pub(crate) mod tests {
         cx.run_until_parked();
 
         // Move somewhere else first so we're not trivially already on the last user prompt.
-        active_thread(&thread_view, cx).update(cx, |view, cx| {
+        active_thread(&conversation_view, cx).update(cx, |view, cx| {
             view.scroll_to_top(cx);
         });
         cx.run_until_parked();
 
-        active_thread(&thread_view, cx).update(cx, |view, cx| {
+        active_thread(&conversation_view, cx).update(cx, |view, cx| {
             view.scroll_to_most_recent_user_prompt(cx);
             let scroll_top = view.list_state.logical_scroll_top();
             // Entries layout is: [User1, Assistant1, User2, Assistant2]
@@ -4579,10 +4602,11 @@ pub(crate) mod tests {
     ) {
         init_test(cx);
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::default_response(), cx).await;
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::default_response(), cx).await;
 
         // With no entries, scrolling should be a no-op and must not panic.
-        active_thread(&thread_view, cx).update(cx, |view, cx| {
+        active_thread(&conversation_view, cx).update(cx, |view, cx| {
             view.scroll_to_most_recent_user_prompt(cx);
             let scroll_top = view.list_state.logical_scroll_top();
             assert_eq!(scroll_top.item_ix, 0);
@@ -4599,18 +4623,20 @@ pub(crate) mod tests {
             acp::ContentChunk::new("Response".into()),
         )]);
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await;
-        add_to_workspace(thread_view.clone(), cx);
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::new(connection), cx).await;
+        add_to_workspace(conversation_view.clone(), cx);
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Original message to edit", window, cx);
         });
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
-        let user_message_editor = thread_view.read_with(cx, |view, cx| {
+        let user_message_editor = conversation_view.read_with(cx, |view, cx| {
             assert_eq!(
                 view.active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4631,7 +4657,7 @@ pub(crate) mod tests {
 
         // Focus
         cx.focus(&user_message_editor);
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             assert_eq!(
                 view.active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4649,7 +4675,7 @@ pub(crate) mod tests {
             window.dispatch_action(Box::new(editor::actions::Cancel), cx);
         });
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             assert_eq!(
                 view.active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4668,16 +4694,17 @@ pub(crate) mod tests {
 
         let connection = StubAgentConnection::new();
 
-        let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await;
-        add_to_workspace(thread_view.clone(), cx);
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::new(connection), cx).await;
+        add_to_workspace(conversation_view.clone(), cx);
 
-        let message_editor = message_editor(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("", window, cx);
         });
 
         let thread = cx.read(|cx| {
-            thread_view
+            conversation_view
                 .read(cx)
                 .active_thread()
                 .unwrap()
@@ -4687,7 +4714,7 @@ pub(crate) mod tests {
         });
         let entries_before = cx.read(|cx| thread.read(cx).entries().len());
 
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| {
+        active_thread(&conversation_view, cx).update_in(cx, |view, window, cx| {
             view.send(window, cx);
         });
         cx.run_until_parked();
@@ -4709,19 +4736,20 @@ pub(crate) mod tests {
             acp::ContentChunk::new("Response".into()),
         )]);
 
-        let (thread_view, cx) =
-            setup_thread_view(StubAgentServer::new(connection.clone()), cx).await;
-        add_to_workspace(thread_view.clone(), cx);
+        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(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Original message to edit", window, cx);
         });
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
-        let user_message_editor = thread_view.read_with(cx, |view, cx| {
+        let user_message_editor = conversation_view.read_with(cx, |view, cx| {
             assert_eq!(
                 view.active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4769,7 +4797,7 @@ pub(crate) mod tests {
 
         cx.run_until_parked();
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             assert_eq!(
                 view.active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4812,19 +4840,20 @@ pub(crate) mod tests {
 
         let connection = StubAgentConnection::new();
 
-        let (thread_view, cx) =
-            setup_thread_view(StubAgentServer::new(connection.clone()), cx).await;
-        add_to_workspace(thread_view.clone(), cx);
+        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(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Original message to edit", window, cx);
         });
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
         cx.run_until_parked();
 
-        let (user_message_editor, session_id) = thread_view.read_with(cx, |view, cx| {
+        let (user_message_editor, session_id) = conversation_view.read_with(cx, |view, cx| {
             let thread = view.active_thread().unwrap().read(cx).thread.read(cx);
             assert_eq!(thread.entries().len(), 1);
 
@@ -4846,7 +4875,7 @@ pub(crate) mod tests {
         // Focus
         cx.focus(&user_message_editor);
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             assert_eq!(
                 view.active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4859,7 +4888,7 @@ pub(crate) mod tests {
             editor.set_text("Edited message content", window, cx);
         });
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             assert_eq!(
                 view.active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4877,7 +4906,7 @@ pub(crate) mod tests {
             connection.end_turn(session_id, acp::StopReason::EndTurn);
         });
 
-        thread_view.read_with(cx, |view, cx| {
+        conversation_view.read_with(cx, |view, cx| {
             assert_eq!(
                 view.active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4891,7 +4920,7 @@ pub(crate) mod tests {
         cx.update(|window, cx| {
             assert!(user_message_editor.focus_handle(cx).is_focused(window));
             assert_eq!(
-                thread_view
+                conversation_view
                     .read(cx)
                     .active_thread()
                     .and_then(|active| active.read(cx).editing_message),
@@ -4905,7 +4934,7 @@ pub(crate) mod tests {
     }
 
     struct GeneratingThreadSetup {
-        thread_view: Entity<ConnectionView>,
+        conversation_view: Entity<ConversationView>,
         thread: Entity<AcpThread>,
         message_editor: Entity<MessageEditor>,
     }
@@ -4915,17 +4944,18 @@ pub(crate) mod tests {
     ) -> (GeneratingThreadSetup, &mut VisualTestContext) {
         let connection = StubAgentConnection::new();
 
-        let (thread_view, cx) =
-            setup_thread_view(StubAgentServer::new(connection.clone()), cx).await;
-        add_to_workspace(thread_view.clone(), cx);
+        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(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Hello", window, cx);
         });
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
-        let (thread, session_id) = thread_view.read_with(cx, |view, cx| {
+        let (thread, session_id) = conversation_view.read_with(cx, |view, cx| {
             let thread = view
                 .active_thread()
                 .as_ref()
@@ -4956,7 +4986,7 @@ pub(crate) mod tests {
 
         (
             GeneratingThreadSetup {
-                thread_view,
+                conversation_view,
                 thread,
                 message_editor,
             },
@@ -4971,13 +5001,13 @@ pub(crate) mod tests {
         let (setup, cx) = setup_generating_thread(cx).await;
 
         let focus_handle = setup
-            .thread_view
+            .conversation_view
             .read_with(cx, |view, cx| view.focus_handle(cx));
         cx.update(|window, cx| {
             window.focus(&focus_handle, cx);
         });
 
-        setup.thread_view.update_in(cx, |_, window, cx| {
+        setup.conversation_view.update_in(cx, |_, window, cx| {
             window.dispatch_action(menu::Cancel.boxed_clone(), cx);
         });
 
@@ -5016,11 +5046,11 @@ pub(crate) mod tests {
     async fn test_escape_when_idle_is_noop(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let (thread_view, cx) =
-            setup_thread_view(StubAgentServer::new(StubAgentConnection::new()), cx).await;
-        add_to_workspace(thread_view.clone(), cx);
+        let (conversation_view, cx) =
+            setup_conversation_view(StubAgentServer::new(StubAgentConnection::new()), cx).await;
+        add_to_workspace(conversation_view.clone(), cx);
 
-        let thread = thread_view.read_with(cx, |view, cx| {
+        let thread = conversation_view.read_with(cx, |view, cx| {
             view.active_thread().unwrap().read(cx).thread.clone()
         });
 
@@ -5028,12 +5058,12 @@ pub(crate) mod tests {
             assert_eq!(thread.status(), ThreadStatus::Idle);
         });
 
-        let focus_handle = thread_view.read_with(cx, |view, _cx| view.focus_handle.clone());
+        let focus_handle = conversation_view.read_with(cx, |view, _cx| view.focus_handle.clone());
         cx.update(|window, cx| {
             window.focus(&focus_handle, cx);
         });
 
-        thread_view.update_in(cx, |_, window, cx| {
+        conversation_view.update_in(cx, |_, window, cx| {
             window.dispatch_action(menu::Cancel.boxed_clone(), cx);
         });
 
@@ -5050,17 +5080,18 @@ pub(crate) mod tests {
 
         let connection = StubAgentConnection::new();
 
-        let (thread_view, cx) =
-            setup_thread_view(StubAgentServer::new(connection.clone()), cx).await;
-        add_to_workspace(thread_view.clone(), cx);
+        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(&thread_view, cx);
+        let message_editor = message_editor(&conversation_view, cx);
         message_editor.update_in(cx, |editor, window, cx| {
             editor.set_text("Message 1", window, cx);
         });
-        active_thread(&thread_view, cx).update_in(cx, |view, window, cx| view.send(window, cx));
+        active_thread(&conversation_view, cx)
+            .update_in(cx, |view, window, cx| view.send(window, cx));
 
-        let (thread, session_id) = thread_view.read_with(cx, |view, cx| {
+        let (thread, session_id) = conversation_view.read_with(cx, |view, cx| {
             let thread = view.active_thread().unwrap().read(cx).thread.clone();
 
             (thread.clone(), thread.read(cx).session_id().clone())

crates/agent_ui/src/connection_view/thread_view.rs β†’ crates/agent_ui/src/conversation_view/thread_view.rs πŸ”—

@@ -167,7 +167,7 @@ pub struct ThreadView {
     pub parent_id: Option<acp::SessionId>,
     pub thread: Entity<AcpThread>,
     pub(crate) conversation: Entity<super::Conversation>,
-    pub server_view: WeakEntity<ConnectionView>,
+    pub server_view: WeakEntity<ConversationView>,
     pub agent_icon: IconName,
     pub agent_icon_from_external_svg: Option<SharedString>,
     pub agent_id: AgentId,
@@ -256,7 +256,7 @@ impl ThreadView {
         parent_id: Option<acp::SessionId>,
         thread: Entity<AcpThread>,
         conversation: Entity<super::Conversation>,
-        server_view: WeakEntity<ConnectionView>,
+        server_view: WeakEntity<ConversationView>,
         agent_icon: IconName,
         agent_icon_from_external_svg: Option<SharedString>,
         agent_id: AgentId,
@@ -882,7 +882,7 @@ impl ThreadView {
                     let agent_id = self.agent_id.clone();
                     let server_view = self.server_view.clone();
                     move |window, cx| {
-                        ConnectionView::handle_auth_required(
+                        ConversationView::handle_auth_required(
                             server_view.clone(),
                             AuthRequired::new(),
                             agent_id,
@@ -1755,7 +1755,7 @@ impl ThreadView {
     pub fn sync_thread(
         &mut self,
         project: Entity<Project>,
-        server_view: Entity<ConnectionView>,
+        server_view: Entity<ConversationView>,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
@@ -7318,7 +7318,7 @@ impl ThreadView {
                     }
                     let connection = this.thread.read(cx).connection().clone();
                     window.defer(cx, |window, cx| {
-                        ConnectionView::handle_auth_required(
+                        ConversationView::handle_auth_required(
                             server_view,
                             AuthRequired::new(),
                             agent_name,

crates/agent_ui/src/message_editor.rs πŸ”—

@@ -1680,7 +1680,7 @@ mod tests {
 
     use crate::completion_provider::{PromptCompletionProviderDelegate, PromptContextType};
     use crate::{
-        connection_view::tests::init_test,
+        conversation_view::tests::init_test,
         message_editor::{Mention, MessageEditor, parse_mention_links},
     };
 

crates/agent_ui/src/sidebar.rs πŸ”—

@@ -486,7 +486,7 @@ impl Sidebar {
         self.focused_thread = active_workspace
             .as_ref()
             .and_then(|ws| ws.read(cx).panel::<AgentPanel>(cx))
-            .and_then(|panel| panel.read(cx).active_connection_view().cloned())
+            .and_then(|panel| panel.read(cx).active_conversation().cloned())
             .and_then(|cv| cv.read(cx).parent_id(cx));
 
         let mut threads_by_paths: HashMap<PathList, Vec<ThreadMetadata>> = HashMap::new();

crates/agent_ui/src/test_support.rs πŸ”—

@@ -82,7 +82,7 @@ pub fn open_thread_with_connection(
 }
 
 pub fn send_message(panel: &Entity<AgentPanel>, cx: &mut VisualTestContext) {
-    let thread_view = panel.read_with(cx, |panel, cx| panel.as_active_thread_view(cx).unwrap());
+    let thread_view = panel.read_with(cx, |panel, cx| panel.active_thread_view(cx).unwrap());
     let message_editor = thread_view.read_with(cx, |view, _cx| view.message_editor.clone());
     message_editor.update_in(cx, |editor, window, cx| {
         editor.set_text("Hello", window, cx);

crates/agent_ui/src/thread_history_view.rs πŸ”—

@@ -1,5 +1,5 @@
 use crate::thread_history::ThreadHistory;
-use crate::{AgentPanel, ConnectionView, RemoveHistory, RemoveSelectedThread};
+use crate::{AgentPanel, ConversationView, RemoveHistory, RemoveSelectedThread};
 use acp_thread::AgentSessionInfo;
 use chrono::{Datelike as _, Local, NaiveDate, TimeDelta, Utc};
 use editor::{Editor, EditorEvent};
@@ -640,7 +640,7 @@ impl Render for ThreadHistoryView {
 #[derive(IntoElement)]
 pub struct HistoryEntryElement {
     entry: AgentSessionInfo,
-    thread_view: WeakEntity<ConnectionView>,
+    conversation_view: WeakEntity<ConversationView>,
     selected: bool,
     hovered: bool,
     supports_delete: bool,
@@ -648,10 +648,10 @@ pub struct HistoryEntryElement {
 }
 
 impl HistoryEntryElement {
-    pub fn new(entry: AgentSessionInfo, thread_view: WeakEntity<ConnectionView>) -> Self {
+    pub fn new(entry: AgentSessionInfo, conversation_view: WeakEntity<ConversationView>) -> Self {
         Self {
             entry,
-            thread_view,
+            conversation_view,
             selected: false,
             hovered: false,
             supports_delete: false,
@@ -725,13 +725,13 @@ impl RenderOnce for HistoryEntryElement {
                             Tooltip::for_action("Delete", &RemoveSelectedThread, cx)
                         })
                         .on_click({
-                            let thread_view = self.thread_view.clone();
+                            let conversation_view = self.conversation_view.clone();
                             let session_id = self.entry.session_id.clone();
 
                             move |_event, _window, cx| {
-                                if let Some(thread_view) = thread_view.upgrade() {
-                                    thread_view.update(cx, |thread_view, cx| {
-                                        thread_view.delete_history_entry(&session_id, cx);
+                                if let Some(conversation_view) = conversation_view.upgrade() {
+                                    conversation_view.update(cx, |conversation_view, cx| {
+                                        conversation_view.delete_history_entry(&session_id, cx);
                                     });
                                 }
                             }
@@ -741,11 +741,11 @@ impl RenderOnce for HistoryEntryElement {
                 None
             })
             .on_click({
-                let thread_view = self.thread_view.clone();
+                let conversation_view = self.conversation_view.clone();
                 let entry = self.entry;
 
                 move |_event, window, cx| {
-                    if let Some(workspace) = thread_view
+                    if let Some(workspace) = conversation_view
                         .upgrade()
                         .and_then(|view| view.read(cx).workspace().upgrade())
                     {

crates/zed/src/visual_test_runner.rs πŸ”—

@@ -3459,7 +3459,7 @@ edition = "2021"
 
     // Insert a message into the active thread's message editor and submit.
     let thread_view = cx
-        .read(|cx| panel.read(cx).as_active_thread_view(cx))
+        .read(|cx| panel.read(cx).active_thread_view(cx))
         .ok_or_else(|| anyhow::anyhow!("No active thread view"))?;
 
     cx.update_window(workspace_window.into(), |_, window, cx| {
@@ -3528,7 +3528,7 @@ edition = "2021"
         new_workspace.read(cx).panel::<AgentPanel>(cx)
     })?;
     if let Some(new_panel) = new_panel {
-        let new_thread_view = cx.read(|cx| new_panel.read(cx).as_active_thread_view(cx));
+        let new_thread_view = cx.read(|cx| new_panel.read(cx).active_thread_view(cx));
         if let Some(new_thread_view) = new_thread_view {
             cx.update_window(workspace_window.into(), |_, window, cx| {
                 let message_editor = new_thread_view.read(cx).message_editor.clone();