Skip to content
4 changes: 4 additions & 0 deletions codex-rs/app-server/src/command_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ impl CommandExecManager {
arg0,
..
} = exec_request;
// TODO(anp): Keep PathUri through the local command launch boundary.
let cwd = cwd
.to_abs_path()
.map_err(|err| invalid_request(format!("invalid command cwd: {err}")))?;

let stream_stdin = tty || stream_stdin;
let stream_stdout_stderr = tty || stream_stdout_stderr;
Expand Down
24 changes: 15 additions & 9 deletions codex-rs/app-server/src/request_processors/thread_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1781,16 +1781,22 @@ impl ThreadRequestProcessor {
.list_background_terminals()
.await
.into_iter()
.map(|terminal| ThreadBackgroundTerminal {
item_id: terminal.item_id,
process_id: terminal.process_id,
command: terminal.command,
cwd: terminal.cwd,
os_pid: None,
cpu_percent: None,
rss_kb: None,
.map(|terminal| {
// TODO(anp): Migrate ThreadBackgroundTerminal to PathUri.
let cwd = terminal.cwd.to_abs_path().map_err(|err| {
internal_error(format!("background terminal has invalid cwd: {err}"))
})?;
Comment thread
anp-oai marked this conversation as resolved.
Ok(ThreadBackgroundTerminal {
item_id: terminal.item_id,
process_id: terminal.process_id,
command: terminal.command,
cwd,
os_pid: None,
cpu_percent: None,
rss_kb: None,
})
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, JSONRPCErrorError>>()?;

let (data, next_cursor) = paginate_background_terminals(&terminals, cursor, limit)?;

Expand Down
11 changes: 8 additions & 3 deletions codex-rs/apply-patch/src/invocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pub async fn verify_apply_patch_args(
MaybeApplyPatchVerified::Body(ApplyPatchAction {
changes,
patch,
cwd: effective_cwd,
cwd: effective_cwd.into(),
})
}

Expand Down Expand Up @@ -811,7 +811,9 @@ PATCH"#,
},
)]),
patch: argv[1].clone(),
cwd: AbsolutePathBuf::from_absolute_path(session_dir.path()).unwrap(),
cwd: AbsolutePathBuf::from_absolute_path(session_dir.path())
.unwrap()
.into(),
})
);
}
Expand Down Expand Up @@ -851,7 +853,10 @@ PATCH"#,
other => panic!("expected verified body, got {other:?}"),
};

assert_eq!(action.cwd.as_path(), worktree_dir.as_path());
assert_eq!(
action.cwd.to_abs_path().unwrap().as_path(),
worktree_dir.as_path()
);

let source_path = worktree_dir.join(source_name);
let change = action
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/apply-patch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ pub struct ApplyPatchAction {
pub patch: String,

/// The working directory that was used to resolve relative paths in the patch.
pub cwd: AbsolutePathBuf,
pub cwd: PathUri,
}

