diff --git a/codex-rs/tui/src/app/agents_overview.rs b/codex-rs/tui/src/app/agents_overview.rs index 14faa045b0..8ca06f0463 100644 --- a/codex-rs/tui/src/app/agents_overview.rs +++ b/codex-rs/tui/src/app/agents_overview.rs @@ -15,6 +15,8 @@ mod loading; #[path = "agents_overview_selection.rs"] mod selection; +use super::agents_overview_discovery::is_overview_root; +use super::agents_overview_discovery::is_visible_in_agents_overview; use super::agents_overview_view::AgentsOverviewGroup; use super::agents_overview_view::AgentsOverviewRow; use super::agents_overview_view::AgentsOverviewView; @@ -257,11 +259,7 @@ impl App { if displayed_refresh && !self.agents_overview.threads.contains_key(&thread_id) && !self.agents_overview.hidden_threads.contains(&thread_id) - && thread.parent_thread_id.is_none() - && !matches!( - thread.source, - SessionSource::SubAgent(SubAgentSource::ThreadSpawn { .. }) - ) + && is_overview_root(&thread) { self.agents_overview.refill_count = self.agents_overview.refill_count.saturating_sub(1); @@ -423,7 +421,7 @@ impl App { threads.push(target.clone()); } } - threads.retain(|thread| !thread.ephemeral); + threads.retain(is_visible_in_agents_overview); for thread in &mut threads { if thread.parent_thread_id.is_none() && let SessionSource::SubAgent(SubAgentSource::ThreadSpawn { diff --git a/codex-rs/tui/src/app/agents_overview_discovery.rs b/codex-rs/tui/src/app/agents_overview_discovery.rs index 63e53ec2db..75e35dbcf3 100644 --- a/codex-rs/tui/src/app/agents_overview_discovery.rs +++ b/codex-rs/tui/src/app/agents_overview_discovery.rs @@ -74,7 +74,7 @@ pub(crate) async fn list_pinned_threads( Ok(page) => { let next_cursor = page.next_cursor.clone(); threads.extend(listed_threads(page).filter(|(thread, _)| { - is_overview_thread(thread) && supports_shared_pinning(&thread.source) + is_overview_root(thread) && supports_shared_pinning(&thread.source) })); let Some(next_cursor) = next_cursor else { break; @@ -93,8 +93,12 @@ pub(crate) async fn list_pinned_threads( Ok(Some(threads)) } -fn is_overview_thread(thread: &Thread) -> bool { - !thread.ephemeral +pub(super) fn is_visible_in_agents_overview(thread: &Thread) -> bool { + !thread.ephemeral && thread.attached_to_thread_id.is_none() +} + +pub(super) fn is_overview_root(thread: &Thread) -> bool { + is_visible_in_agents_overview(thread) && thread.parent_thread_id.is_none() && !matches!( thread.source, @@ -236,7 +240,7 @@ impl SourcePage { self.exhausted = page.next_cursor.is_none(); self.cursor = page.next_cursor.clone(); self.rows.extend( - listed_threads(page).filter(|(thread, _)| is_overview_thread(thread)), + listed_threads(page).filter(|(thread, _)| is_overview_root(thread)), ); return true; } diff --git a/codex-rs/tui/src/app/agents_overview_tests.rs b/codex-rs/tui/src/app/agents_overview_tests.rs index 0c521e5fb6..1ea48a6a60 100644 --- a/codex-rs/tui/src/app/agents_overview_tests.rs +++ b/codex-rs/tui/src/app/agents_overview_tests.rs @@ -1631,6 +1631,14 @@ async fn shared_overview_shows_only_root_sessions() { ); side.ephemeral = true; threads.push(side); + let mut attached = overview_thread( + ThreadId::new(), + /*parent_thread_id*/ None, + "Saved side chat", + ThreadStatus::Idle, + ); + attached.attached_to_thread_id = Some(first_root.to_string()); + threads.push(attached); let default_model = app .model_catalog .models diff --git a/codex-rs/tui/src/app/agents_overview_threads.rs b/codex-rs/tui/src/app/agents_overview_threads.rs index e4be36118d..0156409db1 100644 --- a/codex-rs/tui/src/app/agents_overview_threads.rs +++ b/codex-rs/tui/src/app/agents_overview_threads.rs @@ -5,6 +5,7 @@ use super::App; use super::agents_overview::AGENTS_OVERVIEW_VIEW_ID; use super::agents_overview_details::preview_agent_message; +use super::agents_overview_discovery::is_overview_root; use super::agents_overview_discovery::list_pinned_threads; use super::app_server_event_targets::ServerNotificationThreadTarget; use super::app_server_event_targets::server_notification_thread_target; @@ -15,7 +16,6 @@ use crate::chatwidget::ChatWidget; use codex_app_server_protocol::ClientRequest; use codex_app_server_protocol::RequestId; use codex_app_server_protocol::ServerNotification; -use codex_app_server_protocol::SessionSource; use codex_app_server_protocol::Thread; use codex_app_server_protocol::ThreadItemPayload; use codex_app_server_protocol::ThreadLoadedListParams; @@ -26,7 +26,6 @@ use codex_app_server_protocol::ThreadStatus; use codex_app_server_protocol::ThreadTurnsListParams; use codex_app_server_protocol::ThreadTurnsListResponse; use codex_protocol::ThreadId; -use codex_protocol::protocol::SubAgentSource; use futures::StreamExt; use std::collections::HashMap; use uuid::Uuid; @@ -61,12 +60,7 @@ impl App { self.agents_overview.removed_threads.insert(thread_id); self.agents_overview.unread.remove(&thread_id); if let Some(Some(thread)) = self.agents_overview.threads.remove(&thread_id) - && !thread.ephemeral - && thread.parent_thread_id.is_none() - && !matches!( - thread.source, - SessionSource::SubAgent(SubAgentSource::ThreadSpawn { .. }) - ) + && is_overview_root(&thread) && !self.agents_overview.hidden_threads.contains(&thread_id) { self.agents_overview.refill_count += 1;