diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs index c793b8a43aadcc6c8befed3a368ac2580693023a..ced8850cbb2df8b2d68b9967eb445f9490e66821 100644 --- a/crates/sidebar/src/sidebar.rs +++ b/crates/sidebar/src/sidebar.rs @@ -847,10 +847,6 @@ impl Sidebar { // Merge live info into threads and update notification state // in a single pass. - let is_active_workspace = active_workspace - .as_ref() - .is_some_and(|active| active == workspace); - for thread in &mut threads { let session_id = &thread.session_info.session_id; @@ -865,16 +861,23 @@ impl Sidebar { thread.diff_stats = info.diff_stats; } + let is_thread_workspace_active = match &thread.workspace { + ThreadEntryWorkspace::Open(thread_workspace) => active_workspace + .as_ref() + .is_some_and(|active| active == thread_workspace), + ThreadEntryWorkspace::Closed(_) => false, + }; + if thread.is_background && thread.status == AgentThreadStatus::Completed { notified_threads.insert(session_id.clone()); } else if thread.status == AgentThreadStatus::Completed - && !is_active_workspace + && !is_thread_workspace_active && old_statuses.get(session_id) == Some(&AgentThreadStatus::Running) { notified_threads.insert(session_id.clone()); } - if is_active_workspace && !thread.is_background { + if is_thread_workspace_active && !thread.is_background { notified_threads.remove(session_id); } } @@ -5304,6 +5307,121 @@ mod tests { ); } + #[gpui::test] + async fn test_absorbed_worktree_completion_triggers_notification(cx: &mut TestAppContext) { + agent_ui::test_support::init_test(cx); + cx.update(|cx| { + cx.update_flags(false, vec!["agent-v2".into()]); + ThreadStore::init_global(cx); + SidebarThreadMetadataStore::init_global(cx); + language_model::LanguageModelRegistry::test(cx); + prompt_store::init(cx); + }); + + let fs = FakeFs::new(cx.executor()); + + fs.insert_tree( + "/project", + serde_json::json!({ + ".git": { + "worktrees": { + "feature-a": { + "commondir": "../../", + "HEAD": "ref: refs/heads/feature-a", + }, + }, + }, + "src": {}, + }), + ) + .await; + + fs.insert_tree( + "/wt-feature-a", + serde_json::json!({ + ".git": "gitdir: /project/.git/worktrees/feature-a", + "src": {}, + }), + ) + .await; + + fs.with_git_state(std::path::Path::new("/project/.git"), false, |state| { + state.worktrees.push(git::repository::Worktree { + path: std::path::PathBuf::from("/wt-feature-a"), + ref_name: "refs/heads/feature-a".into(), + sha: "aaa".into(), + }); + }) + .unwrap(); + + cx.update(|cx| ::set_global(fs.clone(), cx)); + + let main_project = project::Project::test(fs.clone(), ["/project".as_ref()], cx).await; + let worktree_project = + project::Project::test(fs.clone(), ["/wt-feature-a".as_ref()], cx).await; + + main_project + .update(cx, |p, cx| p.git_scans_complete(cx)) + .await; + worktree_project + .update(cx, |p, cx| p.git_scans_complete(cx)) + .await; + + let (multi_workspace, cx) = cx.add_window_view(|window, cx| { + MultiWorkspace::test_new(main_project.clone(), window, cx) + }); + + let worktree_workspace = multi_workspace.update_in(cx, |mw, window, cx| { + mw.test_add_workspace(worktree_project.clone(), window, cx) + }); + + let worktree_panel = add_agent_panel(&worktree_workspace, &worktree_project, cx); + + multi_workspace.update_in(cx, |mw, window, cx| { + mw.activate_index(0, window, cx); + }); + + let sidebar = setup_sidebar(&multi_workspace, cx); + + let connection = StubAgentConnection::new(); + open_thread_with_connection(&worktree_panel, connection.clone(), cx); + send_message(&worktree_panel, cx); + + let session_id = active_session_id(&worktree_panel, cx); + let wt_paths = PathList::new(&[std::path::PathBuf::from("/wt-feature-a")]); + save_test_thread_metadata(&session_id, wt_paths, cx).await; + + cx.update(|_, cx| { + connection.send_update( + session_id.clone(), + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk::new("working...".into())), + cx, + ); + }); + cx.run_until_parked(); + + assert_eq!( + visible_entries_as_strings(&sidebar, cx), + vec![ + "v [project]", + " [+ New Thread]", + " Hello {wt-feature-a} * (running)", + ] + ); + + connection.end_turn(session_id, acp::StopReason::EndTurn); + cx.run_until_parked(); + + assert_eq!( + visible_entries_as_strings(&sidebar, cx), + vec![ + "v [project]", + " [+ New Thread]", + " Hello {wt-feature-a} * (!)", + ] + ); + } + #[gpui::test] async fn test_clicking_worktree_thread_opens_workspace_when_none_exists( cx: &mut TestAppContext,