feat(tui2) /personality

This commit is contained in:
Dylan Hurd
2026-01-20 14:48:09 -08:00
parent 24ab98ed85
commit 4d98928c00
13 changed files with 111 additions and 28 deletions
+9 -2
View File
@@ -1725,8 +1725,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,
@@ -2070,6 +2074,9 @@ impl App {
AppEvent::OpenApprovalsPopup => {
self.chat_widget.open_approvals_popup();
}
AppEvent::OpenPermissionsPopup => {
self.chat_widget.open_permissions_popup();
}
AppEvent::OpenReviewBranchPicker(cwd) => {
self.chat_widget.show_review_branch_picker(&cwd).await;
}
+4
View File
@@ -119,6 +119,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.
@@ -201,6 +202,9 @@ pub(crate) enum AppEvent {
/// Re-open the approval presets popup.
OpenApprovalsPopup,
/// Re-open the permissions presets popup.
OpenPermissionsPopup,
/// Open the branch picker option from the review popup.
OpenReviewBranchPicker(PathBuf),
+11 -1
View File
@@ -297,7 +297,17 @@ mod tests {
CommandItem::UserPrompt(_) => None,
})
.collect();
assert_eq!(cmds, vec!["model", "resume", "compact", "mention", "mcp"]);
assert_eq!(
cmds,
vec![
"model",
"permissions",
"resume",
"compact",
"mention",
"mcp"
]
);
}
#[test]
@@ -559,7 +559,7 @@ mod tests {
];
ListSelectionView::new(
SelectionViewParams {
title: Some("Select Approval Mode".to_string()),
title: Some("Update Model Permissions".to_string()),
subtitle: subtitle.map(str::to_string),
footer_hint: Some(standard_popup_hint_line()),
items,
@@ -629,7 +629,7 @@ mod tests {
]);
let view = ListSelectionView::new(
SelectionViewParams {
title: Some("Select Approval Mode".to_string()),
title: Some("Update Model Permissions".to_string()),
footer_note: Some(footer_note),
footer_hint: Some(standard_popup_hint_line()),
items,
@@ -656,7 +656,7 @@ mod tests {
}];
let mut view = ListSelectionView::new(
SelectionViewParams {
title: Some("Select Approval Mode".to_string()),
title: Some("Update Model Permissions".to_string()),
footer_hint: Some(standard_popup_hint_line()),
items,
is_searchable: true,
@@ -5,5 +5,5 @@ expression: terminal.backend()
" "
"› /mo "
" "
" /model choose what model and reasoning effort to use "
" /mention mention a file "
" /model choose what model and reasoning effort to "
" use "
@@ -1,10 +1,9 @@
---
source: tui2/src/bottom_pane/list_selection_view.rs
assertion_line: 640
expression: "render_lines_with_width(&view, 40)"
---
Select Approval Mode
Update Model Permissions
› 1. Read Only (current) Codex can
read files
@@ -3,7 +3,7 @@ source: tui2/src/bottom_pane/list_selection_view.rs
expression: render_lines(&view)
---
Select Approval Mode
Update Model Permissions
Switch between Codex approval presets
› 1. Read Only (current) Codex can read files
@@ -3,7 +3,7 @@ source: tui2/src/bottom_pane/list_selection_view.rs
expression: render_lines(&view)
---
Select Approval Mode
Update Model Permissions
› 1. Read Only (current) Codex can read files
2. Full Access Codex can edit files
+67 -11
View File
@@ -2020,6 +2020,9 @@ impl ChatWidget {
SlashCommand::Approvals => {
self.open_approvals_popup();
}
SlashCommand::Permissions => {
self.open_permissions_popup();
}
SlashCommand::ElevateSandbox => {
#[cfg(target_os = "windows")]
{
@@ -3225,6 +3228,52 @@ 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(
"Update Model Permissions",
true,
false,
|preset, is_windows_degraded| {
if preset.id == "auto" && is_windows_degraded {
"Agent (non-elevated sandbox)".to_string()
} else {
preset.label.to_string()
}
},
|preset| preset.description.to_string(),
);
}
/// 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(
"Update Model Permissions",
include_read_only,
true,
|preset, _| match preset.id {
"read-only" => "Read Only".to_string(),
"auto" => "Default".to_string(),
"full-access" => "Full Access".to_string(),
_ => preset.label.to_string(),
},
|preset| match preset.id {
"auto" => format!("{} Identical to Agent approvals.", preset.description),
_ => preset.description.to_string(),
},
);
}
fn open_approval_mode_popup<F, D>(
&mut self,
title: &str,
include_read_only: bool,
return_to_permissions: bool,
label_for_preset: F,
description_for_preset: D,
) where
F: Fn(&ApprovalPreset, bool) -> String,
D: Fn(&ApprovalPreset) -> String,
{
let current_approval = self.config.approval_policy.value();
let current_sandbox = self.config.sandbox_policy.get();
let mut items: Vec<SelectionItem> = Vec::new();
@@ -3241,15 +3290,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()
} else {
preset.label.to_string()
};
let description_text = preset.description;
let description = Some(description_text.to_string());
let name = label_for_preset(&preset, windows_degraded_sandbox_enabled);
let description = Some(description_for_preset(&preset));
let requires_confirmation = preset.id == "full-access"
&& !self
.config
@@ -3261,6 +3308,7 @@ impl ChatWidget {
vec![Box::new(move |tx| {
tx.send(AppEvent::OpenFullAccessConfirmation {
preset: preset_clone.clone(),
return_to_permissions,
});
})]
} else if preset.id == "auto" {
@@ -3329,7 +3377,7 @@ impl ChatWidget {
});
self.bottom_pane.show_selection_view(SelectionViewParams {
title: Some("Select Approval Mode".to_string()),
title: Some(title.to_string()),
footer_note,
footer_hint: Some(standard_popup_hint_line()),
items,
@@ -3410,7 +3458,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();
@@ -3438,8 +3490,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,11 @@
source: tui2/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.
3. Agent (full access) Codex can edit files outside this workspace and
access the internet. Exercise caution when using.
Press enter to confirm or esc to go back
+1 -1
View File
@@ -2404,7 +2404,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);
+5
View File
@@ -1009,6 +1009,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,
Skills,
@@ -57,6 +58,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::Mcp => "list configured MCP tools",
SlashCommand::Logout => "log out of Codex",
@@ -82,6 +84,7 @@ impl SlashCommand {
// | SlashCommand::Undo
| SlashCommand::Model
| SlashCommand::Approvals
| SlashCommand::Permissions
| SlashCommand::ElevateSandbox
| SlashCommand::Review
| SlashCommand::Logout => false,