diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 9dd602e2..73bb7149 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -279,6 +279,7 @@ impl Session { pub async fn request_patch_approval( &self, sub_id: String, + call_id: String, action: &ApplyPatchAction, reason: Option, grant_root: Option, @@ -287,6 +288,7 @@ impl Session { let event = Event { id: sub_id.clone(), msg: EventMsg::ApplyPatchApprovalRequest(ApplyPatchApprovalRequestEvent { + call_id, changes: convert_apply_patch_to_protocol(action), reason, grant_root, @@ -1629,7 +1631,7 @@ async fn apply_patch( // Compute a readable summary of path changes to include in the // approval request so the user can make an informed decision. let rx_approve = sess - .request_patch_approval(sub_id.clone(), &action, None, None) + .request_patch_approval(sub_id.clone(), call_id.clone(), &action, None, None) .await; match rx_approve.await.unwrap_or_default() { ReviewDecision::Approved | ReviewDecision::ApprovedForSession => false, @@ -1667,7 +1669,13 @@ async fn apply_patch( )); let rx = sess - .request_patch_approval(sub_id.clone(), &action, reason.clone(), Some(root.clone())) + .request_patch_approval( + sub_id.clone(), + call_id.clone(), + &action, + reason.clone(), + Some(root.clone()), + ) .await; if !matches!( @@ -1751,6 +1759,7 @@ async fn apply_patch( let rx = sess .request_patch_approval( sub_id.clone(), + call_id.clone(), &action, reason.clone(), Some(root.clone()), diff --git a/codex-rs/core/src/protocol.rs b/codex-rs/core/src/protocol.rs index 943c0b92..cc201bc7 100644 --- a/codex-rs/core/src/protocol.rs +++ b/codex-rs/core/src/protocol.rs @@ -435,6 +435,8 @@ pub struct ExecApprovalRequestEvent { #[derive(Debug, Clone, Deserialize, Serialize)] pub struct ApplyPatchApprovalRequestEvent { + /// Responses API call id for the associated patch apply call, if available. + pub call_id: String, pub changes: HashMap, /// Optional explanatory reason (e.g. request for extra write access). #[serde(skip_serializing_if = "Option::is_none")] diff --git a/codex-rs/mcp-server/src/codex_tool_runner.rs b/codex-rs/mcp-server/src/codex_tool_runner.rs index 2b7b08f9..4e10d158 100644 --- a/codex-rs/mcp-server/src/codex_tool_runner.rs +++ b/codex-rs/mcp-server/src/codex_tool_runner.rs @@ -156,7 +156,7 @@ async fn run_codex_tool_session_inner( EventMsg::ExecApprovalRequest(ExecApprovalRequestEvent { command, cwd, - call_id: _, + call_id, reason: _, }) => { handle_exec_approval_request( @@ -167,6 +167,7 @@ async fn run_codex_tool_session_inner( request_id.clone(), request_id_str.clone(), event.id.clone(), + call_id, ) .await; continue; @@ -180,11 +181,13 @@ async fn run_codex_tool_session_inner( break; } EventMsg::ApplyPatchApprovalRequest(ApplyPatchApprovalRequestEvent { + call_id, reason, grant_root, changes, }) => { handle_patch_approval_request( + call_id, reason, grant_root, changes, diff --git a/codex-rs/mcp-server/src/exec_approval.rs b/codex-rs/mcp-server/src/exec_approval.rs index fc0c41d0..f073214b 100644 --- a/codex-rs/mcp-server/src/exec_approval.rs +++ b/codex-rs/mcp-server/src/exec_approval.rs @@ -32,6 +32,7 @@ pub struct ExecApprovalElicitRequestParams { pub codex_elicitation: String, pub codex_mcp_tool_call_id: String, pub codex_event_id: String, + pub codex_call_id: String, pub codex_command: Vec, pub codex_cwd: PathBuf, } @@ -45,6 +46,7 @@ pub struct ExecApprovalResponse { pub decision: ReviewDecision, } +#[allow(clippy::too_many_arguments)] pub(crate) async fn handle_exec_approval_request( command: Vec, cwd: PathBuf, @@ -53,6 +55,7 @@ pub(crate) async fn handle_exec_approval_request( request_id: RequestId, tool_call_id: String, event_id: String, + call_id: String, ) { let escaped_command = shlex::try_join(command.iter().map(|s| s.as_str())).unwrap_or_else(|_| command.join(" ")); @@ -71,6 +74,7 @@ pub(crate) async fn handle_exec_approval_request( codex_elicitation: "exec-approval".to_string(), codex_mcp_tool_call_id: tool_call_id.clone(), codex_event_id: event_id.clone(), + codex_call_id: call_id, codex_command: command, codex_cwd: cwd, }; diff --git a/codex-rs/mcp-server/src/patch_approval.rs b/codex-rs/mcp-server/src/patch_approval.rs index bfccfa50..db99ee5f 100644 --- a/codex-rs/mcp-server/src/patch_approval.rs +++ b/codex-rs/mcp-server/src/patch_approval.rs @@ -27,6 +27,7 @@ pub struct PatchApprovalElicitRequestParams { pub codex_elicitation: String, pub codex_mcp_tool_call_id: String, pub codex_event_id: String, + pub codex_call_id: String, #[serde(skip_serializing_if = "Option::is_none")] pub codex_reason: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -41,6 +42,7 @@ pub struct PatchApprovalResponse { #[allow(clippy::too_many_arguments)] pub(crate) async fn handle_patch_approval_request( + call_id: String, reason: Option, grant_root: Option, changes: HashMap, @@ -66,6 +68,7 @@ pub(crate) async fn handle_patch_approval_request( codex_elicitation: "patch-approval".to_string(), codex_mcp_tool_call_id: tool_call_id.clone(), codex_event_id: event_id.clone(), + codex_call_id: call_id, codex_reason: reason, codex_grant_root: grant_root, codex_changes: changes, diff --git a/codex-rs/mcp-server/tests/codex_tool.rs b/codex-rs/mcp-server/tests/codex_tool.rs index d36813ce..0e31eea4 100644 --- a/codex-rs/mcp-server/tests/codex_tool.rs +++ b/codex-rs/mcp-server/tests/codex_tool.rs @@ -171,6 +171,7 @@ fn create_expected_elicitation_request( codex_event_id, codex_command: command, codex_cwd: workdir.to_path_buf(), + codex_call_id: "call1234".to_string(), })?), }) } @@ -384,6 +385,7 @@ fn create_expected_patch_approval_elicitation_request( codex_reason: reason, codex_grant_root: grant_root, codex_changes: changes, + codex_call_id: "call1234".to_string(), })?), }) } diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 51f4df52..38565870 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -328,6 +328,7 @@ impl ChatWidget<'_> { self.bottom_pane.push_approval_request(request); } EventMsg::ApplyPatchApprovalRequest(ApplyPatchApprovalRequestEvent { + call_id: _, changes, reason, grant_root,