Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 6 additions & 136 deletions crates/agent/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,13 @@ pub type DbMessage = crate::Message;
pub type DbSummary = crate::legacy_thread::DetailedSummaryState;
pub type DbLanguageModel = crate::legacy_thread::SerializedLanguageModel;

/// Metadata about the git worktree associated with an agent thread.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentGitWorktreeInfo {
/// The branch name in the git worktree.
pub branch: String,
/// Absolute path to the git worktree on disk.
pub worktree_path: std::path::PathBuf,
/// The base branch/commit the worktree was created from.
pub base_ref: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DbThreadMetadata {
pub id: acp::SessionId,
pub parent_session_id: Option<acp::SessionId>,
#[serde(alias = "summary")]
pub title: SharedString,
pub updated_at: DateTime<Utc>,
/// Denormalized from `DbThread::git_worktree_info.branch` for efficient
/// listing without decompressing thread data. The blob is the source of
/// truth; this column is populated on save for query convenience.
pub worktree_branch: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
Expand All @@ -68,8 +53,6 @@ pub struct DbThread {
pub imported: bool,
#[serde(default)]
pub subagent_context: Option<crate::SubagentContext>,
#[serde(default)]
pub git_worktree_info: Option<AgentGitWorktreeInfo>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down Expand Up @@ -108,7 +91,6 @@ impl SharedThread {
profile: None,
imported: true,
subagent_context: None,
git_worktree_info: None,
}
}

Expand Down Expand Up @@ -283,7 +265,6 @@ impl DbThread {
profile: thread.profile,
imported: false,
subagent_context: None,
git_worktree_info: None,
})
}
}
Expand Down Expand Up @@ -388,13 +369,6 @@ impl ThreadsDatabase {
s().ok();
}

