From 5db8d6d1bc88ccd8ae7f4fb254b26a12f9d67cc3 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Fri, 6 Mar 2026 14:13:23 +0100 Subject: [PATCH] agent: Only use AgentSessionInfo in history (#50933) 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 --- 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 ++++++++++-------- .../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(-) diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index bffddde099c..58252eaddca 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -952,6 +952,8 @@ struct RunningTurn { } pub struct AcpThread { + session_id: acp::SessionId, + cwd: Option, parent_session_id: Option, title: SharedString, provisional_title: Option, @@ -963,7 +965,6 @@ pub struct AcpThread { turn_id: u32, running_turn: Option, connection: Rc, - session_id: acp::SessionId, token_usage: Option, prompt_capabilities: acp::PromptCapabilities, _observe_prompt_capabilities: Task>, @@ -1048,87 +1049,6 @@ pub enum TerminalProviderCommand { }, } -impl AcpThread { - pub fn on_terminal_provider_event( - &mut self, - event: TerminalProviderEvent, - cx: &mut Context, - ) { - 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, title: impl Into, + cwd: Option, connection: Rc, project: Entity, action_log: Entity, @@ -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, + ) { + 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, project: Entity, - _cwd: &Path, + cwd: &Path, cx: &mut App, ) -> Task>> { 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, diff --git a/crates/acp_thread/src/connection.rs b/crates/acp_thread/src/connection.rs index 773508f1c89..644986bc15e 100644 --- a/crates/acp_thread/src/connection.rs +++ b/crates/acp_thread/src/connection.rs @@ -45,9 +45,10 @@ pub trait AgentConnection { /// Load an existing session by ID. fn load_session( self: Rc, - _session: AgentSessionInfo, + _session_id: acp::SessionId, _project: Entity, _cwd: &Path, + _title: Option, _cx: &mut App, ) -> Task>> { 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, - _session: AgentSessionInfo, + _session_id: acp::SessionId, _project: Entity, _cwd: &Path, + _title: Option, _cx: &mut App, ) -> Task>> { Task::ready(Err(anyhow::Error::msg( @@ -619,7 +621,7 @@ mod test_support { fn new_session( self: Rc, project: Entity, - _cwd: &Path, + cwd: &Path, cx: &mut gpui::App, ) -> Task>> { 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, diff --git a/crates/agent/src/agent.rs b/crates/agent/src/agent.rs index a93c2d2062b..d9ad55c7127 100644 --- a/crates/agent/src/agent.rs +++ b/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, - session: AgentSessionInfo, + session_id: acp::SessionId, _project: Entity, _cwd: &Path, + _title: Option, cx: &mut App, ) -> Task>> { 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 { diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs index c63e4fab220..ceceb5b8ae0 100644 --- a/crates/agent_servers/src/acp.rs +++ b/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, - session: AgentSessionInfo, + session_id: acp::SessionId, project: Entity, cwd: &Path, + title: Option, cx: &mut App, ) -> Task>> { 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 = 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, - session: AgentSessionInfo, + session_id: acp::SessionId, project: Entity, cwd: &Path, + title: Option, cx: &mut App, ) -> Task>> { 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 = 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); diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 45c22856c72..b53cb003969 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/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::(cx) { workspace.focus_panel::(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, + title: Option, window: &mut Window, cx: &mut Context, ) { 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, - resume_thread: Option, + resume_session_id: Option, + cwd: Option, + title: Option, initial_content: Option, window: &mut Window, cx: &mut Context, @@ -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.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, + title: Option, window: &mut Window, cx: &mut Context, ) { - 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, - resume_thread: Option, + resume_session_id: Option, + cwd: Option, + title: Option, initial_content: Option, workspace: WeakEntity, project: Entity, @@ -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::(window, cx); if let Some(panel) = workspace.panel::(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. diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs index caecce3d028..eee7a61576e 100644 --- a/crates/agent_ui/src/agent_ui.rs +++ b/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, + }, ContentBlock { blocks: Vec, auto_submit: bool, diff --git a/crates/agent_ui/src/completion_provider.rs b/crates/agent_ui/src/completion_provider.rs index 30778909b2c..40ad7bc7292 100644 --- a/crates/agent_ui/src/completion_provider.rs +++ b/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, entry: PromptContextEntry, } -fn session_title(session: &AgentSessionInfo) -> SharedString { - session - .title - .clone() +fn session_title(title: Option) -> SharedString { + title .filter(|title| !title.is_empty()) .unwrap_or_else(|| SharedString::new_static("New Thread")) } @@ -266,7 +271,8 @@ impl PromptCompletionProvider { } fn completion_for_thread( - thread_entry: AgentSessionInfo, + session_id: acp::SessionId, + title: Option, source_range: Range, recent: bool, source: Arc, @@ -275,9 +281,9 @@ impl PromptCompletionProvider { workspace: Entity, 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 PromptCompletionProvider { 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::>(); let search_task = filter_sessions_by_query(query, cancellation_flag, sessions, cx); cx.spawn(async move |_cx| { @@ -1018,15 +1032,18 @@ impl PromptCompletionProvider { .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 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 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, - sessions: Vec, + sessions: Vec, cx: &mut App, -) -> Task> { +) -> Task> { 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, - sessions: Vec, + sessions: Vec, executor: BackgroundExecutor, -) -> Vec { - let titles = sessions.iter().map(session_title).collect::>(); +) -> Vec { + let titles = sessions + .iter() + .map(|session| session.title.clone()) + .collect::>(); 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]; diff --git a/crates/agent_ui/src/connection_view.rs b/crates/agent_ui/src/connection_view.rs index 84aa9e9c2b1..48aa88a95ba 100644 --- a/crates/agent_ui/src/connection_view.rs +++ b/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, - resume_thread: Option, + resume_session_id: Option, + cwd: Option, + title: Option, initial_content: Option, workspace: WeakEntity, project: Entity, @@ -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) { - 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, - resume_thread: Option, + resume_session_id: Option, + cwd: Option, + title: Option, project: Entity, initial_content: Option, window: &mut Window, cx: &mut Context, ) -> ServerState { - let session_id = resume_thread - .as_ref() - .map(|thread| thread.session_id.clone()); if project.read(cx).is_via_collab() && agent.clone().downcast::().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::>(); @@ -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, conversation: Entity, resumed_without_history: bool, - resume_thread: Option, initial_content: Option, window: &mut Window, cx: &mut Context, @@ -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) { - 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) { + 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, - session: AgentSessionInfo, + session_id: acp::SessionId, project: Entity, _cwd: &Path, + _title: Option, cx: &mut App, ) -> Task>> { - 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, project: Entity, - _cwd: &Path, + cwd: &Path, cx: &mut gpui::App, ) -> Task>> { 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, project: Entity, - _cwd: &Path, + cwd: &Path, cx: &mut gpui::App, ) -> Task>> { 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, project: Entity, - _cwd: &Path, + cwd: &Path, cx: &mut gpui::App, ) -> Task>> { 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, - session: AgentSessionInfo, + session_id: acp::SessionId, project: Entity, cwd: &Path, + _title: Option, cx: &mut App, ) -> Task>> { *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, diff --git a/crates/agent_ui/src/connection_view/thread_view.rs b/crates/agent_ui/src/connection_view/thread_view.rs index 3397c619b7f..1c3b8ba244d 100644 --- a/crates/agent_ui/src/connection_view/thread_view.rs +++ b/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, pub resumed_without_history: bool, - pub resume_thread_metadata: Option, pub _cancel_task: Option>, _save_task: Option>, _draft_resolve_task: Option>, @@ -307,7 +306,6 @@ impl ThreadView { prompt_capabilities: Rc>, available_commands: Rc>>, resumed_without_history: bool, - resume_thread_metadata: Option, project: WeakEntity, thread_store: Option>, history: Entity, @@ -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::(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) }); } } diff --git a/crates/agent_ui/src/message_editor.rs b/crates/agent_ui/src/message_editor.rs index 6ce0b7e356d..cee6725cd15 100644 --- a/crates/agent_ui/src/message_editor.rs +++ b/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, window: &mut Window, cx: &mut Context, ) { @@ -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 { 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 }) }); diff --git a/crates/agent_ui/src/thread_history.rs b/crates/agent_ui/src/thread_history.rs index 8f8488cb94f..6601616e9f2 100644 --- a/crates/agent_ui/src/thread_history.rs +++ b/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::(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, + ); }); } } diff --git a/crates/agent_ui/src/ui/mention_crease.rs b/crates/agent_ui/src/ui/mention_crease.rs index 0a61b8e4ef2..8b813ef7e40 100644 --- a/crates/agent_ui/src/ui/mention_crease.rs +++ b/crates/agent_ui/src/ui/mention_crease.rs @@ -269,24 +269,13 @@ fn open_thread( cx: &mut Context, ) { use crate::AgentPanel; - use acp_thread::AgentSessionInfo; let Some(panel) = workspace.panel::(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) }); } diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs index 7f1512ee549..3bb3ea9ea44 100644 --- a/crates/sidebar/src/sidebar.rs +++ b/crates/sidebar/src/sidebar.rs @@ -1013,7 +1013,13 @@ impl Sidebar { if let Some(agent_panel) = workspace.read(cx).panel::(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, + ); }); } } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 4c188042482..062d0bd1f95 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -979,21 +979,19 @@ fn handle_open_request(request: OpenRequest, app_state: Arc, 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::(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); }