mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat(core): plumb distinct approval ids for command approvals (#12051)
zsh fork PR stack: - https://github.com/openai/codex/pull/12051 👈 - https://github.com/openai/codex/pull/12052 With upcoming support for a fork of zsh that allows us to intercept `execve` and run execpolicy checks for each subcommand as part of a `CommandExecution`, it will be possible for there to be multiple approval requests for a shell command like `/path/to/zsh -lc 'git status && rg \"TODO\" src && make test'`. To support that, this PR introduces a new `approval_id` field across core, protocol, and app-server so that we can associate approvals properly for subcommands.
This commit is contained in:
@@ -2155,7 +2155,7 @@ impl Session {
|
||||
|
||||
/// Emit an exec approval request event and await the user's decision.
|
||||
///
|
||||
/// The request is keyed by `call_id` so matching responses are delivered
|
||||
/// The request is keyed by `call_id` + `approval_id` so matching responses are delivered
|
||||
/// to the correct in-flight turn. If the task is aborted, this returns the
|
||||
/// default `ReviewDecision` (`Denied`).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -2163,32 +2163,36 @@ impl Session {
|
||||
&self,
|
||||
turn_context: &TurnContext,
|
||||
call_id: String,
|
||||
approval_id: Option<String>,
|
||||
command: Vec<String>,
|
||||
cwd: PathBuf,
|
||||
reason: Option<String>,
|
||||
network_approval_context: Option<NetworkApprovalContext>,
|
||||
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
||||
) -> ReviewDecision {
|
||||
// command-level approvals use `call_id`.
|
||||
// `approval_id` is only present for subcommand callbacks (execve intercept)
|
||||
let effective_approval_id = approval_id.clone().unwrap_or_else(|| call_id.clone());
|
||||
// Add the tx_approve callback to the map before sending the request.
|
||||
let (tx_approve, rx_approve) = oneshot::channel();
|
||||
let approval_id = call_id.clone();
|
||||
let prev_entry = {
|
||||
let mut active = self.active_turn.lock().await;
|
||||
match active.as_mut() {
|
||||
Some(at) => {
|
||||
let mut ts = at.turn_state.lock().await;
|
||||
ts.insert_pending_approval(approval_id.clone(), tx_approve)
|
||||
ts.insert_pending_approval(effective_approval_id.clone(), tx_approve)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
};
|
||||
if prev_entry.is_some() {
|
||||
warn!("Overwriting existing pending approval for call_id: {approval_id}");
|
||||
warn!("Overwriting existing pending approval for call_id: {effective_approval_id}");
|
||||
}
|
||||
|
||||
let parsed_cmd = parse_command(&command);
|
||||
let event = EventMsg::ExecApprovalRequest(ExecApprovalRequestEvent {
|
||||
call_id,
|
||||
approval_id,
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
command,
|
||||
cwd,
|
||||
|
||||
@@ -311,8 +311,10 @@ async fn handle_exec_approval(
|
||||
event: ExecApprovalRequestEvent,
|
||||
cancel_token: &CancellationToken,
|
||||
) {
|
||||
let approval_id_for_op = event.effective_approval_id();
|
||||
let ExecApprovalRequestEvent {
|
||||
call_id,
|
||||
approval_id,
|
||||
command,
|
||||
cwd,
|
||||
reason,
|
||||
@@ -320,23 +322,28 @@ async fn handle_exec_approval(
|
||||
proposed_execpolicy_amendment,
|
||||
..
|
||||
} = event;
|
||||
let approval_id = call_id.clone();
|
||||
// Race approval with cancellation and timeout to avoid hangs.
|
||||
let approval_fut = parent_session.request_command_approval(
|
||||
parent_ctx,
|
||||
call_id,
|
||||
approval_id,
|
||||
command,
|
||||
cwd,
|
||||
reason,
|
||||
network_approval_context,
|
||||
proposed_execpolicy_amendment,
|
||||
);
|
||||
let decision =
|
||||
await_approval_with_cancel(approval_fut, parent_session, &approval_id, cancel_token).await;
|
||||
let decision = await_approval_with_cancel(
|
||||
approval_fut,
|
||||
parent_session,
|
||||
&approval_id_for_op,
|
||||
cancel_token,
|
||||
)
|
||||
.await;
|
||||
|
||||
let _ = codex
|
||||
.submit(Op::ExecApproval {
|
||||
id: approval_id,
|
||||
id: approval_id_for_op,
|
||||
turn_id: Some(turn_id),
|
||||
decision,
|
||||
})
|
||||
|
||||
@@ -247,6 +247,7 @@ impl NetworkApprovalService {
|
||||
.request_command_approval(
|
||||
turn_context.as_ref(),
|
||||
attempt.call_id.clone(),
|
||||
None,
|
||||
attempt.command.clone(),
|
||||
attempt.cwd.clone(),
|
||||
Some(format!(
|
||||
|
||||
@@ -109,6 +109,7 @@ impl Approvable<ShellRequest> for ShellRuntime {
|
||||
.request_command_approval(
|
||||
turn,
|
||||
call_id,
|
||||
None,
|
||||
command,
|
||||
cwd,
|
||||
reason,
|
||||
|
||||
@@ -110,6 +110,7 @@ impl Approvable<UnifiedExecRequest> for UnifiedExecRuntime<'_> {
|
||||
.request_command_approval(
|
||||
turn,
|
||||
call_id,
|
||||
None,
|
||||
command,
|
||||
cwd,
|
||||
reason,
|
||||
|
||||
Reference in New Issue
Block a user