Tailor TUI cyber refusal notices to Daybreak eligibility (#42667)

## What changed

- Prefetch ChatGPT account eligibility in the background and cache it across TUI widget replacements.
- Show an application link when Daybreak is available but not enabled, an Astra-specific explanation for unsupported models, and neutral copy when access is already present or eligibility cannot be determined.
- Refresh eligibility after account changes and reconnects without changing the selected model or delaying refusal handling.

## Testing

- Cover eligibility responses, account changes, model-specific notices, fallback behavior, and rendered notice snapshots.

GitOrigin-RevId: 7ae8613a946cb46b8f0b46bf47e5e15ae258ed86
This commit is contained in:
Eric Traut
2026-09-04 03:30:21 +00:00
committed by copyberry
parent a7ab2d66d7
commit ff2f01b0c2
21 changed files with 469 additions and 80 deletions
@@ -225,6 +225,7 @@ impl App {
return;
}
ServerNotification::AccountUpdated(notification) => {
self.chat_widget.cyber_policy_notice = Default::default();
self.rate_limit_hard_stop_generation =
self.rate_limit_hard_stop_generation.wrapping_add(1);
self.rate_limit_refresh_state.invalidate_recovery();
@@ -252,6 +253,13 @@ impl App {
.is_some_and(AuthMode::has_chatgpt_account),
has_codex_backend_auth,
);
if self.chat_widget.has_chatgpt_account() {
crate::daybreak::prefetch_notice(
&self.config,
app_server_client,
self.chat_widget.cyber_policy_notice.clone(),
);
}
return;
}
ServerNotification::ExternalAgentConfigImportCompleted(notification) => {
+8
View File
@@ -244,6 +244,7 @@ impl App {
self.rate_limit_refresh_state.invalidate_recovery();
session.inherit_task_tool_capabilities(app_server);
*app_server = session;
self.chat_widget.cyber_policy_notice = Default::default();
self.chat_widget.requires_openai_auth = bootstrap.requires_openai_auth;
self.chat_widget.remote_connection =
crate::status::remote_connection::remote_connection_status_value(
@@ -389,6 +390,13 @@ impl App {
bootstrap.has_chatgpt_account,
matches!(bootstrap.auth_mode, Some(TelemetryAuthMode::Chatgpt)),
);
if self.chat_widget.has_chatgpt_account() {
crate::daybreak::prefetch_notice(
&self.config,
app_server,
self.chat_widget.cyber_policy_notice.clone(),
);
}
self.feedback_audience = bootstrap.feedback_audience;
self.chat_widget.add_info_message(
"Reconnected. No input was resent. Review uncertain submissions before retrying; recovered queues remain paused.".into(), /*hint*/ None,
@@ -475,6 +475,7 @@ impl App {
/// This helper copies every known nickname/role from `AgentNavigationState` into the
/// replacement widget so that replayed collab items render agent names immediately.
pub(super) fn replace_chat_widget(&mut self, mut chat_widget: ChatWidget) {
chat_widget.cyber_policy_notice = self.chat_widget.cyber_policy_notice.clone();
self.commit_animation = None;
// Transfer the last-written terminal title to the replacement widget
// so it knows what OSC title is currently displayed. Without this, the
+5
View File
@@ -707,6 +707,11 @@ See the Codex keymap documentation for supported actions and examples."
// already has data and available reset credits can be surfaced, without
// delaying the initial frame render.
if requires_openai_auth && has_chatgpt_account {
crate::daybreak::prefetch_notice(
&app.config,
&app_server,
app.chat_widget.cyber_policy_notice.clone(),
);
let reset_hint_request_id = app.chat_widget.start_rate_limit_reset_startup_check();
app.refresh_rate_limits(
&app_server,
+3
View File
@@ -1,5 +1,8 @@
//! App-level orchestration tests for the TUI.
#[path = "tests/daybreak_tests.rs"]
mod daybreak_tests;
#[path = "tests/advanced_reasoning_tests.rs"]
mod advanced_reasoning_tests;
#[path = "tests/agents_navigation_tests.rs"]
@@ -0,0 +1,147 @@
use super::*;
use crate::daybreak::Notice;
use app_test_support::ChatGptAuthFixture;
use app_test_support::write_chatgpt_auth;
use codex_app_server_client::AppServerEvent;
use codex_app_server_protocol::AccountUpdatedNotification;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::CodexErrorInfo;
use codex_app_server_protocol::ErrorNotification;
use codex_login::AuthCredentialsStoreMode;
use pretty_assertions::assert_eq;
use serde_json::json;
#[tokio::test]
async fn cyber_refusal_reads_eligibility_without_changing_the_model() -> Result<()> {
let (mut app, mut events, _ops) = make_test_app_with_channels().await;
let backend = wiremock::MockServer::start().await;
app.config.chatgpt_base_url = backend.uri();
app.config.cli_auth_credentials_store_mode = AuthCredentialsStoreMode::File;
write_chatgpt_auth(
&app.config.codex_home,
ChatGptAuthFixture::new("test-token")
.account_id("account")
.chatgpt_user_id("user"),
AuthCredentialsStoreMode::File,
)
.expect("write synthetic auth");
let server = Box::pin(crate::start_embedded_app_server_for_picker(&app.config)).await?;
let thread_id = ThreadId::new();
app.enqueue_primary_thread_session(
test_thread_session(thread_id, app.config.cwd.to_path_buf()),
Vec::new(),
)
.await?;
for (status, enrolled, model, replace_account, expected) in [
(200, true, "gpt-5.6-sol", false, Notice::Limited),
(200, false, "gpt-5.6-sol", false, Notice::Apply),
(503, false, "gpt-5.6-sol", false, Notice::Limited),
(200, true, "gpt-6-astra", false, Notice::Astra),
(200, false, "gpt-6-astra", false, Notice::Astra),
(503, false, "gpt-6-astra", false, Notice::Astra),
(200, true, "gpt-6-astra-wm", false, Notice::Astra),
(200, true, "unmapped-model", false, Notice::Limited),
(200, false, "gpt-5.6-sol", true, Notice::Limited),
] {
backend.reset().await;
let codex_home = app.config.codex_home.clone();
let response = wiremock::ResponseTemplate::new(status)
.set_delay(if model == "gpt-5.6-sol" && enrolled { Duration::from_secs(1) } else { Duration::ZERO })
.set_body_json(json!({"programs": [{"program":"cyber", "state": if enrolled { "active" } else { "inactive" }, "grants": if enrolled { json!([{"level":"tac2"}]) } else { json!([]) }}]}));
wiremock::Mock::given(wiremock::matchers::path("/accounts/verified_access"))
.and(wiremock::matchers::header(
"authorization",
"Bearer test-token",
))
.respond_with(move |_: &wiremock::Request| {
if replace_account {
write_chatgpt_auth(
&codex_home,
ChatGptAuthFixture::new("test-token").account_id("other-account"),
AuthCredentialsStoreMode::File,
)
.unwrap();
}
response.clone()
})
.expect(1)
.mount(&backend)
.await;
// Account discovery starts before a subsequent model switch to Sol.
app.chat_widget.set_model("gpt-6-astra");
app.handle_app_server_event(
&server,
AppServerEvent::ServerNotification(Box::new(ServerNotification::AccountUpdated(
AccountUpdatedNotification {
auth_mode: Some(AuthMode::Chatgpt),
plan_type: None,
},
))),
)
.await;
if model != "gpt-5.6-sol" || !enrolled {
wait_for_notice(&app.chat_widget.cyber_policy_notice).await;
}
app.active_thread_id = Some(thread_id);
app.chat_widget.set_model(model);
while events.try_recv().is_ok() {}
// In the slow Sol case, discovery is still pending: do not block input on it.
tokio::time::timeout(
Duration::from_millis(250),
app.handle_app_server_event(
&server,
AppServerEvent::ServerNotification(Box::new(ServerNotification::Error(
ErrorNotification {
error: AppServerTurnError {
misalignment: None,
message: "server fallback".into(),
codex_error_info: Some(CodexErrorInfo::CyberPolicy),
additional_details: None,
},
will_retry: false,
thread_id: thread_id.to_string(),
turn_id: "turn".into(),
},
))),
),
)
.await
.expect("refusal must not wait for eligibility");
let event = app.active_thread_rx.as_mut().unwrap().recv().await.unwrap();
app.handle_thread_event_now(event);
let cell = std::iter::from_fn(|| events.try_recv().ok())
.find_map(|event| match event {
AppEvent::InsertHistoryCell(cell) => Some(cell),
_ => None,
})
.expect("cyber refusal cell");
assert_eq!(
cell.display_lines(/*width*/ 80),
history_cell::new_cyber_policy_error_event(expected).display_lines(/*width*/ 80),
"{model}"
);
assert_eq!(
wait_for_notice(&app.chat_widget.cyber_policy_notice)
.await
.for_model(model),
expected
);
backend.verify().await;
assert_eq!(app.chat_widget.current_model(), model);
}
server.shutdown().await?;
Ok(())
}
async fn wait_for_notice(cache: &crate::daybreak::NoticeCache) -> Notice {
tokio::time::timeout(Duration::from_secs(3), async {
loop {
if let Some(notice) = cache.get() {
return *notice;
}
tokio::time::sleep(Duration::from_millis(10)).await;
}
})
.await
.expect("eligibility should finish")
}
+1
View File
@@ -558,6 +558,7 @@ pub(crate) enum ExternalEditorState {
/// (which view gets Ctrl+C), while `ChatWidget` owns process-level decisions such as interrupting
/// active work, arming the double-press quit shortcut, and requesting shutdown-first exit.
pub(crate) struct ChatWidget {
pub(crate) cyber_policy_notice: crate::daybreak::NoticeCache,
app_event_tx: AppEventSender,
codex_op_target: CodexOpTarget,
bottom_pane: BottomPane,
@@ -98,6 +98,7 @@ impl ChatWidget {
pet_http_client.clone(),
);
let mut widget = Self {
cyber_policy_notice: Default::default(),
app_event_tx: app_event_tx.clone(),
frame_requester: frame_requester.clone(),
codex_op_target,
@@ -1642,8 +1642,8 @@ async fn live_app_server_cyber_policy_error_renders_dedicated_notice() {
let cells = drain_insert_history(&mut rx);
assert_eq!(cells.len(), 1);
let rendered = lines_to_single_string(&cells[0]);
assert!(rendered.contains("This content can't be shown"));
assert!(rendered.contains("extra caution with cybersecurity requests"));
assert!(rendered.contains("This content can’t be shown"));
assert!(rendered.contains("We take extra care with some cybersecurity requests"));
assert!(!rendered.contains("server fallback message"));
assert!(!chat.bottom_pane.is_task_running());
}
@@ -91,7 +91,10 @@ async fn resumed_session_hides_unknown_token_usage_until_an_update_arrives() {
#[tokio::test]
async fn app_server_cyber_policy_error_renders_dedicated_notice() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await;
let (mut chat, mut rx, _ops) = make_chatwidget_manual(Some("gpt-5.6-sol")).await;
chat.cyber_policy_notice
.set(crate::daybreak::Notice::Apply)
.unwrap();
handle_error(
&mut chat,
@@ -102,29 +105,12 @@ async fn app_server_cyber_policy_error_renders_dedicated_notice() {
let cells = drain_insert_history(&mut rx);
assert_eq!(cells.len(), 1);
let rendered = lines_to_single_string(&cells[0]);
assert!(rendered.contains("This content can't be shown"));
assert!(rendered.contains("extra caution with cybersecurity requests"));
assert!(rendered.contains("openai.com/form/enterprise-trusted-access-for-cyber"));
assert!(rendered.contains("This content can’t be shown"));
assert!(rendered.contains("We take extra care with some cybersecurity requests"));
assert!(rendered.contains("Apply for Daybreak"));
assert!(!rendered.contains("server fallback message"));
}
#[tokio::test]
async fn app_server_cyber_policy_error_uses_individual_link_for_personal_plan() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await;
chat.plan_type = Some(PlanType::Free);
chat.has_chatgpt_account = true;
handle_error(
&mut chat,
"server fallback message",
Some(CodexErrorInfo::CyberPolicy),
);
let cells = drain_insert_history(&mut rx);
assert_eq!(cells.len(), 1);
assert!(lines_to_single_string(&cells[0]).contains("https://chatgpt.com/cyber/"));
}
#[tokio::test]
async fn app_server_model_verification_renders_warning() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await;
+8 -4
View File
@@ -395,12 +395,16 @@ impl ChatWidget {
pub(super) fn on_cyber_policy_error(&mut self) {
self.input_queue.submit_pending_steers_after_interrupt = false;
self.finalize_turn();
let plan_type = if self.has_chatgpt_account {
self.plan_type
let notice = if self.config.model_provider_id == "openai" {
self.cyber_policy_notice
.get()
.copied()
.unwrap_or_default()
.for_model(self.current_model())
} else {
None
crate::daybreak::Notice::Limited
};
self.add_to_history(history_cell::new_cyber_policy_error_event(plan_type));
self.add_to_history(history_cell::new_cyber_policy_error_event(notice));
self.request_redraw();
// After an error ends the turn, try sending the next queued input.
+153
View File
@@ -0,0 +1,153 @@
//! Read-only Daybreak eligibility used to choose cyber refusal copy.
//! Account-scoped discovery runs in the background; pending/failed reads use neutral copy.
//! Astra takes precedence; the TUI does not configure the app’s Daybreak access program.
use crate::app_server_session::AppServerSession;
use crate::legacy_core::config::Config;
use codex_app_server_client::AppServerRequestHandle;
use codex_app_server_protocol::AuthMode;
use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::GetAuthStatusParams;
use codex_app_server_protocol::GetAuthStatusResponse;
use codex_app_server_protocol::RequestId;
use codex_http_client::ClientRouteClass;
use codex_http_client::RouteAwareClientPool;
use codex_login::CodexAuth;
use serde::Deserialize;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::OnceCell;
pub(crate) type NoticeCache = Arc<OnceCell<Notice>>;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) enum Notice {
Apply,
Astra,
#[default]
Limited,
}
pub(crate) fn prefetch_notice(config: &Config, server: &AppServerSession, cache: NoticeCache) {
if config.model_provider_id != "openai"
|| server.uses_remote_workspace()
|| cache.get().is_some()
{
return;
}
let config = config.clone();
let request_handle = server.request_handle();
tokio::spawn(async move {
cache
.get_or_init(|| read_notice(&config, &request_handle))
.await;
});
}
async fn read_notice(config: &Config, request_handle: &AppServerRequestHandle) -> Notice {
tokio::time::timeout(Duration::from_secs(3), async {
// Allow the server to refresh its token before reading local credentials.
let status: GetAuthStatusResponse = request_handle
.request_typed(ClientRequest::GetAuthStatus {
request_id: RequestId::String(uuid::Uuid::new_v4().to_string()),
params: GetAuthStatusParams {
include_token: Some(true),
refresh_token: Some(false),
},
})
.await
.ok()?;
let auth = config
.auth_config()
.load_auth(/*enable_codex_api_key_env*/ false)
.await
.ok()
.flatten()?;
let CodexAuth::Chatgpt(_) = &auth else {
return None;
};
if status.auth_method != Some(AuthMode::Chatgpt)
|| status.auth_token.as_deref() != Some(auth.get_token().ok()?.as_str())
{
return None;
}
let client = RouteAwareClientPool::new_without_redirects(
config.http_client_factory(),
ClientRouteClass::Api,
);
let url = format!(
"{}/accounts/verified_access",
config.chatgpt_base_url.trim_end_matches('/')
);
let response = client
.get(url)
.headers(codex_model_provider::auth_provider_from_auth(&auth).to_auth_headers())
.send()
.await
.ok()?;
if !response.status().is_success() {
return None;
}
let access = response.json::<VerifiedAccess>().await.ok()?;
let current_auth = config
.auth_config()
.load_auth(/*enable_codex_api_key_env*/ false)
.await
.ok()
.flatten()?;
if current_auth.get_token().ok()? != auth.get_token().ok()?
|| current_auth.get_account_id() != auth.get_account_id()
{
return None;
}
Some(access.notice())
})
.await
.ok()
.flatten()
.unwrap_or_default()
}
#[derive(Deserialize)]
struct VerifiedAccess {
programs: Vec<Program>,
}
#[derive(Deserialize)]
#[serde(tag = "program", rename_all = "snake_case")]
enum Program {
Cyber {
state: String,
grants: Vec<serde::de::IgnoredAny>,
},
#[serde(other)]
Other,
}
impl VerifiedAccess {
fn notice(&self) -> Notice {
let absent = self.programs.iter().all(|program| match program {
Program::Cyber { state, grants } => state == "inactive" && grants.is_empty(),
Program::Other => true,
});
if absent {
Notice::Apply
} else {
Notice::Limited
}
}
}
impl Notice {
pub(crate) fn for_model(self, model: &str) -> Self {
match model {
// Same identifiers as the app's isDaybreakUnavailableModel.
"gpt-6-astra" | "gpt-6-astra-wm" => Self::Astra,
"gpt-5.6-sol" => self,
// Other model/access-program mappings are not established for the TUI.
_ => Self::Limited,
}
}
}
#[cfg(test)]
#[path = "daybreak_tests.rs"]
mod tests;
+37
View File
@@ -0,0 +1,37 @@
use super::*;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn refusal_copy_uses_verified_access() {
for (programs, expected) in [
(json!([]), Notice::Apply),
(
json!([{"program":"cyber","state":"inactive","grants":[]}]),
Notice::Apply,
),
(
json!([{"program":"cyber","state":"unavailable","grants":[]}]),
Notice::Limited,
),
(
json!([{"program":"cyber","state":"active","grants":[{"level":"tac2"}]}]),
Notice::Limited,
),
(
json!([{"program":"cyber","state":"active","grants":[]}]),
Notice::Limited,
),
(
json!([{"program":"cyber","state":"inactive","grants":[{"level":"tac2"}]}]),
Notice::Limited,
),
(
json!([{"program":"cyber","state":"unknown","grants":[]}]),
Notice::Limited,
),
] {
let access: VerifiedAccess = serde_json::from_value(json!({"programs": programs})).unwrap();
assert_eq!(access.notice(), expected);
}
}
+45 -26
View File
@@ -90,33 +90,55 @@ pub(crate) fn new_warning_event(message: String) -> PrefixedWrappedHistoryCell {
#[derive(Debug)]
pub(crate) struct SafetyAccessBlockCell {
title: &'static str,
body: &'static str,
trusted_access_url: &'static str,
actions: &'static [(&'static str, &'static str)],
}
const SAFETY_ACCESS_BLOCK_TITLE: &str = "This content can't be shown";
const SAFETY_ACCESS_BLOCK_LEARN_MORE_URL: &str = "https://help.openai.com/en/articles/20001326";
const CYBER_INDIVIDUAL_TRUSTED_ACCESS_URL: &str = "https://chatgpt.com/cyber/";
const CYBER_ENTERPRISE_TRUSTED_ACCESS_URL: &str =
"https://openai.com/form/enterprise-trusted-access-for-cyber/";
pub(crate) fn new_safety_access_block_event() -> SafetyAccessBlockCell {
SafetyAccessBlockCell {
title: "This content can't be shown",
body: "We take extra caution with requests involving biological research and applications that could pose safety risks. Eligible researchers can apply for Trusted Access.",
trusted_access_url: "https://chatgpt.com/r/b749fb02595e04c3007a54375f3f4374",
actions: &[
(
"Trusted Access",
"https://chatgpt.com/r/b749fb02595e04c3007a54375f3f4374",
),
("Learn more", SAFETY_ACCESS_BLOCK_LEARN_MORE_URL),
],
}
}
pub(crate) fn new_cyber_policy_error_event(plan_type: Option<PlanType>) -> SafetyAccessBlockCell {
let trusted_access_url = match plan_type {
Some(
PlanType::Free | PlanType::Go | PlanType::Plus | PlanType::Pro | PlanType::ProLite,
) => CYBER_INDIVIDUAL_TRUSTED_ACCESS_URL,
_ => CYBER_ENTERPRISE_TRUSTED_ACCESS_URL,
pub(crate) fn new_cyber_policy_error_event(
notice: crate::daybreak::Notice,
) -> SafetyAccessBlockCell {
use crate::daybreak::Notice;
let (body, actions): (_, &'static [(&str, &str)]) = match notice {
Notice::Apply => (
"We take extra care with some cybersecurity requests. If you’re doing authorized security work, apply for Daybreak to get broader access.",
&[
("Learn more", SAFETY_ACCESS_BLOCK_LEARN_MORE_URL),
(
"Apply for Daybreak",
"https://openai.com/form/enterprise-trusted-access-for-cyber/",
),
],
),
Notice::Astra => (
"Daybreak isn’t available for Astra. Some cybersecurity requests may still be limited.",
&[("Learn more", SAFETY_ACCESS_BLOCK_LEARN_MORE_URL)],
),
Notice::Limited => (
"We take extra care with some cybersecurity requests.",
&[("Learn more", SAFETY_ACCESS_BLOCK_LEARN_MORE_URL)],
),
};
SafetyAccessBlockCell {
body: "We take extra caution with cybersecurity requests. If you’re a security professional, you may be able to apply for Trusted Access.",
trusted_access_url,
title: "This content can’t be shown",
body,
actions,
}
}
@@ -127,7 +149,7 @@ impl HistoryCell for SafetyAccessBlockCell {
fn display_hyperlink_lines(&self, width: u16) -> Vec<HyperlinkLine> {
let mut lines = vec![HyperlinkLine::new(
vec!["ⓘ ".cyan(), SAFETY_ACCESS_BLOCK_TITLE.bold()].into(),
vec!["ⓘ ".cyan(), self.title.bold()].into(),
)];
let body = Line::from(vec![" ".into(), self.body.dim()]);
let wrap_width = width.saturating_sub(2).max(1) as usize;
@@ -139,10 +161,7 @@ impl HistoryCell for SafetyAccessBlockCell {
push_owned_lines(&wrapped, &mut wrapped_body);
lines.extend(plain_hyperlink_lines(wrapped_body));
for (label, url) in [
("Trusted Access", self.trusted_access_url),
("Learn more", SAFETY_ACCESS_BLOCK_LEARN_MORE_URL),
] {
for &(label, url) in self.actions {
let source = crate::terminal_hyperlinks::annotate_web_urls_in_line(
vec![format!(" {label}: ").dim(), url.cyan().underlined()].into(),
);
@@ -161,13 +180,13 @@ impl HistoryCell for SafetyAccessBlockCell {
}
fn raw_lines(&self) -> Vec<Line<'static>> {
let trusted_access_url = self.trusted_access_url;
vec![
Line::from(SAFETY_ACCESS_BLOCK_TITLE),
Line::from(self.body),
Line::from(format!("Trusted Access: {trusted_access_url}")),
Line::from(format!("Learn more: {SAFETY_ACCESS_BLOCK_LEARN_MORE_URL}")),
]
let mut lines = vec![Line::from(self.title), Line::from(self.body)];
lines.extend(
self.actions
.iter()
.map(|(label, target)| Line::from(format!("{label}: {target}"))),
);
lines
}
fn transcript_hyperlink_lines(&self, width: u16) -> Vec<HyperlinkLine> {
@@ -0,0 +1,8 @@
---
source: tui/src/history_cell/tests.rs
expression: rendered
---
ⓘ This content can’t be shown
Daybreak isn’t available for Astra. Some cybersecurity requests may still be
limited.
Learn more: https://help.openai.com/en/articles/20001326
@@ -1,9 +0,0 @@
---
source: tui/src/history_cell/tests.rs
expression: rendered
---
ⓘ This content can't be shown
We take extra caution with cybersecurity requests. If you’re a security
professional, you may be able to apply for Trusted Access.
Trusted Access: https://chatgpt.com/cyber/
Learn more: https://help.openai.com/en/articles/20001326
@@ -0,0 +1,7 @@
---
source: tui/src/history_cell/tests.rs
expression: rendered
---
ⓘ This content can’t be shown
We take extra care with some cybersecurity requests.
Learn more: https://help.openai.com/en/articles/20001326
@@ -1,16 +1,17 @@
---
source: tui/src/history_cell/tests.rs
assertion_line: 821
expression: rendered
---
ⓘ This content can't be shown
We take extra caution with
ⓘ This content can’t be shown
We take extra care with some
cybersecurity requests. If
you’re a security professional,
you may be able to apply for
Trusted Access.
Trusted Access: https://
openai.com/form/enterprise-
trusted-access-for-cyber/
you’re doing authorized security
work, apply for Daybreak to get
broader access.
Learn more: https://
help.openai.com/en/
articles/20001326
Apply for Daybreak: https://
openai.com/form/enterprise-
trusted-access-for-cyber/
@@ -1,10 +1,11 @@
---
source: tui/src/history_cell.rs
assertion_line: 3305
source: tui/src/history_cell/tests.rs
assertion_line: 774
expression: rendered
---
ⓘ This content can't be shown
We take extra caution with cybersecurity requests. If you’re a security
professional, you may be able to apply for Trusted Access.
Trusted Access: https://openai.com/form/enterprise-trusted-access-for-cyber/
ⓘ This content can’t be shown
We take extra care with some cybersecurity requests. If you’re doing
authorized security work, apply for Daybreak to get broader access.
Learn more: https://help.openai.com/en/articles/20001326
Apply for Daybreak: https://openai.com/form/enterprise-trusted-access-for-
cyber/
+11 -4
View File
@@ -769,14 +769,21 @@ fn ps_output_multiline_snapshot() {
#[test]
fn cyber_policy_error_event_snapshot() {
let cell = new_cyber_policy_error_event(/*plan_type*/ None);
let cell = new_cyber_policy_error_event(crate::daybreak::Notice::Apply);
let rendered = render_lines(&cell.display_lines(/*width*/ 80)).join("\n");
insta::assert_snapshot!(rendered);
}
#[test]
fn cyber_policy_error_event_individual_snapshot() {
let cell = new_cyber_policy_error_event(Some(PlanType::Pro));
fn cyber_policy_error_event_astra_snapshot() {
let cell = new_cyber_policy_error_event(crate::daybreak::Notice::Astra);
let rendered = render_lines(&cell.display_lines(/*width*/ 80)).join("\n");
insta::assert_snapshot!(rendered);
}
#[test]
fn cyber_policy_error_event_limited_snapshot() {
let cell = new_cyber_policy_error_event(crate::daybreak::Notice::Limited);
let rendered = render_lines(&cell.display_lines(/*width*/ 80)).join("\n");
insta::assert_snapshot!(rendered);
}
@@ -790,7 +797,7 @@ fn safety_access_block_event_snapshot() {
#[test]
fn cyber_policy_error_event_narrow_snapshot() {
let cell = new_cyber_policy_error_event(/*plan_type*/ None);
let cell = new_cyber_policy_error_event(crate::daybreak::Notice::Apply);
let rendered = render_lines(&cell.display_lines(/*width*/ 36)).join("\n");
insta::assert_snapshot!(rendered);
}
+1
View File
@@ -119,6 +119,7 @@ mod collaboration_modes;
mod color;
mod config_update;
pub(crate) mod custom_terminal;
mod daybreak;
mod experimental_features;
mod permission_discovery;
mod pets;