mirror of
https://github.com/openai/codex.git
synced 2026-10-21 09:54:56 +00:00
Route escalated approval retries through Guardian (#38616)
## What changed - Bypass extension approval contributors when an approval request includes a retry reason, ensuring the retry is reviewed by Guardian. - Add coverage showing that Guardian can deny an escalated retry even when an extension contributor would approve it, and that the retry reason is included in the review request. GitOrigin-RevId: c8f556623f26fea605898d4b8e89679cebd63e65
This commit is contained in:
@@ -317,6 +317,7 @@ async fn run_guardian_review(
|
||||
.config_layer_stack
|
||||
.requirements()
|
||||
.auto_review_required_for_model(&turn.model_info.slug)
|
||||
&& reasons.retry.is_none()
|
||||
&& options
|
||||
.external_cancel
|
||||
.as_ref()
|
||||
|
||||
@@ -2981,6 +2981,72 @@ async fn guardian_review_does_not_retry_valid_denial() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn escalated_retry_bypasses_extension_approval_and_runs_guardian() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
struct AutoApprovingReviewContributor;
|
||||
|
||||
impl codex_extension_api::ApprovalReviewContributor for AutoApprovingReviewContributor {
|
||||
fn contribute<'a>(
|
||||
&'a self,
|
||||
_session_store: &'a codex_extension_api::ExtensionData,
|
||||
_thread_store: &'a codex_extension_api::ExtensionData,
|
||||
_prompt: &'a str,
|
||||
) -> codex_extension_api::ExtensionFuture<'a, Option<ReviewDecision>> {
|
||||
Box::pin(async move { Some(ReviewDecision::Approved) })
|
||||
}
|
||||
}
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let denial = serde_json::json!({
|
||||
"risk_level": "high",
|
||||
"user_authorization": "unknown",
|
||||
"outcome": "deny",
|
||||
"rationale": "The original attempt was blocked by the sandbox.",
|
||||
})
|
||||
.to_string();
|
||||
let request_log = mount_sse_once(
|
||||
&server,
|
||||
sse(vec![
|
||||
ev_response_created("resp-escalated-retry"),
|
||||
ev_assistant_message("msg-escalated-retry", &denial),
|
||||
ev_completed("resp-escalated-retry"),
|
||||
]),
|
||||
)
|
||||
.await;
|
||||
|
||||
let (mut session, turn) = guardian_test_session_and_turn(&server).await;
|
||||
let mut extensions = codex_extension_api::ExtensionRegistryBuilder::<Config>::new();
|
||||
extensions.approval_review_contributor(Arc::new(AutoApprovingReviewContributor));
|
||||
Arc::get_mut(&mut session)
|
||||
.expect("session should be uniquely owned")
|
||||
.services
|
||||
.extensions = Arc::new(extensions.build());
|
||||
seed_guardian_parent_history(&session, &turn).await;
|
||||
|
||||
let retry_reason = "The sandbox blocked the original command.";
|
||||
let decision = review_approval_request(
|
||||
&session,
|
||||
&turn,
|
||||
"review-escalated-retry".to_string(),
|
||||
guardian_shell_request("shell-escalated-retry"),
|
||||
ApprovalRequestReasons {
|
||||
approval: None,
|
||||
retry: Some(retry_reason.to_string()),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(matches!(decision, ReviewDecision::Denied { .. }));
|
||||
assert!(
|
||||
request_log
|
||||
.single_request()
|
||||
.body_contains_text(retry_reason)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() -> anyhow::Result<()>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user