agent: Only use AgentSessionInfo in history (#50933)

Ben Brandt created

Previously we required AgentSessionInfo all over the place, which meant
there were lots of unnecessary fake ones created all over the place.

Made the methods and functions only take the data they need so we only
use these in history contexts now, as intended.

Release Notes:

- N/A

Change summary

crates/acp_thread/src/acp_thread.rs                | 172 ++++++++-------
crates/acp_thread/src/connection.rs                |   9 
crates/agent/src/agent.rs                          |   6 
crates/agent_servers/src/acp.rs                    |  42 +--
crates/agent_ui/src/agent_panel.rs                 | 117 ++++++----
crates/agent_ui/src/agent_ui.rs                    |   6 
crates/agent_ui/src/completion_provider.rs         |  74 ++++--
crates/agent_ui/src/connection_view.rs             | 174 +++++++++------
crates/agent_ui/src/connection_view/thread_view.rs |  32 --
crates/agent_ui/src/message_editor.rs              |  46 +--
crates/agent_ui/src/thread_history.rs              |  12 
crates/agent_ui/src/ui/mention_crease.rs           |  13 -
crates/sidebar/src/sidebar.rs                      |   8 
crates/zed/src/main.rs                             |  16 
14 files changed, 391 insertions(+), 336 deletions(-)

Detailed changes

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

@@ -952,6 +952,8 @@ struct RunningTurn {
 }
 
 pub struct AcpThread {
+    session_id: acp::SessionId,
+    cwd: Option<PathBuf>,
     parent_session_id: Option<acp::SessionId>,
     title: SharedString,
     provisional_title: Option<SharedString>,
@@ -963,7 +965,6 @@ pub struct AcpThread {
     turn_id: u32,
     running_turn: Option<RunningTurn>,
     connection: Rc<dyn AgentConnection>,
-    session_id: acp::SessionId,
     token_usage: Option<TokenUsage>,
     prompt_capabilities: acp::PromptCapabilities,
     _observe_prompt_capabilities: Task<anyhow::Result<()>>,
@@ -1048,87 +1049,6 @@ pub enum TerminalProviderCommand {
     },
 }
 
-impl AcpThread {
-    pub fn on_terminal_provider_event(
-        &mut self,
-        event: TerminalProviderEvent,
-        cx: &mut Context<Self>,
-    ) {
-        match event {
-            TerminalProviderEvent::Created {
-                terminal_id,
-                label,
-                cwd,
-                output_byte_limit,
-                terminal,
-            } => {
-                let entity = self.register_terminal_created(
-                    terminal_id.clone(),
-                    label,
-                    cwd,
-                    output_byte_limit,
-                    terminal,
-                    cx,
-                );
-
-                if let Some(mut chunks) = self.pending_terminal_output.remove(&terminal_id) {
-                    for data in chunks.drain(..) {
-                        entity.update(cx, |term, cx| {
-                            term.inner().update(cx, |inner, cx| {
-                                inner.write_output(&data, cx);
-                            })
-                        });
-                    }
-                }
-
-                if let Some(_status) = self.pending_terminal_exit.remove(&terminal_id) {
-                    entity.update(cx, |_term, cx| {
-                        cx.notify();
-                    });
-                }
-
-                cx.notify();
-            }
-            TerminalProviderEvent::Output { terminal_id, data } => {
-                if let Some(entity) = self.terminals.get(&terminal_id) {
-                    entity.update(cx, |term, cx| {
-                        term.inner().update(cx, |inner, cx| {
-                            inner.write_output(&data, cx);
-                        })
-                    });
-                } else {
-                    self.pending_terminal_output
-                        .entry(terminal_id)
-                        .or_default()
-                        .push(data);
-                }
-            }
-            TerminalProviderEvent::TitleChanged { terminal_id, title } => {
-                if let Some(entity) = self.terminals.get(&terminal_id) {
-                    entity.update(cx, |term, cx| {
-                        term.inner().update(cx, |inner, cx| {
-                            inner.breadcrumb_text = title;
-                            cx.emit(::terminal::Event::BreadcrumbsChanged);
-                        })
-                    });
-                }
-            }
-            TerminalProviderEvent::Exit {
-                terminal_id,
-                status,
-            } => {
-                if let Some(entity) = self.terminals.get(&terminal_id) {
-                    entity.update(cx, |_term, cx| {
-                        cx.notify();
-                    });
-                } else {
-                    self.pending_terminal_exit.insert(terminal_id, status);
-                }
-            }
-        }
-    }
-}
-
 #[derive(PartialEq, Eq, Debug)]
 pub enum ThreadStatus {
     Idle,
@@ -1175,6 +1095,7 @@ impl AcpThread {
     pub fn new(
         parent_session_id: Option<acp::SessionId>,
         title: impl Into<SharedString>,
+        cwd: Option<PathBuf>,
         connection: Rc<dyn AgentConnection>,
         project: Entity<Project>,
         action_log: Entity<ActionLog>,
@@ -1195,6 +1116,7 @@ impl AcpThread {
 
         Self {
             parent_session_id,
+            cwd,
             action_log,
             shared_buffers: Default::default(),
             entries: Default::default(),
@@ -1268,6 +1190,10 @@ impl AcpThread {
         &self.session_id
     }
 
+    pub fn cwd(&self) -> Option<&PathBuf> {
+        self.cwd.as_ref()
+    }
+
     pub fn status(&self) -> ThreadStatus {
         if self.running_turn.is_some() {
             ThreadStatus::Generating
@@ -2624,6 +2550,85 @@ impl AcpThread {
             }
         }
     }
+
+    pub fn on_terminal_provider_event(
+        &mut self,
+        event: TerminalProviderEvent,
+        cx: &mut Context<Self>,
+    ) {
+        match event {
+            TerminalProviderEvent::Created {
+                terminal_id,
+                label,
+                cwd,
+                output_byte_limit,
+                terminal,
+            } => {
+                let entity = self.register_terminal_created(
+                    terminal_id.clone(),
+                    label,
+                    cwd,
+                    output_byte_limit,
+                    terminal,
+                    cx,
+                );
+
+                if let Some(mut chunks) = self.pending_terminal_output.remove(&terminal_id) {
+                    for data in chunks.drain(..) {
+                        entity.update(cx, |term, cx| {
+                            term.inner().update(cx, |inner, cx| {
+                                inner.write_output(&data, cx);
+                            })
+                        });
+                    }
+                }
+
+                if let Some(_status) = self.pending_terminal_exit.remove(&terminal_id) {
+                    entity.update(cx, |_term, cx| {
+                        cx.notify();
+                    });
+                }
+
+                cx.notify();
+            }
+            TerminalProviderEvent::Output { terminal_id, data } => {
+                if let Some(entity) = self.terminals.get(&terminal_id) {
+                    entity.update(cx, |term, cx| {
+                        term.inner().update(cx, |inner, cx| {
+                            inner.write_output(&data, cx);
+                        })
+                    });
+                } else {
+                    self.pending_terminal_output
+                        .entry(terminal_id)
+                        .or_default()
+                        .push(data);
+                }
+            }
+            TerminalProviderEvent::TitleChanged { terminal_id, title } => {
+                if let Some(entity) = self.terminals.get(&terminal_id) {
+                    entity.update(cx, |term, cx| {
+                        term.inner().update(cx, |inner, cx| {
+                            inner.breadcrumb_text = title;
+                            cx.emit(::terminal::Event::BreadcrumbsChanged);
+                        })
+                    });
+                }
+            }
+            TerminalProviderEvent::Exit {
+                terminal_id,
+                status,
+            } => {
+                if let Some(entity) = self.terminals.get(&terminal_id) {
+                    entity.update(cx, |_term, cx| {
+                        cx.notify();
+                    });
+                } else {
+                    self.pending_terminal_exit.insert(terminal_id, status);
+                }
+            }
+        }
+    }
 }
 
 fn markdown_for_raw_output(
@@ -3988,7 +3993,7 @@ mod tests {
         fn new_session(
             self: Rc<Self>,
             project: Entity<Project>,
-            _cwd: &Path,
+            cwd: &Path,
             cx: &mut App,
         ) -> Task<gpui::Result<Entity<AcpThread>>> {
             let session_id = acp::SessionId::new(
@@ -4003,6 +4008,7 @@ mod tests {
                 AcpThread::new(
                     None,
                     "Test",
+                    Some(cwd.to_path_buf()),
                     self.clone(),
                     project,
                     action_log,

crates/acp_thread/src/connection.rs πŸ”—

@@ -45,9 +45,10 @@ pub trait AgentConnection {
     /// Load an existing session by ID.
     fn load_session(
         self: Rc<Self>,
-        _session: AgentSessionInfo,
+        _session_id: acp::SessionId,
         _project: Entity<Project>,
         _cwd: &Path,
+        _title: Option<SharedString>,
         _cx: &mut App,
     ) -> Task<Result<Entity<AcpThread>>> {
         Task::ready(Err(anyhow::Error::msg("Loading sessions is not supported")))
@@ -71,9 +72,10 @@ pub trait AgentConnection {
     /// Resume an existing session by ID without replaying previous messages.
     fn resume_session(
         self: Rc<Self>,
-        _session: AgentSessionInfo,
+        _session_id: acp::SessionId,
         _project: Entity<Project>,
         _cwd: &Path,
+        _title: Option<SharedString>,
         _cx: &mut App,
     ) -> Task<Result<Entity<AcpThread>>> {
         Task::ready(Err(anyhow::Error::msg(
@@ -619,7 +621,7 @@ mod test_support {
         fn new_session(
             self: Rc<Self>,
             project: Entity<Project>,
-            _cwd: &Path,
+            cwd: &Path,
             cx: &mut gpui::App,
         ) -> Task<gpui::Result<Entity<AcpThread>>> {
             static NEXT_SESSION_ID: AtomicUsize = AtomicUsize::new(0);
@@ -630,6 +632,7 @@ mod test_support {
                 AcpThread::new(
                     None,
                     "Test",
+                    Some(cwd.to_path_buf()),
                     self.clone(),
                     project,
                     action_log,

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

@@ -361,6 +361,7 @@ impl NativeAgent {
             let mut acp_thread = acp_thread::AcpThread::new(
                 parent_session_id,
                 title,
+                None,
                 connection,
                 project.clone(),
                 action_log.clone(),
@@ -1277,13 +1278,14 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
 
     fn load_session(
         self: Rc<Self>,
-        session: AgentSessionInfo,
+        session_id: acp::SessionId,
         _project: Entity<Project>,
         _cwd: &Path,
+        _title: Option<SharedString>,
         cx: &mut App,
     ) -> Task<Result<Entity<acp_thread::AcpThread>>> {
         self.0
-            .update(cx, |agent, cx| agent.open_thread(session.session_id, cx))
+            .update(cx, |agent, cx| agent.open_thread(session_id, cx))
     }
 
     fn supports_close_session(&self) -> bool {

crates/agent_servers/src/acp.rs πŸ”—

@@ -385,7 +385,7 @@ impl AgentConnection for AcpConnection {
 
         cx.spawn(async move |cx| {
             let response = self.connection
-                .new_session(acp::NewSessionRequest::new(cwd).mcp_servers(mcp_servers))
+                .new_session(acp::NewSessionRequest::new(cwd.clone()).mcp_servers(mcp_servers))
                 .await
                 .map_err(map_acp_error)?;
 
@@ -560,6 +560,7 @@ impl AgentConnection for AcpConnection {
                 AcpThread::new(
                     None,
                     self.display_name.clone(),
+                    Some(cwd),
                     self.clone(),
                     project,
                     action_log,
@@ -598,9 +599,10 @@ impl AgentConnection for AcpConnection {
 
     fn load_session(
         self: Rc<Self>,
-        session: AgentSessionInfo,
+        session_id: acp::SessionId,
         project: Entity<Project>,
         cwd: &Path,
+        title: Option<SharedString>,
         cx: &mut App,
     ) -> Task<Result<Entity<AcpThread>>> {
         if !self.agent_capabilities.load_session {
@@ -612,25 +614,23 @@ impl AgentConnection for AcpConnection {
         let cwd = cwd.to_path_buf();
         let mcp_servers = mcp_servers_for_project(&project, cx);
         let action_log = cx.new(|_| ActionLog::new(project.clone()));
-        let title = session
-            .title
-            .clone()
-            .unwrap_or_else(|| self.display_name.clone());
+        let title = title.unwrap_or_else(|| self.display_name.clone());
         let thread: Entity<AcpThread> = cx.new(|cx| {
             AcpThread::new(
                 None,
                 title,
+                Some(cwd.clone()),
                 self.clone(),
                 project,
                 action_log,
-                session.session_id.clone(),
+                session_id.clone(),
                 watch::Receiver::constant(self.agent_capabilities.prompt_capabilities.clone()),
                 cx,
             )
         });
 
         self.sessions.borrow_mut().insert(
-            session.session_id.clone(),
+            session_id.clone(),
             AcpSession {
                 thread: thread.downgrade(),
                 suppress_abort_err: false,
@@ -644,21 +644,20 @@ impl AgentConnection for AcpConnection {
             let response = match self
                 .connection
                 .load_session(
-                    acp::LoadSessionRequest::new(session.session_id.clone(), cwd)
-                        .mcp_servers(mcp_servers),
+                    acp::LoadSessionRequest::new(session_id.clone(), cwd).mcp_servers(mcp_servers),
                 )
                 .await
             {
                 Ok(response) => response,
                 Err(err) => {
-                    self.sessions.borrow_mut().remove(&session.session_id);
+                    self.sessions.borrow_mut().remove(&session_id);
                     return Err(map_acp_error(err));
                 }
             };
 
             let (modes, models, config_options) =
                 config_state(response.modes, response.models, response.config_options);
-            if let Some(session) = self.sessions.borrow_mut().get_mut(&session.session_id) {
+            if let Some(session) = self.sessions.borrow_mut().get_mut(&session_id) {
                 session.session_modes = modes;
                 session.models = models;
                 session.config_options = config_options.map(ConfigOptions::new);
@@ -670,9 +669,10 @@ impl AgentConnection for AcpConnection {
 
     fn resume_session(
         self: Rc<Self>,
-        session: AgentSessionInfo,
+        session_id: acp::SessionId,
         project: Entity<Project>,
         cwd: &Path,
+        title: Option<SharedString>,
         cx: &mut App,
     ) -> Task<Result<Entity<AcpThread>>> {
         if self
@@ -689,25 +689,23 @@ impl AgentConnection for AcpConnection {
         let cwd = cwd.to_path_buf();
         let mcp_servers = mcp_servers_for_project(&project, cx);
         let action_log = cx.new(|_| ActionLog::new(project.clone()));
-        let title = session
-            .title
-            .clone()
-            .unwrap_or_else(|| self.display_name.clone());
+        let title = title.unwrap_or_else(|| self.display_name.clone());
         let thread: Entity<AcpThread> = cx.new(|cx| {
             AcpThread::new(
                 None,
                 title,
+                Some(cwd.clone()),
                 self.clone(),
                 project,
                 action_log,
-                session.session_id.clone(),
+                session_id.clone(),
                 watch::Receiver::constant(self.agent_capabilities.prompt_capabilities.clone()),
                 cx,
             )
         });
 
         self.sessions.borrow_mut().insert(
-            session.session_id.clone(),
+            session_id.clone(),
             AcpSession {
                 thread: thread.downgrade(),
                 suppress_abort_err: false,
@@ -721,21 +719,21 @@ impl AgentConnection for AcpConnection {
             let response = match self
                 .connection
                 .resume_session(
-                    acp::ResumeSessionRequest::new(session.session_id.clone(), cwd)
+                    acp::ResumeSessionRequest::new(session_id.clone(), cwd)
                         .mcp_servers(mcp_servers),
                 )
                 .await
             {
                 Ok(response) => response,
                 Err(err) => {
-                    self.sessions.borrow_mut().remove(&session.session_id);
+                    self.sessions.borrow_mut().remove(&session_id);
                     return Err(map_acp_error(err));
                 }
             };
 
             let (modes, models, config_options) =
                 config_state(response.modes, response.models, response.config_options);
-            if let Some(session) = self.sessions.borrow_mut().get_mut(&session.session_id) {
+            if let Some(session) = self.sessions.borrow_mut().get_mut(&session_id) {
                 session.session_modes = modes;
                 session.models = models;
                 session.config_options = config_options.map(ConfigOptions::new);

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

@@ -9,7 +9,7 @@ use std::{
     time::Duration,
 };
 
-use acp_thread::{AcpThread, AgentSessionInfo, MentionUri, ThreadStatus};
+use acp_thread::{AcpThread, MentionUri, ThreadStatus};
 use agent::{ContextServerRegistry, SharedThread, ThreadStore};
 use agent_client_protocol as acp;
 use agent_servers::AgentServer;
@@ -191,7 +191,15 @@ pub fn init(cx: &mut App) {
                     if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
                         workspace.focus_panel::<AgentPanel>(window, cx);
                         panel.update(cx, |panel, cx| {
-                            panel.external_thread(action.agent.clone(), None, None, window, cx)
+                            panel.external_thread(
+                                action.agent.clone(),
+                                None,
+                                None,
+                                None,
+                                None,
+                                window,
+                                cx,
+                            )
                         });
                     }
                 })
@@ -322,6 +330,8 @@ pub fn init(cx: &mut App) {
 
                     panel.update(cx, |panel, cx| {
                         panel.external_thread(
+                            None,
+                            None,
                             None,
                             None,
                             Some(AgentInitialContent::ContentBlock {
@@ -715,16 +725,9 @@ impl AgentPanel {
 
                 if let Some(thread_info) = last_active_thread {
                     let agent_type = thread_info.agent_type.clone();
-                    let session_info = AgentSessionInfo {
-                        session_id: acp::SessionId::new(thread_info.session_id),
-                        cwd: thread_info.cwd,
-                        title: thread_info.title.map(SharedString::from),
-                        updated_at: None,
-                        meta: None,
-                    };
                     panel.update(cx, |panel, cx| {
                         panel.selected_agent = agent_type;
-                        panel.load_agent_thread(session_info, window, cx);
+                        panel.load_agent_thread(thread_info.session_id.into(), thread_info.cwd, thread_info.title.map(SharedString::from), window, cx);
                     });
                 }
                 panel
@@ -761,7 +764,13 @@ impl AgentPanel {
             window,
             |this, _, event, window, cx| match event {
                 ThreadHistoryEvent::Open(thread) => {
-                    this.load_agent_thread(thread.clone(), window, cx);
+                    this.load_agent_thread(
+                        thread.session_id.clone(),
+                        thread.cwd.clone(),
+                        thread.title.clone(),
+                        window,
+                        cx,
+                    );
                 }
             },
         )
@@ -950,13 +959,17 @@ impl AgentPanel {
 
     pub fn open_thread(
         &mut self,
-        thread: AgentSessionInfo,
+        session_id: acp::SessionId,
+        cwd: Option<PathBuf>,
+        title: Option<SharedString>,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
         self.external_thread(
             Some(crate::ExternalAgent::NativeAgent),
-            Some(thread),
+            Some(session_id),
+            cwd,
+            title,
             None,
             window,
             cx,
@@ -1015,7 +1028,12 @@ impl AgentPanel {
         self.external_thread(
             Some(ExternalAgent::NativeAgent),
             None,
-            Some(AgentInitialContent::ThreadSummary(thread)),
+            None,
+            None,
+            Some(AgentInitialContent::ThreadSummary {
+                session_id: thread.session_id,
+                title: thread.title,
+            }),
             window,
             cx,
         );
@@ -1067,7 +1085,9 @@ impl AgentPanel {
     fn external_thread(
         &mut self,
         agent_choice: Option<crate::ExternalAgent>,
-        resume_thread: Option<AgentSessionInfo>,
+        resume_session_id: Option<acp::SessionId>,
+        cwd: Option<PathBuf>,
+        title: Option<SharedString>,
         initial_content: Option<AgentInitialContent>,
         window: &mut Window,
         cx: &mut Context<Self>,
@@ -1129,7 +1149,9 @@ impl AgentPanel {
             this.update_in(cx, |agent_panel, window, cx| {
                 agent_panel.create_external_thread(
                     server,
-                    resume_thread,
+                    resume_session_id,
+                    cwd,
+                    title,
                     initial_content,
                     workspace,
                     project,
@@ -1548,16 +1570,8 @@ impl AgentPanel {
                 })
                 .await?;
 
-            let thread_metadata = acp_thread::AgentSessionInfo {
-                session_id,
-                cwd: None,
-                title: Some(title),
-                updated_at: Some(chrono::Utc::now()),
-                meta: None,
-            };
-
             this.update_in(cx, |this, window, cx| {
-                this.open_thread(thread_metadata, window, cx);
+                this.open_thread(session_id, None, Some(title), window, cx);
             })?;
 
             this.update_in(cx, |_, _window, cx| {
@@ -1839,7 +1853,13 @@ impl AgentPanel {
                             let entry = entry.clone();
                             panel
                                 .update(cx, move |this, cx| {
-                                    this.load_agent_thread(entry.clone(), window, cx);
+                                    this.load_agent_thread(
+                                        entry.session_id.clone(),
+                                        entry.cwd.clone(),
+                                        entry.title.clone(),
+                                        window,
+                                        cx,
+                                    );
                                 })
                                 .ok();
                         }
@@ -1981,6 +2001,8 @@ impl AgentPanel {
         cx: &mut Context<Self>,
     ) {
         self.external_thread(
+            None,
+            None,
             None,
             None,
             initial_text.map(|text| AgentInitialContent::ContentBlock {
@@ -2006,6 +2028,8 @@ impl AgentPanel {
                 Some(crate::ExternalAgent::NativeAgent),
                 None,
                 None,
+                None,
+                None,
                 window,
                 cx,
             ),
@@ -2013,6 +2037,8 @@ impl AgentPanel {
                 Some(crate::ExternalAgent::Custom { name }),
                 None,
                 None,
+                None,
+                None,
                 window,
                 cx,
             ),
@@ -2021,11 +2047,12 @@ impl AgentPanel {
 
     pub fn load_agent_thread(
         &mut self,
-        thread: AgentSessionInfo,
+        session_id: acp::SessionId,
+        cwd: Option<PathBuf>,
+        title: Option<SharedString>,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        let session_id = thread.session_id.clone();
         if let Some(server_view) = self.background_threads.remove(&session_id) {
             self.set_active_view(ActiveView::AgentThread { server_view }, true, window, cx);
             return;
@@ -2059,13 +2086,15 @@ impl AgentPanel {
         let Some(agent) = self.selected_external_agent() else {
             return;
         };
-        self.external_thread(Some(agent), Some(thread), None, window, cx);
+        self.external_thread(Some(agent), Some(session_id), cwd, title, None, window, cx);
     }
 
     pub(crate) fn create_external_thread(
         &mut self,
         server: Rc<dyn AgentServer>,
-        resume_thread: Option<AgentSessionInfo>,
+        resume_session_id: Option<acp::SessionId>,
+        cwd: Option<PathBuf>,
+        title: Option<SharedString>,
         initial_content: Option<AgentInitialContent>,
         workspace: WeakEntity<Workspace>,
         project: Entity<Project>,
@@ -2087,7 +2116,9 @@ impl AgentPanel {
         let server_view = cx.new(|cx| {
             crate::ConnectionView::new(
                 server,
-                resume_thread,
+                resume_session_id,
+                cwd,
+                title,
                 initial_content,
                 workspace.clone(),
                 project,
@@ -2598,7 +2629,15 @@ impl AgentPanel {
                 workspace.focus_panel::<AgentPanel>(window, cx);
                 if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
                     panel.update(cx, |panel, cx| {
-                        panel.external_thread(None, None, Some(initial_content), window, cx);
+                        panel.external_thread(
+                            None,
+                            None,
+                            None,
+                            None,
+                            Some(initial_content),
+                            window,
+                            cx,
+                        );
                     });
                 }
             });
@@ -4466,7 +4505,7 @@ impl AgentPanel {
         };
 
         self.create_external_thread(
-            server, None, None, workspace, project, ext_agent, window, cx,
+            server, None, None, None, None, workspace, project, ext_agent, window, cx,
         );
     }
 
@@ -4877,17 +4916,7 @@ mod tests {
 
         // Load thread A back via load_agent_thread β€” should promote from background.
         panel.update_in(&mut cx, |panel, window, cx| {
-            panel.load_agent_thread(
-                AgentSessionInfo {
-                    session_id: session_id_a.clone(),
-                    cwd: None,
-                    title: None,
-                    updated_at: None,
-                    meta: None,
-                },
-                window,
-                cx,
-            );
+            panel.load_agent_thread(session_id_a.clone(), None, None, window, cx);
         });
 
         // Thread A should now be the active view, promoted from background.

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

@@ -35,6 +35,7 @@ mod ui;
 use std::rc::Rc;
 use std::sync::Arc;
 
+use agent_client_protocol as acp;
 use agent_settings::{AgentProfileId, AgentSettings};
 use assistant_slash_command::SlashCommandRegistry;
 use client::Client;
@@ -241,7 +242,10 @@ pub enum StartThreadIn {
 
 /// Content to initialize new external agent with.
 pub enum AgentInitialContent {
-    ThreadSummary(acp_thread::AgentSessionInfo),
+    ThreadSummary {
+        session_id: acp::SessionId,
+        title: Option<SharedString>,
+    },
     ContentBlock {
         blocks: Vec<agent_client_protocol::ContentBlock>,
         auto_submit: bool,

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

@@ -5,7 +5,8 @@ use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
 
 use crate::ThreadHistory;
-use acp_thread::{AgentSessionInfo, MentionUri};
+use acp_thread::MentionUri;
+use agent_client_protocol as acp;
 use anyhow::Result;
 use editor::{
     CompletionProvider, Editor, ExcerptId, code_context_menus::COMPLETION_MENU_MAX_WIDTH,
@@ -144,8 +145,8 @@ impl PromptContextType {
 pub(crate) enum Match {
     File(FileMatch),
     Symbol(SymbolMatch),
-    Thread(AgentSessionInfo),
-    RecentThread(AgentSessionInfo),
+    Thread(SessionMatch),
+    RecentThread(SessionMatch),
     Fetch(SharedString),
     Rules(RulesContextEntry),
     Entry(EntryMatch),
@@ -165,15 +166,19 @@ impl Match {
     }
 }
 
+#[derive(Debug, Clone)]
+pub struct SessionMatch {
+    session_id: acp::SessionId,
+    title: SharedString,
+}
+
 pub struct EntryMatch {
     mat: Option<StringMatch>,
     entry: PromptContextEntry,
 }
 
-fn session_title(session: &AgentSessionInfo) -> SharedString {
-    session
-        .title
-        .clone()
+fn session_title(title: Option<SharedString>) -> SharedString {
+    title
         .filter(|title| !title.is_empty())
         .unwrap_or_else(|| SharedString::new_static("New Thread"))
 }
@@ -266,7 +271,8 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
     }
 
     fn completion_for_thread(
-        thread_entry: AgentSessionInfo,
+        session_id: acp::SessionId,
+        title: Option<SharedString>,
         source_range: Range<Anchor>,
         recent: bool,
         source: Arc<T>,
@@ -275,9 +281,9 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
         workspace: Entity<Workspace>,
         cx: &mut App,
     ) -> Completion {
-        let title = session_title(&thread_entry);
+        let title = session_title(title);
         let uri = MentionUri::Thread {
-            id: thread_entry.session_id,
+            id: session_id,
             name: title.to_string(),
         };
 
@@ -841,7 +847,15 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
 
             Some(PromptContextType::Thread) => {
                 if let Some(history) = self.history.upgrade() {
-                    let sessions = history.read(cx).sessions().to_vec();
+                    let sessions = history
+                        .read(cx)
+                        .sessions()
+                        .iter()
+                        .map(|session| SessionMatch {
+                            session_id: session.session_id.clone(),
+                            title: session_title(session.title.clone()),
+                        })
+                        .collect::<Vec<_>>();
                     let search_task =
                         filter_sessions_by_query(query, cancellation_flag, sessions, cx);
                     cx.spawn(async move |_cx| {
@@ -1018,15 +1032,18 @@ impl<T: PromptCompletionProviderDelegate> PromptCompletionProvider<T> {
                     .read(cx)
                     .sessions()
                     .into_iter()
+                    .map(|session| SessionMatch {
+                        session_id: session.session_id.clone(),
+                        title: session_title(session.title.clone()),
+                    })
                     .filter(|session| {
                         let uri = MentionUri::Thread {
                             id: session.session_id.clone(),
-                            name: session_title(session).to_string(),
+                            name: session.title.to_string(),
                         };
                         !mentions.contains(&uri)
                     })
                     .take(RECENT_COUNT)
-                    .cloned()
                     .map(Match::RecentThread),
             );
             return Task::ready(recent);
@@ -1298,7 +1315,8 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
                                     )
                                 }
                                 Match::Thread(thread) => Some(Self::completion_for_thread(
-                                    thread,
+                                    thread.session_id,
+                                    Some(thread.title),
                                     source_range.clone(),
                                     false,
                                     source.clone(),
@@ -1308,7 +1326,8 @@ impl<T: PromptCompletionProviderDelegate> CompletionProvider for PromptCompletio
                                     cx,
                                 )),
                                 Match::RecentThread(thread) => Some(Self::completion_for_thread(
-                                    thread,
+                                    thread.session_id,
+                                    Some(thread.title),
                                     source_range.clone(),
                                     true,
                                     source.clone(),
@@ -1878,9 +1897,9 @@ pub(crate) fn search_symbols(
 fn filter_sessions_by_query(
     query: String,
     cancellation_flag: Arc<AtomicBool>,
-    sessions: Vec<AgentSessionInfo>,
+    sessions: Vec<SessionMatch>,
     cx: &mut App,
-) -> Task<Vec<AgentSessionInfo>> {
+) -> Task<Vec<SessionMatch>> {
     if query.is_empty() {
         return Task::ready(sessions);
     }
@@ -1893,10 +1912,13 @@ fn filter_sessions_by_query(
 async fn filter_sessions(
     query: String,
     cancellation_flag: Arc<AtomicBool>,
-    sessions: Vec<AgentSessionInfo>,
+    sessions: Vec<SessionMatch>,
     executor: BackgroundExecutor,
-) -> Vec<AgentSessionInfo> {
-    let titles = sessions.iter().map(session_title).collect::<Vec<_>>();
+) -> Vec<SessionMatch> {
+    let titles = sessions
+        .iter()
+        .map(|session| session.title.clone())
+        .collect::<Vec<_>>();
     let candidates = titles
         .iter()
         .enumerate()
@@ -2338,10 +2360,14 @@ mod tests {
 
     #[gpui::test]
     async fn test_filter_sessions_by_query(cx: &mut TestAppContext) {
-        let mut alpha = AgentSessionInfo::new("session-alpha");
-        alpha.title = Some("Alpha Session".into());
-        let mut beta = AgentSessionInfo::new("session-beta");
-        beta.title = Some("Beta Session".into());
+        let alpha = SessionMatch {
+            session_id: acp::SessionId::new("session-alpha"),
+            title: "Alpha Session".into(),
+        };
+        let beta = SessionMatch {
+            session_id: acp::SessionId::new("session-beta"),
+            title: "Beta Session".into(),
+        };
 
         let sessions = vec![alpha.clone(), beta];
 

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

@@ -39,7 +39,7 @@ use prompt_store::{PromptId, PromptStore};
 use rope::Point;
 use settings::{NotifyWhenAgentWaiting, Settings as _, SettingsStore};
 use std::cell::RefCell;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::sync::Arc;
 use std::time::Instant;
 use std::{collections::BTreeMap, rc::Rc, time::Duration};
@@ -470,7 +470,9 @@ impl ConnectedServerState {
 impl ConnectionView {
     pub fn new(
         agent: Rc<dyn AgentServer>,
-        resume_thread: Option<AgentSessionInfo>,
+        resume_session_id: Option<acp::SessionId>,
+        cwd: Option<PathBuf>,
+        title: Option<SharedString>,
         initial_content: Option<AgentInitialContent>,
         workspace: WeakEntity<Workspace>,
         project: Entity<Project>,
@@ -514,7 +516,9 @@ impl ConnectionView {
             prompt_store,
             server_state: Self::initial_state(
                 agent.clone(),
-                resume_thread,
+                resume_session_id,
+                cwd,
+                title,
                 project,
                 initial_content,
                 window,
@@ -540,13 +544,23 @@ impl ConnectionView {
     }
 
     fn reset(&mut self, window: &mut Window, cx: &mut Context<Self>) {
-        let resume_thread_metadata = self
+        let (resume_session_id, cwd, title) = self
             .active_thread()
-            .and_then(|thread| thread.read(cx).resume_thread_metadata.clone());
+            .map(|thread_view| {
+                let thread = thread_view.read(cx).thread.read(cx);
+                (
+                    Some(thread.session_id().clone()),
+                    thread.cwd().cloned(),
+                    Some(thread.title()),
+                )
+            })
+            .unwrap_or((None, None, None));
 
         let state = Self::initial_state(
             self.agent.clone(),
-            resume_thread_metadata,
+            resume_session_id,
+            cwd,
+            title,
             self.project.clone(),
             None,
             window,
@@ -570,15 +584,14 @@ impl ConnectionView {
 
     fn initial_state(
         agent: Rc<dyn AgentServer>,
-        resume_thread: Option<AgentSessionInfo>,
+        resume_session_id: Option<acp::SessionId>,
+        cwd: Option<PathBuf>,
+        title: Option<SharedString>,
         project: Entity<Project>,
         initial_content: Option<AgentInitialContent>,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> ServerState {
-        let session_id = resume_thread
-            .as_ref()
-            .map(|thread| thread.session_id.clone());
         if project.read(cx).is_via_collab()
             && agent.clone().downcast::<NativeAgentServer>().is_none()
         {
@@ -586,7 +599,7 @@ impl ConnectionView {
                 error: LoadError::Other(
                     "External agents are not yet supported in shared projects.".into(),
                 ),
-                session_id,
+                session_id: resume_session_id.clone(),
             };
         }
         let mut worktrees = project.read(cx).visible_worktrees(cx).collect::<Vec<_>>();
@@ -608,28 +621,22 @@ impl ConnectionView {
                 }
             })
             .collect();
-        let session_cwd = resume_thread
-            .as_ref()
-            .and_then(|resume| {
-                resume
-                    .cwd
-                    .as_ref()
-                    .filter(|cwd| {
-                        // Validate with the normalized path (rejects `..` traversals),
-                        // but return the original cwd to preserve its path separators.
-                        // On Windows, `normalize_lexically` rebuilds the path with
-                        // backslashes via `PathBuf::push`, which would corrupt
-                        // forward-slash Linux paths used by WSL agents.
-                        util::paths::normalize_lexically(cwd)
-                            .ok()
-                            .is_some_and(|normalized| {
-                                worktree_roots
-                                    .iter()
-                                    .any(|root| normalized.starts_with(root.as_ref()))
-                            })
+        let session_cwd = cwd
+            .filter(|cwd| {
+                // Validate with the normalized path (rejects `..` traversals),
+                // but return the original cwd to preserve its path separators.
+                // On Windows, `normalize_lexically` rebuilds the path with
+                // backslashes via `PathBuf::push`, which would corrupt
+                // forward-slash Linux paths used by WSL agents.
+                util::paths::normalize_lexically(cwd)
+                    .ok()
+                    .is_some_and(|normalized| {
+                        worktree_roots
+                            .iter()
+                            .any(|root| normalized.starts_with(root.as_ref()))
                     })
-                    .map(|path| Arc::from(path.as_path()))
             })
+            .map(|path| path.into())
             .or_else(|| worktree_roots.first().cloned())
             .unwrap_or_else(|| paths::home_dir().as_path().into());
 
@@ -643,7 +650,7 @@ impl ConnectionView {
         );
 
         let connect_task = agent.connect(delegate, cx);
-        let load_session_id = session_id.clone();
+        let load_session_id = resume_session_id.clone();
         let load_task = cx.spawn_in(window, async move |this, cx| {
             let connection = match connect_task.await {
                 Ok(connection) => connection,
@@ -666,17 +673,25 @@ impl ConnectionView {
             telemetry::event!("Agent Thread Started", agent = connection.telemetry_id());
 
             let mut resumed_without_history = false;
-            let result = if let Some(resume) = resume_thread.clone() {
+            let result = if let Some(session_id) = load_session_id.clone() {
                 cx.update(|_, cx| {
                     if connection.supports_load_session() {
-                        connection
-                            .clone()
-                            .load_session(resume, project.clone(), &session_cwd, cx)
+                        connection.clone().load_session(
+                            session_id,
+                            project.clone(),
+                            &session_cwd,
+                            title,
+                            cx,
+                        )
                     } else if connection.supports_resume_session() {
                         resumed_without_history = true;
-                        connection
-                            .clone()
-                            .resume_session(resume, project.clone(), &session_cwd, cx)
+                        connection.clone().resume_session(
+                            session_id,
+                            project.clone(),
+                            &session_cwd,
+                            title,
+                            cx,
+                        )
                     } else {
                         Task::ready(Err(anyhow!(LoadError::Other(
                             "Loading or resuming sessions is not supported by this agent.".into()
@@ -732,7 +747,6 @@ impl ConnectionView {
                             thread,
                             conversation.clone(),
                             resumed_without_history,
-                            resume_thread,
                             initial_content,
                             window,
                             cx,
@@ -803,7 +817,7 @@ impl ConnectionView {
             });
 
             LoadingView {
-                session_id,
+                session_id: resume_session_id,
                 title: "Loading…".into(),
                 _load_task: load_task,
                 _update_title_task: update_title_task,
@@ -819,7 +833,6 @@ impl ConnectionView {
         thread: Entity<AcpThread>,
         conversation: Entity<Conversation>,
         resumed_without_history: bool,
-        resume_thread: Option<AgentSessionInfo>,
         initial_content: Option<AgentInitialContent>,
         window: &mut Window,
         cx: &mut Context<Self>,
@@ -1002,7 +1015,6 @@ impl ConnectionView {
                 prompt_capabilities,
                 available_commands,
                 resumed_without_history,
-                resume_thread,
                 self.project.downgrade(),
                 self.thread_store.clone(),
                 self.history.clone(),
@@ -1680,9 +1692,10 @@ impl ConnectionView {
         let cwd = root_dir.unwrap_or_else(|| paths::home_dir().as_path().into());
 
         let subagent_thread_task = connected.connection.clone().load_session(
-            AgentSessionInfo::new(subagent_id.clone()),
+            subagent_id.clone(),
             self.project.clone(),
             &cwd,
+            None,
             cx,
         );
 
@@ -1704,7 +1717,6 @@ impl ConnectionView {
                     conversation,
                     false,
                     None,
-                    None,
                     window,
                     cx,
                 );
@@ -2606,10 +2618,10 @@ impl ConnectionView {
         })
     }
 
-    pub fn delete_history_entry(&mut self, entry: AgentSessionInfo, cx: &mut Context<Self>) {
-        let task = self.history.update(cx, |history, cx| {
-            history.delete_session(&entry.session_id, cx)
-        });
+    pub fn delete_history_entry(&mut self, session_id: &acp::SessionId, cx: &mut Context<Self>) {
+        let task = self
+            .history
+            .update(cx, |history, cx| history.delete_session(&session_id, cx));
         task.detach_and_log_err(cx);
     }
 }
@@ -2856,6 +2868,8 @@ pub(crate) mod tests {
                     Rc::new(StubAgentServer::default_response()),
                     None,
                     None,
+                    None,
+                    None,
                     workspace.downgrade(),
                     project,
                     Some(thread_store),
@@ -2939,7 +2953,6 @@ pub(crate) mod tests {
     async fn test_resume_without_history_adds_notice(cx: &mut TestAppContext) {
         init_test(cx);
 
-        let session = AgentSessionInfo::new(SessionId::new("resume-session"));
         let fs = FakeFs::new(cx.executor());
         let project = Project::test(fs, [], cx).await;
         let (multi_workspace, cx) =
@@ -2953,7 +2966,9 @@ pub(crate) mod tests {
             cx.new(|cx| {
                 ConnectionView::new(
                     Rc::new(StubAgentServer::new(ResumeOnlyAgentConnection)),
-                    Some(session),
+                    Some(SessionId::new("resume-session")),
+                    None,
+                    None,
                     None,
                     workspace.downgrade(),
                     project,
@@ -2997,9 +3012,6 @@ pub(crate) mod tests {
         let connection = CwdCapturingConnection::new();
         let captured_cwd = connection.captured_cwd.clone();
 
-        let mut session = AgentSessionInfo::new(SessionId::new("session-1"));
-        session.cwd = Some(PathBuf::from("/project/subdir"));
-
         let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
         let history = cx.update(|window, cx| cx.new(|cx| ThreadHistory::new(None, window, cx)));
 
@@ -3007,7 +3019,9 @@ pub(crate) mod tests {
             cx.new(|cx| {
                 ConnectionView::new(
                     Rc::new(StubAgentServer::new(connection)),
-                    Some(session),
+                    Some(SessionId::new("session-1")),
+                    Some(PathBuf::from("/project/subdir")),
+                    None,
                     None,
                     workspace.downgrade(),
                     project,
@@ -3049,9 +3063,6 @@ pub(crate) mod tests {
         let connection = CwdCapturingConnection::new();
         let captured_cwd = connection.captured_cwd.clone();
 
-        let mut session = AgentSessionInfo::new(SessionId::new("session-1"));
-        session.cwd = Some(PathBuf::from("/some/other/path"));
-
         let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
         let history = cx.update(|window, cx| cx.new(|cx| ThreadHistory::new(None, window, cx)));
 
@@ -3059,7 +3070,9 @@ pub(crate) mod tests {
             cx.new(|cx| {
                 ConnectionView::new(
                     Rc::new(StubAgentServer::new(connection)),
-                    Some(session),
+                    Some(SessionId::new("session-1")),
+                    Some(PathBuf::from("/some/other/path")),
+                    None,
                     None,
                     workspace.downgrade(),
                     project,
@@ -3101,9 +3114,6 @@ pub(crate) mod tests {
         let connection = CwdCapturingConnection::new();
         let captured_cwd = connection.captured_cwd.clone();
 
-        let mut session = AgentSessionInfo::new(SessionId::new("session-1"));
-        session.cwd = Some(PathBuf::from("/project/../outside"));
-
         let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
         let history = cx.update(|window, cx| cx.new(|cx| ThreadHistory::new(None, window, cx)));
 
@@ -3111,7 +3121,9 @@ pub(crate) mod tests {
             cx.new(|cx| {
                 ConnectionView::new(
                     Rc::new(StubAgentServer::new(connection)),
-                    Some(session),
+                    Some(SessionId::new("session-1")),
+                    Some(PathBuf::from("/project/../outside")),
+                    None,
                     None,
                     workspace.downgrade(),
                     project,
@@ -3424,6 +3436,8 @@ pub(crate) mod tests {
                     Rc::new(agent),
                     None,
                     None,
+                    None,
+                    None,
                     workspace1.downgrade(),
                     project1.clone(),
                     Some(thread_store),
@@ -3612,6 +3626,8 @@ pub(crate) mod tests {
                     Rc::new(agent),
                     None,
                     None,
+                    None,
+                    None,
                     workspace.downgrade(),
                     project,
                     Some(thread_store),
@@ -3792,6 +3808,7 @@ pub(crate) mod tests {
             AcpThread::new(
                 None,
                 name,
+                None,
                 connection,
                 project,
                 action_log,
@@ -3894,18 +3911,14 @@ pub(crate) mod tests {
 
         fn resume_session(
             self: Rc<Self>,
-            session: AgentSessionInfo,
+            session_id: acp::SessionId,
             project: Entity<Project>,
             _cwd: &Path,
+            _title: Option<SharedString>,
             cx: &mut App,
         ) -> Task<gpui::Result<Entity<AcpThread>>> {
-            let thread = build_test_thread(
-                self,
-                project,
-                "ResumeOnlyAgentConnection",
-                session.session_id,
-                cx,
-            );
+            let thread =
+                build_test_thread(self, project, "ResumeOnlyAgentConnection", session_id, cx);
             Task::ready(Ok(thread))
         }
 
@@ -3965,7 +3978,7 @@ pub(crate) mod tests {
         fn new_session(
             self: Rc<Self>,
             project: Entity<Project>,
-            _cwd: &Path,
+            cwd: &Path,
             cx: &mut gpui::App,
         ) -> Task<gpui::Result<Entity<AcpThread>>> {
             if !*self.authenticated.lock() {
@@ -3980,6 +3993,7 @@ pub(crate) mod tests {
                 AcpThread::new(
                     None,
                     "AuthGatedAgent",
+                    Some(cwd.to_path_buf()),
                     self,
                     project,
                     action_log,
@@ -4041,7 +4055,7 @@ pub(crate) mod tests {
         fn new_session(
             self: Rc<Self>,
             project: Entity<Project>,
-            _cwd: &Path,
+            cwd: &Path,
             cx: &mut gpui::App,
         ) -> Task<gpui::Result<Entity<AcpThread>>> {
             Task::ready(Ok(cx.new(|cx| {
@@ -4049,6 +4063,7 @@ pub(crate) mod tests {
                 AcpThread::new(
                     None,
                     "SaboteurAgentConnection",
+                    Some(cwd.to_path_buf()),
                     self,
                     project,
                     action_log,
@@ -4106,7 +4121,7 @@ pub(crate) mod tests {
         fn new_session(
             self: Rc<Self>,
             project: Entity<Project>,
-            _cwd: &Path,
+            cwd: &Path,
             cx: &mut gpui::App,
         ) -> Task<gpui::Result<Entity<AcpThread>>> {
             Task::ready(Ok(cx.new(|cx| {
@@ -4114,6 +4129,7 @@ pub(crate) mod tests {
                 AcpThread::new(
                     None,
                     "RefusalAgentConnection",
+                    Some(cwd.to_path_buf()),
                     self,
                     project,
                     action_log,
@@ -4189,6 +4205,7 @@ pub(crate) mod tests {
                 AcpThread::new(
                     None,
                     "CwdCapturingConnection",
+                    Some(cwd.to_path_buf()),
                     self.clone(),
                     project,
                     action_log,
@@ -4211,9 +4228,10 @@ pub(crate) mod tests {
 
         fn load_session(
             self: Rc<Self>,
-            session: AgentSessionInfo,
+            session_id: acp::SessionId,
             project: Entity<Project>,
             cwd: &Path,
+            _title: Option<SharedString>,
             cx: &mut App,
         ) -> Task<gpui::Result<Entity<AcpThread>>> {
             *self.captured_cwd.lock() = Some(cwd.to_path_buf());
@@ -4222,10 +4240,11 @@ pub(crate) mod tests {
                 AcpThread::new(
                     None,
                     "CwdCapturingConnection",
+                    Some(cwd.to_path_buf()),
                     self.clone(),
                     project,
                     action_log,
-                    session.session_id,
+                    session_id,
                     watch::Receiver::constant(
                         acp::PromptCapabilities::new()
                             .image(true)
@@ -4327,6 +4346,8 @@ pub(crate) mod tests {
                     Rc::new(StubAgentServer::new(connection.as_ref().clone())),
                     None,
                     None,
+                    None,
+                    None,
                     workspace.downgrade(),
                     project.clone(),
                     Some(thread_store.clone()),
@@ -6036,6 +6057,7 @@ pub(crate) mod tests {
             AcpThread::new(
                 parent_session_id,
                 "Test Thread",
+                None,
                 connection,
                 project,
                 action_log,

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

@@ -247,7 +247,6 @@ pub struct ThreadView {
     pub is_loading_contents: bool,
     pub new_server_version_available: Option<SharedString>,
     pub resumed_without_history: bool,
-    pub resume_thread_metadata: Option<AgentSessionInfo>,
     pub _cancel_task: Option<Task<()>>,
     _save_task: Option<Task<()>>,
     _draft_resolve_task: Option<Task<()>>,
@@ -307,7 +306,6 @@ impl ThreadView {
         prompt_capabilities: Rc<RefCell<PromptCapabilities>>,
         available_commands: Rc<RefCell<Vec<agent_client_protocol::AvailableCommand>>>,
         resumed_without_history: bool,
-        resume_thread_metadata: Option<AgentSessionInfo>,
         project: WeakEntity<Project>,
         thread_store: Option<Entity<ThreadStore>>,
         history: Entity<ThreadHistory>,
@@ -347,8 +345,8 @@ impl ThreadView {
             );
             if let Some(content) = initial_content {
                 match content {
-                    AgentInitialContent::ThreadSummary(entry) => {
-                        editor.insert_thread_summary(entry, window, cx);
+                    AgentInitialContent::ThreadSummary { session_id, title } => {
+                        editor.insert_thread_summary(session_id, title, window, cx);
                     }
                     AgentInitialContent::ContentBlock {
                         blocks,
@@ -439,7 +437,6 @@ impl ThreadView {
             prompt_capabilities,
             available_commands,
             resumed_without_history,
-            resume_thread_metadata,
             _subscriptions: subscriptions,
             permission_dropdown_handle: PopoverMenuHandle::default(),
             thread_retry_status: None,
@@ -1772,18 +1769,7 @@ impl ThreadView {
                 })
                 .await?;
 
-            let thread_metadata = AgentSessionInfo {
-                session_id,
-                cwd: None,
-                title: Some(format!("πŸ”— {}", response.title).into()),
-                updated_at: Some(chrono::Utc::now()),
-                meta: None,
-            };
-
-            this.update_in(cx, |this, window, cx| {
-                this.resume_thread_metadata = Some(thread_metadata);
-                server_view.update(cx, |server_view, cx| server_view.reset(window, cx));
-            })?;
+            server_view.update_in(cx, |server_view, window, cx| server_view.reset(window, cx))?;
 
             this.update_in(cx, |this, _window, cx| {
                 if let Some(workspace) = this.workspace.upgrade() {
@@ -7906,17 +7892,7 @@ pub(crate) fn open_link(
             MentionUri::Thread { id, name } => {
                 if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
                     panel.update(cx, |panel, cx| {
-                        panel.open_thread(
-                            AgentSessionInfo {
-                                session_id: id,
-                                cwd: None,
-                                title: Some(name.into()),
-                                updated_at: None,
-                                meta: None,
-                            },
-                            window,
-                            cx,
-                        )
+                        panel.open_thread(id, None, Some(name.into()), window, cx)
                     });
                 }
             }

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

@@ -10,7 +10,7 @@ use crate::{
         Mention, MentionImage, MentionSet, insert_crease_for_mention, paste_images_as_context,
     },
 };
-use acp_thread::{AgentSessionInfo, MentionUri};
+use acp_thread::MentionUri;
 use agent::ThreadStore;
 use agent_client_protocol as acp;
 use anyhow::{Result, anyhow};
@@ -301,7 +301,8 @@ impl MessageEditor {
 
     pub fn insert_thread_summary(
         &mut self,
-        thread: AgentSessionInfo,
+        session_id: acp::SessionId,
+        title: Option<SharedString>,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
@@ -311,13 +312,11 @@ impl MessageEditor {
         let Some(workspace) = self.workspace.upgrade() else {
             return;
         };
-        let thread_title = thread
-            .title
-            .clone()
+        let thread_title = title
             .filter(|title| !title.is_empty())
             .unwrap_or_else(|| SharedString::new_static("New Thread"));
         let uri = MentionUri::Thread {
-            id: thread.session_id,
+            id: session_id,
             name: thread_title.to_string(),
         };
         let content = format!("{}\n", uri.as_link());
@@ -1571,7 +1570,7 @@ fn find_matching_bracket(text: &str, open: char, close: char) -> Option<usize> {
 mod tests {
     use std::{cell::RefCell, ops::Range, path::Path, rc::Rc, sync::Arc};
 
-    use acp_thread::{AgentSessionInfo, MentionUri};
+    use acp_thread::MentionUri;
     use agent::{ThreadStore, outline};
     use agent_client_protocol as acp;
     use editor::{
@@ -2811,14 +2810,8 @@ mod tests {
         let history =
             cx.update(|window, cx| cx.new(|cx| crate::ThreadHistory::new(None, window, cx)));
 
-        // Create a thread metadata to insert as summary
-        let thread_metadata = AgentSessionInfo {
-            session_id: acp::SessionId::new("thread-123"),
-            cwd: None,
-            title: Some("Previous Conversation".into()),
-            updated_at: Some(chrono::Utc::now()),
-            meta: None,
-        };
+        let session_id = acp::SessionId::new("thread-123");
+        let title = Some("Previous Conversation".into());
 
         let message_editor = cx.update(|window, cx| {
             cx.new(|cx| {
@@ -2839,17 +2832,17 @@ mod tests {
                     window,
                     cx,
                 );
-                editor.insert_thread_summary(thread_metadata.clone(), window, cx);
+                editor.insert_thread_summary(session_id.clone(), title.clone(), window, cx);
                 editor
             })
         });
 
         // Construct expected values for verification
         let expected_uri = MentionUri::Thread {
-            id: thread_metadata.session_id.clone(),
-            name: thread_metadata.title.as_ref().unwrap().to_string(),
+            id: session_id.clone(),
+            name: title.as_ref().unwrap().to_string(),
         };
-        let expected_title = thread_metadata.title.as_ref().unwrap();
+        let expected_title = title.as_ref().unwrap();
         let expected_link = format!("[@{}]({})", expected_title, expected_uri.to_uri());
 
         message_editor.read_with(cx, |editor, cx| {
@@ -2893,14 +2886,6 @@ mod tests {
         let history =
             cx.update(|window, cx| cx.new(|cx| crate::ThreadHistory::new(None, window, cx)));
 
-        let thread_metadata = AgentSessionInfo {
-            session_id: acp::SessionId::new("thread-123"),
-            cwd: None,
-            title: Some("Previous Conversation".into()),
-            updated_at: Some(chrono::Utc::now()),
-            meta: None,
-        };
-
         let message_editor = cx.update(|window, cx| {
             cx.new(|cx| {
                 let mut editor = MessageEditor::new(
@@ -2920,7 +2905,12 @@ mod tests {
                     window,
                     cx,
                 );
-                editor.insert_thread_summary(thread_metadata, window, cx);
+                editor.insert_thread_summary(
+                    acp::SessionId::new("thread-123"),
+                    Some("Previous Conversation".into()),
+                    window,
+                    cx,
+                );
                 editor
             })
         });

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

@@ -948,12 +948,12 @@ impl RenderOnce for HistoryEntryElement {
                         })
                         .on_click({
                             let thread_view = self.thread_view.clone();
-                            let entry = self.entry.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(entry.clone(), cx);
+                                        thread_view.delete_history_entry(&session_id, cx);
                                     });
                                 }
                             }
@@ -973,7 +973,13 @@ impl RenderOnce for HistoryEntryElement {
                     {
                         if let Some(panel) = workspace.read(cx).panel::<AgentPanel>(cx) {
                             panel.update(cx, |panel, cx| {
-                                panel.load_agent_thread(entry.clone(), window, cx);
+                                panel.load_agent_thread(
+                                    entry.session_id.clone(),
+                                    entry.cwd.clone(),
+                                    entry.title.clone(),
+                                    window,
+                                    cx,
+                                );
                             });
                         }
                     }

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

@@ -269,24 +269,13 @@ fn open_thread(
     cx: &mut Context<Workspace>,
 ) {
     use crate::AgentPanel;
-    use acp_thread::AgentSessionInfo;
 
     let Some(panel) = workspace.panel::<AgentPanel>(cx) else {
         return;
     };
 
     panel.update(cx, |panel, cx| {
-        panel.load_agent_thread(
-            AgentSessionInfo {
-                session_id: id,
-                cwd: None,
-                title: Some(name.into()),
-                updated_at: None,
-                meta: None,
-            },
-            window,
-            cx,
-        )
+        panel.load_agent_thread(id, None, Some(name.into()), window, cx)
     });
 }
 

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

@@ -1013,7 +1013,13 @@ impl Sidebar {
 
         if let Some(agent_panel) = workspace.read(cx).panel::<AgentPanel>(cx) {
             agent_panel.update(cx, |panel, cx| {
-                panel.load_agent_thread(session_info, window, cx);
+                panel.load_agent_thread(
+                    session_info.session_id,
+                    session_info.cwd,
+                    session_info.title,
+                    window,
+                    cx,
+                );
             });
         }
     }

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

@@ -979,21 +979,19 @@ fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut
                         })
                         .await?;
 
-                    let thread_metadata = acp_thread::AgentSessionInfo {
-                        session_id,
-                        cwd: None,
-                        title: Some(format!("πŸ”— {}", response.title).into()),
-                        updated_at: Some(chrono::Utc::now()),
-                        meta: None,
-                    };
-
                     let sharer_username = response.sharer_username.clone();
 
                     multi_workspace.update(cx, |_, window, cx| {
                         workspace.update(cx, |workspace, cx| {
                             if let Some(panel) = workspace.panel::<AgentPanel>(cx) {
                                 panel.update(cx, |panel, cx| {
-                                    panel.open_thread(thread_metadata, window, cx);
+                                    panel.open_thread(
+                                        session_id,
+                                        None,
+                                        Some(format!("πŸ”— {}", response.title).into()),
+                                        window,
+                                        cx,
+                                    );
                                 });
                                 panel.focus_handle(cx).focus(window, cx);
                             }