diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 6e81704752e1..eb67a2051f6f 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -2712,6 +2712,10 @@ impl CodexMessageProcessor { /*has_in_progress_turn*/ false, ); + let sandbox = thread_response_sandbox_policy( + &config_snapshot.permission_profile, + config_snapshot.cwd.as_path(), + ); let permission_profile = thread_response_permission_profile(config_snapshot.permission_profile); @@ -2724,7 +2728,7 @@ impl CodexMessageProcessor { instruction_sources, approval_policy: config_snapshot.approval_policy.into(), approvals_reviewer: config_snapshot.approvals_reviewer.into(), - sandbox: config_snapshot.sandbox_policy.into(), + sandbox, permission_profile, reasoning_effort: config_snapshot.reasoning_effort, }; @@ -3284,7 +3288,7 @@ impl CodexMessageProcessor { builder.model_provider = Some(model_provider.clone()); builder.cwd = config_snapshot.cwd.to_path_buf(); builder.cli_version = Some(env!("CARGO_PKG_VERSION").to_string()); - builder.sandbox_policy = config_snapshot.sandbox_policy.clone(); + builder.sandbox_policy = config_snapshot.sandbox_policy(); builder.approval_mode = config_snapshot.approval_policy; let metadata = builder.build(model_provider.as_str()); if let Err(err) = state_db_ctx.insert_thread_if_absent(&metadata).await { @@ -8099,7 +8103,6 @@ async fn handle_pending_thread_resume_request( service_tier, approval_policy, approvals_reviewer, - sandbox_policy: _, permission_profile, cwd, reasoning_effort, @@ -8323,8 +8326,9 @@ fn collect_resume_override_mismatches( } } if let Some(requested_sandbox) = request.sandbox.as_ref() { + let active_sandbox = config_snapshot.sandbox_policy(); let sandbox_matches = matches!( - (requested_sandbox, &config_snapshot.sandbox_policy), + (requested_sandbox, &active_sandbox), ( SandboxMode::ReadOnly, codex_protocol::protocol::SandboxPolicy::ReadOnly { .. } @@ -8341,8 +8345,7 @@ fn collect_resume_override_mismatches( ); if !sandbox_matches { mismatch_details.push(format!( - "sandbox requested={requested_sandbox:?} active={:?}", - config_snapshot.sandbox_policy + "sandbox requested={requested_sandbox:?} active={active_sandbox:?}" )); } } @@ -10042,7 +10045,6 @@ mod tests { service_tier: Some(codex_protocol::config_types::ServiceTier::Flex), approval_policy: codex_protocol::protocol::AskForApproval::OnRequest, approvals_reviewer: codex_protocol::config_types::ApprovalsReviewer::User, - sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess, permission_profile: codex_protocol::models::PermissionProfile::Disabled, cwd, ephemeral: false, diff --git a/codex-rs/core/src/codex_thread.rs b/codex-rs/core/src/codex_thread.rs index 7454b9865131..fa4bd8067c0d 100644 --- a/codex-rs/core/src/codex_thread.rs +++ b/codex-rs/core/src/codex_thread.rs @@ -47,7 +47,6 @@ pub struct ThreadConfigSnapshot { pub service_tier: Option, pub approval_policy: AskForApproval, pub approvals_reviewer: ApprovalsReviewer, - pub sandbox_policy: SandboxPolicy, pub permission_profile: PermissionProfile, pub cwd: AbsolutePathBuf, pub ephemeral: bool, @@ -56,6 +55,18 @@ pub struct ThreadConfigSnapshot { pub session_source: SessionSource, } +impl ThreadConfigSnapshot { + pub fn sandbox_policy(&self) -> SandboxPolicy { + let file_system_sandbox_policy = self.permission_profile.file_system_sandbox_policy(); + codex_sandboxing::compatibility_sandbox_policy_for_permission_profile( + &self.permission_profile, + &file_system_sandbox_policy, + self.permission_profile.network_sandbox_policy(), + self.cwd.as_path(), + ) + } +} + /// Turn context overrides that app-server validates before starting a turn. #[derive(Clone, Default)] pub struct CodexThreadTurnContextOverrides { diff --git a/codex-rs/core/src/memories/tests.rs b/codex-rs/core/src/memories/tests.rs index c00c77b8e4df..cbf7222b1ade 100644 --- a/codex-rs/core/src/memories/tests.rs +++ b/codex-rs/core/src/memories/tests.rs @@ -883,11 +883,11 @@ mod phase2 { config_snapshot.cwd.as_path(), memory_root(&harness.config.codex_home).as_path() ); - match &config_snapshot.sandbox_policy { + let sandbox_policy = config_snapshot.sandbox_policy(); + match &sandbox_policy { SandboxPolicy::WorkspaceWrite { network_access, .. } => { assert!(!*network_access); - let effective_writable_roots: Vec<_> = config_snapshot - .sandbox_policy + let effective_writable_roots: Vec<_> = sandbox_policy .get_writable_roots_with_cwd(config_snapshot.cwd.as_path()) .into_iter() .map(|root| root.root) @@ -917,7 +917,7 @@ mod phase2 { let file_system_sandbox_policy = turn_context.file_system_sandbox_policy(); let legacy_file_system_sandbox_policy = FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd( - &config_snapshot.sandbox_policy, + &sandbox_policy, config_snapshot.cwd.as_path(), ); assert!( diff --git a/codex-rs/core/src/session/session.rs b/codex-rs/core/src/session/session.rs index 176865ee6cf1..1d37db1b8d8c 100644 --- a/codex-rs/core/src/session/session.rs +++ b/codex-rs/core/src/session/session.rs @@ -126,7 +126,6 @@ impl SessionConfiguration { service_tier: self.service_tier, approval_policy: self.approval_policy.value(), approvals_reviewer: self.approvals_reviewer, - sandbox_policy: self.sandbox_policy(), permission_profile: self.permission_profile(), cwd: self.cwd.clone(), ephemeral: self.original_config_do_not_use.ephemeral, diff --git a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs index 64cc9db032fb..fc861be61cd3 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs @@ -2143,7 +2143,7 @@ async fn spawn_agent_reapplies_runtime_sandbox_after_role_config() { .expect("spawned agent thread should exist") .config_snapshot() .await; - assert_eq!(snapshot.sandbox_policy, expected_sandbox); + assert_eq!(snapshot.sandbox_policy(), expected_sandbox); assert_eq!(snapshot.approval_policy, AskForApproval::OnRequest); assert_eq!(snapshot.permission_profile, expected_permission_profile); let child_thread = manager