diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 11be48b2c..34065b7df 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -651,6 +651,17 @@ fn session_target_from_app_server_thread( } } +pub(crate) fn resume_source_kinds(include_non_interactive: bool) -> Vec { + let mut source_kinds = vec![ThreadSourceKind::Cli, ThreadSourceKind::VsCode]; + if include_non_interactive { + // `thread/list` treats omitted and empty `sourceKinds` as interactive-only, + // so include-non-interactive has to name the user-resumable non-interactive + // sources explicitly until the API grows an unfiltered request. + source_kinds.extend([ThreadSourceKind::Exec, ThreadSourceKind::AppServer]); + } + source_kinds +} + async fn lookup_session_target_by_name_with_app_server( app_server: &mut AppServerSession, name: &str, @@ -756,8 +767,7 @@ fn latest_session_lookup_params( } else { Some(vec![config.model_provider_id.clone()]) }, - source_kinds: (!include_non_interactive) - .then_some(vec![ThreadSourceKind::Cli, ThreadSourceKind::VsCode]), + source_kinds: Some(resume_source_kinds(include_non_interactive)), archived: Some(false), cwd: cwd_filter.map(|cwd| ThreadListCwdFilter::One(cwd.to_string_lossy().to_string())), use_state_db_only: false, @@ -2295,6 +2305,29 @@ mod tests { Ok(()) } + #[tokio::test] + async fn latest_session_lookup_params_can_include_non_interactive_sources() + -> std::io::Result<()> { + let temp_dir = TempDir::new()?; + let config = build_config(&temp_dir).await?; + + let params = latest_session_lookup_params( + /*uses_remote_workspace*/ true, &config, /*cwd_filter*/ None, + /*include_non_interactive*/ true, + ); + + assert_eq!( + params.source_kinds, + Some(vec![ + ThreadSourceKind::Cli, + ThreadSourceKind::VsCode, + ThreadSourceKind::Exec, + ThreadSourceKind::AppServer, + ]) + ); + Ok(()) + } + #[tokio::test] async fn latest_session_lookup_params_keep_explicit_cwd_filter_for_remote_sessions() -> std::io::Result<()> { diff --git a/codex-rs/tui/src/resume_picker.rs b/codex-rs/tui/src/resume_picker.rs index fb021a0fe..63f0c8773 100644 --- a/codex-rs/tui/src/resume_picker.rs +++ b/codex-rs/tui/src/resume_picker.rs @@ -36,7 +36,6 @@ use codex_app_server_protocol::ThreadItem; use codex_app_server_protocol::ThreadListCwdFilter; use codex_app_server_protocol::ThreadListParams; use codex_app_server_protocol::ThreadSortKey; -use codex_app_server_protocol::ThreadSourceKind; use codex_config::types::SessionPickerViewMode; use codex_protocol::ThreadId; use codex_utils_path as path_utils; @@ -1829,8 +1828,7 @@ fn thread_list_params( ProviderFilter::Any => None, ProviderFilter::MatchDefault(default_provider) => Some(vec![default_provider]), }, - source_kinds: (!include_non_interactive) - .then_some(vec![ThreadSourceKind::Cli, ThreadSourceKind::VsCode]), + source_kinds: Some(crate::resume_source_kinds(include_non_interactive)), archived: Some(false), cwd: cwd_filter.map(|cwd| ThreadListCwdFilter::One(cwd.to_string_lossy().into_owned())), use_state_db_only: false, @@ -3207,6 +3205,7 @@ fn render_empty_state_line(state: &PickerState) -> Line<'static> { mod tests { use super::*; use chrono::Duration; + use codex_app_server_protocol::ThreadSourceKind; use codex_config::CONFIG_TOML_FILE; use codex_protocol::ThreadId; use codex_utils_absolute_path::test_support::PathBufExt; @@ -3572,7 +3571,8 @@ mod tests { assert_eq!(params.cursor, Some(String::from("cursor-1"))); assert_eq!(params.model_providers, None); - assert_eq!(params.source_kinds, None); + let source_kinds = crate::resume_source_kinds(/*include_non_interactive*/ true); + assert_eq!(params.source_kinds, Some(source_kinds)); } #[test]