diff --git a/codex-rs/tui/src/app/tests.rs b/codex-rs/tui/src/app/tests.rs index fa54abfe20..1b7c116153 100644 --- a/codex-rs/tui/src/app/tests.rs +++ b/codex-rs/tui/src/app/tests.rs @@ -2796,10 +2796,13 @@ async fn inactive_thread_started_notification_initializes_replay_session() -> Re ThreadId::from_string("00000000-0000-0000-0000-000000000101").expect("valid thread"); let agent_thread_id = ThreadId::from_string("00000000-0000-0000-0000-000000000202").expect("valid thread"); + let primary_cwd = test_path_buf("/tmp/main").abs(); + let shared_root = test_path_buf("/tmp/shared").abs(); let primary_session = ThreadSessionState { approval_policy: AskForApproval::OnRequest, permission_profile: PermissionProfile::workspace_write(), - ..test_thread_session(main_thread_id, test_path_buf("/tmp/main")) + runtime_workspace_roots: vec![primary_cwd.clone(), shared_root.clone()], + ..test_thread_session(main_thread_id, primary_cwd.to_path_buf()) }; app.primary_thread_id = Some(main_thread_id); @@ -2871,6 +2874,10 @@ async fn inactive_thread_started_notification_initializes_replay_session() -> Re assert_eq!(session.model_provider_id, "agent-provider"); assert_eq!(session.approval_policy, primary_session.approval_policy); assert_eq!(session.cwd.as_path(), test_path_buf("/tmp/agent").as_path()); + assert_eq!( + session.runtime_workspace_roots, + vec![test_path_buf("/tmp/agent").abs(), shared_root] + ); assert_eq!(session.rollout_path, Some(rollout_path)); assert_eq!( app.agent_navigation.get(&agent_thread_id), @@ -2892,10 +2899,12 @@ async fn inactive_thread_started_notification_preserves_primary_model_when_path_ ThreadId::from_string("00000000-0000-0000-0000-000000000301").expect("valid thread"); let agent_thread_id = ThreadId::from_string("00000000-0000-0000-0000-000000000302").expect("valid thread"); + let primary_cwd = test_path_buf("/tmp/main").abs(); let primary_session = ThreadSessionState { approval_policy: AskForApproval::OnRequest, permission_profile: PermissionProfile::workspace_write(), - ..test_thread_session(main_thread_id, test_path_buf("/tmp/main")) + runtime_workspace_roots: vec![primary_cwd.clone()], + ..test_thread_session(main_thread_id, primary_cwd.to_path_buf()) }; app.primary_thread_id = Some(main_thread_id); @@ -2997,6 +3006,10 @@ async fn thread_read_session_state_does_not_reuse_primary_permission_profile() { assert_eq!(session.thread_id, read_thread_id); assert_eq!(session.cwd.as_path(), test_path_buf("/tmp/read").as_path()); + assert_eq!( + session.runtime_workspace_roots, + vec![test_path_buf("/tmp/read").abs()] + ); let expected_permission_profile = app .chat_widget .config_ref() diff --git a/codex-rs/tui/src/app/thread_routing.rs b/codex-rs/tui/src/app/thread_routing.rs index 9d6cda012d..716f2eedf0 100644 --- a/codex-rs/tui/src/app/thread_routing.rs +++ b/codex-rs/tui/src/app/thread_routing.rs @@ -895,7 +895,8 @@ impl App { session.thread_id = thread_id; session.thread_name = notification.thread.name.clone(); session.model_provider_id = notification.thread.model_provider.clone(); - session.cwd = notification.thread.cwd.clone(); + session + .set_cwd_retargeting_implicit_runtime_workspace_root(notification.thread.cwd.clone()); let rollout_path = notification.thread.path.clone(); if let Some(model) = read_session_model(self.state_db.as_deref(), thread_id, rollout_path.as_deref()).await diff --git a/codex-rs/tui/src/app/thread_session_state.rs b/codex-rs/tui/src/app/thread_session_state.rs index 73744ab94a..a8d421563b 100644 --- a/codex-rs/tui/src/app/thread_session_state.rs +++ b/codex-rs/tui/src/app/thread_session_state.rs @@ -82,7 +82,7 @@ impl App { session.thread_id = thread_id; session.thread_name = thread.name.clone(); session.model_provider_id = thread.model_provider.clone(); - session.cwd = thread.cwd.clone(); + session.set_cwd_retargeting_implicit_runtime_workspace_root(thread.cwd.clone()); session.permission_profile = permission_profile; session.active_permission_profile = active_permission_profile; session.instruction_source_paths = Vec::new(); diff --git a/codex-rs/tui/src/session_state.rs b/codex-rs/tui/src/session_state.rs index a672b47d28..3e5e16ed5b 100644 --- a/codex-rs/tui/src/session_state.rs +++ b/codex-rs/tui/src/session_state.rs @@ -49,3 +49,32 @@ pub(crate) struct ThreadSessionState { pub(crate) network_proxy: Option, pub(crate) rollout_path: Option, } + +impl ThreadSessionState { + pub(crate) fn set_cwd_retargeting_implicit_runtime_workspace_root( + &mut self, + cwd: AbsolutePathBuf, + ) { + let previous_cwd = std::mem::replace(&mut self.cwd, cwd.clone()); + if !self + .runtime_workspace_roots + .iter() + .any(|root| root == &previous_cwd) + { + return; + } + + let previous_roots = std::mem::take(&mut self.runtime_workspace_roots); + self.runtime_workspace_roots.push(cwd); + for root in previous_roots { + if root != previous_cwd + && !self + .runtime_workspace_roots + .iter() + .any(|existing| existing == &root) + { + self.runtime_workspace_roots.push(root); + } + } + } +}