mirror of
https://github.com/openai/codex.git
synced 2026-10-20 00:18:49 +00:00
Allow API-key accounts to use Daybreak in the TUI (#50433)
## What changed Allow accounts using an API key with the OpenAI provider to access `/daybreak` and Daybreak policy notices. Model availability and the connected server still determine program access. Use the same eligibility checks for turn submission, continuation, and retries. For API-key accounts, send the selected `cyberAccessProgram` when Daybreak is enabled and omit it when Daybreak is off. ## Testing Extend session lifecycle coverage to check API-key turn parameters with Daybreak enabled and disabled. Add an API-key `/daybreak` help snapshot and update the signed-out error snapshot. GitOrigin-RevId: ab6d4313c430f316ef9835d4facb878aab0b9725
This commit is contained in:
@@ -13,8 +13,7 @@ impl App {
|
||||
return;
|
||||
}
|
||||
if enabled
|
||||
&& (!self.chat_widget.has_chatgpt_account()
|
||||
|| self.chat_widget.config_ref().model_provider_id != "openai"
|
||||
&& (!self.chat_widget.daybreak_turn_eligible(enabled)
|
||||
|| !crate::daybreak::available(&self.chat_widget.model_catalog().models))
|
||||
{
|
||||
self.chat_widget.add_error_message(
|
||||
|
||||
@@ -114,11 +114,10 @@ impl App {
|
||||
let Some(message) = review.continuation_message() else {
|
||||
return;
|
||||
};
|
||||
let eligible_account = self.chat_widget.has_chatgpt_account()
|
||||
&& self.chat_widget.config_ref().model_provider_id == "openai";
|
||||
let enabled = self.chat_widget.daybreak_enabled
|
||||
&& !self.chat_widget.side_conversation_active()
|
||||
&& !self.side_threads.contains_key(&review.thread_id);
|
||||
let eligible_account = self.chat_widget.daybreak_turn_eligible(enabled);
|
||||
let cyber_access_program = match crate::daybreak::program_for_turn(
|
||||
&self.chat_widget.model_catalog().models,
|
||||
self.chat_widget.current_model(),
|
||||
|
||||
@@ -85,11 +85,10 @@ impl App {
|
||||
.add_error_message(format!("Failed to retry with a faster model: {err}"));
|
||||
return;
|
||||
}
|
||||
let eligible_account = self.chat_widget.has_chatgpt_account()
|
||||
&& self.chat_widget.config_ref().model_provider_id == "openai";
|
||||
let daybreak_enabled = self.chat_widget.daybreak_enabled
|
||||
&& !self.chat_widget.side_conversation_active()
|
||||
&& !self.side_threads.contains_key(&thread_id);
|
||||
let eligible_account = self.chat_widget.daybreak_turn_eligible(daybreak_enabled);
|
||||
if let Err(message) = crate::daybreak::program_for_turn(
|
||||
&self.chat_widget.model_catalog().models,
|
||||
&model,
|
||||
|
||||
@@ -168,14 +168,30 @@ async fn daybreak_command_persists_and_confirms_each_selection() -> Result<()> {
|
||||
background_thread_id,
|
||||
crate::app::side::SideThreadState::new(thread_id),
|
||||
);
|
||||
app.submit_thread_op(&mut server, background_thread_id, turn)
|
||||
app.submit_thread_op(&mut server, background_thread_id, turn.clone())
|
||||
.await?;
|
||||
let turns = recorded_params(&requests, "turn/start");
|
||||
assert_eq!(turns.len(), 3);
|
||||
assert_eq!(turns[0]["cyberAccessProgram"], "standard");
|
||||
assert_eq!(turns[1]["cyberAccessProgram"], "daybreakBlue");
|
||||
assert_eq!(turns[2]["cyberAccessProgram"], "standard");
|
||||
app.chat_widget.update_account_state(
|
||||
Some(crate::status::StatusAccountDisplay::ApiKey),
|
||||
/*plan_type*/ None,
|
||||
/*has_chatgpt_account*/ false,
|
||||
/*has_codex_backend_auth*/ false,
|
||||
);
|
||||
assert!(
|
||||
!app.chat_widget
|
||||
.set_feature_enabled(Feature::ApiKeyCyberAccessPrograms, /*enabled*/ false,)
|
||||
);
|
||||
app.submit_thread_op(&mut server, thread_id, turn.clone())
|
||||
.await?;
|
||||
app.chat_widget.set_daybreak_enabled(/*enabled*/ false);
|
||||
app.submit_thread_op(&mut server, thread_id, turn).await?;
|
||||
let turns = recorded_params(&requests, "turn/start");
|
||||
assert_eq!(turns[3]["cyberAccessProgram"], "daybreakBlue");
|
||||
assert!(turns[4]["cyberAccessProgram"].is_null());
|
||||
while events.try_recv().is_ok() {}
|
||||
|
||||
let missing_thread_id = ThreadId::new();
|
||||
|
||||
@@ -825,11 +825,10 @@ impl App {
|
||||
}
|
||||
}
|
||||
if should_start_turn {
|
||||
let eligible_account = self.chat_widget.has_chatgpt_account()
|
||||
&& self.chat_widget.config_ref().model_provider_id == "openai";
|
||||
let enabled = self.chat_widget.daybreak_enabled
|
||||
&& !self.chat_widget.side_conversation_active()
|
||||
&& !self.side_threads.contains_key(&thread_id);
|
||||
let eligible_account = self.chat_widget.daybreak_turn_eligible(enabled);
|
||||
let cyber_access_program = match crate::daybreak::program_for_turn(
|
||||
&self.chat_widget.model_catalog().models,
|
||||
model,
|
||||
|
||||
@@ -11,10 +11,25 @@ impl ChatWidget {
|
||||
.set_daybreak_command_description(self.daybreak_command_description());
|
||||
}
|
||||
|
||||
/// UI eligibility only; the catalog and server still determine model/program access.
|
||||
pub(crate) fn daybreak_account_eligible(&self) -> bool {
|
||||
self.config.model_provider_id == "openai"
|
||||
&& (self.has_chatgpt_account
|
||||
|| matches!(
|
||||
self.status_account_display,
|
||||
Some(StatusAccountDisplay::ApiKey)
|
||||
))
|
||||
}
|
||||
|
||||
/// API-key turns need no explicit program while Daybreak is off.
|
||||
pub(crate) fn daybreak_turn_eligible(&self, enabled: bool) -> bool {
|
||||
self.daybreak_account_eligible() && (self.has_chatgpt_account || enabled)
|
||||
}
|
||||
|
||||
pub(super) fn daybreak_command_description(&self) -> Option<&'static str> {
|
||||
if self.daybreak_enabled {
|
||||
Some("Disable broader access for cybersecurity work")
|
||||
} else if !self.has_chatgpt_account || self.config.model_provider_id != "openai" {
|
||||
} else if !self.daybreak_account_eligible() {
|
||||
None
|
||||
} else {
|
||||
match crate::daybreak::availability(&self.model_catalog.models) {
|
||||
|
||||
@@ -190,8 +190,8 @@ impl ChatWidget {
|
||||
match cmd {
|
||||
SlashCommand::Daybreak => {
|
||||
if !self.daybreak_enabled {
|
||||
if !self.has_chatgpt_account || self.config.model_provider_id != "openai" {
|
||||
self.add_error_message("Daybreak requires a signed-in ChatGPT account and the OpenAI provider.".into());
|
||||
if !self.daybreak_account_eligible() {
|
||||
self.add_error_message("Daybreak requires the OpenAI provider with either ChatGPT sign-in or an API key with Daybreak support enabled.".into());
|
||||
return;
|
||||
}
|
||||
if !crate::daybreak::available(&self.model_catalog.models) {
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
source: tui/src/chatwidget/tests/slash_commands.rs
|
||||
expression: "render_bottom_popup(&chat, 80).lines().next().unwrap()"
|
||||
---
|
||||
› /daybreak Learn about broader access for cybersecurity work
|
||||
+1
-1
@@ -3,4 +3,4 @@ source: tui/src/chatwidget/tests/slash_commands.rs
|
||||
assertion_line: 2194
|
||||
expression: "lines_to_single_string(&cells[0])"
|
||||
---
|
||||
■ Daybreak requires a signed-in ChatGPT account and the OpenAI provider.
|
||||
■ Daybreak requires the OpenAI provider with either ChatGPT sign-in or an API key with Daybreak support enabled.
|
||||
|
||||
@@ -2204,6 +2204,20 @@ async fn slash_daybreak_offers_an_application_when_unavailable() {
|
||||
.cyber
|
||||
.push(codex_protocol::turn_input::CyberAccessProgram::DaybreakBlue);
|
||||
chat.model_catalog = std::sync::Arc::new(ModelCatalog::new(vec![model]));
|
||||
chat.has_chatgpt_account = false;
|
||||
chat.status_account_display = Some(StatusAccountDisplay::ApiKey);
|
||||
chat.set_daybreak_enabled(/*enabled*/ false);
|
||||
chat.bottom_pane
|
||||
.set_composer_text("/daybreak".to_string(), Vec::new(), Vec::new());
|
||||
assert_chatwidget_snapshot!(
|
||||
"slash_daybreak_api_key_help",
|
||||
render_bottom_popup(&chat, /*width*/ 80)
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
);
|
||||
chat.has_chatgpt_account = true;
|
||||
chat.status_account_display = None;
|
||||
for (enabled, name) in [
|
||||
(false, "slash_daybreak_help_enable"),
|
||||
(true, "slash_daybreak_help_disable"),
|
||||
|
||||
@@ -392,9 +392,8 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
pub(super) fn on_cyber_policy_error(&mut self) {
|
||||
let can_enable_daybreak = self.has_chatgpt_account
|
||||
&& self.config.model_provider_id == "openai"
|
||||
&& !self.side_conversation_active();
|
||||
let can_enable_daybreak =
|
||||
self.daybreak_account_eligible() && !self.side_conversation_active();
|
||||
let notice = crate::daybreak::notice_for_setting(
|
||||
&self.model_catalog.models,
|
||||
self.current_model(),
|
||||
|
||||
@@ -13,7 +13,7 @@ pub(crate) fn program_for_turn(
|
||||
) -> Result<Option<CyberAccessProgram>, String> {
|
||||
if !eligible_account {
|
||||
return if enabled {
|
||||
Err("Daybreak requires a signed-in ChatGPT account and the OpenAI provider. Turn it off to continue.".into())
|
||||
Err("Daybreak requires the OpenAI provider with either ChatGPT sign-in or an API key with Daybreak support enabled. Turn it off to continue.".into())
|
||||
} else {
|
||||
Ok(None)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user