From a88387556d4cf07103c399475ce653fcc442281f Mon Sep 17 00:00:00 2001 From: Bennet Bo Fenner Date: Tue, 17 Mar 2026 10:29:37 +0100 Subject: [PATCH] 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 --- crates/agent_ui/src/thread_metadata_store.rs | 103 +++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/crates/agent_ui/src/thread_metadata_store.rs b/crates/agent_ui/src/thread_metadata_store.rs index 66a9e05fbb294f253b3b25b782b35f3d503304e4..7a4d8b4bd193acca937e09fae5003019de50a682 100644 --- a/crates/agent_ui/src/thread_metadata_store.rs +++ b/crates/agent_ui/src/thread_metadata_store.rs @@ -159,6 +159,11 @@ impl ThreadMetadataStore { let weak_store = cx.weak_entity(); cx.observe_new::(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, ) { + // 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) -> 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"); + } }