mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
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:
committed by
GitHub
Unverified
parent
836f0343a3
commit
038b78c915
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
@@ -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![
|
||||
|
||||
+11
-6
@@ -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
|
||||
|
||||
+11
-6
@@ -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
|
||||
|
||||
+13
-7
@@ -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
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user