Compare commits

...

4 Commits

Author SHA1 Message Date
iceweasel-oai
e3ba3394fa copy current exe to CODEX_HOME/.sandbox-bin for apply_patch 2026-03-05 18:23:41 -08:00
iceweasel-oai
f1944b4b03 fix schema tests. 2026-03-04 09:46:22 -08:00
iceweasel-oai
7453132c26 derive config from cwd before running sandbox setup 2026-03-04 09:22:42 -08:00
iceweasel-oai
334232f049 allow apps to specify cwd for sandbox setup. 2026-03-04 09:22:42 -08:00
13 changed files with 116 additions and 27 deletions

View File

@@ -2864,6 +2864,12 @@
},
"WindowsSandboxSetupStartParams": {
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"mode": {
"$ref": "#/definitions/WindowsSandboxSetupMode"
}
@@ -3908,4 +3914,4 @@
}
],
"title": "ClientRequest"
}
}

View File

@@ -15325,6 +15325,12 @@
"WindowsSandboxSetupStartParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"mode": {
"$ref": "#/definitions/v2/WindowsSandboxSetupMode"
}
@@ -15385,4 +15391,4 @@
},
"title": "CodexAppServerProtocol",
"type": "object"
}
}

View File

@@ -14226,6 +14226,12 @@
"WindowsSandboxSetupStartParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"mode": {
"$ref": "#/definitions/WindowsSandboxSetupMode"
}
@@ -14285,4 +14291,4 @@
},
"title": "CodexAppServerProtocolV2",
"type": "object"
}
}

View File

@@ -10,6 +10,12 @@
}
},
"properties": {
"cwd": {
"type": [
"string",
"null"
]
},
"mode": {
"$ref": "#/definitions/WindowsSandboxSetupMode"
}
@@ -19,4 +25,4 @@
],
"title": "WindowsSandboxSetupStartParams",
"type": "object"
}
}

View File

@@ -3,4 +3,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { WindowsSandboxSetupMode } from "./WindowsSandboxSetupMode";
export type WindowsSandboxSetupStartParams = { mode: WindowsSandboxSetupMode, };
export type WindowsSandboxSetupStartParams = { mode: WindowsSandboxSetupMode, cwd?: string | null, };

View File

