diff --git a/codex-rs/core/src/session/session.rs b/codex-rs/core/src/session/session.rs index c944907274..5194f85186 100644 --- a/codex-rs/core/src/session/session.rs +++ b/codex-rs/core/src/session/session.rs @@ -351,10 +351,8 @@ impl SessionConfiguration { current_environments: &[TurnEnvironmentSelection], ) -> ConstraintResult { let mut next_configuration = self.clone(); - let current_sandbox_policy = self.sandbox_policy(current_environments); let current_file_system_sandbox_policy = self.file_system_sandbox_policy(current_environments); - let current_network_sandbox_policy = self.network_sandbox_policy(); let file_system_policy_has_rebindable_project_root_write = current_file_system_sandbox_policy .entries @@ -437,35 +435,37 @@ impl SessionConfiguration { network_sandbox_policy, ), )?; - } else if cwd_changed - && file_system_policy_has_rebindable_project_root_write - && current_file_system_sandbox_policy.is_semantically_equivalent_to( + } else if cwd_changed && file_system_policy_has_rebindable_project_root_write { + // Compatibility projection can resolve filesystem paths. Only compute it + // when a cwd-bound legacy policy might need rebinding. + let current_sandbox_policy = self.sandbox_policy(current_environments); + if current_file_system_sandbox_policy.is_semantically_equivalent_to( &FileSystemSandboxPolicy::from_legacy_sandbox_policy_preserving_deny_entries( ¤t_sandbox_policy, self.cwd(), ¤t_file_system_sandbox_policy, ), self.cwd(), - ) - { - // Preserve richer split policies across cwd-only updates; only - // rederive when the session is already using a structurally - // cwd-bound legacy bridge. - let file_system_sandbox_policy = - FileSystemSandboxPolicy::from_legacy_sandbox_policy_preserving_deny_entries( - ¤t_sandbox_policy, - next_configuration.cwd(), - ¤t_file_system_sandbox_policy, - ); - next_configuration - .permission_profile_state - .set_legacy_permission_profile( - PermissionProfile::from_runtime_permissions_with_enforcement( - SandboxEnforcement::from_legacy_sandbox_policy(¤t_sandbox_policy), - &file_system_sandbox_policy, - current_network_sandbox_policy, - ), - )?; + ) { + // Preserve richer split policies across cwd-only updates; only + // rederive when the session is already using a structurally + // cwd-bound legacy bridge. + let file_system_sandbox_policy = + FileSystemSandboxPolicy::from_legacy_sandbox_policy_preserving_deny_entries( + ¤t_sandbox_policy, + next_configuration.cwd(), + ¤t_file_system_sandbox_policy, + ); + next_configuration + .permission_profile_state + .set_legacy_permission_profile( + PermissionProfile::from_runtime_permissions_with_enforcement( + SandboxEnforcement::from_legacy_sandbox_policy(¤t_sandbox_policy), + &file_system_sandbox_policy, + self.network_sandbox_policy(), + ), + )?; + } } if let Some(app_server_client_name) = updates.app_server_client_name.clone() { next_configuration.app_server_client_name = Some(app_server_client_name); diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 95566b13a3..1637cb5d9b 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -5328,6 +5328,55 @@ async fn resolved_environments_for_configuration( (environment_manager, turn_environments.snapshot().await) } +#[tokio::test] +async fn session_configuration_apply_client_metadata_preserves_permissions() { + let mut configuration = make_session_configuration_for_tests().await; + let workspace = tempfile::tempdir().expect("create workspace"); + let cwd = workspace.path().abs(); + configuration.legacy_fallback_cwd = cwd.clone(); + let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement( + SandboxEnforcement::Managed, + &FileSystemSandboxPolicy::restricted(vec![ + FileSystemSandboxEntry::new( + FileSystemPath::Path { + path: cwd.join("writable").into(), + }, + FileSystemAccessMode::Write, + ), + FileSystemSandboxEntry::new( + FileSystemPath::Path { + path: cwd.join("writable/private").into(), + }, + FileSystemAccessMode::Deny, + ), + ]), + NetworkSandboxPolicy::Restricted, + ); + configuration + .set_permission_profile_for_tests(permission_profile) + .expect("set custom permission profile"); + let expected = configuration.thread_settings_snapshot(&[]); + let updated = configuration + .apply( + &SessionSettingsUpdate { + app_server_client_name: Some("codex-tui".to_string()), + app_server_client_version: Some("1.0.0".to_string()), + ..Default::default() + }, + &[], + ) + .expect("update client metadata"); + + assert_eq!(updated.thread_settings_snapshot(&[]), expected); + assert_eq!( + ( + updated.app_server_client_name, + updated.app_server_client_version + ), + (Some("codex-tui".to_string()), Some("1.0.0".to_string())), + ); +} + #[tokio::test] async fn session_configuration_apply_preserves_profile_file_system_policy_on_cwd_only_update() { let mut session_configuration = make_session_configuration_for_tests().await;