diff --git a/codex-rs/tui/src/app/agents_overview_tests.rs b/codex-rs/tui/src/app/agents_overview_tests.rs index ce5f219883..e0d8ba0ae0 100644 --- a/codex-rs/tui/src/app/agents_overview_tests.rs +++ b/codex-rs/tui/src/app/agents_overview_tests.rs @@ -430,7 +430,7 @@ async fn shared_overview_seeds_once_and_retains_locally_resumed_history() -> Res let mut app = make_test_app().await; trust_fixture_folders(&mut app); let mut ids = Vec::new(); - for day in 1..=22 { + for day in 1..=12 { let source = match day { 3 => codex_protocol::protocol::SessionSource::Custom("atlas".to_string()), 4 => codex_protocol::protocol::SessionSource::Custom("chatgpt".to_string()), @@ -457,13 +457,13 @@ async fn shared_overview_seeds_once_and_retains_locally_resumed_history() -> Res } let message_path = app_test_support::rollout_path( &app.config.codex_home, - "2025-01-21T12-00-00", - &ids[20].to_string(), + "2025-01-11T12-00-00", + &ids[10].to_string(), ); let mut history = std::fs::read_to_string(&message_path)?; history.push_str( &serde_json::json!({ - "timestamp": "2025-01-21T12:00:01Z", + "timestamp": "2025-01-11T12:00:01Z", "type": "event_msg", "payload": { "type": "agent_message", "message": "Found the regression in the parser." } }) @@ -473,7 +473,7 @@ async fn shared_overview_seeds_once_and_retains_locally_resumed_history() -> Res std::fs::write(message_path, history)?; let config = app.config.clone(); let mut app_server = Box::pin(crate::start_embedded_app_server_for_picker(&config)).await?; - for thread_id in [ids[0], ids[21]] { + for thread_id in [ids[0], ids[11]] { app_server .resume_thread( &app.local_settings, @@ -486,8 +486,8 @@ async fn shared_overview_seeds_once_and_retains_locally_resumed_history() -> Res // A newer rollout missing from the index must not trigger a startup filesystem scan. app_test_support::create_fake_rollout_with_source( &app.config.codex_home, - "2025-01-23T12-00-00", - "2025-01-23T12:00:00Z", + "2025-01-13T12-00-00", + "2025-01-13T12:00:00Z", "Unindexed task", Some(&app.config.model_provider_id), /*git_info*/ None, @@ -511,9 +511,9 @@ async fn shared_overview_seeds_once_and_retains_locally_resumed_history() -> Res assert_eq!(retained, expected); assert_eq!( app.agents_overview.last_messages, - HashMap::from([(ids[20], "Found the regression in the parser.".to_string())]) + HashMap::from([(ids[10], "Found the regression in the parser.".to_string())]) ); - let thread = app.agents_overview.threads[&ids[20]].as_ref().unwrap(); + let thread = app.agents_overview.threads[&ids[10]].as_ref().unwrap(); assert_eq!(thread.status, ThreadStatus::NotLoaded); let created = app_server.start_thread(&config).await?.session.thread_id; @@ -596,6 +596,12 @@ async fn shared_overview_seeds_once_and_retains_locally_resumed_history() -> Res finish_overview_refresh(&mut restarted, &app_server, &mut event_rx).await; let retained: HashSet<_> = restarted.agents_overview.threads.keys().copied().collect(); assert_eq!(retained, recent_ids); + restarted.open_agents_overview(&app_server); + insta::assert_snapshot!( + "agents_overview_recent_sessions", + render_bottom_popup(&restarted.chat_widget, /*width*/ 80) + .replace(&test_path_display("/"), "/") + ); app_server.shutdown().await?; Ok(()) } diff --git a/codex-rs/tui/src/app/agents_overview_threads.rs b/codex-rs/tui/src/app/agents_overview_threads.rs index 04f1a0a6f5..a8e3043969 100644 --- a/codex-rs/tui/src/app/agents_overview_threads.rs +++ b/codex-rs/tui/src/app/agents_overview_threads.rs @@ -34,6 +34,9 @@ use codex_protocol::protocol::SubAgentSource; use std::collections::HashMap; use uuid::Uuid; +// Seed the command center with this many recent sessions, in addition to loaded sessions. +const RECENT_SESSION_LIMIT: usize = 10; + impl App { pub(super) fn track_agents_overview_notification(&mut self, notification: &ServerNotification) { let ServerNotificationThreadTarget::Thread(thread_id) = @@ -226,14 +229,14 @@ impl App { let mut recent = Vec::new(); let mut cursor = None; let mut sort_key = ThreadSortKey::RecencyAt; - while recent.len() < 20 { + while recent.len() < RECENT_SESSION_LIMIT { let page = match request_handle .request_typed::(ClientRequest::ThreadList { request_id: RequestId::String(Uuid::new_v4().to_string()), params: ThreadListParams { originators: None, cursor, - limit: Some(20), + limit: Some(RECENT_SESSION_LIMIT as u32), sort_key: Some(sort_key), sort_direction: None, model_providers: Some(Vec::new()), @@ -276,7 +279,7 @@ impl App { ) ) }) - .take(20 - recent.len()), + .take(RECENT_SESSION_LIMIT - recent.len()), ); cursor = page.next_cursor; if cursor.is_none() { @@ -301,7 +304,7 @@ impl App { .cmp(&left.recency_at.unwrap_or(left.updated_at)) .then_with(|| right.id.cmp(&left.id)) }); - recent.truncate(20); + recent.truncate(RECENT_SESSION_LIMIT); Ok::<_, TypedRequestError>(recent) }; let (loaded, recent) = tokio::join!(loaded, recent); diff --git a/codex-rs/tui/src/app/snapshots/codex_tui__app__agents_overview__tests__agents_overview_recent_sessions.snap b/codex-rs/tui/src/app/snapshots/codex_tui__app__agents_overview__tests__agents_overview_recent_sessions.snap new file mode 100644 index 0000000000..8f7970d365 --- /dev/null +++ b/codex-rs/tui/src/app/snapshots/codex_tui__app__agents_overview__tests__agents_overview_recent_sessions.snap @@ -0,0 +1,28 @@ +--- +source: tui/src/app/agents_overview_tests.rs +expression: "render_bottom_popup(&restarted.chat_widget,\n80).replace(&test_path_display(\"/\"), \"/\")" +--- + Agent command center + 0 need input 0 working 0 ready + ──────────────────────────────────────────────────────────────────────────── + / 10 + › ✓ Task 12 Finished + ✓ Task 11 Finished + ✓ Task 10 Finished + ✓ Task 9 Finished + ✓ Task 8 Finished + ✓ Task 7 Finished + ✓ Task 6 Finished + ✓ Task 5 Finished + ✓ Task 4 Finished + ✓ Task 3 Finished + + + + + + + + + ↑↓ navigate o resume enter open n new w new worktree f search + g group: project r rename x stop h hide a archive del delete ctrl+c quit