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
+14
View File
@@ -331,6 +331,8 @@ use codex_protocol::protocol::InitialHistory;
use codex_protocol::protocol::McpServerRefreshConfig;
use codex_protocol::protocol::ModelRerouteEvent;
use codex_protocol::protocol::ModelRerouteReason;
use codex_protocol::protocol::ModelVerification;
use codex_protocol::protocol::ModelVerificationEvent;
use codex_protocol::protocol::NetworkApprovalContext;
use codex_protocol::protocol::NonSteerableTurnKind;
use codex_protocol::protocol::Op;
@@ -2385,6 +2387,18 @@ impl Session {
true
}
pub(crate) async fn emit_model_verification(
self: &Arc<Self>,
turn_context: &Arc<TurnContext>,
verifications: Vec<ModelVerification>,
) {
self.send_event(
turn_context,
EventMsg::ModelVerification(ModelVerificationEvent { verifications }),
)
.await;
}
pub(crate) async fn replace_history(
&self,
items: Vec<ResponseItem>,
+3
View File
@@ -1,5 +1,6 @@
use super::turn_context::image_generation_tool_auth_allowed;
use super::*;
use std::sync::atomic::AtomicBool;
/// Spawn a review thread using the given prompt.
pub(super) async fn spawn_review_thread(
@@ -140,6 +141,8 @@ pub(super) async fn spawn_review_thread(
turn_metadata_state,
turn_skills: TurnSkillsContext::new(parent_turn_context.turn_skills.outcome.clone()),
turn_timing_state: Arc::new(TurnTimingState::default()),
server_model_warning_emitted: AtomicBool::new(false),
model_verification_emitted: AtomicBool::new(false),
};
// Seed the child task with the review prompt as the initial user message.
+17 -7
View File
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use crate::SkillInjections;
use crate::SkillLoadOutcome;
@@ -364,7 +365,6 @@ pub(crate) async fn run_turn(
// Although from the perspective of codex.rs, TurnDiffTracker has the lifecycle of a Task which contains
// many turns, from the perspective of the user, it is a single turn.
let turn_diff_tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::new()));
let mut server_model_warning_emitted_for_turn = false;
// `ModelClientSession` is turn-scoped and caches WebSocket + sticky routing state, so we reuse
// one instance across retries within this turn.
@@ -455,7 +455,6 @@ pub(crate) async fn run_turn(
sampling_request_input,
&explicitly_enabled_connectors,
skills_outcome,
&mut server_model_warning_emitted_for_turn,
cancellation_token.child_token(),
)
.await
@@ -1022,7 +1021,6 @@ async fn run_sampling_request(
input: Vec<ResponseItem>,
explicitly_enabled_connectors: &HashSet<String>,
skills_outcome: Option<&SkillLoadOutcome>,
server_model_warning_emitted_for_turn: &mut bool,
cancellation_token: CancellationToken,
) -> CodexResult<SamplingRequestResult> {
let router = built_tools(
@@ -1076,7 +1074,6 @@ async fn run_sampling_request(
client_session,
turn_metadata_header,
Arc::clone(&turn_diff_tracker),
server_model_warning_emitted_for_turn,
&prompt,
cancellation_token.child_token(),
)
@@ -1490,6 +1487,7 @@ pub(super) fn realtime_text_for_event(msg: &EventMsg) -> Option<String> {
| EventMsg::RealtimeConversationRealtime(_)
| EventMsg::RealtimeConversationClosed(_)
| EventMsg::ModelReroute(_)
| EventMsg::ModelVerification(_)
| EventMsg::ContextCompacted(_)
| EventMsg::ThreadRolledBack(_)
| EventMsg::TurnStarted(_)
@@ -1866,7 +1864,6 @@ async fn try_run_sampling_request(
client_session: &mut ModelClientSession,
turn_metadata_header: Option<&str>,
turn_diff_tracker: SharedTurnDiffTracker,
server_model_warning_emitted_for_turn: &mut bool,
prompt: &Prompt,
cancellation_token: CancellationToken,
) -> CodexResult<SamplingRequestResult> {
@@ -2099,12 +2096,25 @@ async fn try_run_sampling_request(
}
}
ResponseEvent::ServerModel(server_model) => {
if !*server_model_warning_emitted_for_turn
if !turn_context
.server_model_warning_emitted
.load(Ordering::Relaxed)
&& sess
.maybe_warn_on_server_model_mismatch(&turn_context, server_model)
.await
{
*server_model_warning_emitted_for_turn = true;
turn_context
.server_model_warning_emitted
.store(true, Ordering::Relaxed);
}
}
ResponseEvent::ModelVerifications(verifications) => {
if !turn_context
.model_verification_emitted
.swap(true, Ordering::Relaxed)
{
sess.emit_model_verification(&turn_context, verifications)
.await;
}
}
ResponseEvent::ServerReasoningIncluded(included) => {
+12
View File
@@ -3,6 +3,8 @@ use codex_model_provider::SharedModelProvider;
use codex_model_provider::create_model_provider;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_sandboxing::policy_transforms::merge_permission_profiles;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
pub(super) fn image_generation_tool_auth_allowed(auth_manager: Option<&AuthManager>) -> bool {
matches!(
@@ -82,6 +84,8 @@ pub(crate) struct TurnContext {
pub(crate) turn_metadata_state: Arc<TurnMetadataState>,
pub(crate) turn_skills: TurnSkillsContext,
pub(crate) turn_timing_state: Arc<TurnTimingState>,
pub(crate) server_model_warning_emitted: AtomicBool,
pub(crate) model_verification_emitted: AtomicBool,
}
impl TurnContext {
pub(crate) fn permission_profile(&self) -> PermissionProfile {
@@ -216,6 +220,12 @@ impl TurnContext {
turn_metadata_state: self.turn_metadata_state.clone(),
turn_skills: self.turn_skills.clone(),
turn_timing_state: Arc::clone(&self.turn_timing_state),
server_model_warning_emitted: AtomicBool::new(
self.server_model_warning_emitted.load(Ordering::Relaxed),
),
model_verification_emitted: AtomicBool::new(
self.model_verification_emitted.load(Ordering::Relaxed),
),
}
}
@@ -469,6 +479,8 @@ impl Session {
turn_metadata_state,
turn_skills: TurnSkillsContext::new(skills_outcome),
turn_timing_state: Arc::new(TurnTimingState::default()),
server_model_warning_emitted: AtomicBool::new(false),
model_verification_emitted: AtomicBool::new(false),
}
}
+1
View File
@@ -143,6 +143,7 @@ fn response_event_records_turn_ttft(event: &ResponseEvent) -> bool {
| ResponseEvent::ReasoningContentDelta { .. } => true,
ResponseEvent::Created
| ResponseEvent::ServerModel(_)
| ResponseEvent::ModelVerifications(_)
| ResponseEvent::ServerReasoningIncluded(_)
| ResponseEvent::ToolCallInputDelta { .. }
| ResponseEvent::Completed { .. }