diff --git a/codex-rs/exec/src/cli.rs b/codex-rs/exec/src/cli.rs index e12e86939..3d2001ae6 100644 --- a/codex-rs/exec/src/cli.rs +++ b/codex-rs/exec/src/cli.rs @@ -119,6 +119,10 @@ pub struct ResumeArgs { #[arg(long = "last", default_value_t = false)] pub last: bool, + /// Show all sessions (disables cwd filtering). + #[arg(long = "all", default_value_t = false)] + pub all: bool, + /// Optional image(s) to attach to the prompt sent after resuming. #[arg( long = "image", diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 966dc6534..cdd8bf7da 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -507,17 +507,21 @@ async fn resolve_resume_path( ) -> anyhow::Result> { if args.last { let default_provider_filter = vec![config.model_provider_id.clone()]; - match codex_core::RolloutRecorder::list_threads( + let filter_cwd = if args.all { + None + } else { + Some(config.cwd.as_path()) + }; + match codex_core::RolloutRecorder::find_latest_thread_path( &config.codex_home, - 1, - None, &[], Some(default_provider_filter.as_slice()), &config.model_provider_id, + filter_cwd, ) .await { - Ok(page) => Ok(page.items.first().map(|it| it.path.clone())), + Ok(path) => Ok(path), Err(e) => { error!("Error listing threads: {e}"); Ok(None) diff --git a/codex-rs/exec/tests/suite/resume.rs b/codex-rs/exec/tests/suite/resume.rs index 32913983e..ec7fec083 100644 --- a/codex-rs/exec/tests/suite/resume.rs +++ b/codex-rs/exec/tests/suite/resume.rs @@ -5,6 +5,7 @@ use core_test_support::test_codex_exec::test_codex_exec; use pretty_assertions::assert_eq; use serde_json::Value; use std::string::ToString; +use tempfile::TempDir; use uuid::Uuid; use walkdir::WalkDir; @@ -219,6 +220,90 @@ fn exec_resume_last_accepts_prompt_after_flag_in_json_mode() -> anyhow::Result<( Ok(()) } +#[test] +fn exec_resume_last_respects_cwd_filter_and_all_flag() -> anyhow::Result<()> { + let test = test_codex_exec(); + let fixture = exec_fixture()?; + + let dir_a = TempDir::new()?; + let dir_b = TempDir::new()?; + + let marker_a = format!("resume-cwd-a-{}", Uuid::new_v4()); + let prompt_a = format!("echo {marker_a}"); + test.cmd() + .env("CODEX_RS_SSE_FIXTURE", &fixture) + .env("OPENAI_BASE_URL", "http://unused.local") + .arg("--skip-git-repo-check") + .arg("-C") + .arg(dir_a.path()) + .arg(&prompt_a) + .assert() + .success(); + + let marker_b = format!("resume-cwd-b-{}", Uuid::new_v4()); + let prompt_b = format!("echo {marker_b}"); + test.cmd() + .env("CODEX_RS_SSE_FIXTURE", &fixture) + .env("OPENAI_BASE_URL", "http://unused.local") + .arg("--skip-git-repo-check") + .arg("-C") + .arg(dir_b.path()) + .arg(&prompt_b) + .assert() + .success(); + + let sessions_dir = test.home_path().join("sessions"); + let path_a = find_session_file_containing_marker(&sessions_dir, &marker_a) + .expect("no session file found for marker_a"); + let path_b = find_session_file_containing_marker(&sessions_dir, &marker_b) + .expect("no session file found for marker_b"); + + let marker_b2 = format!("resume-cwd-b-2-{}", Uuid::new_v4()); + let prompt_b2 = format!("echo {marker_b2}"); + test.cmd() + .env("CODEX_RS_SSE_FIXTURE", &fixture) + .env("OPENAI_BASE_URL", "http://unused.local") + .arg("--skip-git-repo-check") + .arg("-C") + .arg(dir_a.path()) + .arg("resume") + .arg("--last") + .arg("--all") + .arg(&prompt_b2) + .assert() + .success(); + + let resumed_path_all = find_session_file_containing_marker(&sessions_dir, &marker_b2) + .expect("no resumed session file containing marker_b2"); + assert_eq!( + resumed_path_all, path_b, + "resume --last --all should pick newest session" + ); + + let marker_a2 = format!("resume-cwd-a-2-{}", Uuid::new_v4()); + let prompt_a2 = format!("echo {marker_a2}"); + test.cmd() + .env("CODEX_RS_SSE_FIXTURE", &fixture) + .env("OPENAI_BASE_URL", "http://unused.local") + .arg("--skip-git-repo-check") + .arg("-C") + .arg(dir_a.path()) + .arg("resume") + .arg("--last") + .arg(&prompt_a2) + .assert() + .success(); + + let resumed_path_cwd = find_session_file_containing_marker(&sessions_dir, &marker_a2) + .expect("no resumed session file containing marker_a2"); + assert_eq!( + resumed_path_cwd, path_a, + "resume --last should prefer sessions from the same cwd" + ); + + Ok(()) +} + #[test] fn exec_resume_accepts_global_flags_after_subcommand() -> anyhow::Result<()> { let test = test_codex_exec();