Use AbsolutePathBuf for exec cwd plumbing (#17063)

## Summary
- Carry `AbsolutePathBuf` through tool cwd parsing/resolution instead of
resolving workdirs to raw `PathBuf`s.
- Type exec/sandbox request cwd fields as `AbsolutePathBuf` through
`ExecParams`, `ExecRequest`, `SandboxCommand`, and unified exec runtime
requests.
- Keep `PathBuf` conversions at external/event boundaries and update
existing tests/fixtures for the typed cwd.

## Validation
- `cargo check -p codex-core --tests`
- `cargo check -p codex-sandboxing --tests`
- `cargo test -p codex-sandboxing`
- `cargo test -p codex-core --lib tools::handlers::`
- `just fix -p codex-sandboxing`
- `just fix -p codex-core`
- `just fmt`

Full `codex-core` test suite was not run locally; per repo guidance I
kept local validation targeted.
This commit is contained in:
pakrym-oai
2026-04-08 10:54:12 -07:00
committed by GitHub
Unverified
parent d90a348870
commit 35b5720e8d
31 changed files with 119 additions and 126 deletions
+2 -2
View File
@@ -24,12 +24,12 @@
use std::collections::HashMap;
use std::collections::HashSet;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Weak;
use codex_network_proxy::NetworkProxy;
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
use rand::Rng;
use rand::rng;
use tokio::sync::Mutex;
@@ -91,7 +91,7 @@ pub(crate) struct ExecCommandRequest {
pub process_id: i32,
pub yield_time_ms: u64,
pub max_output_tokens: Option<usize>,
pub workdir: Option<PathBuf>,
pub workdir: Option<AbsolutePathBuf>,
pub network: Option<NetworkProxy>,
pub tty: bool,
pub sandbox_permissions: SandboxPermissions,
+7 -5
View File
@@ -51,7 +51,7 @@ fn shell_env() -> HashMap<String, String> {
fn test_exec_request(
turn: &TurnContext,
command: Vec<String>,
cwd: PathBuf,
cwd: AbsolutePathBuf,
env: HashMap<String, String>,
) -> ExecRequest {
let windows_sandbox_private_desktop = false;
@@ -87,7 +87,9 @@ async fn exec_command_with_tty(
) -> Result<ExecCommandToolOutput, UnifiedExecError> {
let manager = &session.services.unified_exec_manager;
let process_id = manager.allocate_process_id().await;
let cwd = workdir.unwrap_or_else(|| turn.cwd.clone().to_path_buf());
let cwd = workdir
.as_ref()
.map_or_else(|| turn.cwd.clone(), |workdir| turn.cwd.join(workdir));
let command = vec!["bash".to_string(), "-lc".to_string(), cmd.to_string()];
let request = test_exec_request(turn, command.clone(), cwd.clone(), shell_env());
@@ -502,7 +504,7 @@ async fn completed_pipe_commands_preserve_exit_code() -> anyhow::Result<()> {
let request = test_exec_request(
&turn,
vec!["bash".to_string(), "-lc".to_string(), "exit 17".to_string()],
PathBuf::from("/tmp"),
turn.cwd.clone(),
shell_env(),
);
@@ -544,7 +546,7 @@ async fn unified_exec_uses_remote_exec_server_when_configured() -> anyhow::Resul
let request = test_exec_request(
&turn,
vec!["bash".to_string(), "-i".to_string()],
PathBuf::from("/tmp"),
remote_test_env.cwd().clone(),
shell_env(),
);
@@ -598,7 +600,7 @@ async fn remote_exec_server_rejects_inherited_fd_launches() -> anyhow::Result<()
let request = test_exec_request(
&turn,
vec!["bash".to_string(), "-lc".to_string(), "echo ok".to_string()],
PathBuf::from("/tmp"),
turn.cwd.clone(),
shell_env(),
);
@@ -2,7 +2,6 @@ use rand::Rng;
use std::cmp::Reverse;
use std::collections::HashMap;
use std::collections::HashSet;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
@@ -49,6 +48,7 @@ use crate::unified_exec::process::OutputHandles;
use crate::unified_exec::process::SpawnLifecycleHandle;
use crate::unified_exec::process::UnifiedExecProcess;
use codex_protocol::protocol::ExecCommandSource;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_output_truncation::approx_token_count;
const UNIFIED_EXEC_ENV: [(&str, &str); 10] = [
@@ -165,7 +165,7 @@ impl UnifiedExecProcessManager {
let cwd = request
.workdir
.clone()
.unwrap_or_else(|| context.turn.cwd.to_path_buf());
.unwrap_or_else(|| context.turn.cwd.clone());
let process = self
.open_session_with_sandbox(&request, cwd.clone(), context)
.await;
@@ -189,7 +189,7 @@ impl UnifiedExecProcessManager {
);
let emitter = ToolEmitter::unified_exec(
&request.command,
cwd.clone(),
cwd.to_path_buf(),
ExecCommandSource::UnifiedExecStartup,
Some(request.process_id.to_string()),
);
@@ -255,7 +255,7 @@ impl UnifiedExecProcessManager {
Arc::clone(&context.turn),
context.call_id.clone(),
request.command.clone(),
cwd.clone(),
cwd.to_path_buf(),
Some(request.process_id.to_string()),
Arc::clone(&transcript),
message.clone(),
@@ -298,7 +298,7 @@ impl UnifiedExecProcessManager {
Arc::clone(&context.turn),
context.call_id.clone(),
request.command.clone(),
cwd.clone(),
cwd.to_path_buf(),
Some(process_id.to_string()),
Arc::clone(&transcript),
text.clone(),
@@ -526,7 +526,7 @@ impl UnifiedExecProcessManager {
process: Arc<UnifiedExecProcess>,
context: &UnifiedExecContext,
command: &[String],
cwd: PathBuf,
cwd: AbsolutePathBuf,
started_at: Instant,
process_id: i32,
tty: bool,
@@ -572,7 +572,7 @@ impl UnifiedExecProcessManager {
Arc::clone(&context.turn),
context.call_id.clone(),
command.to_vec(),
cwd,
cwd.to_path_buf(),
process_id,
transcript,
started_at,
@@ -605,7 +605,7 @@ impl UnifiedExecProcessManager {
.start(codex_exec_server::ExecParams {
process_id: exec_server_process_id(process_id).into(),
argv: request.command.clone(),
cwd: request.cwd.clone(),
cwd: request.cwd.to_path_buf(),
env: request.env.clone(),
tty,
arg0: request.arg0.clone(),
@@ -646,7 +646,7 @@ impl UnifiedExecProcessManager {
pub(super) async fn open_session_with_sandbox(
&self,
request: &ExecCommandRequest,
cwd: PathBuf,
cwd: AbsolutePathBuf,
context: &UnifiedExecContext,
) -> Result<(UnifiedExecProcess, Option<DeferredNetworkApproval>), UnifiedExecError> {
let env = apply_unified_exec_env(create_env(