Update guardian output schema (#17061)

## Summary
- Update guardian output schema to separate risk, authorization,
outcome, and rationale.
- Feed guardian rationale into rejection messages.
- Split the guardian policy into template and tenant-config sections.

## Validation
- `cargo test -p codex-core mcp_tool_call`
- `env -u CODEX_SANDBOX_NETWORK_DISABLED INSTA_UPDATE=always cargo test
-p codex-core guardian::`

---------

Co-authored-by: Owen Lin <owen@openai.com>
This commit is contained in:
maja-openai
2026-04-08 15:47:29 -07:00
committed by GitHub
parent 49677ec71f
commit dcbc91fd39
45 changed files with 673 additions and 312 deletions

View File

@@ -54,6 +54,7 @@ use codex_protocol::protocol::ExecCommandSource as CoreExecCommandSource;
use codex_protocol::protocol::ExecCommandStatus as CoreExecCommandStatus;
use codex_protocol::protocol::GranularApprovalConfig as CoreGranularApprovalConfig;
use codex_protocol::protocol::GuardianRiskLevel as CoreGuardianRiskLevel;
use codex_protocol::protocol::GuardianUserAuthorization as CoreGuardianUserAuthorization;
use codex_protocol::protocol::HookEventName as CoreHookEventName;
use codex_protocol::protocol::HookExecutionMode as CoreHookExecutionMode;
use codex_protocol::protocol::HookHandlerType as CoreHookHandlerType;
@@ -4496,6 +4497,7 @@ pub enum GuardianRiskLevel {
Low,
Medium,
High,
Critical,
}
impl From<CoreGuardianRiskLevel> for GuardianRiskLevel {
@@ -4504,6 +4506,29 @@ impl From<CoreGuardianRiskLevel> for GuardianRiskLevel {
CoreGuardianRiskLevel::Low => Self::Low,
CoreGuardianRiskLevel::Medium => Self::Medium,
CoreGuardianRiskLevel::High => Self::High,
CoreGuardianRiskLevel::Critical => Self::Critical,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "lowercase")]
#[ts(export_to = "v2/")]
/// [UNSTABLE] Authorization level assigned by guardian approval review.
pub enum GuardianUserAuthorization {
Unknown,
Low,
Medium,
High,
}
impl From<CoreGuardianUserAuthorization> for GuardianUserAuthorization {
fn from(value: CoreGuardianUserAuthorization) -> Self {
match value {
CoreGuardianUserAuthorization::Unknown => Self::Unknown,
CoreGuardianUserAuthorization::Low => Self::Low,
CoreGuardianUserAuthorization::Medium => Self::Medium,
CoreGuardianUserAuthorization::High => Self::High,
}
}
}
@@ -4516,9 +4541,8 @@ impl From<CoreGuardianRiskLevel> for GuardianRiskLevel {
#[ts(export_to = "v2/")]
pub struct GuardianApprovalReview {
pub status: GuardianApprovalReviewStatus,
#[ts(type = "number | null")]
pub risk_score: Option<u8>,
pub risk_level: Option<GuardianRiskLevel>,
pub user_authorization: Option<GuardianUserAuthorization>,
pub rationale: Option<String>,
}
@@ -7789,8 +7813,8 @@ mod tests {
fn automatic_approval_review_deserializes_aborted_status() {
let review: GuardianApprovalReview = serde_json::from_value(json!({
"status": "aborted",
"riskScore": null,
"riskLevel": null,
"userAuthorization": null,
"rationale": null
}))
.expect("aborted automatic review should deserialize");
@@ -7798,8 +7822,8 @@ mod tests {
review,
GuardianApprovalReview {
status: GuardianApprovalReviewStatus::Aborted,
risk_score: None,
risk_level: None,
user_authorization: None,
rationale: None,
}
);