mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
.
This commit is contained in:
@@ -120,50 +120,81 @@ impl ToolOrchestrator {
|
||||
codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(),
|
||||
};
|
||||
|
||||
let mut out = tool.run(req, &initial_attempt, tool_ctx).await;
|
||||
match tool.run(req, &initial_attempt, tool_ctx).await {
|
||||
Ok(out) => Ok(out),
|
||||
Err(ToolError::Codex(CodexErr::Sandbox(SandboxErr::Denied { output }))) => {
|
||||
if !tool.escalate_on_failure() {
|
||||
return Err(ToolError::Codex(CodexErr::Sandbox(SandboxErr::Denied {
|
||||
output,
|
||||
})));
|
||||
}
|
||||
|
||||
if matches!(initial_sandbox, crate::exec::SandboxType::None) {
|
||||
// If we already skipped sandboxing on the first attempt, there's no
|
||||
// fallback path.
|
||||
return out;
|
||||
}
|
||||
if !tool.wants_no_sandbox_approval(approval_policy) {
|
||||
return Err(ToolError::Codex(CodexErr::Sandbox(SandboxErr::Denied {
|
||||
output,
|
||||
})));
|
||||
}
|
||||
|
||||
// 3) Retry without sandbox on approval.
|
||||
if matches!(&out, Err(ToolError::Codex(CodexErr::Sandbox(_))))
|
||||
&& tool.sandbox_preference() != crate::tools::sandboxing::SandboxablePreference::Require
|
||||
&& tool.escalate_on_failure()
|
||||
&& tool.should_bypass_approval(approval_policy, already_approved)
|
||||
{
|
||||
// Attempt a retry without sandbox.
|
||||
out = tool.run(
|
||||
req,
|
||||
&SandboxAttempt {
|
||||
if !tool.should_bypass_approval(approval_policy, already_approved) {
|
||||
let mut risk = None;
|
||||
|
||||
if let Some(metadata) = req.sandbox_retry_data() {
|
||||
let err = SandboxErr::Denied {
|
||||
output: output.clone(),
|
||||
};
|
||||
let friendly = get_error_message_ui(&CodexErr::Sandbox(err));
|
||||
let failure_summary = format!("failed in sandbox: {friendly}");
|
||||
|
||||
risk = tool_ctx
|
||||
.session
|
||||
.assess_sandbox_command(
|
||||
turn_ctx,
|
||||
&tool_ctx.call_id,
|
||||
&metadata.command,
|
||||
Some(failure_summary.as_str()),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
let reason_msg = build_denial_reason_from_output(output.as_ref());
|
||||
let approval_ctx = ApprovalCtx {
|
||||
session: tool_ctx.session,
|
||||
turn: turn_ctx,
|
||||
call_id: &tool_ctx.call_id,
|
||||
retry_reason: Some(reason_msg),
|
||||
risk,
|
||||
};
|
||||
|
||||
let decision = tool.start_approval_async(req, approval_ctx).await;
|
||||
otel.tool_decision(otel_tn, otel_ci, &decision, otel_user);
|
||||
|
||||
match decision {
|
||||
ReviewDecision::Denied | ReviewDecision::Abort => {
|
||||
return Err(ToolError::Rejected("rejected by user".to_string()));
|
||||
}
|
||||
ReviewDecision::Approved
|
||||
| ReviewDecision::ApprovedExecpolicyAmendment { .. }
|
||||
| ReviewDecision::ApprovedForSession => {}
|
||||
}
|
||||
}
|
||||
|
||||
let escalated_attempt = SandboxAttempt {
|
||||
sandbox: crate::exec::SandboxType::None,
|
||||
policy: &crate::protocol::SandboxPolicy::DangerFullAccess,
|
||||
policy: &turn_ctx.sandbox_policy,
|
||||
manager: &self.sandbox,
|
||||
sandbox_cwd: &turn_ctx.cwd,
|
||||
codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(),
|
||||
},
|
||||
tool_ctx,
|
||||
)
|
||||
.await;
|
||||
if let Err(ToolError::Codex(CodexErr::Sandbox(SandboxErr::Denied { output }))) = &out {
|
||||
return Err(ToolError::Rejected(format!(
|
||||
"sandbox denied the command, even after approving it without sandbox: {}",
|
||||
get_error_message_ui(output)
|
||||
)));
|
||||
codex_linux_sandbox_exe: None,
|
||||
};
|
||||
|
||||
tool.run(req, &escalated_attempt, tool_ctx).await
|
||||
}
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
/// Translate result from tool runner to library level result (errors not in ToolError become CodexErr).
|
||||
pub fn translate_response(result: Result<ExecToolCallOutput, ToolError>) -> Result<(), CodexErr> {
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(ToolError::Codex(err)) => Err(err),
|
||||
Err(ToolError::Rejected(reason)) => Err(CodexErr::Rejected(reason)),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_denial_reason_from_output(_output: &ExecToolCallOutput) -> String {
|
||||
// Keep approval reason terse and stable for UX/tests, but accept the
|
||||
// output so we can evolve heuristics later without touching call sites.
|
||||
"command failed; retry without sandbox?".to_string()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user