@@ -3927,6 +3927,8 @@ pub enum WindowsSandboxSetupMode {
#[ts(export_to = "v2/")]
pub struct WindowsSandboxSetupStartParams {
pub mode: WindowsSandboxSetupMode,
#[ts(optional = nullable)]
pub cwd: Option<PathBuf>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]

View File

@@ -157,7 +157,7 @@ Example with notification opt-out:
- `tool/requestUserInput` — prompt the user with 13 short questions for a tool call and return their answers (experimental).
- `config/mcpServer/reload` — reload MCP server config from disk and queue a refresh for loaded threads (applied on each thread's next active turn); returns `{}`. Use this after editing `config.toml` without restarting the server.
- `mcpServerStatus/list` — enumerate configured MCP servers with their tools, resources, resource templates, and auth status; supports cursor+limit pagination.
- `windowsSandbox/setupStart` — start Windows sandbox setup for the selected mode (`elevated` or `unelevated`); returns `{ started: true }` immediately and later emits `windowsSandbox/setupCompleted`.
- `windowsSandbox/setupStart` — start Windows sandbox setup for the selected mode (`elevated` or `unelevated`); accepts an optional `cwd` to target setup for a specific workspace, returns `{ started: true }` immediately, and later emits `windowsSandbox/setupCompleted`.
- `feedback/upload` — submit a feedback report (classification + optional reason/logs, conversation_id, and optional `extraLogFiles` attachments array); returns the tracking thread id.
- `command/exec` — run a single command under the server sandbox without starting a thread/turn (handy for utilities and validation).
- `config/read` — fetch the effective config on disk after resolving config layering.

View File

@@ -6119,21 +6119,39 @@ impl CodexMessageProcessor {
WindowsSandboxSetupMode::Unelevated => CoreWindowsSandboxSetupMode::Unelevated,
};
let config = Arc::clone(&self.config);
let cli_overrides = self.cli_overrides.clone();
let cloud_requirements = self.current_cloud_requirements();
let command_cwd = params.cwd.unwrap_or_else(|| config.cwd.clone());
let outgoing = Arc::clone(&self.outgoing);
let connection_id = request_id.connection_id;
tokio::spawn(async move {
let setup_request = WindowsSandboxSetupRequest {
mode,
policy: config.permissions.sandbox_policy.get().clone(),
policy_cwd: config.cwd.clone(),
command_cwd: config.cwd.clone(),
env_map: std::env::vars().collect(),
codex_home: config.codex_home.clone(),
active_profile: config.active_profile.clone(),
let derived_config = derive_config_for_cwd(
&cli_overrides,
None,
ConfigOverrides {
cwd: Some(command_cwd.clone()),
..Default::default()
},
Some(command_cwd.clone()),
&cloud_requirements,
)
.await;
let setup_result = match derived_config {
Ok(config) => {
let setup_request = WindowsSandboxSetupRequest {
mode,
policy: config.permissions.sandbox_policy.get().clone(),
policy_cwd: config.cwd.clone(),
command_cwd,
env_map: std::env::vars().collect(),
codex_home: config.codex_home.clone(),
active_profile: config.active_profile.clone(),
};
codex_core::windows_sandbox::run_windows_sandbox_setup(setup_request).await
}
Err(err) => Err(err.into()),
};
let setup_result =
codex_core::windows_sandbox::run_windows_sandbox_setup(setup_request).await;
let notification = WindowsSandboxSetupCompletedNotification {
mode: match mode {
CoreWindowsSandboxSetupMode::Elevated => WindowsSandboxSetupMode::Elevated,

View File

@@ -37,6 +37,7 @@ async fn windows_sandbox_setup_start_emits_completion_notification() -> Result<(
let request_id = mcp
.send_windows_sandbox_setup_start_request(WindowsSandboxSetupStartParams {
mode: WindowsSandboxSetupMode::Unelevated,
cwd: None,
})
.await?;
let response: JSONRPCResponse = timeout(

View File

@@ -46,13 +46,26 @@ impl ApplyPatchRuntime {
Self
}
fn build_command_spec(req: &ApplyPatchRequest) -> Result<CommandSpec, ToolError> {
use std::env;
fn build_command_spec(
req: &ApplyPatchRequest,
codex_home: &std::path::Path,
) -> Result<CommandSpec, ToolError> {
let exe = if let Some(path) = &req.codex_exe {
path.clone()
} else {
env::current_exe()
.map_err(|e| ToolError::Rejected(format!("failed to determine codex exe: {e}")))?
#[cfg(target_os = "windows")]
{
codex_windows_sandbox::resolve_current_exe_for_launch(
codex_home,
"codex.exe",
)
}
#[cfg(not(target_os = "windows"))]
{
std::env::current_exe().map_err(|e| {
ToolError::Rejected(format!("failed to determine codex exe: {e}"))
})?
}
};
let program = exe.to_string_lossy().to_string();
Ok(CommandSpec {
@@ -159,7 +172,7 @@ impl ToolRuntime<ApplyPatchRequest, ExecToolCallOutput> for ApplyPatchRuntime {
attempt: &SandboxAttempt<'_>,
ctx: &ToolCtx,
) -> Result<ExecToolCallOutput, ToolError> {
let spec = Self::build_command_spec(req)?;
let spec = Self::build_command_spec(req, &ctx.turn.config.codex_home)?;
let env = attempt
.env_for(spec, None)
.map_err(|err| ToolError::Codex(err.into()))?;

View File

@@ -1,27 +1,27 @@
mod windows_impl {
use crate::acl::allow_null_device;
use crate::allow::compute_allow_paths;
use crate::allow::AllowDenyPaths;
use crate::allow::compute_allow_paths;
use crate::cap::load_or_create_cap_sids;
use crate::env::ensure_non_interactive_pager;
use crate::env::inherit_path_env;
use crate::env::normalize_null_device_env;
use crate::helper_materialization::resolve_helper_for_launch;
use crate::helper_materialization::HelperExecutable;
use crate::helper_materialization::resolve_helper_for_launch;
use crate::identity::require_logon_sandbox_creds;
use crate::logging::log_failure;
use crate::logging::log_note;
use crate::logging::log_start;
use crate::logging::log_success;
use crate::policy::parse_policy;
use crate::policy::SandboxPolicy;
use crate::policy::parse_policy;
use crate::token::convert_string_sid_to_sid;
use crate::winutil::quote_windows_arg;
use crate::winutil::to_wide;
use anyhow::Result;
use rand::rngs::SmallRng;
use rand::Rng;
use rand::SeedableRng;
use rand::rngs::SmallRng;
use std::collections::HashMap;
use std::ffi::c_void;
use std::fs;
@@ -45,11 +45,11 @@ mod windows_impl {
use windows_sys::Win32::System::Pipes::PIPE_WAIT;
use windows_sys::Win32::System::Threading::CreateProcessWithLogonW;
use windows_sys::Win32::System::Threading::GetExitCodeProcess;
use windows_sys::Win32::System::Threading::WaitForSingleObject;
use windows_sys::Win32::System::Threading::INFINITE;
use windows_sys::Win32::System::Threading::LOGON_WITH_PROFILE;
use windows_sys::Win32::System::Threading::PROCESS_INFORMATION;
use windows_sys::Win32::System::Threading::STARTUPINFOW;
use windows_sys::Win32::System::Threading::WaitForSingleObject;
/// Ensures the parent directory of a path exists before writing to it.
/// Walks upward from `start` to locate the git worktree root, following gitfile redirects.
@@ -515,8 +515,8 @@ pub use windows_impl::run_windows_sandbox_capture;
#[cfg(not(target_os = "windows"))]
mod stub {
use anyhow::bail;
use anyhow::Result;
use anyhow::bail;
use codex_protocol::protocol::SandboxPolicy;
use std::collections::HashMap;
use std::path::Path;

View File

@@ -88,6 +88,34 @@ pub(crate) fn resolve_helper_for_launch(
}
}
pub fn resolve_current_exe_for_launch(
codex_home: &Path,
fallback_executable: &str,
) -> PathBuf {
let source = match std::env::current_exe() {
Ok(path) => path,
Err(_) => return PathBuf::from(fallback_executable),
};
let Some(file_name) = source.file_name() else {
return source;
};
let destination = helper_bin_dir(codex_home).join(file_name);
match copy_from_source_if_needed(&source, &destination) {
Ok(_) => destination,
Err(err) => {
let sandbox_log_dir = crate::sandbox_dir(codex_home);
log_note(
&format!(
"helper copy failed for current executable: {err:#}; falling back to legacy path {}",
source.display()
),
Some(&sandbox_log_dir),
);
source
}
}
}
pub(crate) fn copy_helper_if_needed(
kind: HelperExecutable,
codex_home: &Path,
@@ -349,3 +377,4 @@ mod tests {
);
}
}

View File

@@ -58,6 +58,8 @@ pub use dpapi::protect as dpapi_protect;
#[cfg(target_os = "windows")]
pub use dpapi::unprotect as dpapi_unprotect;
#[cfg(target_os = "windows")]
pub use helper_materialization::resolve_current_exe_for_launch;
#[cfg(target_os = "windows")]
pub use elevated_impl::run_windows_sandbox_capture as run_windows_sandbox_capture_elevated;
#[cfg(target_os = "windows")]
pub use hide_users::hide_current_user_profile_dir;