From bc5a1b961e205420a8afbc6b136310281171ff94 Mon Sep 17 00:00:00 2001 From: canvrno-oai Date: Tue, 28 Apr 2026 10:35:10 -0700 Subject: [PATCH] Move local /resume cwd filtering into thread/list (#19931) Move local resume and fork cwd filtering to `thread/list` instead of filtering in the TUI. This makes the `/resume` menu feel slightly faster to load when working in repos with many historical threads, and centralizes the cwd filtering in app-server. **Affected:** - /resume from inside the TUI. - codex resume with no session ID and without --last - codex resume --all - codex fork with no session ID and without --last - codex fork --all **Not affected:** - codex resume - codex fork - codex resume --last - codex fork --last Steps to test performance improvement in a real Codex environment: - Launch `codex resume` using compiled binary in a directory that has seen many threads. - Launch `codex resume` using release binary in same directory. - Observe difference in time-to-full-page as threads load. --- codex-rs/tui/src/resume_picker.rs | 75 ++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/codex-rs/tui/src/resume_picker.rs b/codex-rs/tui/src/resume_picker.rs index 43fa6c948..24c46e41d 100644 --- a/codex-rs/tui/src/resume_picker.rs +++ b/codex-rs/tui/src/resume_picker.rs @@ -143,8 +143,8 @@ struct PickerPage { /// sessions appear during pagination. /// /// Filtering happens in two layers: -/// 1. Provider and source filtering at the backend. -/// 2. Working-directory filtering at the picker (unless `--all` is passed). +/// 1. Provider, source, and eligible working-directory filtering at the backend. +/// 2. Typed search filtering over loaded rows in the picker. pub async fn run_resume_picker_with_app_server( tui: &mut Tui, config: &Config, @@ -154,11 +154,12 @@ pub async fn run_resume_picker_with_app_server( ) -> Result { let (bg_tx, bg_rx) = mpsc::unbounded_channel(); let is_remote = app_server.is_remote(); - let cwd_filter = if show_all { - None - } else { - app_server.remote_cwd_override().map(Path::to_path_buf) - }; + let cwd_filter = picker_cwd_filter( + config.cwd.as_path(), + show_all, + is_remote, + app_server.remote_cwd_override(), + ); run_session_picker_with_loader( tui, config, @@ -179,11 +180,12 @@ pub async fn run_fork_picker_with_app_server( ) -> Result { let (bg_tx, bg_rx) = mpsc::unbounded_channel(); let is_remote = app_server.is_remote(); - let cwd_filter = if show_all { - None - } else { - app_server.remote_cwd_override().map(Path::to_path_buf) - }; + let cwd_filter = picker_cwd_filter( + config.cwd.as_path(), + show_all, + is_remote, + app_server.remote_cwd_override(), + ); run_session_picker_with_loader( tui, config, @@ -213,14 +215,10 @@ async fn run_session_picker_with_loader( } else { ProviderFilter::MatchDefault(config.model_provider_id.to_string()) }; - let filter_cwd = if show_all || is_remote { - // Remote sessions live in the server's filesystem namespace, so the client - // process cwd is not a meaningful row filter. If the user provided an - // explicit remote --cd, filtering is handled server-side in thread/list. - None - } else { - std::env::current_dir().ok() - }; + // Remote sessions live in the server's filesystem namespace, so the client + // process cwd is not a meaningful row filter. Local cwd filtering and explicit + // remote --cd filtering are handled server-side in thread/list. + let filter_cwd = None; let mut state = PickerState::new( alt.tui.frame_requester(), @@ -270,6 +268,21 @@ async fn run_session_picker_with_loader( Ok(SessionSelection::StartFresh) } +fn picker_cwd_filter( + config_cwd: &Path, + show_all: bool, + is_remote: bool, + remote_cwd_override: Option<&Path>, +) -> Option { + if show_all { + None + } else if is_remote { + remote_cwd_override.map(Path::to_path_buf) + } else { + Some(config_cwd.to_path_buf()) + } +} + fn spawn_app_server_page_loader( app_server: AppServerSession, cwd_filter: Option, @@ -1559,6 +1572,28 @@ mod tests { assert_eq!(row.display_preview(), "My session"); } + #[test] + fn local_picker_thread_list_params_include_cwd_filter() { + let cwd_filter = picker_cwd_filter( + Path::new("/tmp/project"), + /*show_all*/ false, + /*is_remote*/ false, + /*remote_cwd_override*/ None, + ); + let params = thread_list_params( + Some(String::from("cursor-1")), + cwd_filter.as_deref(), + ProviderFilter::MatchDefault(String::from("openai")), + ThreadSortKey::UpdatedAt, + /*include_non_interactive*/ false, + ); + + assert_eq!( + params.cwd, + Some(ThreadListCwdFilter::One(String::from("/tmp/project"))) + ); + } + #[test] fn remote_thread_list_params_omit_provider_filter() { let params = thread_list_params(