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:
Owen Lin
2026-02-17 17:55:57 -08:00
committed by GitHub
parent b3a8571219
commit db4d2599b5
33 changed files with 331 additions and 114 deletions

View File

@@ -1603,7 +1603,7 @@ async fn run_scenario(scenario: &ScenarioSpec) -> Result<()> {
}
test.codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: decision.clone(),
})
@@ -1823,7 +1823,7 @@ async fn approving_execpolicy_amendment_persists_policy_and_skips_future_prompts
test.codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::ApprovedExecpolicyAmendment {
proposed_execpolicy_amendment: expected_execpolicy_amendment.clone(),
@@ -1988,7 +1988,7 @@ async fn compound_command_with_one_safe_command_still_requires_approval() -> Res
let approval = expect_exec_approval(&test, expected_command.as_str()).await;
test.codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::Denied,
})

View File

@@ -100,7 +100,7 @@ async fn codex_delegate_forwards_exec_approval_and_proceeds_on_approval() {
// Approve via parent using the emitted approval call ID.
test.codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::Approved,
})

View File

@@ -1042,7 +1042,7 @@ async fn handle_container_exec_user_approved_records_tool_decision() {
codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::Approved,
})
@@ -1108,7 +1108,7 @@ async fn handle_container_exec_user_approved_for_session_records_tool_decision()
codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::ApprovedForSession,
})
@@ -1174,7 +1174,7 @@ async fn handle_sandbox_error_user_approves_retry_records_tool_decision() {
codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::Approved,
})
@@ -1240,7 +1240,7 @@ async fn handle_container_exec_user_denies_records_tool_decision() {
codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::Denied,
})
@@ -1306,7 +1306,7 @@ async fn handle_sandbox_error_user_approves_for_session_records_tool_decision()
codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::ApprovedForSession,
})
@@ -1373,7 +1373,7 @@ async fn handle_sandbox_error_user_denies_records_tool_decision() {
codex
.submit(Op::ExecApproval {
id: approval.call_id,
id: approval.effective_approval_id(),
turn_id: None,
decision: ReviewDecision::Denied,
})