Update safety check links (#30491)

## Summary

Bio/Cyber safety surfaces in the TUI could send users to stale Trusted
Access pages, and safety buffering did not always expose the Help
Center.

This follow-up to #30317 adds the missing Learn more action, refreshes
the Bio access URL and block copy, and updates the affected snapshots
while preserving the existing retry and wait behavior.
This commit is contained in:
Eric Traut authored and GitHub committed 2026-06-29 11:10:11 -07:00
1 parent 80f54d1266
commit 9d13291955
6 files changed
+76 -49

No files matched your search

+44 -38
View File
@@ -4,6 +4,7 @@ use super::*;
use codex_app_server_protocol::ModelSafetyBufferingUpdatedNotification; use codex_app_server_protocol::ModelSafetyBufferingUpdatedNotification;
const SAFETY_BUFFERING_PROMPT_VIEW_ID: &str = "safety-buffering-prompt"; const SAFETY_BUFFERING_PROMPT_VIEW_ID: &str = "safety-buffering-prompt";
const SAFETY_BUFFERING_LEARN_MORE_URL: &str = "https://help.openai.com/en/articles/20001326";
const SAFETY_BUFFERING_MESSAGE_WITH_RETRY: &str = "This request requires additional safety checks, which can take extra time. Hang tight or retry with a faster model for a quicker response, though it may be less capable of handling complex requests."; const SAFETY_BUFFERING_MESSAGE_WITH_RETRY: &str = "This request requires additional safety checks, which can take extra time. Hang tight or retry with a faster model for a quicker response, though it may be less capable of handling complex requests.";
const SAFETY_BUFFERING_MESSAGE_WITHOUT_RETRY: &str = const SAFETY_BUFFERING_MESSAGE_WITHOUT_RETRY: &str =
@@ -12,7 +13,7 @@ const SAFETY_BUFFERING_MESSAGE_WITHOUT_RETRY: &str =
#[derive(Debug)] #[derive(Debug)]
struct ActiveSafetyBuffering { struct ActiveSafetyBuffering {
turn_id: String, turn_id: String,
retry_prompt_shown: bool, last_prompt_had_retry: bool,
agent_message_started: bool, agent_message_started: bool,
} }
@@ -119,22 +120,18 @@ impl ChatWidget {
.map(|(_, turn)| turn.clone()); .map(|(_, turn)| turn.clone());
let thread_id = self.thread_id; let thread_id = self.thread_id;
let can_offer_retry = faster_model.is_some() && retry_turn.is_some() && thread_id.is_some(); let can_offer_retry = faster_model.is_some() && retry_turn.is_some() && thread_id.is_some();
if !can_offer_retry {
self.bottom_pane
.dismiss_view_by_id(SAFETY_BUFFERING_PROMPT_VIEW_ID);
}
let previous_active = self let previous_active = self
.safety_buffering .safety_buffering
.active .active
.as_ref() .as_ref()
.filter(|active| active.turn_id == turn_id); .filter(|active| active.turn_id == turn_id);
let retry_prompt_shown = previous_active.is_some_and(|active| active.retry_prompt_shown); let should_show_prompt =
let should_show_retry_prompt = can_offer_retry && !retry_prompt_shown; previous_active.is_none_or(|active| active.last_prompt_had_retry != can_offer_retry);
let agent_message_started = let agent_message_started =
previous_active.is_some_and(|active| active.agent_message_started); previous_active.is_some_and(|active| active.agent_message_started);
self.safety_buffering.active = Some(ActiveSafetyBuffering { self.safety_buffering.active = Some(ActiveSafetyBuffering {
turn_id: turn_id.clone(), turn_id: turn_id.clone(),
retry_prompt_shown: retry_prompt_shown || should_show_retry_prompt, last_prompt_had_retry: can_offer_retry,
agent_message_started, agent_message_started,
}); });
@@ -151,45 +148,54 @@ impl ChatWidget {
/*details_max_lines*/ 6, /*details_max_lines*/ 6,
); );
let (Some(faster_model), Some(turn), Some(thread_id)) = if !should_show_prompt {
(faster_model, retry_turn, thread_id)
else {
return;
};
if !should_show_retry_prompt {
return; return;
} }
self.bottom_pane
.dismiss_view_by_id(SAFETY_BUFFERING_PROMPT_VIEW_ID);
let header = ColumnRenderable::with(vec![ let header = ColumnRenderable::with(vec![
Box::new(Line::from("Additional safety checks").bold()) as Box<dyn Renderable>, Box::new(Line::from("Additional safety checks").bold()) as Box<dyn Renderable>,
Box::new( Box::new(Paragraph::new(Line::from(message).dim()).wrap(Wrap { trim: false })),
Paragraph::new(Line::from(SAFETY_BUFFERING_MESSAGE_WITH_RETRY).dim()) ]);
.wrap(Wrap { trim: false }), let mut items = Vec::new();
), if let (Some(faster_model), Some(turn), Some(thread_id)) =
(faster_model, retry_turn, thread_id)
{
items.push(SelectionItem {
name: "Retry with a faster model".to_string(),
actions: vec![Box::new(move |tx| {
tx.send(AppEvent::RetrySafetyBufferedTurn {
thread_id,
turn_id: turn_id.clone(),
model: faster_model.clone(),
turn: turn.clone(),
});
})],
dismiss_on_select: true,
..Default::default()
});
}
items.extend([
SelectionItem {
name: "Keep waiting".to_string(),
dismiss_on_select: true,
..Default::default()
},
SelectionItem {
name: "Learn more".to_string(),
actions: vec![Box::new(|tx| {
tx.send(AppEvent::OpenUrlInBrowser {
url: SAFETY_BUFFERING_LEARN_MORE_URL.to_string(),
});
})],
..Default::default()
},
]); ]);
self.bottom_pane.show_selection_view(SelectionViewParams { self.bottom_pane.show_selection_view(SelectionViewParams {
view_id: Some(SAFETY_BUFFERING_PROMPT_VIEW_ID), view_id: Some(SAFETY_BUFFERING_PROMPT_VIEW_ID),
header: Box::new(header), header: Box::new(header),
items: vec![ items,
SelectionItem {
name: "Retry with a faster model".to_string(),
actions: vec![Box::new(move |tx| {
tx.send(AppEvent::RetrySafetyBufferedTurn {
thread_id,
turn_id: turn_id.clone(),
model: faster_model.clone(),
turn: turn.clone(),
});
})],
dismiss_on_select: true,
..Default::default()
},
SelectionItem {
name: "Keep waiting".to_string(),
dismiss_on_select: true,
..Default::default()
},
],
..Default::default() ..Default::default()
}); });
} }
@@ -9,5 +9,6 @@ expression: popup
› 1. Retry with a faster model › 1. Retry with a faster model
2. Keep waiting 2. Keep waiting
3. Learn more
Press enter to confirm or esc to go back Press enter to confirm or esc to go back
@@ -1,11 +1,11 @@
--- ---
source: tui/src/chatwidget/tests/app_server.rs source: tui/src/chatwidget/tests/app_server.rs
expression: "render_bottom_popup(&chat, 80)" expression: popup
--- ---
• Working (0s • esc to interrupt) Additional safety checks
└ This request requires additional safety checks, which can take extra time. This request requires additional safety checks, which can take extra time.
› 1. Keep waiting
2. Learn more
› Ask Codex to do anything Press enter to confirm or esc to go back
gpt-5.5 default · /tmp/project
@@ -125,6 +125,21 @@ async fn safety_buffering_offers_one_retry_with_app_wording() {
let popup = render_bottom_popup(&chat, /*width*/ 80); let popup = render_bottom_popup(&chat, /*width*/ 80);
assert_chatwidget_snapshot!("safety_buffering_retry_prompt", popup); assert_chatwidget_snapshot!("safety_buffering_retry_prompt", popup);
chat.handle_key_event(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE));
chat.handle_key_event(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE));
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
let opened_url = loop {
match rx.try_recv() {
Ok(AppEvent::OpenUrlInBrowser { url }) => break url,
Ok(_) => continue,
Err(err) => panic!("expected learn-more URL event: {err}"),
}
};
assert_eq!(opened_url, "https://help.openai.com/en/articles/20001326");
assert!(render_bottom_popup(&chat, /*width*/ 80).contains("Additional safety checks"));
chat.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE));
chat.handle_key_event(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE));
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
let (event_thread_id, event_turn_id, model, turn) = loop { let (event_thread_id, event_turn_id, model, turn) = loop {
match rx.try_recv() { match rx.try_recv() {
@@ -201,6 +216,9 @@ async fn safety_buffering_without_retry_shows_short_app_message() {
Some(ReplayKind::ThreadSnapshot), Some(ReplayKind::ThreadSnapshot),
); );
assert_eq!(render_popup(&chat), popup); assert_eq!(render_popup(&chat), popup);
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
assert!(!render_bottom_popup(&chat, /*width*/ 80).contains("Additional safety checks"));
} }
#[tokio::test] #[tokio::test]
+2 -2
View File
@@ -96,8 +96,8 @@ const SAFETY_ACCESS_BLOCK_LEARN_MORE_URL: &str = "https://help.openai.com/en/art
pub(crate) fn new_safety_access_block_event() -> SafetyAccessBlockCell { pub(crate) fn new_safety_access_block_event() -> SafetyAccessBlockCell {
SafetyAccessBlockCell { SafetyAccessBlockCell {
body: "We take extra caution with requests involving biological research and applications that could pose safety risks. If you’re a researcher at an approved organization, you may be able to apply for Trusted Access.", body: "We take extra caution with requests involving biological research and applications that could pose safety risks. Eligible researchers can apply for Trusted Access. If you’re a researcher at an approved organization, you may be able to apply for Trusted Access.",
trusted_access_url: "https://openai.com/form/trusted-access-for-life-sciences", trusted_access_url: "https://www.openai.com/form/trusted-access-for-biology-research/",
} }
} }
@@ -4,7 +4,9 @@ expression: rendered
--- ---
ⓘ This content can't be shown ⓘ This content can't be shown
We take extra caution with requests involving biological research and We take extra caution with requests involving biological research and
applications that could pose safety risks. If you’re a researcher at an applications that could pose safety risks. Eligible researchers can apply
approved organization, you may be able to apply for Trusted Access. for Trusted Access. If you’re a researcher at an approved organization, you
Trusted Access: https://openai.com/form/trusted-access-for-life-sciences may be able to apply for Trusted Access.
Trusted Access: https://www.openai.com/form/trusted-access-for-biology-
research/
Learn more: https://help.openai.com/en/articles/20001326 Learn more: https://help.openai.com/en/articles/20001326