unified-exec: retain PathUri in command events (#28780)

## Why

App-server must report command events containing foreign-platform paths
without changing existing client or rollout path-string formats.

## What changed

- retain `PathUri` through exec command begin/end events
- convert cwd values to `LegacyAppPathString` at the app-server
compatibility boundary
- drop command actions with foreign paths and log them
- serialize rollout-trace cwd values using their inferred native path
representation
- restore Wine coverage for retained Windows cwd values and successful
completion
This commit is contained in:
Adam Perry @ OpenAI
2026-06-18 05:00:04 +00:00
committed by GitHub
parent 285eff6c3e
commit 3931bc2bde
56 changed files with 566 additions and 125 deletions
@@ -12,7 +12,9 @@ fn agent_status_uses_bounded_buffered_activity() {
item: ThreadItem::CommandExecution {
id: "command-1".to_string(),
command: "cargo test -p codex-tui".to_string(),
cwd: AbsolutePathBuf::try_from("/workspace").expect("absolute path"),
cwd: AbsolutePathBuf::try_from("/workspace")
.expect("absolute path")
.into(),
process_id: None,
source: CommandExecutionSource::Agent,
status: CommandExecutionStatus::Completed,
@@ -619,7 +619,7 @@ mod tests {
reason: None,
network_approval_context: None,
command: Some("echo hi".to_string()),
cwd: Some(test_path_buf("/tmp").abs()),
cwd: Some(test_path_buf("/tmp").abs().into()),
command_actions: None,
additional_permissions: None,
proposed_execpolicy_amendment: None,
+1 -1
View File
@@ -4697,7 +4697,7 @@ fn exec_approval_request(
reason: Some("needs approval".to_string()),
network_approval_context: None,
command: Some("echo hello".to_string()),
cwd: Some(test_path_buf("/tmp/project").abs()),
cwd: Some(test_path_buf("/tmp/project").abs().into()),
command_actions: None,
additional_permissions: None,
proposed_execpolicy_amendment: None,
+1 -1
View File
@@ -492,7 +492,7 @@ mod tests {
reason: Some("needs approval".to_string()),
network_approval_context: None,
command: Some("echo hello".to_string()),
cwd: Some(test_path_buf("/tmp/project").abs()),
cwd: Some(test_path_buf("/tmp/project").abs().into()),
command_actions: None,
additional_permissions: None,
proposed_execpolicy_amendment: None,
+7 -1
View File
@@ -840,6 +840,12 @@ fn exec_approval_request_from_params(
params: CommandExecutionRequestApprovalParams,
fallback_cwd: &AbsolutePathBuf,
) -> ExecApprovalRequestEvent {
// TODO(anp): Keep this as PathUri once `tui::approval_events::ExecApprovalRequestEvent` and
// approval rendering support foreign paths.
let cwd = params
.cwd
.and_then(|cwd| cwd.to_inferred_abs_path())
.unwrap_or_else(|| fallback_cwd.clone());
ExecApprovalRequestEvent {
call_id: params.item_id,
command: params
@@ -847,7 +853,7 @@ fn exec_approval_request_from_params(
.as_deref()
.map(split_command_string)
.unwrap_or_default(),
cwd: params.cwd.unwrap_or_else(|| fallback_cwd.clone()),
cwd,
reason: params.reason,
network_approval_context: params.network_approval_context,
additional_permissions: params.additional_permissions,
+1 -1
View File
@@ -177,7 +177,7 @@ mod tests {
ThreadItem::CommandExecution {
id: call_id.to_string(),
command: "true".to_string(),
cwd: AbsolutePathBuf::current_dir().expect("current dir"),
cwd: AbsolutePathBuf::current_dir().expect("current dir").into(),
process_id: None,
source: CommandExecutionSource::Agent,
status: CommandExecutionStatus::InProgress,
@@ -521,7 +521,7 @@ async fn live_app_server_command_execution_strips_shell_wrapper() {
item: AppServerThreadItem::CommandExecution {
id: "cmd-1".to_string(),
command: command.clone(),
cwd: test_path_buf("/tmp").abs(),
cwd: test_path_buf("/tmp").abs().into(),
process_id: None,
source: AppServerCommandExecutionSource::UserShell,
status: AppServerCommandExecutionStatus::InProgress,
@@ -543,7 +543,7 @@ async fn live_app_server_command_execution_strips_shell_wrapper() {
item: AppServerThreadItem::CommandExecution {
id: "cmd-1".to_string(),
command,
cwd: test_path_buf("/tmp").abs(),
cwd: test_path_buf("/tmp").abs().into(),
process_id: None,
source: AppServerCommandExecutionSource::UserShell,
status: AppServerCommandExecutionStatus::Completed,
@@ -65,7 +65,7 @@ fn app_server_exec_approval_request_splits_shell_wrapped_command() {
shlex::try_join(["/bin/zsh", "-lc", script])
.expect("round-trippable shell wrapper"),
),
cwd: Some(test_path_buf("/tmp").abs()),
cwd: Some(test_path_buf("/tmp").abs().into()),
command_actions: None,
additional_permissions: None,
proposed_execpolicy_amendment: None,
@@ -107,7 +107,7 @@ fn app_server_exec_approval_request_preserves_permissions_context() {
protocol: codex_app_server_protocol::NetworkApprovalProtocol::Socks5Tcp,
}),
command: Some("ls".to_string()),
cwd: Some(test_path_buf("/tmp").abs()),
cwd: Some(test_path_buf("/tmp").abs().into()),
command_actions: None,
additional_permissions: Some(AppServerAdditionalPermissionProfile {
network: Some(AppServerAdditionalNetworkPermissions {
@@ -64,7 +64,7 @@ fn app_server_exec_approval_request_splits_shell_wrapped_command() {
shlex::try_join(["/bin/zsh", "-lc", script])
.expect("round-trippable shell wrapper"),
),
cwd: Some(test_path_buf("/tmp").abs()),
cwd: Some(test_path_buf("/tmp").abs().into()),
command_actions: None,
additional_permissions: None,
proposed_execpolicy_amendment: None,
@@ -363,7 +363,7 @@ async fn exec_end_without_begin_uses_event_command() {
AppServerThreadItem::CommandExecution {
id: "call-orphan".to_string(),
command: codex_shell_command::parse_command::shlex_join(&command),
cwd,
cwd: cwd.into(),
process_id: None,
source: ExecCommandSource::Agent,
status: AppServerCommandExecutionStatus::Completed,
+2 -2
View File
@@ -821,7 +821,7 @@ pub(super) fn begin_exec_with_source(
let item = AppServerThreadItem::CommandExecution {
id: call_id.to_string(),
command: codex_shell_command::parse_command::shlex_join(&command),
cwd: chat.config.cwd.clone(),
cwd: chat.config.cwd.clone().into(),
process_id: None,
source,
status: AppServerCommandExecutionStatus::InProgress,
@@ -844,7 +844,7 @@ pub(super) fn begin_unified_exec_startup(
let item = AppServerThreadItem::CommandExecution {
id: call_id.to_string(),
command: codex_shell_command::parse_command::shlex_join(&command),
cwd: chat.config.cwd.clone(),
cwd: chat.config.cwd.clone().into(),
process_id: Some(process_id.to_string()),
source: ExecCommandSource::UnifiedExecStartup,
status: AppServerCommandExecutionStatus::InProgress,
@@ -3614,7 +3614,7 @@ async fn chatwidget_exec_and_status_layout_vt100_snapshot() {
AppServerThreadItem::CommandExecution {
id: "c1".into(),
command: codex_shell_command::parse_command::shlex_join(&command),
cwd: cwd.clone(),
cwd: cwd.clone().into(),
process_id: None,
source: ExecCommandSource::Agent,
status: AppServerCommandExecutionStatus::InProgress,
@@ -3629,7 +3629,7 @@ async fn chatwidget_exec_and_status_layout_vt100_snapshot() {
AppServerThreadItem::CommandExecution {
id: "c1".into(),
command: codex_shell_command::parse_command::shlex_join(&command),
cwd,
cwd: cwd.into(),
process_id: None,
source: ExecCommandSource::Agent,
status: AppServerCommandExecutionStatus::Completed,