@@ -4,7 +4,7 @@ use acp_thread::ThreadStatus;
use action_log::DiffStats;
use agent_client_protocol::{self as acp};
use agent_settings::AgentSettings;
-use agent_ui::thread_metadata_store::ThreadMetadataStore;
+use agent_ui::thread_metadata_store::{ThreadMetadata, ThreadMetadataStore};
use agent_ui::threads_archive_view::{
ThreadsArchiveView, ThreadsArchiveViewEvent, format_history_entry_timestamp,
};
@@ -92,19 +92,6 @@ struct ActiveThreadInfo {
diff_stats: DiffStats,
}
-impl From<&ActiveThreadInfo> for acp_thread::AgentSessionInfo {
- fn from(info: &ActiveThreadInfo) -> Self {
- Self {
- session_id: info.session_id.clone(),
- work_dirs: None,
- title: Some(info.title.clone()),
- updated_at: Some(Utc::now()),
- created_at: Some(Utc::now()),
- meta: None,
- }
- }
-}
-
#[derive(Clone)]
enum ThreadEntryWorkspace {
Open(Entity<Workspace>),
@@ -120,8 +107,7 @@ struct WorktreeInfo {
#[derive(Clone)]
struct ThreadEntry {
- agent: Agent,
- session_info: acp_thread::AgentSessionInfo,
+ metadata: ThreadMetadata,
icon: IconName,
icon_from_external_svg: Option<SharedString>,
status: AgentThreadStatus,
@@ -141,7 +127,7 @@ impl ThreadEntry {
/// but if we have a correspond thread already loaded we want to apply the
/// live information.
fn apply_active_info(&mut self, info: &ActiveThreadInfo) {
- self.session_info.title = Some(info.title.clone());
+ self.metadata.title = info.title.clone();
self.status = info.status;
self.icon = info.icon;
self.icon_from_external_svg = info.icon_from_external_svg.clone();
@@ -191,7 +177,7 @@ impl ListEntry {
fn session_id(&self) -> Option<&acp::SessionId> {
match self {
- ListEntry::Thread(thread_entry) => Some(&thread_entry.session_info.session_id),
+ ListEntry::Thread(thread_entry) => Some(&thread_entry.metadata.session_id),
_ => None,
}
}
@@ -690,7 +676,7 @@ impl Sidebar {
.iter()
.filter_map(|entry| match entry {
ListEntry::Thread(thread) if thread.is_live => {
- Some((thread.session_info.session_id.clone(), thread.status))
+ Some((thread.metadata.session_id.clone(), thread.status))
}
_ => None,
})
@@ -710,7 +696,7 @@ impl Sidebar {
.iter()
.any(|ws| !workspace_path_list(ws, cx).paths().is_empty());
- let resolve_agent = |agent_id: &AgentId| -> (Agent, IconName, Option<SharedString>) {
+ let resolve_agent_icon = |agent_id: &AgentId| -> (IconName, Option<SharedString>) {
let agent = Agent::from(agent_id.clone());
let icon = match agent {
Agent::NativeAgent => IconName::ZedAgent,
@@ -719,7 +705,7 @@ impl Sidebar {
let icon_from_external_svg = agent_server_store
.as_ref()
.and_then(|store| store.read(cx).agent_icon(&agent_id));
- (agent, icon, icon_from_external_svg)
+ (icon, icon_from_external_svg)
};
for (group_name, group) in project_groups.groups() {
@@ -768,19 +754,11 @@ impl Sidebar {
if !seen_session_ids.insert(row.session_id.clone()) {
continue;
}
- let (agent, icon, icon_from_external_svg) = resolve_agent(&row.agent_id);
+ let (icon, icon_from_external_svg) = resolve_agent_icon(&row.agent_id);
let worktrees =
worktree_info_from_thread_paths(&row.folder_paths, &project_groups);
threads.push(ThreadEntry {
- agent,
- session_info: acp_thread::AgentSessionInfo {
- session_id: row.session_id.clone(),
- work_dirs: None,
- title: Some(row.title.clone()),
- updated_at: Some(row.updated_at),
- created_at: row.created_at,
- meta: None,
- },
+ metadata: row,
icon,
icon_from_external_svg,
status: AgentThreadStatus::default(),
@@ -818,19 +796,11 @@ impl Sidebar {
if !seen_session_ids.insert(row.session_id.clone()) {
continue;
}
- let (agent, icon, icon_from_external_svg) = resolve_agent(&row.agent_id);
+ let (icon, icon_from_external_svg) = resolve_agent_icon(&row.agent_id);
let worktrees =
worktree_info_from_thread_paths(&row.folder_paths, &project_groups);
threads.push(ThreadEntry {
- agent,
- session_info: acp_thread::AgentSessionInfo {
- session_id: row.session_id.clone(),
- work_dirs: None,
- title: Some(row.title.clone()),
- updated_at: Some(row.updated_at),
- created_at: row.created_at,
- meta: None,
- },
+ metadata: row,
icon,
icon_from_external_svg,
status: AgentThreadStatus::default(),
@@ -862,11 +832,11 @@ impl Sidebar {
// Merge live info into threads and update notification state
// in a single pass.
for thread in &mut threads {
- if let Some(info) = live_info_by_session.get(&thread.session_info.session_id) {
+ if let Some(info) = live_info_by_session.get(&thread.metadata.session_id) {
thread.apply_active_info(info);
}
- let session_id = &thread.session_info.session_id;
+ let session_id = &thread.metadata.session_id;
let is_thread_workspace_active = match &thread.workspace {
ThreadEntryWorkspace::Open(thread_workspace) => active_workspace
@@ -890,16 +860,16 @@ impl Sidebar {
threads.sort_by(|a, b| {
let a_time = self
.thread_last_message_sent_or_queued
- .get(&a.session_info.session_id)
+ .get(&a.metadata.session_id)
.copied()
- .or(a.session_info.created_at)
- .or(a.session_info.updated_at);
+ .or(a.metadata.created_at)
+ .or(Some(a.metadata.updated_at));
let b_time = self
.thread_last_message_sent_or_queued
- .get(&b.session_info.session_id)
+ .get(&b.metadata.session_id)
.copied()
- .or(b.session_info.created_at)
- .or(b.session_info.updated_at);
+ .or(b.metadata.created_at)
+ .or(Some(b.metadata.updated_at));
b_time.cmp(&a_time)
});
} else {
@@ -920,12 +890,7 @@ impl Sidebar {
let mut matched_threads: Vec<ThreadEntry> = Vec::new();
for mut thread in threads {
- let title = thread
- .session_info
- .title
- .as_ref()
- .map(|s| s.as_ref())
- .unwrap_or("");
+ let title: &str = &thread.metadata.title;
if let Some(positions) = fuzzy_match_positions(&query, title) {
thread.highlight_positions = positions;
}
@@ -960,7 +925,7 @@ impl Sidebar {
});
for thread in matched_threads {
- current_session_ids.insert(thread.session_info.session_id.clone());
+ current_session_ids.insert(thread.metadata.session_id.clone());
entries.push(thread.into());
}
} else {
@@ -1011,7 +976,7 @@ impl Sidebar {
for (index, thread) in threads.into_iter().enumerate() {
let is_hidden = index >= count;
- let session_id = &thread.session_info.session_id;
+ let session_id = &thread.metadata.session_id;
if is_hidden {
let is_promoted = thread.status == AgentThreadStatus::Running
|| thread.status == AgentThreadStatus::WaitingForConfirmation
@@ -1861,22 +1826,15 @@ impl Sidebar {
self.toggle_collapse(&path_list, window, cx);
}
ListEntry::Thread(thread) => {
- let session_info = thread.session_info.clone();
+ let metadata = thread.metadata.clone();
match &thread.workspace {
ThreadEntryWorkspace::Open(workspace) => {
let workspace = workspace.clone();
- self.activate_thread(
- thread.agent.clone(),
- session_info,
- &workspace,
- window,
- cx,
- );
+ self.activate_thread(metadata, &workspace, window, cx);
}
ThreadEntryWorkspace::Closed(path_list) => {
self.open_workspace_and_activate_thread(
- thread.agent.clone(),
- session_info,
+ metadata,
path_list.clone(),
window,
cx,
@@ -1942,8 +1900,7 @@ impl Sidebar {
fn load_agent_thread_in_workspace(
workspace: &Entity<Workspace>,
- agent: Agent,
- session_info: acp_thread::AgentSessionInfo,
+ metadata: &ThreadMetadata,
focus: bool,
window: &mut Window,
cx: &mut App,
@@ -1955,10 +1912,10 @@ impl Sidebar {
if let Some(agent_panel) = workspace.read(cx).panel::<AgentPanel>(cx) {
agent_panel.update(cx, |panel, cx| {
panel.load_agent_thread(
- agent,
- session_info.session_id,
- session_info.work_dirs,
- session_info.title,
+ Agent::from(metadata.agent_id.clone()),
+ metadata.session_id.clone(),
+ Some(metadata.folder_paths.clone()),
+ Some(metadata.title.clone()),
focus,
window,
cx,
@@ -1969,8 +1926,7 @@ impl Sidebar {
fn activate_thread_locally(
&mut self,
- agent: Agent,
- session_info: acp_thread::AgentSessionInfo,
+ metadata: &ThreadMetadata,
workspace: &Entity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
@@ -1982,40 +1938,32 @@ impl Sidebar {
// Set focused_thread eagerly so the sidebar highlight updates
// immediately, rather than waiting for a deferred AgentPanel
// event which can race with ActiveWorkspaceChanged clearing it.
- self.focused_thread = Some(session_info.session_id.clone());
- self.record_thread_access(&session_info.session_id);
+ self.focused_thread = Some(metadata.session_id.clone());
+ self.record_thread_access(&metadata.session_id);
multi_workspace.update(cx, |multi_workspace, cx| {
multi_workspace.activate(workspace.clone(), cx);
});
- Self::load_agent_thread_in_workspace(workspace, agent, session_info, true, window, cx);
+ Self::load_agent_thread_in_workspace(workspace, metadata, true, window, cx);
self.update_entries(cx);
}
fn activate_thread_in_other_window(
&self,
- agent: Agent,
- session_info: acp_thread::AgentSessionInfo,
+ metadata: ThreadMetadata,
workspace: Entity<Workspace>,
target_window: WindowHandle<MultiWorkspace>,
cx: &mut Context<Self>,
) {
- let target_session_id = session_info.session_id.clone();
+ let target_session_id = metadata.session_id.clone();
let activated = target_window
.update(cx, |multi_workspace, window, cx| {
window.activate_window();
multi_workspace.activate(workspace.clone(), cx);
- Self::load_agent_thread_in_workspace(
- &workspace,
- agent,
- session_info,
- true,
- window,
- cx,
- );
+ Self::load_agent_thread_in_workspace(&workspace, &metadata, true, window, cx);
})
.log_err()
.is_some();
@@ -2040,8 +1988,7 @@ impl Sidebar {
fn activate_thread(
&mut self,
- agent: Agent,
- session_info: acp_thread::AgentSessionInfo,
+ metadata: ThreadMetadata,
workspace: &Entity<Workspace>,
window: &mut Window,
cx: &mut Context<Self>,
@@ -2050,7 +1997,7 @@ impl Sidebar {
.find_workspace_in_current_window(cx, |candidate, _| candidate == workspace)
.is_some()
{
- self.activate_thread_locally(agent, session_info, &workspace, window, cx);
+ self.activate_thread_locally(&metadata, &workspace, window, cx);
return;
}
@@ -2060,13 +2007,12 @@ impl Sidebar {
return;
};
- self.activate_thread_in_other_window(agent, session_info, workspace, target_window, cx);
+ self.activate_thread_in_other_window(metadata, workspace, target_window, cx);
}
fn open_workspace_and_activate_thread(
&mut self,
- agent: Agent,
- session_info: acp_thread::AgentSessionInfo,
+ metadata: ThreadMetadata,
path_list: PathList,
window: &mut Window,
cx: &mut Context<Self>,
@@ -2084,7 +2030,7 @@ impl Sidebar {
let workspace = open_task.await?;
this.update_in(cx, |this, window, cx| {
- this.activate_thread(agent, session_info, &workspace, window, cx);
+ this.activate_thread(metadata, &workspace, window, cx);
})?;
anyhow::Ok(())
})
@@ -2113,31 +2059,23 @@ impl Sidebar {
fn activate_archived_thread(
&mut self,
- agent: Agent,
- session_info: acp_thread::AgentSessionInfo,
+ metadata: ThreadMetadata,
window: &mut Window,
cx: &mut Context<Self>,
) {
- ThreadMetadataStore::global(cx).update(cx, |store, cx| {
- store.unarchive(&session_info.session_id, cx)
- });
+ ThreadMetadataStore::global(cx)
+ .update(cx, |store, cx| store.unarchive(&metadata.session_id, cx));
- if let Some(path_list) = &session_info.work_dirs {
- if let Some(workspace) = self.find_current_workspace_for_path_list(path_list, cx) {
- self.activate_thread_locally(agent, session_info, &workspace, window, cx);
+ if !metadata.folder_paths.paths().is_empty() {
+ let path_list = metadata.folder_paths.clone();
+ if let Some(workspace) = self.find_current_workspace_for_path_list(&path_list, cx) {
+ self.activate_thread_locally(&metadata, &workspace, window, cx);
} else if let Some((target_window, workspace)) =
- self.find_open_workspace_for_path_list(path_list, cx)
+ self.find_open_workspace_for_path_list(&path_list, cx)
{
- self.activate_thread_in_other_window(
- agent,
- session_info,
- workspace,
- target_window,
- cx,
- );
+ self.activate_thread_in_other_window(metadata, workspace, target_window, cx);
} else {
- let path_list = path_list.clone();
- self.open_workspace_and_activate_thread(agent, session_info, path_list, window, cx);
+ self.open_workspace_and_activate_thread(metadata, path_list, window, cx);
}
return;
}
@@ -2150,7 +2088,7 @@ impl Sidebar {
});
if let Some(workspace) = active_workspace {
- self.activate_thread_locally(agent, session_info, &workspace, window, cx);
+ self.activate_thread_locally(&metadata, &workspace, window, cx);
}
}
@@ -2306,7 +2244,7 @@ impl Sidebar {
// a blank new thread in the panel instead.
if self.focused_thread.as_ref() == Some(session_id) {
let current_pos = self.contents.entries.iter().position(|entry| {
- matches!(entry, ListEntry::Thread(t) if &t.session_info.session_id == session_id)
+ matches!(entry, ListEntry::Thread(t) if &t.metadata.session_id == session_id)
});
// Find the workspace that owns this thread's project group by
@@ -2358,10 +2296,7 @@ impl Sidebar {
});
if let Some(next) = next_thread {
- let next_session_id = next.session_info.session_id.clone();
- let next_agent = next.agent.clone();
- let next_work_dirs = next.session_info.work_dirs.clone();
- let next_title = next.session_info.title.clone();
+ let next_metadata = next.metadata.clone();
// Use the thread's own workspace when it has one open (e.g. an absorbed
// linked worktree thread that appears under the main workspace's header
// but belongs to its own workspace). Loading into the wrong panel binds
@@ -2371,17 +2306,17 @@ impl Sidebar {
ThreadEntryWorkspace::Open(ws) => Some(ws.clone()),
ThreadEntryWorkspace::Closed(_) => group_workspace,
};
- self.focused_thread = Some(next_session_id.clone());
- self.record_thread_access(&next_session_id);
+ self.focused_thread = Some(next_metadata.session_id.clone());
+ self.record_thread_access(&next_metadata.session_id);
if let Some(workspace) = target_workspace {
if let Some(agent_panel) = workspace.read(cx).panel::<AgentPanel>(cx) {
agent_panel.update(cx, |panel, cx| {
panel.load_agent_thread(
- next_agent,
- next_session_id,
- next_work_dirs,
- next_title,
+ Agent::from(next_metadata.agent_id.clone()),
+ next_metadata.session_id.clone(),
+ Some(next_metadata.folder_paths.clone()),
+ Some(next_metadata.title.clone()),
true,
window,
cx,
@@ -2419,7 +2354,7 @@ impl Sidebar {
AgentThreadStatus::Completed | AgentThreadStatus::Error => {}
}
- let session_id = thread.session_info.session_id.clone();
+ let session_id = thread.metadata.session_id.clone();
self.archive_thread(&session_id, window, cx)
}
@@ -2453,28 +2388,22 @@ impl Sidebar {
};
let notified = self
.contents
- .is_thread_notified(&thread.session_info.session_id);
- let timestamp: SharedString = self
- .thread_last_message_sent_or_queued
- .get(&thread.session_info.session_id)
- .copied()
- .or(thread.session_info.created_at)
- .or(thread.session_info.updated_at)
- .map(format_history_entry_timestamp)
- .unwrap_or_default()
- .into();
+ .is_thread_notified(&thread.metadata.session_id);
+ let timestamp: SharedString = format_history_entry_timestamp(
+ self.thread_last_message_sent_or_queued
+ .get(&thread.metadata.session_id)
+ .copied()
+ .or(thread.metadata.created_at)
+ .unwrap_or(thread.metadata.updated_at),
+ )
+ .into();
Some(ThreadSwitcherEntry {
- session_id: thread.session_info.session_id.clone(),
- title: thread
- .session_info
- .title
- .clone()
- .unwrap_or_else(|| "Untitled".into()),
+ session_id: thread.metadata.session_id.clone(),
+ title: thread.metadata.title.clone(),
icon: thread.icon,
icon_from_external_svg: thread.icon_from_external_svg.clone(),
status: thread.status,
- agent: thread.agent.clone(),
- session_info: thread.session_info.clone(),
+ metadata: thread.metadata.clone(),
workspace,
worktree_name: thread.worktrees.first().map(|wt| wt.name.clone()),
@@ -2505,8 +2434,8 @@ impl Sidebar {
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => {
- let a_time = a.session_info.created_at.or(a.session_info.updated_at);
- let b_time = b.session_info.created_at.or(b.session_info.updated_at);
+ let a_time = a.metadata.created_at.or(Some(a.metadata.updated_at));
+ let b_time = b.metadata.created_at.or(Some(b.metadata.updated_at));
b_time.cmp(&a_time)
}
}
@@ -2560,16 +2489,11 @@ impl Sidebar {
let weak_multi_workspace = self.multi_workspace.clone();
- let original_agent = self
- .focused_thread
- .as_ref()
- .and_then(|focused_id| entries.iter().find(|e| &e.session_id == focused_id))
- .map(|e| e.agent.clone());
- let original_session_info = self
+ let original_metadata = self
.focused_thread
.as_ref()
.and_then(|focused_id| entries.iter().find(|e| &e.session_id == focused_id))
- .map(|e| e.session_info.clone());
+ .map(|e| e.metadata.clone());
let original_workspace = self
.multi_workspace
.upgrade()
@@ -2583,8 +2507,7 @@ impl Sidebar {
let thread_switcher = thread_switcher.clone();
move |this, _emitter, event: &ThreadSwitcherEvent, window, cx| match event {
ThreadSwitcherEvent::Preview {
- agent,
- session_info,
+ metadata,
workspace,
} => {
if let Some(mw) = weak_multi_workspace.upgrade() {
@@ -2592,22 +2515,14 @@ impl Sidebar {
mw.activate(workspace.clone(), cx);
});
}
- this.focused_thread = Some(session_info.session_id.clone());
+ this.focused_thread = Some(metadata.session_id.clone());
this.update_entries(cx);
- Self::load_agent_thread_in_workspace(
- workspace,
- agent.clone(),
- session_info.clone(),
- false,
- window,
- cx,
- );
+ Self::load_agent_thread_in_workspace(workspace, metadata, false, window, cx);
let focus = thread_switcher.focus_handle(cx);
window.focus(&focus, cx);
}
ThreadSwitcherEvent::Confirmed {
- agent,
- session_info,
+ metadata,
workspace,
} => {
if let Some(mw) = weak_multi_workspace.upgrade() {
@@ -2615,17 +2530,10 @@ impl Sidebar {
mw.activate(workspace.clone(), cx);
});
}
- this.record_thread_access(&session_info.session_id);
- this.focused_thread = Some(session_info.session_id.clone());
+ this.record_thread_access(&metadata.session_id);
+ this.focused_thread = Some(metadata.session_id.clone());
this.update_entries(cx);
- Self::load_agent_thread_in_workspace(
- workspace,
- agent.clone(),
- session_info.clone(),
- false,
- window,
- cx,
- );
+ Self::load_agent_thread_in_workspace(workspace, metadata, false, window, cx);
this.dismiss_thread_switcher(cx);
workspace.update(cx, |workspace, cx| {
workspace.focus_panel::<AgentPanel>(window, cx);
@@ -2639,15 +2547,13 @@ impl Sidebar {
});
}
}
- if let Some(session_info) = &original_session_info {
- this.focused_thread = Some(session_info.session_id.clone());
+ if let Some(metadata) = &original_metadata {
+ this.focused_thread = Some(metadata.session_id.clone());
this.update_entries(cx);
- let agent = original_agent.clone().unwrap_or(Agent::NativeAgent);
if let Some(original_ws) = &original_workspace {
Self::load_agent_thread_in_workspace(
original_ws,
- agent,
- session_info.clone(),
+ metadata,
false,
window,
cx,
@@ -2672,13 +2578,10 @@ impl Sidebar {
// Replay the initial preview that was emitted during construction
// before subscriptions were wired up.
- let initial_preview = thread_switcher.read(cx).selected_entry().map(|entry| {
- (
- entry.agent.clone(),
- entry.session_info.clone(),
- entry.workspace.clone(),
- )
- });
+ let initial_preview = thread_switcher
+ .read(cx)
+ .selected_entry()
+ .map(|entry| (entry.metadata.clone(), entry.workspace.clone()));
self.thread_switcher = Some(thread_switcher);
self._thread_switcher_subscriptions = subscriptions;
@@ -2688,22 +2591,15 @@ impl Sidebar {
});
}
- if let Some((agent, session_info, workspace)) = initial_preview {
+ if let Some((metadata, workspace)) = initial_preview {
if let Some(mw) = self.multi_workspace.upgrade() {
mw.update(cx, |mw, cx| {
mw.activate(workspace.clone(), cx);
});
}
- self.focused_thread = Some(session_info.session_id.clone());
+ self.focused_thread = Some(metadata.session_id.clone());
self.update_entries(cx);
- Self::load_agent_thread_in_workspace(
- &workspace,
- agent,
- session_info,
- false,
- window,
- cx,
- );
+ Self::load_agent_thread_in_workspace(&workspace, &metadata, false, window, cx);
}
window.focus(&focus, cx);
@@ -2718,36 +2614,32 @@ impl Sidebar {
) -> AnyElement {
let has_notification = self
.contents
- .is_thread_notified(&thread.session_info.session_id);
-
- let title: SharedString = thread
- .session_info
- .title
- .clone()
- .unwrap_or_else(|| "Untitled".into());
- let session_info = thread.session_info.clone();
+ .is_thread_notified(&thread.metadata.session_id);
+
+ let title: SharedString = thread.metadata.title.clone();
+ let metadata = thread.metadata.clone();
let thread_workspace = thread.workspace.clone();
let is_hovered = self.hovered_thread_index == Some(ix);
- let is_selected = self.agent_panel_visible
- && self.focused_thread.as_ref() == Some(&session_info.session_id);
+ let is_selected =
+ self.agent_panel_visible && self.focused_thread.as_ref() == Some(&metadata.session_id);
let is_running = matches!(
thread.status,
AgentThreadStatus::Running | AgentThreadStatus::WaitingForConfirmation
);
- let session_id_for_delete = thread.session_info.session_id.clone();
+ let session_id_for_delete = thread.metadata.session_id.clone();
let focus_handle = self.focus_handle.clone();
let id = SharedString::from(format!("thread-entry-{}", ix));
- let timestamp = self
- .thread_last_message_sent_or_queued
- .get(&thread.session_info.session_id)
- .copied()
- .or(thread.session_info.created_at)
- .or(thread.session_info.updated_at)
- .map(format_history_entry_timestamp);
+ let timestamp = format_history_entry_timestamp(
+ self.thread_last_message_sent_or_queued
+ .get(&thread.metadata.session_id)
+ .copied()
+ .or(thread.metadata.created_at)
+ .unwrap_or(thread.metadata.updated_at),
+ );
ThreadItem::new(id, title)
.icon(thread.icon)
@@ -2766,7 +2658,7 @@ impl Sidebar {
})
.collect(),
)
- .when_some(timestamp, |this, ts| this.timestamp(ts))
+ .timestamp(timestamp)
.highlight_positions(thread.highlight_positions.to_vec())
.title_generating(thread.is_title_generating)
.notified(has_notification)
@@ -2827,23 +2719,15 @@ impl Sidebar {
)
})
.on_click({
- let agent = thread.agent.clone();
cx.listener(move |this, _, window, cx| {
this.selection = None;
match &thread_workspace {
ThreadEntryWorkspace::Open(workspace) => {
- this.activate_thread(
- agent.clone(),
- session_info.clone(),
- workspace,
- window,
- cx,
- );
+ this.activate_thread(metadata.clone(), workspace, window, cx);
}
ThreadEntryWorkspace::Closed(path_list) => {
this.open_workspace_and_activate_thread(
- agent.clone(),
- session_info.clone(),
+ metadata.clone(),
path_list.clone(),
window,
cx,
@@ -3333,18 +3217,7 @@ impl Sidebar {
}
ThreadsArchiveViewEvent::Unarchive { thread } => {
this.show_thread_list(window, cx);
-
- let agent = Agent::from(thread.agent_id.clone());
- let session_info = acp_thread::AgentSessionInfo {
- session_id: thread.session_id.clone(),
- work_dirs: Some(thread.folder_paths.clone()),
- title: Some(thread.title.clone()),
- updated_at: Some(thread.updated_at),
- created_at: thread.created_at,
- meta: None,
- };
-
- this.activate_archived_thread(agent, session_info, window, cx);
+ this.activate_archived_thread(thread.clone(), window, cx);
}
},
);
@@ -11,6 +11,7 @@ use feature_flags::FeatureFlagAppExt as _;
use fs::FakeFs;
use gpui::TestAppContext;
use pretty_assertions::assert_eq;
+use project::AgentId;
use settings::SettingsStore;
use std::{path::PathBuf, sync::Arc};
use util::path_list::PathList;
@@ -30,9 +31,11 @@ fn init_test(cx: &mut TestAppContext) {
}
fn has_thread_entry(sidebar: &Sidebar, session_id: &acp::SessionId) -> bool {
- sidebar.contents.entries.iter().any(
- |entry| matches!(entry, ListEntry::Thread(t) if &t.session_info.session_id == session_id),
- )
+ sidebar
+ .contents
+ .entries
+ .iter()
+ .any(|entry| matches!(entry, ListEntry::Thread(t) if &t.metadata.session_id == session_id))
}
async fn init_test_project(
@@ -176,12 +179,7 @@ fn visible_entries_as_strings(
format!("{} [{}]{}", icon, label, selected)
}
ListEntry::Thread(thread) => {
- let title = thread
- .session_info
- .title
- .as_ref()
- .map(|s| s.as_ref())
- .unwrap_or("Untitled");
+ let title = thread.metadata.title.as_ref();
let active = if thread.is_live { " *" } else { "" };
let status_str = match thread.status {
AgentThreadStatus::Running => " (running)",
@@ -191,7 +189,7 @@ fn visible_entries_as_strings(
};
let notified = if sidebar
.contents
- .is_thread_notified(&thread.session_info.session_id)
+ .is_thread_notified(&thread.metadata.session_id)
{
" (!)"
} else {
@@ -565,14 +563,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
is_active: true,
},
ListEntry::Thread(ThreadEntry {
- agent: Agent::NativeAgent,
- session_info: acp_thread::AgentSessionInfo {
+ metadata: ThreadMetadata {
session_id: acp::SessionId::new(Arc::from("t-1")),
- work_dirs: None,
- title: Some("Completed thread".into()),
- updated_at: Some(Utc::now()),
+ agent_id: AgentId::new("zed-agent"),
+ folder_paths: PathList::default(),
+ title: "Completed thread".into(),
+ updated_at: Utc::now(),
created_at: Some(Utc::now()),
- meta: None,
+ archived: false,
},
icon: IconName::ZedAgent,
icon_from_external_svg: None,
@@ -587,14 +585,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
}),
// Active thread with Running status
ListEntry::Thread(ThreadEntry {
- agent: Agent::NativeAgent,
- session_info: acp_thread::AgentSessionInfo {
+ metadata: ThreadMetadata {
session_id: acp::SessionId::new(Arc::from("t-2")),
- work_dirs: None,
- title: Some("Running thread".into()),
- updated_at: Some(Utc::now()),
+ agent_id: AgentId::new("zed-agent"),
+ folder_paths: PathList::default(),
+ title: "Running thread".into(),
+ updated_at: Utc::now(),
created_at: Some(Utc::now()),
- meta: None,
+ archived: false,
},
icon: IconName::ZedAgent,
icon_from_external_svg: None,
@@ -609,14 +607,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
}),
// Active thread with Error status
ListEntry::Thread(ThreadEntry {
- agent: Agent::NativeAgent,
- session_info: acp_thread::AgentSessionInfo {
+ metadata: ThreadMetadata {
session_id: acp::SessionId::new(Arc::from("t-3")),
- work_dirs: None,
- title: Some("Error thread".into()),
- updated_at: Some(Utc::now()),
+ agent_id: AgentId::new("zed-agent"),
+ folder_paths: PathList::default(),
+ title: "Error thread".into(),
+ updated_at: Utc::now(),
created_at: Some(Utc::now()),
- meta: None,
+ archived: false,
},
icon: IconName::ZedAgent,
icon_from_external_svg: None,
@@ -631,14 +629,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
}),
// Thread with WaitingForConfirmation status, not active
ListEntry::Thread(ThreadEntry {
- agent: Agent::NativeAgent,
- session_info: acp_thread::AgentSessionInfo {
+ metadata: ThreadMetadata {
session_id: acp::SessionId::new(Arc::from("t-4")),
- work_dirs: None,
- title: Some("Waiting thread".into()),
- updated_at: Some(Utc::now()),
+ agent_id: AgentId::new("zed-agent"),
+ folder_paths: PathList::default(),
+ title: "Waiting thread".into(),
+ updated_at: Utc::now(),
created_at: Some(Utc::now()),
- meta: None,
+ archived: false,
},
icon: IconName::ZedAgent,
icon_from_external_svg: None,
@@ -653,14 +651,14 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
}),
// Background thread that completed (should show notification)
ListEntry::Thread(ThreadEntry {
- agent: Agent::NativeAgent,
- session_info: acp_thread::AgentSessionInfo {
+ metadata: ThreadMetadata {
session_id: acp::SessionId::new(Arc::from("t-5")),
- work_dirs: None,
- title: Some("Notified thread".into()),
- updated_at: Some(Utc::now()),
+ agent_id: AgentId::new("zed-agent"),
+ folder_paths: PathList::default(),
+ title: "Notified thread".into(),
+ updated_at: Utc::now(),
created_at: Some(Utc::now()),
- meta: None,
+ archived: false,
},
icon: IconName::ZedAgent,
icon_from_external_svg: None,
@@ -1919,14 +1917,14 @@ async fn test_focused_thread_tracks_user_intent(cx: &mut TestAppContext) {
sidebar.update_in(cx, |sidebar, window, cx| {
sidebar.activate_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: session_id_a.clone(),
- work_dirs: None,
- title: Some("Test".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "Test".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: PathList::default(),
+ archived: false,
},
&workspace_a,
window,
@@ -1974,14 +1972,14 @@ async fn test_focused_thread_tracks_user_intent(cx: &mut TestAppContext) {
// which also triggers a workspace switch.
sidebar.update_in(cx, |sidebar, window, cx| {
sidebar.activate_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: session_id_b.clone(),
- work_dirs: None,
- title: Some("Thread B".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "Thread B".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: PathList::default(),
+ archived: false,
},
&workspace_b,
window,
@@ -3231,24 +3229,14 @@ async fn test_clicking_worktree_thread_does_not_briefly_render_as_separate_proje
);
}
ListEntry::Thread(thread)
- if thread
- .session_info
- .title
- .as_ref()
- .map(|title| title.as_ref())
- == Some("WT Thread")
+ if thread.metadata.title.as_ref() == "WT Thread"
&& thread.worktrees.first().map(|wt| wt.name.as_ref())
== Some("wt-feature-a") =>
{
saw_expected_thread = true;
}
ListEntry::Thread(thread) => {
- let title = thread
- .session_info
- .title
- .as_ref()
- .map(|title| title.as_ref())
- .unwrap_or("Untitled");
+ let title = thread.metadata.title.as_ref();
let worktree_name = thread
.worktrees
.first()
@@ -3446,14 +3434,14 @@ async fn test_activate_archived_thread_with_saved_paths_activates_matching_works
// switch to the workspace for project-b.
sidebar.update_in(cx, |sidebar, window, cx| {
sidebar.activate_archived_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: session_id.clone(),
- work_dirs: Some(PathList::new(&[PathBuf::from("/project-b")])),
- title: Some("Archived Thread".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "Archived Thread".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: PathList::new(&[PathBuf::from("/project-b")]),
+ archived: false,
},
window,
cx,
@@ -3507,14 +3495,14 @@ async fn test_activate_archived_thread_cwd_fallback_with_matching_workspace(
// No thread saved to the store – cwd is the only path hint.
sidebar.update_in(cx, |sidebar, window, cx| {
sidebar.activate_archived_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: acp::SessionId::new(Arc::from("unknown-session")),
- work_dirs: Some(PathList::new(&[std::path::PathBuf::from("/project-b")])),
- title: Some("CWD Thread".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "CWD Thread".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: PathList::new(&[std::path::PathBuf::from("/project-b")]),
+ archived: false,
},
window,
cx,
@@ -3568,14 +3556,14 @@ async fn test_activate_archived_thread_no_paths_no_cwd_uses_active_workspace(
// No saved thread, no cwd – should fall back to the active workspace.
sidebar.update_in(cx, |sidebar, window, cx| {
sidebar.activate_archived_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: acp::SessionId::new(Arc::from("no-context-session")),
- work_dirs: None,
- title: Some("Contextless Thread".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "Contextless Thread".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: PathList::default(),
+ archived: false,
},
window,
cx,
@@ -3622,14 +3610,14 @@ async fn test_activate_archived_thread_saved_paths_opens_new_workspace(cx: &mut
sidebar.update_in(cx, |sidebar, window, cx| {
sidebar.activate_archived_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: session_id.clone(),
- work_dirs: Some(path_list_b),
- title: Some("New WS Thread".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "New WS Thread".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: path_list_b,
+ archived: false,
},
window,
cx,
@@ -3671,14 +3659,14 @@ async fn test_activate_archived_thread_reuses_workspace_in_another_window(cx: &m
sidebar.update_in(cx_a, |sidebar, window, cx| {
sidebar.activate_archived_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: session_id.clone(),
- work_dirs: Some(PathList::new(&[PathBuf::from("/project-b")])),
- title: Some("Cross Window Thread".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "Cross Window Thread".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: PathList::new(&[PathBuf::from("/project-b")]),
+ archived: false,
},
window,
cx,
@@ -3747,14 +3735,14 @@ async fn test_activate_archived_thread_reuses_workspace_in_another_window_with_t
sidebar_a.update_in(cx_a, |sidebar, window, cx| {
sidebar.activate_archived_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: session_id.clone(),
- work_dirs: Some(PathList::new(&[PathBuf::from("/project-b")])),
- title: Some("Cross Window Thread".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "Cross Window Thread".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: PathList::new(&[PathBuf::from("/project-b")]),
+ archived: false,
},
window,
cx,
@@ -3822,14 +3810,14 @@ async fn test_activate_archived_thread_prefers_current_window_for_matching_paths
sidebar_a.update_in(cx_a, |sidebar, window, cx| {
sidebar.activate_archived_thread(
- Agent::NativeAgent,
- acp_thread::AgentSessionInfo {
+ ThreadMetadata {
session_id: session_id.clone(),
- work_dirs: Some(PathList::new(&[PathBuf::from("/project-a")])),
- title: Some("Current Window Thread".into()),
- updated_at: None,
+ agent_id: agent::ZED_AGENT_ID.clone(),
+ title: "Current Window Thread".into(),
+ updated_at: Utc::now(),
created_at: None,
- meta: None,
+ folder_paths: PathList::new(&[PathBuf::from("/project-a")]),
+ archived: false,
},
window,
cx,