feat(tui) /permissions flow (#9561)

## Summary
Adds the `/permissions` command, with a (usually) shorter set of
permissions. `/approvals` still exists, for backwards compatibility.

<img width="863" height="309" alt="Screenshot 2026-01-20 at 4 12 51 PM"
src="https://github.com/user-attachments/assets/c49b5ba5-bc47-46dd-9067-e1a5670328fe"
/>


## Testing
- [x] updated unit tests
- [x] Tested locally
This commit is contained in:
Dylan Hurd
2026-01-21 21:38:46 -08:00
committed by GitHub
Unverified
parent 836f0343a3
commit 038b78c915
10 changed files with 93 additions and 33 deletions
+5 -5
View File
@@ -24,21 +24,21 @@ pub fn builtin_approval_presets() -> Vec<ApprovalPreset> {
ApprovalPreset {
id: "read-only",
label: "Read Only",
description: "Requires approval to edit files and run commands.",
description: "Codex can read files in the current workspace. Approval is required to edit files or access the internet.",
approval: AskForApproval::OnRequest,
sandbox: SandboxPolicy::ReadOnly,
},
ApprovalPreset {
id: "auto",
label: "Agent",
description: "Read and edit files, and run commands.",
label: "Default",
description: "Codex can read and edit files in the current workspace, and run commands. Approval is required to access the internet or edit other files. (Identical to Agent mode)",
approval: AskForApproval::OnRequest,
sandbox: SandboxPolicy::new_workspace_write_policy(),
},
ApprovalPreset {
id: "full-access",
label: "Agent (full access)",
description: "Codex can edit files outside this workspace and run commands with network access. Exercise caution when using.",
label: "Full Access",
description: "Codex can edit files outside this workspace and access the internet without asking for approval. Exercise caution when using.",
approval: AskForApproval::Never,
sandbox: SandboxPolicy::DangerFullAccess,
},
+9 -2
View File
@@ -1060,8 +1060,12 @@ impl App {
AppEvent::OpenAllModelsPopup { models } => {
self.chat_widget.open_all_models_popup(models);
}
AppEvent::OpenFullAccessConfirmation { preset } => {
self.chat_widget.open_full_access_confirmation(preset);
AppEvent::OpenFullAccessConfirmation {
preset,
return_to_permissions,
} => {
self.chat_widget
.open_full_access_confirmation(preset, return_to_permissions);
}
AppEvent::OpenWorldWritableWarningConfirmation {
preset,
@@ -1503,6 +1507,9 @@ impl App {
}
}
}
AppEvent::OpenPermissionsPopup => {
self.chat_widget.open_permissions_popup();
}
AppEvent::OpenReviewBranchPicker(cwd) => {
self.chat_widget.show_review_branch_picker(&cwd).await;
}
+4
View File
@@ -125,6 +125,7 @@ pub(crate) enum AppEvent {
/// Open the confirmation prompt before enabling full access mode.
OpenFullAccessConfirmation {
preset: ApprovalPreset,
return_to_permissions: bool,
},
/// Open the Windows world-writable directories warning.
@@ -227,6 +228,9 @@ pub(crate) enum AppEvent {
/// Notify that the manage skills popup was closed.
ManageSkillsClosed,
/// Re-open the permissions presets popup.
OpenPermissionsPopup,
/// Open the branch picker option from the review popup.
OpenReviewBranchPicker(PathBuf),
+30 -5
View File
@@ -2292,6 +2292,9 @@ impl ChatWidget {
SlashCommand::Approvals => {
self.open_approvals_popup();
}
SlashCommand::Permissions => {
self.open_permissions_popup();
}
SlashCommand::ElevateSandbox => {
#[cfg(target_os = "windows")]
{
@@ -3565,6 +3568,16 @@ impl ChatWidget {
/// Open a popup to choose the approvals mode (ask for approval policy + sandbox policy).
pub(crate) fn open_approvals_popup(&mut self) {
self.open_approval_mode_popup(true);
}
/// Open a popup to choose the permissions mode (approval policy + sandbox policy).
pub(crate) fn open_permissions_popup(&mut self) {
let include_read_only = cfg!(target_os = "windows");
self.open_approval_mode_popup(include_read_only);
}
fn open_approval_mode_popup(&mut self, include_read_only: bool) {
let current_approval = self.config.approval_policy.value();
let current_sandbox = self.config.sandbox_policy.get();
let mut items: Vec<SelectionItem> = Vec::new();
@@ -3581,10 +3594,13 @@ impl ChatWidget {
&& presets.iter().any(|preset| preset.id == "auto");
for preset in presets.into_iter() {
if !include_read_only && preset.id == "read-only" {
continue;
}
let is_current =
Self::preset_matches_current(current_approval, current_sandbox, &preset);
let name = if preset.id == "auto" && windows_degraded_sandbox_enabled {
"Agent (non-elevated sandbox)".to_string()
"Default (non-elevated sandbox)".to_string()
} else {
preset.label.to_string()
};
@@ -3604,6 +3620,7 @@ impl ChatWidget {
vec![Box::new(move |tx| {
tx.send(AppEvent::OpenFullAccessConfirmation {
preset: preset_clone.clone(),
return_to_permissions: !include_read_only,
});
})]
} else if preset.id == "auto" {
@@ -3673,7 +3690,7 @@ impl ChatWidget {
});
self.bottom_pane.show_selection_view(SelectionViewParams {
title: Some("Select Approval Mode".to_string()),
title: Some("Update Model Permissions".to_string()),
footer_note,
footer_hint: Some(standard_popup_hint_line()),
items,
@@ -3773,7 +3790,11 @@ impl ChatWidget {
None
}
pub(crate) fn open_full_access_confirmation(&mut self, preset: ApprovalPreset) {
pub(crate) fn open_full_access_confirmation(
&mut self,
preset: ApprovalPreset,
return_to_permissions: bool,
) {
let approval = preset.approval;
let sandbox = preset.sandbox;
let mut header_children: Vec<Box<dyn Renderable>> = Vec::new();
@@ -3801,8 +3822,12 @@ impl ChatWidget {
tx.send(AppEvent::PersistFullAccessWarningAcknowledged);
}));
let deny_actions: Vec<SelectionAction> = vec![Box::new(|tx| {
tx.send(AppEvent::OpenApprovalsPopup);
let deny_actions: Vec<SelectionAction> = vec![Box::new(move |tx| {
if return_to_permissions {
tx.send(AppEvent::OpenPermissionsPopup);
} else {
tx.send(AppEvent::OpenApprovalsPopup);
}
})];
let items = vec![
@@ -2,12 +2,17 @@
source: tui/src/chatwidget/tests.rs
expression: popup
---
Select Approval Mode
Update Model Permissions
1. Read Only (current) Requires approval to edit files and run commands.
2. Agent Read and edit files, and run commands.
3. Agent (full access) Codex can edit files outside this workspace and run
commands with network access. Exercise caution when
using.
1. Read Only (current) Codex can read files in the current workspace.
Approval is required to edit files or access the
internet.
2. Default Codex can read and edit files in the current
workspace, and run commands. Approval is required to
access the internet or edit other files. (Identical
to Agent mode)
3. Full Access Codex can edit files outside this workspace and
access the internet without asking for approval.
Exercise caution when using.
Press enter to confirm or esc to go back
@@ -3,12 +3,17 @@ source: tui/src/chatwidget/tests.rs
assertion_line: 2654
expression: popup
---
Select Approval Mode
Update Model Permissions
1. Read Only (current) Requires approval to edit files and run commands.
2. Agent Read and edit files, and run commands.
3. Agent (full access) Codex can edit files outside this workspace and run
commands with network access. Exercise caution when
using.
1. Read Only (current) Codex can read files in the current workspace.
Approval is required to edit files or access the
internet.
2. Default Codex can read and edit files in the current
workspace, and run commands. Approval is required to
access the internet or edit other files. (Identical
to Agent mode)
3. Full Access Codex can edit files outside this workspace and
access the internet without asking for approval.
Exercise caution when using.
Press enter to confirm or esc to go back
@@ -3,14 +3,20 @@ source: tui/src/chatwidget/tests.rs
assertion_line: 2003
expression: popup
---
Select Approval Mode
Update Model Permissions
1. Read Only (current) Requires approval to edit files and run
commands.
2. Agent (non-elevated sandbox) Read and edit files, and run commands.
3. Agent (full access) Codex can edit files outside this workspace
and run commands with network access.
Exercise caution when using.
1. Read Only (current) Codex can read files in the current
workspace. Approval is required to edit
files or access the internet.
2. Default (non-elevated sandbox) Codex can read and edit files in the
current workspace, and run commands.
Approval is required to access the
internet or edit other files. (Identical
to Agent mode)
3. Full Access Codex can edit files outside this
workspace and access the internet without
asking for approval. Exercise caution
when using.
The non-elevated sandbox protects your files and prevents network access under
most circumstances. However, it carries greater risk if prompt injected. To
+2 -2
View File
@@ -2791,7 +2791,7 @@ async fn full_access_confirmation_popup_snapshot() {
.into_iter()
.find(|preset| preset.id == "full-access")
.expect("full access preset");
chat.open_full_access_confirmation(preset);
chat.open_full_access_confirmation(preset, false);
let popup = render_bottom_popup(&chat, 80);
assert_snapshot!("full_access_confirmation_popup", popup);
@@ -3112,7 +3112,7 @@ async fn approvals_popup_navigation_skips_disabled() {
.expect("render approvals popup after disabled selection");
let screen = terminal.backend().vt100().screen().contents();
assert!(
screen.contains("Select Approval Mode"),
screen.contains("Update Model Permissions"),
"popup should remain open after selecting a disabled entry"
);
assert!(
+5
View File
@@ -947,6 +947,11 @@ pub(crate) fn new_session_info(
"/approvals".into(),
" - choose what Codex can do without approval".dim(),
]),
Line::from(vec![
" ".into(),
"/permissions".into(),
" - choose what Codex is allowed to do".dim(),
]),
Line::from(vec![
" ".into(),
"/model".into(),
+3
View File
@@ -14,6 +14,7 @@ pub enum SlashCommand {
// more frequently used commands should be listed first.
Model,
Approvals,
Permissions,
#[strum(serialize = "setup-elevated-sandbox")]
ElevateSandbox,
Experimental,
@@ -60,6 +61,7 @@ impl SlashCommand {
SlashCommand::Model => "choose what model and reasoning effort to use",
SlashCommand::Collab => "change collaboration mode (experimental)",
SlashCommand::Approvals => "choose what Codex can do without approval",
SlashCommand::Permissions => "choose what Codex is allowed to do",
SlashCommand::ElevateSandbox => "set up elevated agent sandbox",
SlashCommand::Experimental => "toggle beta features",
SlashCommand::Mcp => "list configured MCP tools",
@@ -86,6 +88,7 @@ impl SlashCommand {
// | SlashCommand::Undo
| SlashCommand::Model
| SlashCommand::Approvals
| SlashCommand::Permissions
| SlashCommand::ElevateSandbox
| SlashCommand::Experimental
| SlashCommand::Review