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

@@ -1003,6 +1003,7 @@ mod tests {
let params = v1::ExecCommandApprovalParams {
conversation_id,
call_id: "call-42".to_string(),
approval_id: Some("approval-42".to_string()),
command: vec!["echo".to_string(), "hello".to_string()],
cwd: PathBuf::from("/tmp"),
reason: Some("because tests".to_string()),
@@ -1022,6 +1023,7 @@ mod tests {
"params": {
"conversationId": "67e55044-10b1-426f-9247-bb680e5fe0c8",
"callId": "call-42",
"approvalId": "approval-42",
"command": ["echo", "hello"],
"cwd": "/tmp",
"reason": "because tests",

View File

@@ -258,6 +258,8 @@ pub struct ExecCommandApprovalParams {
/// Use to correlate this with [codex_core::protocol::ExecCommandBeginEvent]
/// and [codex_core::protocol::ExecCommandEndEvent].
pub call_id: String,
/// Identifier for this specific approval callback.
pub approval_id: Option<String>,
pub command: Vec<String>,
pub cwd: PathBuf,
pub reason: Option<String>,

View File

@@ -3181,7 +3181,18 @@ pub struct CommandExecutionRequestApprovalParams {
pub thread_id: String,
pub turn_id: String,
pub item_id: String,
/// Unique identifier for this specific approval callback.
///
/// For regular shell/unified_exec approvals, this is null.
///
/// For zsh-exec-bridge subcommand approvals, multiple callbacks can belong to
/// one parent `itemId`, so `approvalId` is a distinct opaque callback id
/// (a UUID) used to disambiguate routing.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable)]
pub approval_id: Option<String>,
/// Optional explanatory reason (e.g. request for network access).
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable)]
pub reason: Option<String>,
/// The command to be executed.
@@ -3197,6 +3208,7 @@ pub struct CommandExecutionRequestApprovalParams {
#[ts(optional = nullable)]
pub command_actions: Option<Vec<CommandAction>>,
/// Optional proposed execpolicy amendment to allow similar commands without prompting.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional = nullable)]
pub proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
}