sidebar: Fix subagent threads showing up (#51739)

Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- N/A
This commit is contained in:
Bennet Bo Fenner authored and GitHub committed 2026-03-17 09:29:37 +00:00
1 parent b41b8dd7c6
commit 7cbc752cab
1 file changed
+103
@@ -159,6 +159,11 @@ impl ThreadMetadataStore {
let weak_store = cx.weak_entity();
cx.observe_new::<acp_thread::AcpThread>(move |thread, _window, cx| {
// Don't track subagent threads in the sidebar.
if thread.parent_session_id().is_some() {
return;
}
let thread_entity = cx.entity();
cx.on_release({
@@ -195,6 +200,11 @@ impl ThreadMetadataStore {
event: &acp_thread::AcpThreadEvent,
cx: &mut Context<Self>,
) {
// Don't track subagent threads in the sidebar.
if thread.read(cx).parent_session_id().is_some() {
return;
}
match event {
acp_thread::AcpThreadEvent::NewEntry
| acp_thread::AcpThreadEvent::EntryUpdated(_)
@@ -365,8 +375,17 @@ impl Column for ThreadMetadata {
#[cfg(test)]
mod tests {
use super::*;
use acp_thread::{AgentConnection, StubAgentConnection};
use action_log::ActionLog;
use agent::DbThread;
use agent_client_protocol as acp;
use feature_flags::FeatureFlagAppExt;
use gpui::TestAppContext;
use project::FakeFs;
use project::Project;
use std::path::Path;
use std::rc::Rc;
use util::path_list::PathList;
fn make_db_thread(title: &str, updated_at: DateTime<Utc>) -> DbThread {
DbThread {
@@ -525,4 +544,88 @@ mod tests {
assert_eq!(list.len(), 1);
assert_eq!(list[0].session_id.0.as_ref(), "existing-session");
}
#[gpui::test]
async fn test_subagent_threads_excluded_from_sidebar_metadata(cx: &mut TestAppContext) {
cx.update(|cx| {
let settings_store = settings::SettingsStore::test(cx);
cx.set_global(settings_store);
cx.update_flags(true, vec!["agent-v2".to_string()]);
ThreadStore::init_global(cx);
ThreadMetadataStore::init_global(cx);
});
let fs = FakeFs::new(cx.executor());
let project = Project::test(fs, None::<&Path>, cx).await;
let connection = Rc::new(StubAgentConnection::new());
// Create a regular (non-subagent) AcpThread.
let regular_thread = cx
.update(|cx| {
connection
.clone()
.new_session(project.clone(), PathList::default(), cx)
})
.await
.unwrap();
let regular_session_id = cx.read(|cx| regular_thread.read(cx).session_id().clone());
// Set a title on the regular thread to trigger a save via handle_thread_update.
cx.update(|cx| {
regular_thread.update(cx, |thread, cx| {
thread.set_title("Regular Thread".into(), cx).detach();
});
});
cx.run_until_parked();
// Create a subagent AcpThread
let subagent_session_id = acp::SessionId::new("subagent-session");
let subagent_thread = cx.update(|cx| {
let action_log = cx.new(|_| ActionLog::new(project.clone()));
cx.new(|cx| {
acp_thread::AcpThread::new(
Some(regular_session_id.clone()),
"Subagent Thread",
None,
connection.clone(),
project.clone(),
action_log,
subagent_session_id.clone(),
watch::Receiver::constant(acp::PromptCapabilities::new()),
cx,
)
})
});
// Set a title on the subagent thread to trigger handle_thread_update.
cx.update(|cx| {
subagent_thread.update(cx, |thread, cx| {
thread
.set_title("Subagent Thread Title".into(), cx)
.detach();
});
});
cx.run_until_parked();
// List all metadata from the store.
let metadata_list = cx.update(|cx| {
let store = ThreadMetadataStore::global(cx);
store.read(cx).list(cx)
});
let list = metadata_list.await.unwrap();
// The subagent thread should NOT appear in the sidebar metadata.
// Only the regular thread should be listed.
assert_eq!(
list.len(),
1,
"Expected only the regular thread in sidebar metadata, \
but found {} entries (subagent threads are leaking into the sidebar)",
list.len(),
);
assert_eq!(list[0].session_id, regular_session_id);
assert_eq!(list[0].title.as_ref(), "Regular Thread");
}
}