Add safety check notification and error handling (#19055)

Adds a new app-server notification that fires when a user account has
been flagged for potential safety reasons.
This commit is contained in:
Eric Traut
2026-04-22 22:24:12 -07:00
committed by GitHub
Unverified
parent 02170996e6
commit bbff4ee61a
61 changed files with 1414 additions and 15 deletions
+5
View File
@@ -70,6 +70,8 @@ pub(super) use codex_app_server_protocol::MarketplaceInterface;
pub(super) use codex_app_server_protocol::McpServerStartupState;
pub(super) use codex_app_server_protocol::McpServerStatusDetail;
pub(super) use codex_app_server_protocol::McpServerStatusUpdatedNotification;
pub(super) use codex_app_server_protocol::ModelVerification as AppServerModelVerification;
pub(super) use codex_app_server_protocol::ModelVerificationNotification;
pub(super) use codex_app_server_protocol::PatchApplyStatus as AppServerPatchApplyStatus;
pub(super) use codex_app_server_protocol::PatchChangeKind;
pub(super) use codex_app_server_protocol::PermissionsRequestApprovalParams as AppServerPermissionsRequestApprovalParams;
@@ -147,6 +149,7 @@ pub(super) use codex_protocol::protocol::CodexErrorInfo;
pub(super) use codex_protocol::protocol::CollabAgentSpawnBeginEvent;
pub(super) use codex_protocol::protocol::CollabAgentSpawnEndEvent;
pub(super) use codex_protocol::protocol::CreditsSnapshot;
pub(super) use codex_protocol::protocol::ErrorEvent;
pub(super) use codex_protocol::protocol::Event;
pub(super) use codex_protocol::protocol::EventMsg;
pub(super) use codex_protocol::protocol::ExecApprovalRequestEvent;
@@ -169,6 +172,8 @@ pub(super) use codex_protocol::protocol::ItemCompletedEvent;
pub(super) use codex_protocol::protocol::McpStartupCompleteEvent;
pub(super) use codex_protocol::protocol::McpStartupStatus;
pub(super) use codex_protocol::protocol::McpStartupUpdateEvent;
pub(super) use codex_protocol::protocol::ModelVerification as CoreModelVerification;
pub(super) use codex_protocol::protocol::ModelVerificationEvent;
pub(super) use codex_protocol::protocol::NonSteerableTurnKind;
pub(super) use codex_protocol::protocol::Op;
pub(super) use codex_protocol::protocol::PatchApplyBeginEvent;
@@ -678,6 +678,71 @@ async fn live_app_server_server_overloaded_error_renders_warning() {
assert!(!chat.bottom_pane.is_task_running());
}
#[tokio::test]
async fn live_app_server_cyber_policy_error_renders_dedicated_notice() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.handle_server_notification(
ServerNotification::TurnStarted(TurnStartedNotification {
thread_id: "thread-1".to_string(),
turn: AppServerTurn {
id: "turn-1".to_string(),
items: Vec::new(),
status: AppServerTurnStatus::InProgress,
error: None,
started_at: Some(0),
completed_at: None,
duration_ms: None,
},
}),
/*replay_kind*/ None,
);
drain_insert_history(&mut rx);
chat.handle_server_notification(
ServerNotification::Error(ErrorNotification {
error: AppServerTurnError {
message: "server fallback message".to_string(),
codex_error_info: Some(CodexErrorInfo::CyberPolicy.into()),
additional_details: None,
},
will_retry: false,
thread_id: "thread-1".to_string(),
turn_id: "turn-1".to_string(),
}),
/*replay_kind*/ None,
);
let cells = drain_insert_history(&mut rx);
assert_eq!(cells.len(), 1);
let rendered = lines_to_single_string(&cells[0]);
assert!(rendered.contains("This chat was flagged for possible cybersecurity risk"));
assert!(rendered.contains("Trusted Access for Cyber"));
assert!(!rendered.contains("server fallback message"));
assert!(!chat.bottom_pane.is_task_running());
}
#[tokio::test]
async fn live_app_server_model_verification_renders_warning() {
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
chat.handle_server_notification(
ServerNotification::ModelVerification(ModelVerificationNotification {
thread_id: "thread-1".to_string(),
turn_id: "turn-1".to_string(),
verifications: vec![AppServerModelVerification::TrustedAccessForCyber],
}),
/*replay_kind*/ None,
);
let cells = drain_insert_history(&mut rx);
assert_eq!(cells.len(), 1);
let rendered = lines_to_single_string(&cells[0]);
assert!(rendered.contains("flagged for potentially high-risk cyber activity"));
assert!(rendered.contains("slower while additional verification"));
assert!(rendered.contains("https://chatgpt.com/cyber"));
}
#[tokio::test]
async fn live_app_server_invalid_thread_name_update_is_ignored() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
@@ -28,6 +28,45 @@ async fn token_count_none_resets_context_indicator() {
assert_eq!(chat.bottom_pane.context_window_percent(), None);
}
#[tokio::test]
async fn core_cyber_policy_error_renders_dedicated_notice() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await;
chat.handle_codex_event(Event {
id: "cyber-policy".into(),
msg: EventMsg::Error(ErrorEvent {
message: "server fallback message".to_string(),
codex_error_info: Some(CodexErrorInfo::CyberPolicy),
}),
});
let cells = drain_insert_history(&mut rx);
assert_eq!(cells.len(), 1);
let rendered = lines_to_single_string(&cells[0]);
assert!(rendered.contains("This chat was flagged for possible cybersecurity risk"));
assert!(rendered.contains("Trusted Access for Cyber"));
assert!(!rendered.contains("server fallback message"));
}
#[tokio::test]
async fn core_model_verification_renders_warning() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await;
chat.handle_codex_event(Event {
id: "model-verification".into(),
msg: EventMsg::ModelVerification(ModelVerificationEvent {
verifications: vec![CoreModelVerification::TrustedAccessForCyber],
}),
});
let cells = drain_insert_history(&mut rx);
assert_eq!(cells.len(), 1);
let rendered = lines_to_single_string(&cells[0]);
assert!(rendered.contains("flagged for potentially high-risk cyber activity"));
assert!(rendered.contains("slower while additional verification"));
assert!(rendered.contains("https://chatgpt.com/cyber"));
}
#[tokio::test]
async fn context_indicator_shows_used_tokens_when_window_unknown() {
let (mut chat, _rx, _ops) = make_chatwidget_manual(Some("unknown-model")).await;