Support renaming ACP threads (#56446)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #54689 Release Notes: - Support editing the thread title for external agent threads
This commit is contained in:
1 parent
8875562192
commit
22c3a5ad9a
6 files changed
+198
-39
No files matched your search
@@ -679,6 +679,7 @@ fn thread_metadata_to_debug_json(
|
||||
"session_id": metadata.session_id.as_ref().map(|s| s.0.to_string()),
|
||||
"agent_id": metadata.agent_id.0.to_string(),
|
||||
"title": metadata.title.as_ref().map(|t| t.to_string()),
|
||||
"title_override": metadata.title_override.as_ref().map(|t| t.to_string()),
|
||||
"updated_at": format_timestamp_human(&metadata.updated_at),
|
||||
"created_at": metadata.created_at.as_ref().map(format_timestamp_human),
|
||||
"interacted_at": metadata.interacted_at.as_ref().map(format_timestamp_human),
|
||||
@@ -5295,6 +5296,7 @@ mod tests {
|
||||
session_id: Some(resume_session_id.clone()),
|
||||
agent_id: ProjectAgentId::new("Flaky"),
|
||||
title: Some("Persistent chat".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
interacted_at: None,
|
||||
|
||||
@@ -792,7 +792,7 @@ impl ConversationView {
|
||||
.and_then(|id| {
|
||||
let store = ThreadMetadataStore::try_global(cx)?;
|
||||
let entry = store.read(cx).entry_by_session(id)?;
|
||||
Some((Some(entry.folder_paths().clone()), entry.title.clone()))
|
||||
Some((Some(entry.folder_paths().clone()), entry.title()))
|
||||
})
|
||||
.unwrap_or((None, None));
|
||||
(session_id, work_dirs, title)
|
||||
@@ -1186,6 +1186,7 @@ impl ConversationView {
|
||||
let weak = cx.weak_entity();
|
||||
cx.new(|cx| {
|
||||
ThreadView::new(
|
||||
self.thread_id,
|
||||
thread,
|
||||
conversation,
|
||||
weak,
|
||||
@@ -1614,7 +1615,14 @@ impl ConversationView {
|
||||
);
|
||||
}
|
||||
AcpThreadEvent::TitleUpdated => {
|
||||
if let Some(title) = thread.read(cx).title()
|
||||
let override_title = ThreadMetadataStore::try_global(cx).and_then(|store| {
|
||||
store
|
||||
.read(cx)
|
||||
.entry(self.thread_id)
|
||||
.and_then(|m| m.title_override.clone())
|
||||
});
|
||||
let title = override_title.or_else(|| thread.read(cx).title());
|
||||
if let Some(title) = title
|
||||
&& let Some(active_thread) = self.thread_view(&session_id)
|
||||
{
|
||||
let title_editor = active_thread.read(cx).title_editor.clone();
|
||||
@@ -3589,6 +3597,7 @@ pub(crate) mod tests {
|
||||
session_id: Some(resume_session_id.clone()),
|
||||
agent_id: ProjectAgentId::new("Flaky"),
|
||||
title: Some(stored_title.clone()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
interacted_at: None,
|
||||
@@ -7119,24 +7128,6 @@ pub(crate) mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_title_editor_is_read_only_when_set_title_unsupported(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let (conversation_view, cx) =
|
||||
setup_conversation_view(StubAgentServer::new(ResumeOnlyAgentConnection), cx).await;
|
||||
|
||||
let active = active_thread(&conversation_view, cx);
|
||||
let title_editor = cx.read(|cx| active.read(cx).title_editor.clone());
|
||||
|
||||
title_editor.read_with(cx, |editor, cx| {
|
||||
assert!(
|
||||
editor.read_only(cx),
|
||||
"Title editor should be read-only when the connection does not support set_title"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_max_tokens_error_is_rendered(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
DEFAULT_THREAD_TITLE, SelectPermissionGranularity,
|
||||
agent_configuration::configure_context_server_modal::default_markdown_style,
|
||||
thread_metadata_store::{ThreadId, ThreadMetadataStore},
|
||||
};
|
||||
use agent_client_protocol::schema as acp;
|
||||
use std::cell::RefCell;
|
||||
@@ -264,6 +265,7 @@ impl PermissionSelection {
|
||||
}
|
||||
|
||||
pub struct ThreadView {
|
||||
pub(crate) root_thread_id: ThreadId,
|
||||
pub session_id: acp::SessionId,
|
||||
pub parent_session_id: Option<acp::SessionId>,
|
||||
pub thread: Entity<AcpThread>,
|
||||
@@ -353,6 +355,7 @@ pub struct TurnFields {
|
||||
|
||||
impl ThreadView {
|
||||
pub(crate) fn new(
|
||||
root_thread_id: ThreadId,
|
||||
thread: Entity<AcpThread>,
|
||||
conversation: Entity<super::Conversation>,
|
||||
server_view: WeakEntity<ConversationView>,
|
||||
@@ -438,15 +441,17 @@ impl ThreadView {
|
||||
&& agent_id.as_ref() == "Codex";
|
||||
|
||||
let title_editor = {
|
||||
let can_edit = thread.update(cx, |thread, cx| thread.can_set_title(cx));
|
||||
let metadata = ThreadMetadataStore::try_global(cx)
|
||||
.and_then(|store| store.read(cx).entry(root_thread_id).cloned());
|
||||
let initial_title = if parent_session_id.is_none() {
|
||||
metadata.as_ref().and_then(|m| m.title())
|
||||
} else {
|
||||
thread.read(cx).title()
|
||||
}
|
||||
.unwrap_or_else(|| DEFAULT_THREAD_TITLE.into());
|
||||
let editor = cx.new(|cx| {
|
||||
let mut editor = Editor::single_line(window, cx);
|
||||
if let Some(title) = thread.read(cx).title() {
|
||||
editor.set_text(title, window, cx);
|
||||
} else {
|
||||
editor.set_text(DEFAULT_THREAD_TITLE, window, cx);
|
||||
}
|
||||
editor.set_read_only(!can_edit);
|
||||
editor.set_text(initial_title, window, cx);
|
||||
editor
|
||||
});
|
||||
subscriptions.push(cx.subscribe_in(&editor, window, Self::handle_title_editor_event));
|
||||
@@ -490,6 +495,7 @@ impl ThreadView {
|
||||
}));
|
||||
|
||||
let mut this = Self {
|
||||
root_thread_id,
|
||||
session_id,
|
||||
parent_session_id,
|
||||
focus_handle: cx.focus_handle(),
|
||||
@@ -1647,11 +1653,23 @@ impl ThreadView {
|
||||
}
|
||||
|
||||
let new_title = title_editor.read(cx).text(cx);
|
||||
if new_title.is_empty() {
|
||||
return;
|
||||
}
|
||||
let title = SharedString::from(new_title);
|
||||
if let Some(store) = ThreadMetadataStore::try_global(cx)
|
||||
&& !self.is_subagent()
|
||||
{
|
||||
let thread_id = self.root_thread_id;
|
||||
store.update(cx, |store, cx| {
|
||||
store.set_title_override(thread_id, title.clone(), cx);
|
||||
});
|
||||
}
|
||||
thread.update(cx, |thread, cx| {
|
||||
thread
|
||||
.set_title(new_title.into(), cx)
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
if thread.can_set_title(cx) {
|
||||
thread.set_title(title, cx).detach_and_log_err(cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
EditorEvent::Blurred => {
|
||||
if title_editor.read(cx).text(cx).is_empty() {
|
||||
|
||||
@@ -590,6 +590,7 @@ fn collect_importable_threads(
|
||||
session_id: Some(session.session_id),
|
||||
agent_id: agent_id.clone(),
|
||||
title: session.title,
|
||||
title_override: None,
|
||||
updated_at: session.updated_at.unwrap_or_else(|| Utc::now()),
|
||||
created_at: session.created_at,
|
||||
interacted_at: None,
|
||||
|
||||
@@ -135,6 +135,7 @@ fn migrate_thread_metadata(cx: &mut App) -> Task<anyhow::Result<()>> {
|
||||
} else {
|
||||
Some(entry.title)
|
||||
},
|
||||
title_override: None,
|
||||
updated_at: entry.updated_at,
|
||||
created_at: entry.created_at,
|
||||
interacted_at: None,
|
||||
@@ -310,6 +311,10 @@ pub struct ThreadMetadata {
|
||||
pub session_id: Option<acp::SessionId>,
|
||||
pub agent_id: AgentId,
|
||||
pub title: Option<SharedString>,
|
||||
/// User-supplied title that takes precedence over `title`. Set when the
|
||||
/// user renames a thread, so that subsequent agent-driven title updates
|
||||
/// (e.g. from `SessionInfoUpdate`) don't clobber the user's choice.
|
||||
pub title_override: Option<SharedString>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub created_at: Option<DateTime<Utc>>,
|
||||
/// When a user last interacted to send a message (including queueing).
|
||||
@@ -328,11 +333,14 @@ impl ThreadMetadata {
|
||||
}
|
||||
|
||||
pub fn display_title(&self) -> SharedString {
|
||||
self.title
|
||||
.clone()
|
||||
self.title()
|
||||
.unwrap_or_else(|| crate::DEFAULT_THREAD_TITLE.into())
|
||||
}
|
||||
|
||||
pub fn title(&self) -> Option<SharedString> {
|
||||
self.title_override.clone().or_else(|| self.title.clone())
|
||||
}
|
||||
|
||||
pub fn folder_paths(&self) -> &PathList {
|
||||
self.worktree_paths.folder_path_list()
|
||||
}
|
||||
@@ -422,7 +430,7 @@ impl From<&ThreadMetadata> for acp_thread::AgentSessionInfo {
|
||||
Self {
|
||||
session_id,
|
||||
work_dirs: Some(meta.folder_paths().clone()),
|
||||
title: meta.title.clone(),
|
||||
title: meta.title(),
|
||||
updated_at: Some(meta.updated_at),
|
||||
created_at: meta.created_at,
|
||||
meta: None,
|
||||
@@ -674,6 +682,26 @@ impl ThreadMetadataStore {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Set or clear the user-supplied title for a thread.
|
||||
pub fn set_title_override(
|
||||
&mut self,
|
||||
thread_id: ThreadId,
|
||||
title_override: SharedString,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(existing) = self.entry(thread_id) else {
|
||||
return;
|
||||
};
|
||||
if existing.title_override.as_ref() == Some(&title_override) {
|
||||
return;
|
||||
}
|
||||
let metadata = ThreadMetadata {
|
||||
title_override: Some(title_override),
|
||||
..existing.clone()
|
||||
};
|
||||
self.save(metadata, cx);
|
||||
}
|
||||
|
||||
fn save_internal(&mut self, metadata: ThreadMetadata) {
|
||||
if let Some(thread) = self.threads.get(&metadata.thread_id) {
|
||||
if thread.folder_paths() != metadata.folder_paths() {
|
||||
@@ -1196,6 +1224,7 @@ impl ThreadMetadataStore {
|
||||
Some(thread_ref.session_id().clone())
|
||||
};
|
||||
let title = thread_ref.title();
|
||||
let title_override = existing_thread.and_then(|t| t.title_override.clone());
|
||||
|
||||
let updated_at = Utc::now();
|
||||
|
||||
@@ -1249,6 +1278,7 @@ impl ThreadMetadataStore {
|
||||
session_id,
|
||||
agent_id,
|
||||
title,
|
||||
title_override,
|
||||
created_at: Some(created_at),
|
||||
interacted_at,
|
||||
updated_at,
|
||||
@@ -1364,6 +1394,9 @@ impl Domain for ThreadMetadataDb {
|
||||
sql!(
|
||||
ALTER TABLE sidebar_threads ADD COLUMN interacted_at TEXT;
|
||||
),
|
||||
sql!(
|
||||
ALTER TABLE sidebar_threads ADD COLUMN title_override TEXT;
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1380,7 +1413,7 @@ impl ThreadMetadataDb {
|
||||
|
||||
const LIST_QUERY: &str = "SELECT thread_id, session_id, agent_id, title, updated_at, \
|
||||
created_at, interacted_at, folder_paths, folder_paths_order, archived, main_worktree_paths, \
|
||||
main_worktree_paths_order, remote_connection \
|
||||
main_worktree_paths_order, remote_connection, title_override \
|
||||
FROM sidebar_threads \
|
||||
ORDER BY updated_at DESC";
|
||||
|
||||
@@ -1430,12 +1463,13 @@ impl ThreadMetadataDb {
|
||||
.map(serde_json::to_string)
|
||||
.transpose()
|
||||
.context("serialize thread metadata remote connection")?;
|
||||
let title_override = row.title_override.as_ref().map(|t| t.to_string());
|
||||
let thread_id = row.thread_id;
|
||||
let archived = row.archived;
|
||||
|
||||
self.write(move |conn| {
|
||||
let sql = "INSERT INTO sidebar_threads(thread_id, session_id, agent_id, title, updated_at, created_at, interacted_at, folder_paths, folder_paths_order, archived, main_worktree_paths, main_worktree_paths_order, remote_connection) \
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13) \
|
||||
let sql = "INSERT INTO sidebar_threads(thread_id, session_id, agent_id, title, updated_at, created_at, interacted_at, folder_paths, folder_paths_order, archived, main_worktree_paths, main_worktree_paths_order, remote_connection, title_override) \
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14) \
|
||||
ON CONFLICT(thread_id) DO UPDATE SET \
|
||||
session_id = excluded.session_id, \
|
||||
agent_id = excluded.agent_id, \
|
||||
@@ -1448,7 +1482,8 @@ impl ThreadMetadataDb {
|
||||
archived = excluded.archived, \
|
||||
main_worktree_paths = excluded.main_worktree_paths, \
|
||||
main_worktree_paths_order = excluded.main_worktree_paths_order, \
|
||||
remote_connection = excluded.remote_connection";
|
||||
remote_connection = excluded.remote_connection, \
|
||||
title_override = excluded.title_override";
|
||||
let mut stmt = Statement::prepare(conn, sql)?;
|
||||
let mut i = stmt.bind(&thread_id, 1)?;
|
||||
i = stmt.bind(&session_id, i)?;
|
||||
@@ -1462,7 +1497,8 @@ impl ThreadMetadataDb {
|
||||
i = stmt.bind(&archived, i)?;
|
||||
i = stmt.bind(&main_worktree_paths, i)?;
|
||||
i = stmt.bind(&main_worktree_paths_order, i)?;
|
||||
stmt.bind(&remote_connection, i)?;
|
||||
i = stmt.bind(&remote_connection, i)?;
|
||||
stmt.bind(&title_override, i)?;
|
||||
stmt.exec()
|
||||
})
|
||||
.await
|
||||
@@ -1619,6 +1655,7 @@ impl Column for ThreadMetadata {
|
||||
Column::column(statement, next)?;
|
||||
let (remote_connection_json, next): (Option<String>, i32) =
|
||||
Column::column(statement, next)?;
|
||||
let (title_override, next): (Option<String>, i32) = Column::column(statement, next)?;
|
||||
|
||||
let agent_id = agent_id
|
||||
.map(|id| AgentId::new(id))
|
||||
@@ -1676,6 +1713,9 @@ impl Column for ThreadMetadata {
|
||||
} else {
|
||||
Some(title.into())
|
||||
},
|
||||
title_override: title_override
|
||||
.filter(|t| !t.is_empty())
|
||||
.map(SharedString::from),
|
||||
updated_at,
|
||||
created_at,
|
||||
interacted_at,
|
||||
@@ -1765,6 +1805,7 @@ mod tests {
|
||||
} else {
|
||||
Some(title.to_string().into())
|
||||
},
|
||||
title_override: None,
|
||||
updated_at,
|
||||
created_at: Some(updated_at),
|
||||
interacted_at: None,
|
||||
@@ -1821,6 +1862,83 @@ mod tests {
|
||||
cx.run_until_parked();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_thread_metadata_title_prefers_override() {
|
||||
let mut metadata = make_metadata(
|
||||
"session-1",
|
||||
"Agent Generated Title",
|
||||
Utc::now(),
|
||||
PathList::default(),
|
||||
);
|
||||
metadata.title_override = Some("User Title".into());
|
||||
|
||||
assert_eq!(metadata.title().as_deref(), Some("User Title"));
|
||||
assert_eq!(metadata.display_title().as_ref(), "User Title");
|
||||
|
||||
metadata.title_override = None;
|
||||
assert_eq!(metadata.title().as_deref(), Some("Agent Generated Title"));
|
||||
assert_eq!(metadata.display_title().as_ref(), "Agent Generated Title");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_database_round_trips_title_override(_cx: &mut TestAppContext) {
|
||||
let now = Utc::now();
|
||||
let mut metadata = make_metadata(
|
||||
"session-1",
|
||||
"Agent Generated Title",
|
||||
now,
|
||||
PathList::new(&[Path::new("/project-a")]),
|
||||
);
|
||||
metadata.title_override = Some("User Title".into());
|
||||
|
||||
let thread = std::thread::current();
|
||||
let test_name = thread.name().unwrap_or("unknown_test");
|
||||
let db_name = format!("THREAD_METADATA_DB_{}", test_name);
|
||||
let db = ThreadMetadataDb(gpui::block_on(db::open_test_db::<ThreadMetadataDb>(
|
||||
&db_name,
|
||||
)));
|
||||
|
||||
db.save(metadata).await.unwrap();
|
||||
|
||||
let rows = db.list().unwrap();
|
||||
assert_eq!(rows.len(), 1);
|
||||
assert_eq!(rows[0].title.as_deref(), Some("Agent Generated Title"));
|
||||
assert_eq!(rows[0].title_override.as_deref(), Some("User Title"));
|
||||
assert_eq!(rows[0].title().as_deref(), Some("User Title"));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_store_set_title_override_updates_cached_metadata(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let metadata = make_metadata(
|
||||
"session-1",
|
||||
"Agent Generated Title",
|
||||
Utc::now(),
|
||||
PathList::default(),
|
||||
);
|
||||
let thread_id = metadata.thread_id;
|
||||
|
||||
cx.update(|cx| {
|
||||
let store = ThreadMetadataStore::global(cx);
|
||||
store.update(cx, |store, cx| {
|
||||
store.save(metadata, cx);
|
||||
store.set_title_override(thread_id, "User Title".into(), cx);
|
||||
});
|
||||
});
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
cx.update(|cx| {
|
||||
let store = ThreadMetadataStore::global(cx);
|
||||
let store = store.read(cx);
|
||||
let metadata = store.entry(thread_id).expect("metadata should be cached");
|
||||
assert_eq!(metadata.title.as_deref(), Some("Agent Generated Title"));
|
||||
assert_eq!(metadata.title_override.as_deref(), Some("User Title"));
|
||||
assert_eq!(metadata.display_title().as_ref(), "User Title");
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_store_initializes_cache_from_database(cx: &mut TestAppContext) {
|
||||
let first_paths = PathList::new(&[Path::new("/project-a")]);
|
||||
@@ -1947,6 +2065,7 @@ mod tests {
|
||||
session_id: Some(acp::SessionId::new("session-1")),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("First Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: updated_time,
|
||||
created_at: Some(updated_time),
|
||||
interacted_at: None,
|
||||
@@ -2031,6 +2150,7 @@ mod tests {
|
||||
session_id: Some(acp::SessionId::new("a-session-0")),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Existing Metadata".into()),
|
||||
title_override: None,
|
||||
updated_at: now - chrono::Duration::seconds(10),
|
||||
created_at: Some(now - chrono::Duration::seconds(10)),
|
||||
interacted_at: None,
|
||||
@@ -2156,6 +2276,7 @@ mod tests {
|
||||
session_id: Some(acp::SessionId::new("existing-session")),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Existing Metadata".into()),
|
||||
title_override: None,
|
||||
updated_at: existing_updated_at,
|
||||
created_at: Some(existing_updated_at),
|
||||
interacted_at: None,
|
||||
@@ -2900,6 +3021,7 @@ mod tests {
|
||||
session_id: Some(acp::SessionId::new("local-linked")),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Local Linked".into()),
|
||||
title_override: None,
|
||||
updated_at: now,
|
||||
created_at: Some(now),
|
||||
interacted_at: None,
|
||||
@@ -2913,6 +3035,7 @@ mod tests {
|
||||
session_id: Some(acp::SessionId::new("remote-linked")),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Remote Linked".into()),
|
||||
title_override: None,
|
||||
updated_at: now - chrono::Duration::seconds(1),
|
||||
created_at: Some(now - chrono::Duration::seconds(1)),
|
||||
interacted_at: None,
|
||||
|
||||
@@ -377,6 +377,7 @@ fn save_thread_metadata(
|
||||
session_id: Some(session_id),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title,
|
||||
title_override: None,
|
||||
updated_at,
|
||||
created_at,
|
||||
interacted_at,
|
||||
@@ -412,6 +413,7 @@ fn save_thread_metadata_with_main_paths(
|
||||
session_id: Some(session_id),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some(title),
|
||||
title_override: None,
|
||||
updated_at,
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -891,6 +893,7 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
worktree_paths: WorktreePaths::default(),
|
||||
title: Some("Completed thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
interacted_at: None,
|
||||
@@ -917,6 +920,7 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
worktree_paths: WorktreePaths::default(),
|
||||
title: Some("Running thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
interacted_at: None,
|
||||
@@ -943,6 +947,7 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
worktree_paths: WorktreePaths::default(),
|
||||
title: Some("Error thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
interacted_at: None,
|
||||
@@ -970,6 +975,7 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
worktree_paths: WorktreePaths::default(),
|
||||
title: Some("Waiting thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
interacted_at: None,
|
||||
@@ -997,6 +1003,7 @@ async fn test_visible_entries_as_strings(cx: &mut TestAppContext) {
|
||||
agent_id: AgentId::new("zed-agent"),
|
||||
worktree_paths: WorktreePaths::default(),
|
||||
title: Some("Notified thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: Some(Utc::now()),
|
||||
interacted_at: None,
|
||||
@@ -4986,6 +4993,7 @@ async fn test_sidebar_keeps_multi_root_thread_with_stale_main_paths(cx: &mut Tes
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Stale Multi-Root Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -5066,6 +5074,7 @@ async fn test_activate_archived_thread_with_saved_paths_activates_matching_works
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Archived Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -5135,6 +5144,7 @@ async fn test_activate_archived_thread_cwd_fallback_with_matching_workspace(
|
||||
session_id: Some(acp::SessionId::new(Arc::from("unknown-session"))),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("CWD Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -5202,6 +5212,7 @@ async fn test_activate_archived_thread_no_paths_no_cwd_uses_active_workspace(
|
||||
session_id: Some(acp::SessionId::new(Arc::from("no-context-session"))),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Contextless Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -5259,6 +5270,7 @@ async fn test_activate_archived_thread_saved_paths_opens_new_workspace(cx: &mut
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("New WS Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -5315,6 +5327,7 @@ async fn test_activate_archived_thread_reuses_workspace_in_another_window(cx: &m
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Cross Window Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -5393,6 +5406,7 @@ async fn test_activate_archived_thread_reuses_workspace_in_another_window_with_t
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Cross Window Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -5475,6 +5489,7 @@ async fn test_activate_archived_thread_prefers_current_window_for_matching_paths
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Current Window Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -6412,6 +6427,7 @@ async fn test_archive_last_worktree_thread_not_blocked_by_remote_thread_at_same_
|
||||
session_id: Some(acp::SessionId::new(Arc::from("remote-wt-thread"))),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Remote Worktree Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: chrono::TimeZone::with_ymd_and_hms(&Utc, 2024, 1, 1, 0, 0, 0).unwrap(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -7226,6 +7242,7 @@ async fn test_unarchive_first_thread_in_group_does_not_create_spurious_draft(
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Unarchived Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -7319,6 +7336,7 @@ async fn test_unarchive_into_new_workspace_does_not_create_duplicate_real_thread
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Unarchived Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -7545,6 +7563,7 @@ async fn test_unarchive_into_inactive_existing_workspace_does_not_leave_active_d
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Restored In Inactive Workspace".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -8395,6 +8414,7 @@ async fn test_unarchive_linked_worktree_thread_into_project_group_shows_only_res
|
||||
session_id: Some(session_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Unarchived Linked Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: Utc::now(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -8945,6 +8965,7 @@ async fn test_legacy_thread_with_canonical_path_opens_main_repo_workspace(cx: &m
|
||||
session_id: Some(legacy_session.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Legacy Main Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: chrono::TimeZone::with_ymd_and_hms(&Utc, 2024, 1, 1, 0, 0, 0).unwrap(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -9934,6 +9955,7 @@ mod property_test {
|
||||
session_id: Some(session_id),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some(title),
|
||||
title_override: None,
|
||||
updated_at,
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -10846,6 +10868,7 @@ async fn test_remote_project_integration_does_not_briefly_render_as_separate_pro
|
||||
session_id: Some(remote_thread_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Worktree Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: chrono::TimeZone::with_ymd_and_hms(&Utc, 2024, 1, 1, 0, 0, 1).unwrap(),
|
||||
created_at: None,
|
||||
interacted_at: None,
|
||||
@@ -11567,6 +11590,7 @@ async fn test_remote_archive_thread_with_active_connection(
|
||||
session_id: Some(wt_thread_id.clone()),
|
||||
agent_id: agent::ZED_AGENT_ID.clone(),
|
||||
title: Some("Worktree Thread".into()),
|
||||
title_override: None,
|
||||
updated_at: chrono::TimeZone::with_ymd_and_hms(&chrono::Utc, 2024, 1, 1, 0, 0, 0)
|
||||
.unwrap(),
|
||||
created_at: None,
|
||||
|
||||
Reference in new issue
Block a user