impl ApplyPatchAction {
Expand Down Expand Up @@ -174,7 +174,7 @@ impl ApplyPatchAction {
#[expect(clippy::expect_used)]
Self {
changes,
cwd: path.parent().expect("path should have parent"),
cwd: path.parent().expect("path should have parent").into(),
patch,
}
}
Expand Down
14 changes: 13 additions & 1 deletion codex-rs/core/src/apply_patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,24 @@ pub(crate) async fn apply_patch(
file_system_sandbox_policy: &FileSystemSandboxPolicy,
action: ApplyPatchAction,
) -> InternalApplyPatchInvocation {
// TODO(anp): Migrate patch safety checks to PathUri.
let cwd = match action.cwd.to_abs_path() {
Ok(cwd) => cwd,
Err(err) => {
return InternalApplyPatchInvocation::Output(Err(FunctionCallError::RespondToModel(
format!(
"patch cwd `{}` is not native to the Codex host: {err}",
action.cwd
),
)));
}
};
match assess_patch_safety(
&action,
turn_context.approval_policy.value(),
&turn_context.permission_profile(),
file_system_sandbox_policy,
&action.cwd,
&cwd,
turn_context.windows_sandbox_level,
) {
SafetyCheck::AutoApprove {
Expand Down
3 changes: 2 additions & 1 deletion codex-rs/core/src/codex_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use codex_thread_store::ThreadMetadataPatch;
use codex_thread_store::ThreadStoreError;
use codex_thread_store::ThreadStoreResult;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use rmcp::model::ReadResourceRequestParams;
use std::collections::BTreeMap;
use std::collections::HashMap;
Expand Down Expand Up @@ -164,7 +165,7 @@ pub struct BackgroundTerminalInfo {
pub item_id: String,
pub process_id: String,
pub command: String,
pub cwd: AbsolutePathBuf,
pub cwd: PathUri,
}

/// Conduit for the bidirectional stream of messages that compose a thread
Expand Down
11 changes: 10 additions & 1 deletion codex-rs/core/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ pub fn build_exec_request(
})
.map(|request| {
let windows_sandbox_workspace_roots = if windows_sandbox_workspace_roots.is_empty() {
vec![request.sandbox_policy_cwd.clone()]
vec![sandbox_cwd.clone()]
} else {
windows_sandbox_workspace_roots.to_vec()
};
Expand Down Expand Up @@ -440,6 +440,15 @@ pub(crate) async fn execute_exec_request(
arg0,
} = exec_request;

// TODO(anp): Keep PathUri through the local process launch boundary.
let cwd = cwd
.to_abs_path()
.map_err(|err| CodexErr::InvalidRequest(format!("invalid exec cwd: {err}")))?;
// TODO(anp): Keep PathUri through the Windows sandbox launch boundary.
let windows_sandbox_policy_cwd = windows_sandbox_policy_cwd
.to_abs_path()
.map_err(|err| CodexErr::InvalidRequest(format!("invalid sandbox cwd: {err}")))?;

let params = ExecParams {
command,
cwd,
Expand Down
6 changes: 4 additions & 2 deletions codex-rs/core/src/sandboxing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use codex_sandboxing::SandboxExecRequest;
use codex_sandboxing::SandboxType;
use codex_sandboxing::WindowsSandboxFilesystemOverrides;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use std::collections::HashMap;

#[derive(Debug)]
Expand All @@ -42,14 +43,14 @@ pub(crate) struct ExecServerEnvConfig {
#[derive(Debug)]
pub struct ExecRequest {
pub command: Vec<String>,
pub cwd: AbsolutePathBuf,
pub cwd: PathUri,
pub env: HashMap<String, String>,
pub(crate) exec_server_env_config: Option<ExecServerEnvConfig>,
pub network: Option<NetworkProxy>,
pub expiration: ExecExpiration,
pub capture_policy: ExecCapturePolicy,
pub sandbox: SandboxType,
pub windows_sandbox_policy_cwd: AbsolutePathBuf,
pub windows_sandbox_policy_cwd: PathUri,
pub windows_sandbox_workspace_roots: Vec<AbsolutePathBuf>,
pub windows_sandbox_level: WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
Expand All @@ -76,6 +77,7 @@ impl ExecRequest {
permission_profile: PermissionProfile,
arg0: Option<String>,
) -> Self {
let cwd = PathUri::from_abs_path(&cwd);
let windows_sandbox_policy_cwd = cwd.clone();
let (file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
Expand Down
4 changes: 2 additions & 2 deletions codex-rs/core/src/tasks/user_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ pub(crate) async fn execute_user_shell_command(
let permission_profile = PermissionProfile::Disabled;
let exec_env = ExecRequest {
command: exec_command.clone(),
cwd: cwd.clone(),
cwd: cwd.clone().into(),
env: exec_env_map,
exec_server_env_config: None,
// `/shell` is the explicit full-access escape hatch, so it must not
Expand All @@ -210,7 +210,7 @@ pub(crate) async fn execute_user_shell_command(
expiration: USER_SHELL_TIMEOUT_MS.into(),
capture_policy: ExecCapturePolicy::ShellTool,
sandbox: SandboxType::None,
windows_sandbox_policy_cwd: cwd.clone(),
windows_sandbox_policy_cwd: cwd.clone().into(),
windows_sandbox_workspace_roots: turn_context.config.effective_workspace_roots(),
windows_sandbox_level: turn_context.windows_sandbox_level,
windows_sandbox_private_desktop: turn_context
Expand Down
11 changes: 8 additions & 3 deletions codex-rs/core/src/tools/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use codex_protocol::protocol::PatchApplyStatus;
use codex_protocol::protocol::TurnDiffEvent;
use codex_shell_command::parse_command::parse_command;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;
Expand Down Expand Up @@ -133,7 +134,7 @@ pub(crate) enum ToolEmitter {
},
UnifiedExec {
command: Vec<String>,
cwd: AbsolutePathBuf,
cwd: PathUri,
source: ExecCommandSource,
parsed_cmd: Vec<ParsedCommand>,
process_id: Option<String>,
Expand Down Expand Up @@ -165,7 +166,7 @@ impl ToolEmitter {

pub fn unified_exec(
command: &[String],
cwd: AbsolutePathBuf,
cwd: PathUri,
source: ExecCommandSource,
process_id: Option<String>,
) -> Self {
Expand Down Expand Up @@ -320,11 +321,15 @@ impl ToolEmitter {
},
stage,
) => {
// TODO(anp): Migrate exec command protocol events to PathUri.
let Ok(cwd) = cwd.to_abs_path() else {
return;
Comment thread
anp-oai marked this conversation as resolved.
};
emit_exec_stage(
ctx,
ExecCommandInput::new(
command,
cwd,
&cwd,
parsed_cmd,
*source,
/*interaction_input*/ None,
Expand Down
9 changes: 6 additions & 3 deletions codex-rs/core/src/tools/handlers/apply_patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,19 @@ fn format_update_chunks_for_progress(chunks: &[codex_apply_patch::UpdateFileChun

fn file_paths_for_action(action: &ApplyPatchAction) -> Vec<AbsolutePathBuf> {
let mut keys = Vec::new();
let cwd = &action.cwd;
// TODO(anp): Migrate permission path accounting to PathUri.
let Ok(cwd) = action.cwd.to_abs_path() else {
return keys;
};

for (path, change) in action.changes() {
if let Some(key) = to_abs_path(cwd, path) {
if let Some(key) = to_abs_path(&cwd, path) {
keys.push(key);
}

if let ApplyPatchFileChange::Update { move_path, .. } = change
&& let Some(dest) = move_path
&& let Some(key) = to_abs_path(cwd, dest)
&& let Some(key) = to_abs_path(&cwd, dest)
{
keys.push(key);
}
Expand Down
Loading
Loading