feat(tui): Add confirmation prompt for enabling full access approvals (#4980)
## Summary Adds a confirmation screen when a user attempts to select Full Access via the `/approvals` flow in the TUI. If the user selects the remember option, the preference is persisted to config.toml as `full_access_warning_acknowledged`, so they will not be prompted again. ## Testing - [x] Adds snapshot test coverage for the approvals flow and the confirmation flow <img width="865" height="187" alt="Screenshot 2025-10-08 at 6 04 59 PM" src="https://github.com/user-attachments/assets/fd1dac62-28b0-4835-ba91-5da6dc5ec4c4" /> ------ https://chatgpt.com/codex/tasks/task_i_68e6c5c458088322a28efa3207058180 --------- Co-authored-by: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Co-authored-by: Fouad Matin <fouad@openai.com>
This commit is contained in:
@@ -7,6 +7,7 @@ use crate::config_types::DEFAULT_OTEL_ENVIRONMENT;
|
||||
use crate::config_types::History;
|
||||
use crate::config_types::McpServerConfig;
|
||||
use crate::config_types::McpServerTransportConfig;
|
||||
use crate::config_types::Notice;
|
||||
use crate::config_types::Notifications;
|
||||
use crate::config_types::OtelConfig;
|
||||
use crate::config_types::OtelConfigToml;
|
||||
@@ -242,6 +243,9 @@ pub struct Config {
|
||||
/// Tracks whether the Windows onboarding screen has been acknowledged.
|
||||
pub windows_wsl_setup_acknowledged: bool,
|
||||
|
||||
/// Collection of various notices we show the user
|
||||
pub notices: Notice,
|
||||
|
||||
/// When true, disables burst-paste detection for typed input entirely.
|
||||
/// All characters are inserted as they are received, and no buffering
|
||||
/// or placeholder replacement will occur for fast keypress bursts.
|
||||
@@ -557,6 +561,54 @@ pub fn set_windows_wsl_setup_acknowledged(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Persist the acknowledgement flag for the full access warning prompt.
|
||||
pub fn set_hide_full_access_warning(codex_home: &Path, acknowledged: bool) -> anyhow::Result<()> {
|
||||
let config_path = codex_home.join(CONFIG_TOML_FILE);
|
||||
let mut doc = match std::fs::read_to_string(config_path.clone()) {
|
||||
Ok(s) => s.parse::<DocumentMut>()?,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => DocumentMut::new(),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
let notices_table = load_or_create_top_level_table(&mut doc, Notice::TABLE_KEY)?;
|
||||
|
||||
notices_table["hide_full_access_warning"] = toml_edit::value(acknowledged);
|
||||
|
||||
std::fs::create_dir_all(codex_home)?;
|
||||
let tmp_file = NamedTempFile::new_in(codex_home)?;
|
||||
std::fs::write(tmp_file.path(), doc.to_string())?;
|
||||
tmp_file.persist(config_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_or_create_top_level_table<'a>(
|
||||
doc: &'a mut DocumentMut,
|
||||
key: &str,
|
||||
) -> anyhow::Result<&'a mut toml_edit::Table> {
|
||||
let mut created_table = false;
|
||||
|
||||
let root = doc.as_table_mut();
|
||||
let needs_table =
|
||||
!root.contains_key(key) || root.get(key).and_then(|item| item.as_table()).is_none();
|
||||
if needs_table {
|
||||
root.insert(key, toml_edit::table());
|
||||
created_table = true;
|
||||
}
|
||||
|
||||
let Some(table) = doc[key].as_table_mut() else {
|
||||
return Err(anyhow::anyhow!(format!(
|
||||
"table [{key}] missing after initialization"
|
||||
)));
|
||||
};
|
||||
|
||||
if created_table {
|
||||
table.set_implicit(true);
|
||||
}
|
||||
|
||||
Ok(table)
|
||||
}
|
||||
|
||||
fn ensure_profile_table<'a>(
|
||||
doc: &'a mut DocumentMut,
|
||||
profile_name: &str,
|
||||
@@ -832,6 +884,10 @@ pub struct ConfigToml {
|
||||
/// Tracks whether the Windows onboarding screen has been acknowledged.
|
||||
pub windows_wsl_setup_acknowledged: Option<bool>,
|
||||
|
||||
/// Collection of in-product notices (different from notifications)
|
||||
/// See [`crate::config_types::Notices`] for more details
|
||||
pub notice: Option<Notice>,
|
||||
|
||||
/// Legacy, now use features
|
||||
pub experimental_instructions_file: Option<PathBuf>,
|
||||
pub experimental_use_exec_command_tool: Option<bool>,
|
||||
@@ -1247,6 +1303,7 @@ impl Config {
|
||||
active_profile: active_profile_name,
|
||||
active_project,
|
||||
windows_wsl_setup_acknowledged: cfg.windows_wsl_setup_acknowledged.unwrap_or(false),
|
||||
notices: cfg.notice.unwrap_or_default(),
|
||||
disable_paste_burst: cfg.disable_paste_burst.unwrap_or(false),
|
||||
tui_notifications: cfg
|
||||
.tui
|
||||
@@ -2330,6 +2387,7 @@ model_verbosity = "high"
|
||||
active_profile: Some("o3".to_string()),
|
||||
active_project: ProjectConfig { trust_level: None },
|
||||
windows_wsl_setup_acknowledged: false,
|
||||
notices: Default::default(),
|
||||
disable_paste_burst: false,
|
||||
tui_notifications: Default::default(),
|
||||
otel: OtelConfig::default(),
|
||||
@@ -2396,6 +2454,7 @@ model_verbosity = "high"
|
||||
active_profile: Some("gpt3".to_string()),
|
||||
active_project: ProjectConfig { trust_level: None },
|
||||
windows_wsl_setup_acknowledged: false,
|
||||
notices: Default::default(),
|
||||
disable_paste_burst: false,
|
||||
tui_notifications: Default::default(),
|
||||
otel: OtelConfig::default(),
|
||||
@@ -2477,6 +2536,7 @@ model_verbosity = "high"
|
||||
active_profile: Some("zdr".to_string()),
|
||||
active_project: ProjectConfig { trust_level: None },
|
||||
windows_wsl_setup_acknowledged: false,
|
||||
notices: Default::default(),
|
||||
disable_paste_burst: false,
|
||||
tui_notifications: Default::default(),
|
||||
otel: OtelConfig::default(),
|
||||
@@ -2544,6 +2604,7 @@ model_verbosity = "high"
|
||||
active_profile: Some("gpt5".to_string()),
|
||||
active_project: ProjectConfig { trust_level: None },
|
||||
windows_wsl_setup_acknowledged: false,
|
||||
notices: Default::default(),
|
||||
disable_paste_burst: false,
|
||||
tui_notifications: Default::default(),
|
||||
otel: OtelConfig::default(),
|
||||
|
||||
@@ -322,6 +322,20 @@ pub struct Tui {
|
||||
pub notifications: Notifications,
|
||||
}
|
||||
|
||||
/// Settings for notices we display to users via the tui and app-server clients
|
||||
/// (primarily the Codex IDE extension). NOTE: these are different from
|
||||
/// notifications - notices are warnings, NUX screens, acknowledgements, etc.
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Default)]
|
||||
pub struct Notice {
|
||||
/// Tracks whether the user has acknowledged the full access warning prompt.
|
||||
pub hide_full_access_warning: Option<bool>,
|
||||
}
|
||||
|
||||
impl Notice {
|
||||
/// used by set_hide_full_access_warning until we refactor config updates
|
||||
pub(crate) const TABLE_KEY: &'static str = "notice";
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Default)]
|
||||
pub struct SandboxWorkspaceWrite {
|
||||
#[serde(default)]
|
||||
|
||||
Reference in New Issue
Block a user