diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 9007577112..0030092ffc 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -2263,6 +2263,18 @@ pub struct CommandExecutionRequestApprovalParams { pub item_id: String, /// Optional explanatory reason (e.g. request for network access). pub reason: Option, + /// The command to be executed. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub command: Option, + /// The command's working directory. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub cwd: Option, + /// Best-effort parsed command actions for friendly display. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub command_actions: Option>, /// Optional proposed execpolicy amendment to allow similar commands without prompting. pub proposed_execpolicy_amendment: Option, } diff --git a/codex-rs/app-server-test-client/src/main.rs b/codex-rs/app-server-test-client/src/main.rs index 1407c2401d..1e2b94bd8f 100644 --- a/codex-rs/app-server-test-client/src/main.rs +++ b/codex-rs/app-server-test-client/src/main.rs @@ -842,6 +842,9 @@ impl CodexClient { turn_id, item_id, reason, + command, + cwd, + command_actions, proposed_execpolicy_amendment, } = params; @@ -851,6 +854,17 @@ impl CodexClient { if let Some(reason) = reason.as_deref() { println!("< reason: {reason}"); } + if let Some(command) = command.as_deref() { + println!("< command: {command}"); + } + if let Some(cwd) = cwd.as_ref() { + println!("< cwd: {}", cwd.display()); + } + if let Some(command_actions) = command_actions.as_ref() + && !command_actions.is_empty() + { + println!("< command actions: {command_actions:?}"); + } if let Some(execpolicy_amendment) = proposed_execpolicy_amendment.as_ref() { println!("< proposed execpolicy amendment: {execpolicy_amendment:?}"); } diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 470581fe4f..92d3cae049 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -445,7 +445,7 @@ Certain actions (shell commands or modifying files) may require explicit user ap Order of messages: 1. `item/started` — shows the pending `commandExecution` item with `command`, `cwd`, and other fields so you can render the proposed action. -2. `item/commandExecution/requestApproval` (request) — carries the same `itemId`, `threadId`, `turnId`, optionally `reason` or `risk`, plus `parsedCmd` for friendly display. +2. `item/commandExecution/requestApproval` (request) — carries the same `itemId`, `threadId`, `turnId`, optionally `reason`, plus `command`, `cwd`, and `commandActions` for friendly display. 3. Client response — `{ "decision": "accept", "acceptSettings": { "forSession": false } }` or `{ "decision": "decline" }`. 4. `item/completed` — final `commandExecution` item with `status: "completed" | "failed" | "declined"` and execution output. Render this as the authoritative result. diff --git a/codex-rs/app-server/src/bespoke_event_handling.rs b/codex-rs/app-server/src/bespoke_event_handling.rs index f9332e6f61..931c1073aa 100644 --- a/codex-rs/app-server/src/bespoke_event_handling.rs +++ b/codex-rs/app-server/src/bespoke_event_handling.rs @@ -241,6 +241,9 @@ pub(crate) async fn apply_bespoke_event_handling( // and emit the corresponding EventMsg, we repurpose the call_id as the item_id. item_id: item_id.clone(), reason, + command: Some(command_string.clone()), + cwd: Some(cwd.clone()), + command_actions: Some(command_actions.clone()), proposed_execpolicy_amendment: proposed_execpolicy_amendment_v2, }; let rx = outgoing