mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
PAC 1 - Add system proxy feature config surface (#26706)
## Summary Introduces the default-off `respect_system_proxy` feature flag used to gate first-class system PAC/proxy support for Codex-owned native clients. With the feature disabled or absent, behavior remains unchanged. This PR establishes the configuration and managed-requirement surface; proxy discovery and request routing are implemented by follow-up PRs. ## Configuration User configuration uses the standard boolean feature form: ```toml [features] respect_system_proxy = true ``` Managed feature requirements use the corresponding boolean key. The effective runtime configuration is exposed as a boolean and defaults to `false`. ## Implementation - Registers `respect_system_proxy` as an under-development, default-off feature. - Resolves user configuration and managed feature requirements into `Config.respect_system_proxy`. - Provides bootstrap resolution for startup paths that must evaluate the feature before full configuration loading completes. - Uses the standard feature CLI and config-editing behavior. - Excludes `features.respect_system_proxy` from project-local configuration. - Updates the generated configuration schema. ## End-user behavior - No networking behavior changes when the feature is absent or disabled. - Enabling the feature makes the boolean available to the native proxy-routing implementation in follow-up PRs. - Repository-local configuration cannot enable the feature. ## Test coverage Covers scalar configuration and CLI override resolution, managed requirement constraints, bootstrap resolution, and project-local filtering.
This commit is contained in:
committed by
GitHub
Unverified
parent
a397b59887
commit
f0cb96bcb1
@@ -945,6 +945,11 @@ fn sanitize_project_config(config: &mut TomlValue) -> Vec<String> {
|
||||
ignored_keys.push((*key).to_string());
|
||||
}
|
||||
}
|
||||
if let Some(features) = table.get_mut("features").and_then(TomlValue::as_table_mut)
|
||||
&& features.remove("respect_system_proxy").is_some()
|
||||
{
|
||||
ignored_keys.push("features.respect_system_proxy".to_string());
|
||||
}
|
||||
|
||||
ignored_keys
|
||||
}
|
||||
|
||||
@@ -587,6 +587,9 @@
|
||||
"resize_all_images": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"respect_system_proxy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"responses_websockets": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -4745,6 +4748,9 @@
|
||||
"resize_all_images": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"respect_system_proxy": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"responses_websockets": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -5280,4 +5286,4 @@
|
||||
},
|
||||
"title": "ConfigToml",
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
@@ -2931,6 +2931,9 @@ notify = ["sh", "-c", "echo attacker"]
|
||||
profile = "attacker"
|
||||
experimental_realtime_ws_base_url = "wss://attacker.example/realtime"
|
||||
|
||||
[features]
|
||||
respect_system_proxy = true
|
||||
|
||||
[otel]
|
||||
environment = "attacker"
|
||||
|
||||
@@ -2984,6 +2987,7 @@ wire_api = "responses"
|
||||
"profiles",
|
||||
"experimental_realtime_ws_base_url",
|
||||
"otel",
|
||||
"features.respect_system_proxy",
|
||||
];
|
||||
let expected_startup_warnings = vec![format!(
|
||||
concat!(
|
||||
|
||||
@@ -1361,6 +1361,92 @@ sandbox = "elevated"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn respect_system_proxy_feature_resolves_enabled() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml {
|
||||
features: Some(
|
||||
toml::from_str(
|
||||
r#"
|
||||
respect_system_proxy = true
|
||||
"#,
|
||||
)
|
||||
.expect("valid features"),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
ConfigOverrides::default(),
|
||||
codex_home.abs(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert!(config.respect_system_proxy);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bootstrap_respect_system_proxy_honors_feature_requirements() -> std::io::Result<()> {
|
||||
let configured = ConfigToml {
|
||||
features: Some(
|
||||
toml::from_str(
|
||||
r#"
|
||||
respect_system_proxy = true
|
||||
"#,
|
||||
)
|
||||
.expect("valid features"),
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
let disabled = Sourced::new(
|
||||
FeatureRequirementsToml {
|
||||
entries: BTreeMap::from([("respect_system_proxy".to_string(), false)]),
|
||||
},
|
||||
RequirementSource::Unknown,
|
||||
);
|
||||
assert!(!resolve_bootstrap_respect_system_proxy(
|
||||
&configured,
|
||||
Some(&disabled)
|
||||
)?);
|
||||
|
||||
let configured = ConfigToml::default();
|
||||
let enabled = Sourced::new(
|
||||
FeatureRequirementsToml {
|
||||
entries: BTreeMap::from([("respect_system_proxy".to_string(), true)]),
|
||||
},
|
||||
RequirementSource::Unknown,
|
||||
);
|
||||
assert!(resolve_bootstrap_respect_system_proxy(
|
||||
&configured,
|
||||
Some(&enabled)
|
||||
)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn respect_system_proxy_cli_override_enables_feature() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"
|
||||
[features]
|
||||
respect_system_proxy = false
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.cli_overrides(vec![(
|
||||
"features.respect_system_proxy".to_string(),
|
||||
toml::Value::Boolean(true),
|
||||
)])
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
assert!(config.respect_system_proxy);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn experimental_network_requirements_enable_proxy_without_feature() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -947,6 +947,9 @@ pub struct Config {
|
||||
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
|
||||
pub chatgpt_base_url: String,
|
||||
|
||||
/// Whether Codex-owned clients should respect host system proxy settings.
|
||||
pub respect_system_proxy: bool,
|
||||
|
||||
/// Optional product SKU forwarded to the host-owned apps MCP server.
|
||||
pub apps_mcp_product_sku: Option<String>,
|
||||
|
||||
@@ -2507,6 +2510,27 @@ fn network_proxy_toml_config(features: Option<&FeaturesToml>) -> Option<&Network
|
||||
}
|
||||
}
|
||||
|
||||
/// Bootstrap-only resolver for the cloud-config fetch.
|
||||
///
|
||||
/// Call before a cloud-config bundle is available. Final [`Config`] loading
|
||||
/// resolves the effective feature value after all layers are available.
|
||||
pub fn resolve_bootstrap_respect_system_proxy(
|
||||
cfg: &ConfigToml,
|
||||
feature_requirements: Option<&Sourced<FeatureRequirementsToml>>,
|
||||
) -> std::io::Result<bool> {
|
||||
let configured_features = Features::from_sources(
|
||||
FeatureConfigSource {
|
||||
features: cfg.features.as_ref(),
|
||||
experimental_use_unified_exec_tool: cfg.experimental_use_unified_exec_tool,
|
||||
},
|
||||
FeatureConfigSource::default(),
|
||||
FeatureOverrides::default(),
|
||||
);
|
||||
let features =
|
||||
ManagedFeatures::from_configured(configured_features, feature_requirements.cloned())?;
|
||||
Ok(features.get().enabled(Feature::RespectSystemProxy))
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_web_search_mode_for_turn(
|
||||
web_search_mode: &Constrained<WebSearchMode>,
|
||||
permission_profile: &PermissionProfile,
|
||||
@@ -2770,6 +2794,7 @@ impl Config {
|
||||
feature_requirements,
|
||||
&mut startup_warnings,
|
||||
)?;
|
||||
let respect_system_proxy = features.enabled(Feature::RespectSystemProxy);
|
||||
let enable_network_proxy = features.enabled(Feature::NetworkProxy);
|
||||
let configured_windows_sandbox_mode = resolve_windows_sandbox_mode(&cfg);
|
||||
// Keep the configured mode separate so a requirement-constrained mode
|
||||
@@ -3617,6 +3642,7 @@ impl Config {
|
||||
chatgpt_base_url: cfg
|
||||
.chatgpt_base_url
|
||||
.unwrap_or("https://chatgpt.com/backend-api/".to_string()),
|
||||
respect_system_proxy,
|
||||
apps_mcp_product_sku: cfg.apps_mcp_product_sku.clone(),
|
||||
realtime_audio: cfg
|
||||
.audio
|
||||
|
||||
@@ -135,6 +135,8 @@ pub enum Feature {
|
||||
EnableRequestCompression,
|
||||
/// Start the managed network proxy for sandboxed sessions.
|
||||
NetworkProxy,
|
||||
/// Respect host system proxy settings for Codex-owned network clients.
|
||||
RespectSystemProxy,
|
||||
/// Enable collab tools.
|
||||
Collab,
|
||||
/// Enable task-path-based multi-agent routing.
|
||||
@@ -967,6 +969,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
},
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::RespectSystemProxy,
|
||||
key: "respect_system_proxy",
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::Collab,
|
||||
key: "multi_agent",
|
||||
|
||||
@@ -649,6 +649,7 @@ fn materialize_resolved_enabled_writes_all_features_and_preserves_custom_config(
|
||||
features.enable(Feature::CodeMode);
|
||||
features.enable(Feature::MultiAgentV2);
|
||||
features.enable(Feature::NetworkProxy);
|
||||
features.enable(Feature::RespectSystemProxy);
|
||||
|
||||
let mut features_toml = FeaturesToml {
|
||||
multi_agent_v2: Some(FeatureToml::Config(crate::MultiAgentV2ConfigToml {
|
||||
|
||||
@@ -255,6 +255,7 @@ fn new_config(model: Option<String>, arg0_paths: Arg0DispatchPaths) -> anyhow::R
|
||||
model_catalog: None,
|
||||
model_verbosity: None,
|
||||
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
|
||||
respect_system_proxy: false,
|
||||
apps_mcp_product_sku: None,
|
||||
realtime_audio: RealtimeAudioConfig::default(),
|
||||
experimental_realtime_ws_base_url: None,
|
||||
|
||||
Reference in New Issue
Block a user