From dae588fa36eb0379a643d584a58e5c1e853b6701 Mon Sep 17 00:00:00 2001 From: Dylan Date: Fri, 17 Oct 2025 11:14:51 -0700 Subject: [PATCH] Add sandbox mode to config profiles --- codex-rs/app-server-protocol/src/protocol.rs | 1 + codex-rs/app-server/tests/suite/config.rs | 2 ++ codex-rs/core/src/config.rs | 32 +++++++++++++++++++- codex-rs/core/src/config_profile.rs | 3 ++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/codex-rs/app-server-protocol/src/protocol.rs b/codex-rs/app-server-protocol/src/protocol.rs index 916560c60b..7f19ba5897 100644 --- a/codex-rs/app-server-protocol/src/protocol.rs +++ b/codex-rs/app-server-protocol/src/protocol.rs @@ -507,6 +507,7 @@ pub struct Profile { pub model_reasoning_summary: Option, pub model_verbosity: Option, pub chatgpt_base_url: Option, + pub sandbox_mode: Option, } /// MCP representation of a [`codex_core::config::ToolsToml`]. #[derive(Deserialize, Debug, Clone, PartialEq, Serialize, TS)] diff --git a/codex-rs/app-server/tests/suite/config.rs b/codex-rs/app-server/tests/suite/config.rs index 577eeb388d..574076fa10 100644 --- a/codex-rs/app-server/tests/suite/config.rs +++ b/codex-rs/app-server/tests/suite/config.rs @@ -52,6 +52,7 @@ model_reasoning_summary = "detailed" model_verbosity = "medium" model_provider = "openai" chatgpt_base_url = "https://api.chatgpt.com" +sandbox_mode = "danger-full-access" "#, ) } @@ -111,6 +112,7 @@ async fn get_config_toml_parses_all_fields() { model_verbosity: Some(Verbosity::Medium), model_provider: Some("openai".into()), chatgpt_base_url: Some("https://api.chatgpt.com".into()), + sandbox_mode: Some(SandboxMode::DangerFullAccess), }, )]), }, diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index f422e4ab81..6d4ac02e99 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -1026,7 +1026,8 @@ impl Config { let features = Features::from_config(&cfg, &config_profile, feature_overrides); - let sandbox_policy = cfg.derive_sandbox_policy(sandbox_mode); + let sandbox_policy = + cfg.derive_sandbox_policy(sandbox_mode.or(config_profile.sandbox_mode)); let mut model_providers = built_in_model_providers(); // Merge user-defined providers into the built-in list. @@ -1509,6 +1510,35 @@ approve_all = true Ok(()) } + #[test] + fn profile_sandbox_mode_overrides_base_config() -> std::io::Result<()> { + let codex_home = TempDir::new()?; + let mut profiles = HashMap::new(); + profiles.insert( + "danger".to_string(), + ConfigProfile { + sandbox_mode: Some(SandboxMode::DangerFullAccess), + ..Default::default() + }, + ); + let cfg = ConfigToml { + sandbox_mode: Some(SandboxMode::ReadOnly), + profiles, + profile: Some("danger".to_string()), + ..Default::default() + }; + + let config = Config::load_from_base_config_with_overrides( + cfg, + ConfigOverrides::default(), + codex_home.path().to_path_buf(), + )?; + + assert_eq!(config.sandbox_policy, SandboxPolicy::DangerFullAccess); + + Ok(()) + } + #[test] fn feature_table_overrides_legacy_flags() -> std::io::Result<()> { let codex_home = TempDir::new()?; diff --git a/codex-rs/core/src/config_profile.rs b/codex-rs/core/src/config_profile.rs index ba2201ed9c..d57a2f9219 100644 --- a/codex-rs/core/src/config_profile.rs +++ b/codex-rs/core/src/config_profile.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use crate::protocol::AskForApproval; use codex_protocol::config_types::ReasoningEffort; use codex_protocol::config_types::ReasoningSummary; +use codex_protocol::config_types::SandboxMode; use codex_protocol::config_types::Verbosity; /// Collection of common configuration options that a user can define as a unit @@ -19,6 +20,7 @@ pub struct ConfigProfile { pub model_reasoning_summary: Option, pub model_verbosity: Option, pub chatgpt_base_url: Option, + pub sandbox_mode: Option, pub experimental_instructions_file: Option, pub include_plan_tool: Option, pub include_apply_patch_tool: Option, @@ -44,6 +46,7 @@ impl From for codex_app_server_protocol::Profile { model_reasoning_summary: config_profile.model_reasoning_summary, model_verbosity: config_profile.model_verbosity, chatgpt_base_url: config_profile.chatgpt_base_url, + sandbox_mode: config_profile.sandbox_mode, } } }