tui: plumb permission profile selection (#23708)

## Why

The named-profile `/permissions` picker needs a small TUI action path
that can select permission profiles without folding the menu UI and
profile metadata into the same review.

## What changed

- Carry permission-profile selections through the TUI app event flow.
- Persist selected profiles while preserving the existing approval
settings and guardrail prompts.
- Keep the legacy `/permissions` picker behavior in this layer; the
profile-mode menu stays in the follow-up PR.

## Stack

1. [#22931](https://github.com/openai/codex/pull/22931):
runtime/session/network propagation for active permission profiles.
2. **This PR**: TUI selection plumbing and guardrail flow.
3. [#21559](https://github.com/openai/codex/pull/21559): profile-aware
`/permissions` menu and custom profile display.

<img width="1632" height="1186" alt="image"
src="https://github.com/user-attachments/assets/69ddcd5e-b57c-468d-8c1d-246916323c15"
/>

## Validation

- `git diff --cached --check` before commit.
- Full test run skipped at the user request while pushing the split
stack.
This commit is contained in:
viyatb-oai authored and GitHub committed 2026-05-21 12:26:36 -03:00
1 parent e0e304b123
commit fcff0d6c52
13 files changed
+627 -144

No files matched your search

+19 -1
View File
@@ -10,6 +10,7 @@ use crate::app_event::AppEvent;
use crate::app_event::ExitMode;
use crate::app_event::FeedbackCategory;
use crate::app_event::HistoryLookupResponse;
use crate::app_event::PermissionProfileSelection;
use crate::app_event::RateLimitRefreshOrigin;
use crate::app_event::RealtimeAudioDeviceKind;
#[cfg(target_os = "windows")]
@@ -484,7 +485,7 @@ pub(crate) struct App {
harness_overrides: ConfigOverrides,
loader_overrides: LoaderOverrides,
runtime_approval_policy_override: Option<AskForApproval>,
runtime_permission_profile_override: Option<PermissionProfile>,
runtime_permission_profile_override: Option<RuntimePermissionProfileOverride>,
pub(crate) file_search: FileSearchManager,
@@ -554,6 +555,23 @@ pub(crate) struct App {
pending_hook_enabled_writes: HashMap<String, Option<bool>>,
}
#[derive(Debug, Clone, PartialEq)]
struct RuntimePermissionProfileOverride {
permission_profile: PermissionProfile,
active_permission_profile: Option<ActivePermissionProfile>,
network: Option<crate::legacy_core::config::NetworkProxySpec>,
}
impl RuntimePermissionProfileOverride {
fn from_config(config: &Config) -> Self {
Self {
permission_profile: config.permissions.permission_profile().clone(),
active_permission_profile: config.permissions.active_permission_profile(),
network: config.permissions.network.clone(),
}
}
}
fn active_turn_not_steerable_turn_error(error: &TypedRequestError) -> Option<AppServerTurnError> {
let TypedRequestError::Server { source, .. } = error else {
return None;