route manual write_stdin approvals through guardian flow

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
ashwinnathan-openai
2026-03-16 19:12:11 -07:00
co-authored by Codex
parent 5cc05f66aa
commit 80721b465f
2 changed files with 100 additions and 54 deletions
@@ -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<crate::codex::Session>,
turn: &Arc<crate::codex::TurnContext>,
call_id: String,
command: Vec<String>,
cwd: PathBuf,
sandbox_permissions: SandboxPermissions,
additional_permissions: Option<PermissionProfile>,
justification: Option<String>,
tty: bool,
retry_reason: Option<String>,
network_approval_context: Option<NetworkApprovalContext>,
proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
approval_keys: Option<Vec<UnifiedExecApprovalKey>>,
) -> 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<UnifiedExecRequest> 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
})
}
@@ -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