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

@@ -1146,16 +1146,18 @@
}
]
},
"riskScore": {
"format": "uint8",
"minimum": 0.0,
"type": [
"integer",
"null"
]
},
"status": {
"$ref": "#/definitions/GuardianApprovalReviewStatus"
},
"userAuthorization": {
"anyOf": [
{
"$ref": "#/definitions/GuardianUserAuthorization"
},
{
"type": "null"
}
]
}
},
"required": [
@@ -1353,6 +1355,17 @@
"GuardianRiskLevel": {
"description": "[UNSTABLE] Risk level assigned by guardian approval review.",
"enum": [
"low",
"medium",
"high",
"critical"
],
"type": "string"
},
"GuardianUserAuthorization": {
"description": "[UNSTABLE] Authorization level assigned by guardian approval review.",
"enum": [
"unknown",
"low",
"medium",
"high"

View File

@@ -8095,16 +8095,18 @@
}
]
},
"riskScore": {
"format": "uint8",
"minimum": 0.0,
"type": [
"integer",
"null"
]
},
"status": {
"$ref": "#/definitions/v2/GuardianApprovalReviewStatus"
},
"userAuthorization": {
"anyOf": [
{
"$ref": "#/definitions/v2/GuardianUserAuthorization"
},
{
"type": "null"
}
]
}
},
"required": [
@@ -8302,6 +8304,17 @@
"GuardianRiskLevel": {
"description": "[UNSTABLE] Risk level assigned by guardian approval review.",
"enum": [
"low",
"medium",
"high",
"critical"
],
"type": "string"
},
"GuardianUserAuthorization": {
"description": "[UNSTABLE] Authorization level assigned by guardian approval review.",
"enum": [
"unknown",
"low",
"medium",
"high"

View File

@@ -4854,16 +4854,18 @@
}
]
},
"riskScore": {
"format": "uint8",
"minimum": 0.0,
"type": [
"integer",
"null"
]
},
"status": {
"$ref": "#/definitions/GuardianApprovalReviewStatus"
},
"userAuthorization": {
"anyOf": [
{
"$ref": "#/definitions/GuardianUserAuthorization"
},
{
"type": "null"
}
]
}
},
"required": [
@@ -5061,6 +5063,17 @@
"GuardianRiskLevel": {
"description": "[UNSTABLE] Risk level assigned by guardian approval review.",
"enum": [
"low",
"medium",
"high",
"critical"
],
"type": "string"
},
"GuardianUserAuthorization": {
"description": "[UNSTABLE] Authorization level assigned by guardian approval review.",
"enum": [
"unknown",
"low",
"medium",
"high"

View File

@@ -20,16 +20,18 @@
}
]
},
"riskScore": {
"format": "uint8",
"minimum": 0.0,
"type": [
"integer",
"null"
]
},
"status": {
"$ref": "#/definitions/GuardianApprovalReviewStatus"
},
"userAuthorization": {
"anyOf": [
{
"$ref": "#/definitions/GuardianUserAuthorization"
},
{
"type": "null"
}
]
}
},
"required": [
@@ -227,6 +229,17 @@
"GuardianRiskLevel": {
"description": "[UNSTABLE] Risk level assigned by guardian approval review.",
"enum": [
"low",
"medium",
"high",
"critical"
],
"type": "string"
},
"GuardianUserAuthorization": {
"description": "[UNSTABLE] Authorization level assigned by guardian approval review.",
"enum": [
"unknown",
"low",
"medium",
"high"

View File

@@ -20,16 +20,18 @@
}
]
},
"riskScore": {
"format": "uint8",
"minimum": 0.0,
"type": [
"integer",
"null"
]
},
"status": {
"$ref": "#/definitions/GuardianApprovalReviewStatus"
},
"userAuthorization": {
"anyOf": [
{
"$ref": "#/definitions/GuardianUserAuthorization"
},
{
"type": "null"
}
]
}
},
"required": [
@@ -227,6 +229,17 @@
"GuardianRiskLevel": {
"description": "[UNSTABLE] Risk level assigned by guardian approval review.",
"enum": [
"low",
"medium",
"high",
"critical"
],
"type": "string"
},
"GuardianUserAuthorization": {
"description": "[UNSTABLE] Authorization level assigned by guardian approval review.",
"enum": [
"unknown",
"low",
"medium",
"high"

View File

@@ -3,10 +3,11 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { GuardianApprovalReviewStatus } from "./GuardianApprovalReviewStatus";
import type { GuardianRiskLevel } from "./GuardianRiskLevel";
import type { GuardianUserAuthorization } from "./GuardianUserAuthorization";
/**
* [UNSTABLE] Temporary guardian approval review payload used by
* `item/autoApprovalReview/*` notifications. This shape is expected to change
* soon.
*/
export type GuardianApprovalReview = { status: GuardianApprovalReviewStatus, riskScore: number | null, riskLevel: GuardianRiskLevel | null, rationale: string | null, };
export type GuardianApprovalReview = { status: GuardianApprovalReviewStatus, riskLevel: GuardianRiskLevel | null, userAuthorization: GuardianUserAuthorization | null, rationale: string | null, };

View File

@@ -5,4 +5,4 @@
/**
* [UNSTABLE] Risk level assigned by guardian approval review.
*/
export type GuardianRiskLevel = "low" | "medium" | "high";
export type GuardianRiskLevel = "low" | "medium" | "high" | "critical";

View File

@@ -0,0 +1,8 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* [UNSTABLE] Authorization level assigned by guardian approval review.
*/
export type GuardianUserAuthorization = "unknown" | "low" | "medium" | "high";

View File

@@ -127,6 +127,7 @@ export type { GuardianApprovalReviewAction } from "./GuardianApprovalReviewActio
export type { GuardianApprovalReviewStatus } from "./GuardianApprovalReviewStatus";
export type { GuardianCommandSource } from "./GuardianCommandSource";
export type { GuardianRiskLevel } from "./GuardianRiskLevel";
export type { GuardianUserAuthorization } from "./GuardianUserAuthorization";
export type { HookCompletedNotification } from "./HookCompletedNotification";
export type { HookEventName } from "./HookEventName";
export type { HookExecutionMode } from "./HookExecutionMode";

View File

@@ -225,8 +225,8 @@ pub fn guardian_auto_approval_review_notification(
GuardianApprovalReviewStatus::Aborted
}
},
risk_score: assessment.risk_score,
risk_level: assessment.risk_level.map(Into::into),
user_authorization: assessment.user_authorization.map(Into::into),
rationale: assessment.rationale.clone(),
};
let action = assessment.action.clone().into();

View File

@@ -2091,8 +2091,8 @@ mod tests {
id: "guardian-exec".into(),
turn_id: "turn-1".into(),
status: GuardianAssessmentStatus::InProgress,
risk_score: None,
risk_level: None,
user_authorization: None,
rationale: None,
action: serde_json::from_value(serde_json::json!({
"type": "command",
@@ -2106,8 +2106,8 @@ mod tests {
id: "guardian-exec".into(),
turn_id: "turn-1".into(),
status: GuardianAssessmentStatus::Denied,
risk_score: Some(97),
risk_level: Some(codex_protocol::protocol::GuardianRiskLevel::High),
user_authorization: Some(codex_protocol::protocol::GuardianUserAuthorization::Low),
rationale: Some("Would delete user data.".into()),
action: serde_json::from_value(serde_json::json!({
"type": "command",
@@ -2164,8 +2164,8 @@ mod tests {
id: "guardian-execve".into(),
turn_id: "turn-1".into(),
status: GuardianAssessmentStatus::InProgress,
risk_score: None,
risk_level: None,
user_authorization: None,
rationale: None,
action: serde_json::from_value(serde_json::json!({
"type": "execve",

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,
}
);