mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
## What changed Add the default-off `features.prefer_mxc` flag to select MXC for local Windows execution when native support is available and effective network settings do not explicitly forbid local binding. Otherwise, retain the configured backend and legacy setup behavior. Resolve the preference during config loading and use the effective local backend for execution, network proxy setup, sandbox diagnostics, and `windowsSandbox/readiness`. Preserve the configured backend for remote executors and `config/read`. Explicit `windows.sandbox = "mxc"` remains strict, and command failures do not trigger backend fallback. ## Testing Add coverage for preference resolution, startup readiness without rewriting configuration, local and remote backend selection across environment updates and child threads, and workspace-write permission context in an agent turn. GitOrigin-RevId: b51178f0371d38ebb58e41af54a068fca67fa316
145 lines
4.9 KiB
Rust
145 lines
4.9 KiB
Rust
use anyhow::Context;
|
|
use anyhow::Result;
|
|
use app_test_support::TestAppServer;
|
|
use app_test_support::create_mock_responses_server_sequence_unchecked;
|
|
use app_test_support::to_response;
|
|
use app_test_support::write_mock_responses_config_toml;
|
|
use codex_app_server_protocol::JSONRPCResponse;
|
|
use codex_app_server_protocol::RequestId;
|
|
use codex_app_server_protocol::WindowsSandboxReadiness;
|
|
use codex_app_server_protocol::WindowsSandboxReadinessResponse;
|
|
use codex_app_server_protocol::WindowsSandboxSetupCompletedNotification;
|
|
use codex_app_server_protocol::WindowsSandboxSetupMode;
|
|
use codex_app_server_protocol::WindowsSandboxSetupStartParams;
|
|
use codex_app_server_protocol::WindowsSandboxSetupStartResponse;
|
|
use pretty_assertions::assert_eq;
|
|
use std::collections::BTreeMap;
|
|
use tempfile::TempDir;
|
|
use tokio::time::timeout;
|
|
|
|
const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
|
|
|
|
#[test_case::test_case(false, false)]
|
|
#[test_case::test_case(true, false)]
|
|
#[test_case::test_case(true, true)]
|
|
#[tokio::test]
|
|
async fn startup_mxc_preference_is_resolved_before_readiness(
|
|
prefer_mxc: bool,
|
|
deny_local_binding: bool,
|
|
) -> Result<()> {
|
|
let codex_home = TempDir::new()?;
|
|
let mut config_toml = "[features]\nprefer_mxc = false\n".to_string();
|
|
if deny_local_binding {
|
|
config_toml
|
|
.push_str("[features.network_proxy]\nenabled = true\nallow_local_binding = false\n");
|
|
}
|
|
let config_path = codex_home.path().join("config.toml");
|
|
std::fs::write(&config_path, &config_toml)?;
|
|
let mut mcp = TestAppServer::builder()
|
|
.with_codex_home(codex_home.path())
|
|
.without_auto_env()
|
|
.with_args(&["-c", &format!("features.prefer_mxc={prefer_mxc}")])
|
|
.build_initialized_with_timeout(DEFAULT_READ_TIMEOUT)
|
|
.await?;
|
|
let mxc_selected =
|
|
prefer_mxc && !deny_local_binding && codex_sandboxing::windows_mxc_available();
|
|
let request_id = mcp
|
|
.send_raw_request("windowsSandbox/readiness", /*params*/ None)
|
|
.await?;
|
|
let readiness: WindowsSandboxReadinessResponse =
|
|
timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(request_id)).await??;
|
|
assert_eq!(
|
|
readiness,
|
|
WindowsSandboxReadinessResponse {
|
|
status: if mxc_selected {
|
|
WindowsSandboxReadiness::Ready
|
|
} else {
|
|
WindowsSandboxReadiness::NotConfigured
|
|
}
|
|
}
|
|
);
|
|
assert_eq!(std::fs::read_to_string(config_path)?, config_toml);
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn windows_sandbox_setup_start_emits_completion_notification() -> Result<()> {
|
|
let responses = Vec::new();
|
|
let server = create_mock_responses_server_sequence_unchecked(responses).await;
|
|
let codex_home = TempDir::new()?;
|
|
write_mock_responses_config_toml(
|
|
codex_home.path(),
|
|
&server.uri(),
|
|
&BTreeMap::new(),
|
|
/*auto_compact_limit*/ 500_000,
|
|
Some(false),
|
|
"mock_provider",
|
|
"compact prompt",
|
|
)?;
|
|
let mut mcp = TestAppServer::builder()
|
|
.with_codex_home(codex_home.path())
|
|
.without_auto_env()
|
|
.build()
|
|
.await?;
|
|
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
|
|
|
let request_id = mcp
|
|
.send_windows_sandbox_setup_start_request(WindowsSandboxSetupStartParams {
|
|
mode: WindowsSandboxSetupMode::Unelevated,
|
|
cwd: None,
|
|
})
|
|
.await?;
|
|
let response: JSONRPCResponse = timeout(
|
|
DEFAULT_READ_TIMEOUT,
|
|
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
|
|
)
|
|
.await??;
|
|
let start_payload: WindowsSandboxSetupStartResponse = to_response(response)?;
|
|
assert!(start_payload.started);
|
|
|
|
let notification = timeout(
|
|
DEFAULT_READ_TIMEOUT,
|
|
mcp.read_stream_until_notification_message("windowsSandbox/setupCompleted"),
|
|
)
|
|
.await??;
|
|
let payload: WindowsSandboxSetupCompletedNotification = serde_json::from_value(
|
|
notification
|
|
.params
|
|
.context("missing windowsSandbox/setupCompleted params")?,
|
|
)?;
|
|
|
|
assert_eq!(payload.mode, WindowsSandboxSetupMode::Unelevated);
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn windows_sandbox_setup_start_rejects_relative_cwd() -> Result<()> {
|
|
let codex_home = TempDir::new()?;
|
|
let mut mcp = TestAppServer::builder()
|
|
.with_codex_home(codex_home.path())
|
|
.without_auto_env()
|
|
.build()
|
|
.await?;
|
|
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
|
|
|
let request_id = mcp
|
|
.send_raw_request(
|
|
"windowsSandbox/setupStart",
|
|
Some(serde_json::json!({
|
|
"mode": "unelevated",
|
|
"cwd": "relative-root",
|
|
})),
|
|
)
|
|
.await?;
|
|
|
|
let err = timeout(
|
|
DEFAULT_READ_TIMEOUT,
|
|
mcp.read_stream_until_error_message(RequestId::Integer(request_id)),
|
|
)
|
|
.await??;
|
|
|
|
assert_eq!(err.error.code, -32600);
|
|
assert!(err.error.message.contains("Invalid request"));
|
|
Ok(())
|
|
}
|