if let Ok(mut s) = connection.exec(indoc! {"
ALTER TABLE threads ADD COLUMN worktree_branch TEXT
"})
{
s().ok();
}

let db = Self {
executor,
connection: Arc::new(Mutex::new(connection)),
Expand Down Expand Up @@ -423,10 +397,6 @@ impl ThreadsDatabase {
.subagent_context
.as_ref()
.map(|ctx| ctx.parent_thread_id.0.clone());
let worktree_branch = thread
.git_worktree_info
.as_ref()
.map(|info| info.branch.clone());
let json_data = serde_json::to_string(&SerializedThread {
thread,
version: DbThread::VERSION,
Expand All @@ -438,19 +408,11 @@ impl ThreadsDatabase {
let data_type = DataType::Zstd;
let data = compressed;

let mut insert = connection.exec_bound::<(Arc<str>, Option<Arc<str>>, Option<String>, String, String, DataType, Vec<u8>)>(indoc! {"
INSERT OR REPLACE INTO threads (id, parent_id, worktree_branch, summary, updated_at, data_type, data) VALUES (?, ?, ?, ?, ?, ?, ?)
let mut insert = connection.exec_bound::<(Arc<str>, Option<Arc<str>>, String, String, DataType, Vec<u8>)>(indoc! {"
INSERT OR REPLACE INTO threads (id, parent_id, summary, updated_at, data_type, data) VALUES (?, ?, ?, ?, ?, ?)
"})?;

insert((
id.0,
parent_id,
worktree_branch,
title,
updated_at,
data_type,
data,
))?;
insert((id.0, parent_id, title, updated_at, data_type, data))?;

Ok(())
}
Expand All @@ -462,20 +424,19 @@ impl ThreadsDatabase {
let connection = connection.lock();

let mut select = connection
.select_bound::<(), (Arc<str>, Option<Arc<str>>, Option<String>, String, String)>(indoc! {"
SELECT id, parent_id, worktree_branch, summary, updated_at FROM threads ORDER BY updated_at DESC
.select_bound::<(), (Arc<str>, Option<Arc<str>>, String, String)>(indoc! {"
SELECT id, parent_id, summary, updated_at FROM threads ORDER BY updated_at DESC
"})?;

let rows = select(())?;
let mut threads = Vec::new();

for (id, parent_id, worktree_branch, summary, updated_at) in rows {
for (id, parent_id, summary, updated_at) in rows {
threads.push(DbThreadMetadata {
id: acp::SessionId::new(id),
parent_session_id: parent_id.map(acp::SessionId::new),
title: summary.into(),
updated_at: DateTime::parse_from_rfc3339(&updated_at)?.with_timezone(&Utc),
worktree_branch,
});
}

Expand Down Expand Up @@ -609,7 +570,6 @@ mod tests {
profile: None,
imported: false,
subagent_context: None,
git_worktree_info: None,
}
}

Expand Down Expand Up @@ -753,94 +713,4 @@ mod tests {
"Regular threads should have no subagent_context"
);
}

#[gpui::test]
async fn test_git_worktree_info_roundtrip(cx: &mut TestAppContext) {
let database = ThreadsDatabase::new(cx.executor()).unwrap();

let thread_id = session_id("worktree-thread");
let mut thread = make_thread(
"Worktree Thread",
Utc.with_ymd_and_hms(2024, 6, 15, 12, 0, 0).unwrap(),
);
thread.git_worktree_info = Some(AgentGitWorktreeInfo {
branch: "zed/agent/a4Xiu".to_string(),
worktree_path: std::path::PathBuf::from("/repo/worktrees/zed/agent/a4Xiu"),
base_ref: "main".to_string(),
});

database
.save_thread(thread_id.clone(), thread)
.await
.unwrap();

let loaded = database
.load_thread(thread_id)
.await
.unwrap()
.expect("thread should exist");

let info = loaded
.git_worktree_info
.expect("git_worktree_info should be restored");
assert_eq!(info.branch, "zed/agent/a4Xiu");
assert_eq!(
info.worktree_path,
std::path::PathBuf::from("/repo/worktrees/zed/agent/a4Xiu")
);
assert_eq!(info.base_ref, "main");
}

#[gpui::test]
async fn test_session_list_includes_worktree_meta(cx: &mut TestAppContext) {
let database = ThreadsDatabase::new(cx.executor()).unwrap();

// Save a thread with worktree info
let worktree_id = session_id("wt-thread");
let mut worktree_thread = make_thread(
"With Worktree",
Utc.with_ymd_and_hms(2024, 6, 15, 12, 0, 0).unwrap(),
);
worktree_thread.git_worktree_info = Some(AgentGitWorktreeInfo {
branch: "zed/agent/bR9kz".to_string(),
worktree_path: std::path::PathBuf::from("/repo/worktrees/zed/agent/bR9kz"),
base_ref: "develop".to_string(),
});

database
.save_thread(worktree_id.clone(), worktree_thread)
.await
.unwrap();

// Save a thread without worktree info
let plain_id = session_id("plain-thread");
let plain_thread = make_thread(
"Without Worktree",
Utc.with_ymd_and_hms(2024, 6, 15, 11, 0, 0).unwrap(),
);

database
.save_thread(plain_id.clone(), plain_thread)
.await
.unwrap();

// List threads and verify worktree_branch is populated correctly
let threads = database.list_threads().await.unwrap();
assert_eq!(threads.len(), 2);

let wt_entry = threads
.iter()
.find(|t| t.id == worktree_id)
.expect("should find worktree thread");
assert_eq!(wt_entry.worktree_branch.as_deref(), Some("zed/agent/bR9kz"));

let plain_entry = threads
.iter()
.find(|t| t.id == plain_id)
.expect("should find plain thread");
assert!(
plain_entry.worktree_branch.is_none(),
"plain thread should have no worktree_branch"
);
}
}
13 changes: 4 additions & 9 deletions crates/agent/src/thread.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
AgentGitWorktreeInfo, ContextServerRegistry, CopyPathTool, CreateDirectoryTool,
DbLanguageModel, DbThread, DeletePathTool, DiagnosticsTool, EditFileTool, FetchTool,
FindPathTool, GrepTool, ListDirectoryTool, MovePathTool, NowTool, OpenTool, ProjectSnapshot,
ReadFileTool, RestoreFileFromDiskTool, SaveFileTool, SpawnAgentTool, StreamingEditFileTool,
ContextServerRegistry, CopyPathTool, CreateDirectoryTool, DbLanguageModel, DbThread,
DeletePathTool, DiagnosticsTool, EditFileTool, FetchTool, FindPathTool, GrepTool,
ListDirectoryTool, MovePathTool, NowTool, OpenTool, ProjectSnapshot, ReadFileTool,
RestoreFileFromDiskTool, SaveFileTool, SpawnAgentTool, StreamingEditFileTool,
SystemPromptTemplate, Template, Templates, TerminalTool, ToolPermissionDecision, WebSearchTool,
decide_permission_from_settings,
};
Expand Down Expand Up @@ -900,8 +900,6 @@ pub struct Thread {
subagent_context: Option<SubagentContext>,
/// Weak references to running subagent threads for cancellation propagation
running_subagents: Vec<WeakEntity<Thread>>,
/// Git worktree info if this thread is running in an agent worktree.
git_worktree_info: Option<AgentGitWorktreeInfo>,
}

impl Thread {
Expand Down Expand Up @@ -992,7 +990,6 @@ impl Thread {
imported: false,
subagent_context: None,
running_subagents: Vec::new(),
git_worktree_info: None,
}
}

Expand Down Expand Up @@ -1217,7 +1214,6 @@ impl Thread {
imported: db_thread.imported,
subagent_context: db_thread.subagent_context,
running_subagents: Vec::new(),
git_worktree_info: db_thread.git_worktree_info,
}
}

Expand All @@ -1238,7 +1234,6 @@ impl Thread {
profile: Some(self.profile_id.clone()),
imported: self.imported,
subagent_context: self.subagent_context.clone(),
git_worktree_info: self.git_worktree_info.clone(),
};

cx.background_spawn(async move {
Expand Down
1 change: 0 additions & 1 deletion crates/agent/src/thread_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ mod tests {
profile: None,
imported: false,
subagent_context: None,
git_worktree_info: None,
}
}

Expand Down