From 7a367c3db760c34027cdc2026b4215ff93c5f0b1 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Thu, 30 Apr 2026 07:06:47 -0700 Subject: [PATCH] turn-context: stop writing legacy sandbox policy --- .../core/src/context_manager/history_tests.rs | 5 +---- .../session/rollout_reconstruction_tests.rs | 16 +++++++-------- codex-rs/core/src/session/tests.rs | 20 +++++++++---------- codex-rs/core/src/session/turn_context.rs | 19 ++---------------- codex-rs/core/tests/suite/resume_warning.rs | 2 +- codex-rs/protocol/src/protocol.rs | 16 ++++++++++----- codex-rs/rollout/src/recorder_tests.rs | 2 +- codex-rs/state/src/extract.rs | 20 ++++++++++--------- codex-rs/tui/src/app/tests.rs | 4 +--- codex-rs/tui/src/lib.rs | 6 +----- 10 files changed, 46 insertions(+), 64 deletions(-) diff --git a/codex-rs/core/src/context_manager/history_tests.rs b/codex-rs/core/src/context_manager/history_tests.rs index 6ef5daec2a..2b8148f24d 100644 --- a/codex-rs/core/src/context_manager/history_tests.rs +++ b/codex-rs/core/src/context_manager/history_tests.rs @@ -121,9 +121,6 @@ fn developer_msg_with_fragments(texts: &[&str]) -> ResponseItem { fn reference_context_item() -> TurnContextItem { let cwd = PathBuf::from("/tmp/reference-cwd"); let permission_profile = PermissionProfile::read_only(); - let sandbox_policy = permission_profile - .to_legacy_sandbox_policy(cwd.as_path()) - .expect("read-only permission profile should have a legacy projection"); TurnContextItem { turn_id: Some("reference-turn".to_string()), @@ -132,7 +129,7 @@ fn reference_context_item() -> TurnContextItem { current_date: Some("2026-03-23".to_string()), timezone: Some("America/Los_Angeles".to_string()), approval_policy: AskForApproval::OnRequest, - sandbox_policy, + sandbox_policy: None, permission_profile: Some(permission_profile), network: None, file_system_sandbox_policy: None, diff --git a/codex-rs/core/src/session/rollout_reconstruction_tests.rs b/codex-rs/core/src/session/rollout_reconstruction_tests.rs index 5cfcc38053..e0a0b6ec2f 100644 --- a/codex-rs/core/src/session/rollout_reconstruction_tests.rs +++ b/codex-rs/core/src/session/rollout_reconstruction_tests.rs @@ -64,7 +64,7 @@ async fn record_initial_history_resumed_bare_turn_context_does_not_hydrate_previ current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -105,7 +105,7 @@ async fn record_initial_history_resumed_hydrates_previous_turn_settings_from_lif current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -915,7 +915,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -993,7 +993,7 @@ async fn record_initial_history_resumed_turn_context_after_compaction_reestablis current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -1024,7 +1024,7 @@ async fn record_initial_history_resumed_aborted_turn_without_id_clears_active_tu current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -1139,7 +1139,7 @@ async fn record_initial_history_resumed_unmatched_abort_preserves_active_turn_fo current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -1253,7 +1253,7 @@ async fn record_initial_history_resumed_trailing_incomplete_turn_compaction_clea current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -1405,7 +1405,7 @@ async fn record_initial_history_resumed_replaced_incomplete_compacted_turn_clear current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 85de7df901..ae63abcf6b 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -1745,7 +1745,7 @@ async fn record_initial_history_forked_hydrates_previous_turn_settings() { current_date: turn_context.current_date.clone(), timezone: turn_context.timezone.clone(), approval_policy: turn_context.approval_policy.value(), - sandbox_policy: turn_context.sandbox_policy(), + sandbox_policy: Some(turn_context.sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -5926,6 +5926,7 @@ async fn turn_context_item_omits_legacy_equivalent_file_system_sandbox_policy() let item = turn_context.to_turn_context_item(); + assert_eq!(item.sandbox_policy, None); assert_eq!(item.file_system_sandbox_policy, None); assert_eq!( item.permission_profile, @@ -5934,7 +5935,7 @@ async fn turn_context_item_omits_legacy_equivalent_file_system_sandbox_policy() } #[tokio::test] -async fn turn_context_item_stores_split_file_system_sandbox_policy_when_different() { +async fn turn_context_item_stores_split_file_system_policy_in_permission_profile() { let (_session, mut turn_context) = make_session_and_context().await; let file_system_sandbox_policy = file_system_policy_with_unreadable_glob(&turn_context); turn_context.permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement( @@ -5945,10 +5946,8 @@ async fn turn_context_item_stores_split_file_system_sandbox_policy_when_differen let item = turn_context.to_turn_context_item(); - assert_eq!( - item.file_system_sandbox_policy, - Some(file_system_sandbox_policy) - ); + assert_eq!(item.sandbox_policy, None); + assert_eq!(item.file_system_sandbox_policy, None); assert_eq!( item.permission_profile, Some(turn_context.permission_profile()) @@ -6073,8 +6072,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline } #[tokio::test] -async fn record_context_updates_and_set_reference_context_item_persists_split_file_system_policy_to_rollout() - { +async fn record_context_updates_and_set_reference_context_item_persists_profile_to_rollout() { let (mut session, mut turn_context) = make_session_and_context().await; let file_system_sandbox_policy = file_system_policy_with_unreadable_glob(&turn_context); turn_context.permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement( @@ -6096,12 +6094,12 @@ async fn record_context_updates_and_set_reference_context_item_persists_split_fi else { panic!("expected resumed rollout history"); }; - let persisted_file_system_sandbox_policy = resumed.history.iter().find_map(|item| match item { - RolloutItem::TurnContext(ctx) => ctx.file_system_sandbox_policy.clone(), + let persisted_permission_profile = resumed.history.iter().find_map(|item| match item { + RolloutItem::TurnContext(ctx) => ctx.permission_profile.clone(), _ => None, }); assert_eq!( - persisted_file_system_sandbox_policy, + persisted_permission_profile.map(|profile| profile.file_system_sandbox_policy()), Some(file_system_sandbox_policy) ); } diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index 09690f93de..2c7d602f60 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -303,21 +303,6 @@ impl TurnContext { } } - fn non_legacy_file_system_sandbox_policy(&self) -> Option { - // Omit the derived split filesystem policy when it is equivalent to - // the legacy sandbox policy. This keeps turn-context payloads stable - // while both fields exist; once callers consume only the split policy, - // this comparison and the legacy projection should go away. - let legacy_file_system_sandbox_policy = - FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd( - &self.sandbox_policy(), - &self.cwd, - ); - let file_system_sandbox_policy = self.file_system_sandbox_policy(); - (file_system_sandbox_policy != legacy_file_system_sandbox_policy) - .then_some(file_system_sandbox_policy) - } - pub(crate) fn compact_prompt(&self) -> &str { self.compact_prompt .as_deref() @@ -332,10 +317,10 @@ impl TurnContext { current_date: self.current_date.clone(), timezone: self.timezone.clone(), approval_policy: self.approval_policy.value(), - sandbox_policy: self.sandbox_policy(), + sandbox_policy: None, permission_profile: Some(self.permission_profile()), network: self.turn_context_network_item(), - file_system_sandbox_policy: self.non_legacy_file_system_sandbox_policy(), + file_system_sandbox_policy: None, model: self.model_info.slug.clone(), personality: self.personality, collaboration_mode: Some(self.collaboration_mode.clone()), diff --git a/codex-rs/core/tests/suite/resume_warning.rs b/codex-rs/core/tests/suite/resume_warning.rs index 9b2faba367..e5df4ecd23 100644 --- a/codex-rs/core/tests/suite/resume_warning.rs +++ b/codex-rs/core/tests/suite/resume_warning.rs @@ -32,7 +32,7 @@ fn resume_history( current_date: None, timezone: None, approval_policy: config.permissions.approval_policy.value(), - sandbox_policy: config.legacy_sandbox_policy(), + sandbox_policy: Some(config.legacy_sandbox_policy()), permission_profile: None, network: None, file_system_sandbox_policy: None, diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index fa7a9aa6ad..b3d40d26e3 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -2856,7 +2856,8 @@ pub struct TurnContextItem { #[serde(default, skip_serializing_if = "Option::is_none")] pub timezone: Option, pub approval_policy: AskForApproval, - pub sandbox_policy: SandboxPolicy, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sandbox_policy: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub permission_profile: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -2886,17 +2887,22 @@ pub struct TurnContextItem { impl TurnContextItem { pub fn permission_profile(&self) -> PermissionProfile { self.permission_profile.clone().unwrap_or_else(|| { + let Some(sandbox_policy) = self.sandbox_policy.as_ref() else { + panic!( + "turn context item must contain permission_profile or legacy sandbox_policy" + ); + }; let file_system_sandbox_policy = self.file_system_sandbox_policy.clone().unwrap_or_else(|| { FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd( - &self.sandbox_policy, + sandbox_policy, &self.cwd, ) }); PermissionProfile::from_runtime_permissions_with_enforcement( - SandboxEnforcement::from_legacy_sandbox_policy(&self.sandbox_policy), + SandboxEnforcement::from_legacy_sandbox_policy(sandbox_policy), &file_system_sandbox_policy, - NetworkSandboxPolicy::from(&self.sandbox_policy), + NetworkSandboxPolicy::from(sandbox_policy), ) }) } @@ -5101,7 +5107,7 @@ mod tests { current_date: None, timezone: None, approval_policy: AskForApproval::Never, - sandbox_policy: SandboxPolicy::DangerFullAccess, + sandbox_policy: Some(SandboxPolicy::DangerFullAccess), permission_profile: None, network: Some(TurnContextNetworkItem { allowed_domains: vec!["api.example.com".to_string()], diff --git a/codex-rs/rollout/src/recorder_tests.rs b/codex-rs/rollout/src/recorder_tests.rs index 6e1f65b50d..69f133e050 100644 --- a/codex-rs/rollout/src/recorder_tests.rs +++ b/codex-rs/rollout/src/recorder_tests.rs @@ -1105,7 +1105,7 @@ async fn resume_candidate_matches_cwd_reads_latest_turn_context() -> std::io::Re current_date: None, timezone: None, approval_policy: AskForApproval::Never, - sandbox_policy: permission_profile.to_legacy_sandbox_policy(latest_cwd.as_path())?, + sandbox_policy: None, permission_profile: Some(permission_profile), network: None, file_system_sandbox_policy: None, diff --git a/codex-rs/state/src/extract.rs b/codex-rs/state/src/extract.rs index 633eb63404..ba5093ca12 100644 --- a/codex-rs/state/src/extract.rs +++ b/codex-rs/state/src/extract.rs @@ -303,9 +303,7 @@ mod tests { current_date: None, timezone: None, approval_policy: AskForApproval::Never, - sandbox_policy: PermissionProfile::read_only() - .to_legacy_sandbox_policy(Path::new("/")) - .expect("read-only profile should project to legacy sandbox"), + sandbox_policy: None, permission_profile: Some(PermissionProfile::Disabled), network: None, file_system_sandbox_policy: None, @@ -342,9 +340,11 @@ mod tests { current_date: None, timezone: None, approval_policy: AskForApproval::OnRequest, - sandbox_policy: PermissionProfile::read_only() - .to_legacy_sandbox_policy(Path::new("/")) - .expect("read-only profile should project to legacy sandbox"), + sandbox_policy: Some( + PermissionProfile::read_only() + .to_legacy_sandbox_policy(Path::new("/")) + .expect("read-only profile should project to legacy sandbox"), + ), permission_profile: None, network: None, file_system_sandbox_policy: None, @@ -378,9 +378,11 @@ mod tests { current_date: None, timezone: None, approval_policy: AskForApproval::OnRequest, - sandbox_policy: PermissionProfile::read_only() - .to_legacy_sandbox_policy(Path::new("/")) - .expect("read-only profile should project to legacy sandbox"), + sandbox_policy: Some( + PermissionProfile::read_only() + .to_legacy_sandbox_policy(Path::new("/")) + .expect("read-only profile should project to legacy sandbox"), + ), permission_profile: None, network: None, file_system_sandbox_policy: None, diff --git a/codex-rs/tui/src/app/tests.rs b/codex-rs/tui/src/app/tests.rs index 756c50869d..99216e959d 100644 --- a/codex-rs/tui/src/app/tests.rs +++ b/codex-rs/tui/src/app/tests.rs @@ -2746,9 +2746,7 @@ async fn inactive_thread_started_notification_initializes_replay_session() -> Re current_date: None, timezone: None, approval_policy: primary_session.approval_policy, - sandbox_policy: permission_profile - .to_legacy_sandbox_policy(test_path_buf("/tmp/agent").as_path()) - .expect("workspace profile must be legacy-compatible"), + sandbox_policy: None, permission_profile: Some(permission_profile), network: None, file_system_sandbox_policy: None, diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 93446a53e5..9d40b95cec 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -2210,10 +2210,6 @@ mod tests { .clone() .unwrap_or_else(|| "gpt-5.1".to_string()); let permission_profile = config.permissions.permission_profile(); - let sandbox_policy = permission_compat::legacy_compatible_sandbox_policy( - &permission_profile, - config.cwd.as_path(), - ); TurnContextItem { turn_id: None, trace_id: None, @@ -2221,7 +2217,7 @@ mod tests { current_date: None, timezone: None, approval_policy: config.permissions.approval_policy.value(), - sandbox_policy, + sandbox_policy: None, permission_profile: Some(permission_profile), network: None, file_system_sandbox_policy: None,