diff --git a/crates/agent_ui/src/thread_metadata_store.rs b/crates/agent_ui/src/thread_metadata_store.rs index 234eb6221fa1cd62e2e864e657868dc513fc9299..987dbf4ba754292056a2c3a1aec3d0b61fae7177 100644 --- a/crates/agent_ui/src/thread_metadata_store.rs +++ b/crates/agent_ui/src/thread_metadata_store.rs @@ -25,7 +25,7 @@ pub use project::WorktreePaths; use project::{AgentId, linked_worktree_short_name}; use remote::{RemoteConnectionOptions, same_remote_connection_identity}; use ui::{App, Context, SharedString, ThreadItemWorktreeInfo, WorktreeKind}; -use util::ResultExt as _; +use util::{ResultExt as _, debug_panic}; use workspace::{PathList, SerializedWorkspaceLocation, WorkspaceDb}; use crate::DEFAULT_THREAD_TITLE; @@ -593,20 +593,7 @@ impl ThreadMetadataStore { this.threads_by_session.clear(); for row in rows { - if let Some(sid) = &row.session_id { - this.threads_by_session.insert(sid.clone(), row.thread_id); - } - this.threads_by_paths - .entry(row.folder_paths().clone()) - .or_default() - .insert(row.thread_id); - if !row.main_worktree_paths().is_empty() { - this.threads_by_main_paths - .entry(row.main_worktree_paths().clone()) - .or_default() - .insert(row.thread_id); - } - this.threads.insert(row.thread_id, row); + this.cache_thread_metadata(row); } cx.notify(); @@ -631,6 +618,11 @@ impl ThreadMetadataStore { } fn save_internal(&mut self, metadata: ThreadMetadata) { + if metadata.session_id.is_none() { + debug_panic!("cannot store thread metadata without a session_id"); + return; + }; + if let Some(thread) = self.threads.get(&metadata.thread_id) { if thread.folder_paths() != metadata.folder_paths() { if let Some(thread_ids) = self.threads_by_paths.get_mut(thread.folder_paths()) { @@ -649,10 +641,20 @@ impl ThreadMetadataStore { } } - if let Some(sid) = &metadata.session_id { - self.threads_by_session - .insert(sid.clone(), metadata.thread_id); - } + self.cache_thread_metadata(metadata.clone()); + self.pending_thread_ops_tx + .try_send(DbOperation::Upsert(metadata)) + .log_err(); + } + + fn cache_thread_metadata(&mut self, metadata: ThreadMetadata) { + let Some(session_id) = metadata.session_id.as_ref() else { + debug_panic!("cannot store thread metadata without a session_id"); + return; + }; + + self.threads_by_session + .insert(session_id.clone(), metadata.thread_id); self.threads.insert(metadata.thread_id, metadata.clone()); @@ -667,10 +669,6 @@ impl ThreadMetadataStore { .or_default() .insert(metadata.thread_id); } - - self.pending_thread_ops_tx - .try_send(DbOperation::Upsert(metadata)) - .log_err(); } pub fn update_working_directories( @@ -1254,6 +1252,19 @@ impl Domain for ThreadMetadataDb { DROP TABLE sidebar_threads; ALTER TABLE sidebar_threads_v2 RENAME TO sidebar_threads; ), + sql!( + DELETE FROM thread_archived_worktrees + WHERE thread_id IN ( + SELECT thread_id FROM sidebar_threads WHERE session_id IS NULL + ); + + DELETE FROM sidebar_threads WHERE session_id IS NULL; + + DELETE FROM archived_git_worktrees + WHERE id NOT IN ( + SELECT archived_worktree_id FROM thread_archived_worktrees + ); + ), ]; } @@ -1264,6 +1275,7 @@ impl ThreadMetadataDb { pub fn list_ids(&self) -> anyhow::Result> { self.select::( "SELECT thread_id FROM sidebar_threads \ + WHERE session_id IS NOT NULL \ ORDER BY updated_at DESC", )?() } @@ -1273,12 +1285,18 @@ impl ThreadMetadataDb { self.select::( "SELECT thread_id, session_id, agent_id, title, updated_at, created_at, folder_paths, folder_paths_order, archived, main_worktree_paths, main_worktree_paths_order, remote_connection \ FROM sidebar_threads \ + WHERE session_id IS NOT NULL \ ORDER BY updated_at DESC" )?() } /// Upsert metadata for a thread. pub async fn save(&self, row: ThreadMetadata) -> anyhow::Result<()> { + anyhow::ensure!( + row.session_id.is_some(), + "refusing to persist thread metadata without a session_id" + ); + let session_id = row.session_id.as_ref().map(|s| s.0.clone()); let agent_id = if row.agent_id.as_ref() == ZED_AGENT_ID.as_ref() { None @@ -3370,7 +3388,8 @@ mod tests { .unwrap()() .unwrap(); - // Run all migrations (0-7). sqlez skips 0-6 and runs only migration 7. + // Run all current migrations. sqlez skips the already-applied ones and + // runs the remaining migrations. connection .migrate( ThreadMetadataDb::NAME,