Let users remember the working directory for resumed sessions (#33950)

## What changed

- Add `tui.resume_cwd` with `current` and `session` modes for resume and fork flows.
- Let users persist either mode directly from the working-directory prompt, while preserving one-time choices when no preference is configured.
- Apply the preference consistently during startup and in-app resume, honor explicit `--cd` overrides, and reject unsupported or unavailable directory choices with a clear error.

## Testing

- Cover configuration parsing and persistence, prompt behavior, startup and in-app flows, session metadata fallback, and remote workspace constraints.

GitOrigin-RevId: 7bf144c4b8c55bdb868cc8a71caf397c7740451a
This commit is contained in:
Anais Killian authored and copyberry committed 2026-07-18 03:58:20 +00:00
1 parent 82b294c73c
commit 2895d82b5e
21 files changed
+1469 -79

No files matched your search

+45
View File
@@ -55,6 +55,7 @@ use codex_config::types::NotificationMethod;
use codex_config::types::Notifications;
use codex_config::types::OtelConfigToml;
use codex_config::types::OtelExporterKind;
use codex_config::types::ResumeCwdMode;
use codex_config::types::SandboxWorkspaceWrite;
use codex_config::types::SessionPickerViewMode;
use codex_config::types::SkillsConfig;
@@ -946,6 +947,7 @@ fn config_toml_deserializes_model_availability_nux() {
pet: None,
pet_anchor: TuiPetAnchor::Composer,
session_picker_view: None,
resume_cwd: None,
keymap: TuiKeymap::default(),
model_availability_nux: ModelAvailabilityNuxConfig {
shown_count: HashMap::from([
@@ -3684,6 +3686,19 @@ session_picker_view = "dense"
);
}
#[test]
fn tui_resume_cwd_deserializes_from_toml() {
let cfg = r#"
[tui]
resume_cwd = "current"
"#;
let parsed = toml::from_str::<ConfigToml>(cfg).expect("TOML deserialization should succeed");
assert_eq!(
parsed.tui.as_ref().and_then(|t| t.resume_cwd),
Some(ResumeCwdMode::Current),
);
}
#[test]
fn tui_pet_deserializes_from_toml() {
let cfg = r#"
@@ -3785,6 +3800,7 @@ fn tui_config_missing_notifications_field_defaults_to_enabled() {
pet: None,
pet_anchor: TuiPetAnchor::Composer,
session_picker_view: None,
resume_cwd: None,
keymap: TuiKeymap::default(),
model_availability_nux: ModelAvailabilityNuxConfig::default(),
terminal_resize_reflow_max_rows: None,
@@ -3967,6 +3983,35 @@ async fn runtime_config_resolves_session_picker_view_default_and_override() {
);
}
#[tokio::test]
async fn runtime_config_resolves_resume_cwd_default_and_override() {
let cfg = Config::load_from_base_config_with_overrides(
ConfigToml::default(),
ConfigOverrides::default(),
tempdir().expect("tempdir").abs(),
)
.await
.expect("load default config");
assert_eq!(cfg.tui_resume_cwd, None);
let cfg = Config::load_from_base_config_with_overrides(
ConfigToml {
tui: Some(Tui {
resume_cwd: Some(ResumeCwdMode::Session),
..Default::default()
}),
..Default::default()
},
ConfigOverrides::default(),
tempdir().expect("tempdir").abs(),
)
.await
.expect("load root override config");
assert_eq!(cfg.tui_resume_cwd, Some(ResumeCwdMode::Session));
}
#[tokio::test]
async fn test_sandbox_config_parsing() {
let sandbox_full_access = r#"
+9
View File
@@ -3,6 +3,7 @@ use crate::path_utils::write_atomically;
use anyhow::Context;
use codex_config::CONFIG_TOML_FILE;
use codex_config::types::McpServerConfig;
use codex_config::types::ResumeCwdMode;
use codex_config::types::SessionPickerViewMode;
use codex_config::types::ToolSuggestDisabledTool;
use codex_features::FEATURES;
@@ -962,6 +963,14 @@ impl ConfigEditsBuilder {
self
}
pub fn set_resume_cwd(mut self, mode: ResumeCwdMode) -> Self {
self.edits.push(ConfigEdit::SetPath {
segments: vec!["tui".to_string(), "resume_cwd".to_string()],
value: value(mode.as_str()),
});
self
}
pub fn with_edits<I>(mut self, edits: I) -> Self
where
I: IntoIterator<Item = ConfigEdit>,
+6
View File
@@ -43,6 +43,7 @@ use codex_config::types::MemoriesConfig;
use codex_config::types::ModelAvailabilityNuxConfig;
use codex_config::types::Notice;
use codex_config::types::OAuthCredentialsStoreMode;
use codex_config::types::ResumeCwdMode;
use codex_config::types::SessionPickerViewMode;
use codex_config::types::ToolSuggestConfig;
use codex_config::types::ToolSuggestDisabledTool;
@@ -789,6 +790,10 @@ pub struct Config {
/// Preferred layout for resume/fork session picker results.
pub tui_session_picker_view: SessionPickerViewMode,
/// Working directory to use when resuming or forking a session.
/// When unset, prompt if the current and session directories differ.
pub tui_resume_cwd: Option<ResumeCwdMode>,
/// Terminal resize-reflow tuning knobs.
pub terminal_resize_reflow: TerminalResizeReflowConfig,
@@ -4118,6 +4123,7 @@ impl Config {
.as_ref()
.and_then(|t| t.session_picker_view)
.unwrap_or_default(),
tui_resume_cwd: cfg.tui.as_ref().and_then(|t| t.resume_cwd),
terminal_resize_reflow,
tui_keymap: cfg
.tui