From 80721b465fdf25c95e70391851c4b0233d6882e1 Mon Sep 17 00:00:00 2001 From: ashwinnathan-openai Date: Mon, 16 Mar 2026 19:12:11 -0700 Subject: [PATCH] route manual write_stdin approvals through guardian flow Co-authored-by: Codex --- .../core/src/tools/runtimes/unified_exec.rs | 120 ++++++++++++------ .../core/src/unified_exec/process_manager.rs | 34 ++--- 2 files changed, 100 insertions(+), 54 deletions(-) diff --git a/codex-rs/core/src/tools/runtimes/unified_exec.rs b/codex-rs/core/src/tools/runtimes/unified_exec.rs index 22fc732f60..ba86a00c8d 100644 --- a/codex-rs/core/src/tools/runtimes/unified_exec.rs +++ b/codex-rs/core/src/tools/runtimes/unified_exec.rs @@ -38,11 +38,14 @@ use crate::unified_exec::UnifiedExecError; use crate::unified_exec::UnifiedExecProcess; use crate::unified_exec::UnifiedExecProcessManager; use codex_network_proxy::NetworkProxy; +use codex_protocol::approvals::ExecPolicyAmendment; +use codex_protocol::approvals::NetworkApprovalContext; use codex_protocol::models::PermissionProfile; use codex_protocol::protocol::ReviewDecision; use futures::future::BoxFuture; use std::collections::HashMap; use std::path::PathBuf; +use std::sync::Arc; #[derive(Clone, Debug)] pub struct UnifiedExecRequest { @@ -83,6 +86,68 @@ impl<'a> UnifiedExecRuntime<'a> { } } +#[allow(clippy::too_many_arguments)] +pub(crate) async fn request_unified_exec_approval( + session: &Arc, + turn: &Arc, + call_id: String, + command: Vec, + cwd: PathBuf, + sandbox_permissions: SandboxPermissions, + additional_permissions: Option, + justification: Option, + tty: bool, + retry_reason: Option, + network_approval_context: Option, + proposed_execpolicy_amendment: Option, + approval_keys: Option>, +) -> ReviewDecision { + let reason = retry_reason.clone().or_else(|| justification.clone()); + + if routes_approval_to_guardian(turn) { + return review_approval_request( + session, + turn, + GuardianApprovalRequest::ExecCommand { + id: call_id, + command, + cwd, + sandbox_permissions, + additional_permissions, + justification, + tty, + }, + retry_reason, + ) + .await; + } + + let request_approval = || async { + let available_decisions = None; + session + .request_command_approval( + turn, + call_id, + /*approval_id*/ None, + command, + cwd, + reason, + network_approval_context, + proposed_execpolicy_amendment, + additional_permissions, + /*skill_metadata*/ None, + available_decisions, + ) + .await + }; + + if let Some(keys) = approval_keys { + with_cached_approval(&session.services, "unified_exec", keys, request_approval).await + } else { + request_approval().await + } +} + impl Sandboxable for UnifiedExecRuntime<'_> { fn sandbox_preference(&self) -> SandboxablePreference { SandboxablePreference::Auto @@ -118,45 +183,24 @@ impl Approvable for UnifiedExecRuntime<'_> { let command = req.command.clone(); let cwd = req.cwd.clone(); let retry_reason = ctx.retry_reason.clone(); - let reason = retry_reason.clone().or_else(|| req.justification.clone()); Box::pin(async move { - if routes_approval_to_guardian(turn) { - return review_approval_request( - session, - turn, - GuardianApprovalRequest::ExecCommand { - id: call_id, - command, - cwd, - sandbox_permissions: req.sandbox_permissions, - additional_permissions: req.additional_permissions.clone(), - justification: req.justification.clone(), - tty: req.tty, - }, - retry_reason, - ) - .await; - } - with_cached_approval(&session.services, "unified_exec", keys, || async move { - let available_decisions = None; - session - .request_command_approval( - turn, - call_id, - /*approval_id*/ None, - command, - cwd, - reason, - ctx.network_approval_context.clone(), - req.exec_approval_requirement - .proposed_execpolicy_amendment() - .cloned(), - req.additional_permissions.clone(), - /*skill_metadata*/ None, - available_decisions, - ) - .await - }) + request_unified_exec_approval( + session, + turn, + call_id, + command, + cwd, + req.sandbox_permissions, + req.additional_permissions.clone(), + req.justification.clone(), + req.tty, + retry_reason, + ctx.network_approval_context.clone(), + req.exec_approval_requirement + .proposed_execpolicy_amendment() + .cloned(), + Some(keys), + ) .await }) } diff --git a/codex-rs/core/src/unified_exec/process_manager.rs b/codex-rs/core/src/unified_exec/process_manager.rs index b7c1e06658..4beb3fbd5f 100644 --- a/codex-rs/core/src/unified_exec/process_manager.rs +++ b/codex-rs/core/src/unified_exec/process_manager.rs @@ -17,6 +17,7 @@ use crate::exec_env::create_env; use crate::exec_policy::ExecApprovalRequest; use crate::protocol::ExecCommandSource; use crate::sandboxing::ExecRequest; +use crate::sandboxing::SandboxPermissions; use crate::tools::context::ExecCommandToolOutput; use crate::tools::events::ToolEmitter; use crate::tools::events::ToolEventCtx; @@ -26,6 +27,7 @@ use crate::tools::network_approval::finish_deferred_network_approval; use crate::tools::orchestrator::ToolOrchestrator; use crate::tools::runtimes::unified_exec::UnifiedExecRequest as UnifiedExecToolRequest; use crate::tools::runtimes::unified_exec::UnifiedExecRuntime; +use crate::tools::runtimes::unified_exec::request_unified_exec_approval; use crate::tools::sandboxing::ToolCtx; use crate::truncate::approx_token_count; use crate::unified_exec::ExecCommandRequest; @@ -340,22 +342,22 @@ impl UnifiedExecProcessManager { } else { Some(format!("interactive terminal input: {input}")) }; - let decision = context - .session - .request_command_approval( - context.turn.as_ref(), - context.call_id.clone(), - /*approval_id*/ None, - session_command.clone(), - cwd.clone(), - reason, - /*network_approval_context*/ None, - /*proposed_execpolicy_amendment*/ None, - /*additional_permissions*/ None, - /*skill_metadata*/ None, - /*available_decisions*/ None, - ) - .await; + let decision = request_unified_exec_approval( + &context.session, + &context.turn, + context.call_id.clone(), + session_command.clone(), + cwd.clone(), + SandboxPermissions::UseDefault, + /*additional_permissions*/ None, + reason, + tty, + /*retry_reason*/ None, + /*network_approval_context*/ None, + /*proposed_execpolicy_amendment*/ None, + /*approval_keys*/ None, + ) + .await; if !matches!( decision, codex_protocol::protocol::ReviewDecision::Approved