From 67ed4e717acfb8f729c9aa3f2bbdf19bbae73817 Mon Sep 17 00:00:00 2001 From: charlesgong-openai Date: Wed, 19 Aug 2026 01:00:58 +0000 Subject: [PATCH] Stop migrating Cursor sandbox settings (#39325) ## What changed - Ignore `.cursor/sandbox.json` during external agent config migration. - Continue importing supported settings from `.cursor/cli-config.json`. ## Testing - Update app-server migration coverage to verify that environment variables from `cli-config.json` are imported. GitOrigin-RevId: 6094f1cb6f698392d58056f826da21c1f1389a1b --- .../tests/suite/v2/external_agent_config.rs | 7 +- .../src/migration_source.rs | 3 +- .../external-agent-migration/src/service.rs | 3 +- .../src/source/cur.rs | 88 +------------------ .../src/source_cur_tests.rs | 65 -------------- 5 files changed, 10 insertions(+), 156 deletions(-) diff --git a/codex-rs/app-server/tests/suite/v2/external_agent_config.rs b/codex-rs/app-server/tests/suite/v2/external_agent_config.rs index 98d9e2d7db..eeff9816f4 100644 --- a/codex-rs/app-server/tests/suite/v2/external_agent_config.rs +++ b/codex-rs/app-server/tests/suite/v2/external_agent_config.rs @@ -338,7 +338,10 @@ async fn external_agent_config_migration_source_drives_detect_and_import() -> Re let codex_home = TempDir::new()?; let source_home = secondary_external_agent_home(codex_home.path()); std::fs::create_dir_all(&source_home)?; - std::fs::write(source_home.join("sandbox.json"), r#"{"type":"read_only"}"#)?; + std::fs::write( + source_home.join("cli-config.json"), + r#"{"env":{"SOURCE":"secondary"}}"#, + )?; let home_dir = codex_home.path().display().to_string(); let mut mcp = TestAppServer::builder() .with_codex_home(codex_home.path()) @@ -387,7 +390,7 @@ async fn external_agent_config_migration_source_drives_detect_and_import() -> Re assert_eq!(completed.item_type_results[0].failures, Vec::new()); assert!( std::fs::read_to_string(codex_home.path().join("config.toml"))? - .contains("sandbox_mode = \"read-only\"") + .contains("SOURCE = \"secondary\"") ); Ok(()) diff --git a/codex-rs/external-agent-migration/src/migration_source.rs b/codex-rs/external-agent-migration/src/migration_source.rs index 8897f8b7e7..7980ef436d 100644 --- a/codex-rs/external-agent-migration/src/migration_source.rs +++ b/codex-rs/external-agent-migration/src/migration_source.rs @@ -98,12 +98,11 @@ impl ExternalAgentSource { pub(super) fn effective_settings( self, - source_config_dir: &Path, source_settings: &Path, ) -> io::Result> { match self { Self::Cla => ClaSource::effective_settings(source_settings), - Self::Cur => CurSource::effective_settings(source_config_dir, source_settings), + Self::Cur => CurSource::effective_settings(source_settings), } } diff --git a/codex-rs/external-agent-migration/src/service.rs b/codex-rs/external-agent-migration/src/service.rs index f48e552194..129ee8f01d 100644 --- a/codex-rs/external-agent-migration/src/service.rs +++ b/codex-rs/external-agent-migration/src/service.rs @@ -485,8 +485,7 @@ impl ExternalAgentConfigService { scope: &MigrationScope, ) -> io::Result> { let source_settings = self.source_settings(scope); - self.source - .effective_settings(self.source_config_dir(scope).as_path(), &source_settings) + self.source.effective_settings(&source_settings) } pub(crate) fn build_mcp_config( diff --git a/codex-rs/external-agent-migration/src/source/cur.rs b/codex-rs/external-agent-migration/src/source/cur.rs index 8aab2da9d2..0f70d2557d 100644 --- a/codex-rs/external-agent-migration/src/source/cur.rs +++ b/codex-rs/external-agent-migration/src/source/cur.rs @@ -7,7 +7,6 @@ use crate::build_mcp_config_from_json_file; use crate::hook_migration_event_names_cur; use crate::import_hooks_cur; use crate::import_subagents_with_rewrite_profile; -use crate::invalid_data_error; use serde_json::Value as JsonValue; use std::fs; use std::io; @@ -22,97 +21,16 @@ impl CurSource { pub const LEGACY_RULES_FILE: &'static str = ".cursorrules"; pub const HOME_CONFIG_FILE: &'static str = "cli-config.json"; pub const PROJECT_CONFIG_FILE: &'static str = "cli.json"; - pub const SANDBOX_CONFIG_FILE: &'static str = "sandbox.json"; pub const HOOKS_CONFIG_FILE: &'static str = "hooks.json"; - pub const SANDBOX_SETTINGS_KEY: &'static str = "__cursorSandbox"; pub const REWRITE_PROFILE: RewriteProfile = RewriteProfile::new(Self::LEGACY_RULES_FILE, &[]) .with_case_sensitive_term_variants(&["Cursor"]); - pub fn effective_settings( - source_dir: &Path, - source_settings: &Path, - ) -> io::Result> { - let mut effective = read_json_file(source_settings)?; - let sandbox_settings = read_json_file(&source_dir.join(Self::SANDBOX_CONFIG_FILE))?; - if let Some(sandbox_settings) = sandbox_settings { - let effective = - effective.get_or_insert_with(|| JsonValue::Object(serde_json::Map::new())); - let Some(effective) = effective.as_object_mut() else { - return Err(invalid_data_error( - "external agent settings root must be an object", - )); - }; - effective.insert(Self::SANDBOX_SETTINGS_KEY.to_string(), sandbox_settings); - } - Ok(effective) + pub fn effective_settings(source_settings: &Path) -> io::Result> { + read_json_file(source_settings) } pub fn build_config(settings: &JsonValue) -> io::Result { - build_config(settings, Self::append_config) - } - - pub fn append_config( - root: &mut toml::map::Map, - settings: &serde_json::Map, - ) { - let Some(sandbox) = settings - .get(Self::SANDBOX_SETTINGS_KEY) - .and_then(JsonValue::as_object) - else { - return; - }; - let sandbox_mode = match sandbox.get("type").and_then(JsonValue::as_str) { - Some("workspace_readwrite") => Some("workspace-write"), - Some("read_only") => Some("read-only"), - _ => None, - }; - if let Some(sandbox_mode) = sandbox_mode { - root.insert( - "sandbox_mode".to_string(), - TomlValue::String(sandbox_mode.to_string()), - ); - } - if sandbox_mode != Some("workspace-write") { - return; - } - - let mut workspace_write = toml::map::Map::new(); - if let Some(paths) = sandbox - .get("additionalReadwritePaths") - .and_then(JsonValue::as_array) - { - let paths = paths - .iter() - .filter_map(JsonValue::as_str) - .filter(|path| Path::new(path).is_absolute()) - .map(|path| TomlValue::String(path.to_string())) - .collect::>(); - if !paths.is_empty() { - workspace_write.insert("writable_roots".to_string(), TomlValue::Array(paths)); - } - } - if sandbox.get("disableTmpWrite").and_then(JsonValue::as_bool) == Some(true) { - workspace_write.insert("exclude_slash_tmp".to_string(), TomlValue::Boolean(true)); - workspace_write.insert( - "exclude_tmpdir_env_var".to_string(), - TomlValue::Boolean(true), - ); - } - if sandbox - .get("networkPolicy") - .and_then(JsonValue::as_object) - .and_then(|network| network.get("default")) - .and_then(JsonValue::as_str) - == Some("allow") - { - workspace_write.insert("network_access".to_string(), TomlValue::Boolean(true)); - } - if !workspace_write.is_empty() { - root.insert( - "sandbox_workspace_write".to_string(), - TomlValue::Table(workspace_write), - ); - } + build_config(settings, |_, _| {}) } pub fn build_mcp_config(source_dir: &Path) -> io::Result { diff --git a/codex-rs/external-agent-migration/src/source_cur_tests.rs b/codex-rs/external-agent-migration/src/source_cur_tests.rs index 1d3bf7de62..f3fc3dee71 100644 --- a/codex-rs/external-agent-migration/src/source_cur_tests.rs +++ b/codex-rs/external-agent-migration/src/source_cur_tests.rs @@ -7,71 +7,6 @@ use crate::model::PluginsMigration; use pretty_assertions::assert_eq; use std::collections::HashSet; use tempfile::TempDir; -use toml::Value as TomlValue; - -#[test] -fn effective_settings_merge_sandbox_configuration() { - let root = TempDir::new().expect("tempdir"); - let source_dir = root.path().join(CurSource::CONFIG_DIR); - let source_settings = source_dir.join(CurSource::HOME_CONFIG_FILE); - fs::create_dir_all(&source_dir).expect("source directory"); - fs::write(&source_settings, r#"{"env":{"FOO":"bar"}}"#).expect("source settings"); - fs::write( - source_dir.join(CurSource::SANDBOX_CONFIG_FILE), - r#"{"type":"read_only"}"#, - ) - .expect("sandbox settings"); - - assert_eq!( - CurSource::effective_settings(&source_dir, &source_settings).expect("effective settings"), - Some(serde_json::json!({ - "env": {"FOO": "bar"}, - (CurSource::SANDBOX_SETTINGS_KEY): {"type": "read_only"} - })) - ); -} - -#[test] -fn append_config_maps_workspace_permissions() { - let root = TempDir::new().expect("tempdir"); - let writable_root = root.path().join("generated"); - let settings = serde_json::json!({ - (CurSource::SANDBOX_SETTINGS_KEY): { - "type": "workspace_readwrite", - "additionalReadwritePaths": [writable_root.display().to_string(), "relative/path"], - "disableTmpWrite": true, - "networkPolicy": {"default": "allow"} - } - }); - let mut config = toml::map::Map::new(); - - CurSource::append_config(&mut config, settings.as_object().expect("settings object")); - - let mut workspace_write = toml::map::Map::new(); - workspace_write.insert( - "writable_roots".to_string(), - TomlValue::Array(vec![TomlValue::String( - writable_root.to_string_lossy().into_owned(), - )]), - ); - workspace_write.insert("exclude_slash_tmp".to_string(), TomlValue::Boolean(true)); - workspace_write.insert( - "exclude_tmpdir_env_var".to_string(), - TomlValue::Boolean(true), - ); - workspace_write.insert("network_access".to_string(), TomlValue::Boolean(true)); - let mut expected = toml::map::Map::new(); - expected.insert( - "sandbox_mode".to_string(), - TomlValue::String("workspace-write".to_string()), - ); - expected.insert( - "sandbox_workspace_write".to_string(), - TomlValue::Table(workspace_write), - ); - - assert_eq!(TomlValue::Table(config), TomlValue::Table(expected)); -} #[test] fn cached_marketplace_plugins_require_manifest_and_cache_entries() {