mirror of
https://github.com/openai/codex.git
synced 2026-11-05 08:28:29 +00:00
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:
1 parent
e0e304b123
commit
fcff0d6c52
13 files changed
+627
-144
No files matched your search
@@ -5,6 +5,8 @@
|
||||
//! loop.
|
||||
|
||||
use super::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_utils_approval_presets::ApprovalPreset;
|
||||
|
||||
impl App {
|
||||
pub(super) async fn rebuild_config_for_cwd(&self, cwd: PathBuf) -> Result<Config> {
|
||||
@@ -21,6 +23,161 @@ impl App {
|
||||
.wrap_err_with(|| format!("Failed to rebuild config for cwd {cwd_display}"))
|
||||
}
|
||||
|
||||
pub(super) async fn rebuild_config_for_permission_profile(
|
||||
&self,
|
||||
profile_id: &str,
|
||||
) -> Result<Config> {
|
||||
let mut overrides = self.harness_overrides.clone();
|
||||
overrides.cwd = Some(self.chat_widget.config_ref().cwd.to_path_buf());
|
||||
overrides.sandbox_mode = None;
|
||||
overrides.permission_profile = None;
|
||||
overrides.default_permissions = Some(profile_id.to_string());
|
||||
ConfigBuilder::default()
|
||||
.codex_home(self.config.codex_home.to_path_buf())
|
||||
.cli_overrides(self.cli_kv_overrides.clone())
|
||||
.harness_overrides(overrides)
|
||||
.loader_overrides(self.loader_overrides.clone())
|
||||
.build()
|
||||
.await
|
||||
.wrap_err_with(|| {
|
||||
format!("Failed to rebuild config for permission profile {profile_id}")
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(super) async fn permission_profile_for_windows_setup(
|
||||
&self,
|
||||
preset: &ApprovalPreset,
|
||||
profile_selection: Option<&PermissionProfileSelection>,
|
||||
) -> Result<PermissionProfile> {
|
||||
match profile_selection {
|
||||
Some(selection) => Ok(self
|
||||
.rebuild_config_for_permission_profile(selection.profile_id.as_str())
|
||||
.await?
|
||||
.permissions
|
||||
.permission_profile()
|
||||
.clone()),
|
||||
None => Ok(preset.permission_profile.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn apply_permission_profile_selection(
|
||||
&mut self,
|
||||
selection: PermissionProfileSelection,
|
||||
) -> bool {
|
||||
let PermissionProfileSelection {
|
||||
profile_id,
|
||||
approval_policy,
|
||||
approvals_reviewer,
|
||||
display_label,
|
||||
} = selection;
|
||||
let selected_config = match self
|
||||
.rebuild_config_for_permission_profile(profile_id.as_str())
|
||||
.await
|
||||
{
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
error = %err,
|
||||
profile_id,
|
||||
"failed to resolve selected permission profile"
|
||||
);
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to set permission profile `{profile_id}`: {err}"
|
||||
));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let permission_profile = selected_config.permissions.permission_profile();
|
||||
let active_permission_profile = selected_config.permissions.active_permission_profile();
|
||||
let network = selected_config.permissions.network.clone();
|
||||
|
||||
let mut config = self.config.clone();
|
||||
if let Some(policy) = approval_policy
|
||||
&& !self.try_set_approval_policy_on_config(
|
||||
&mut config,
|
||||
policy,
|
||||
"Failed to set approval policy",
|
||||
"failed to set selected permission profile approval policy on app config",
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if let Err(err) = config
|
||||
.permissions
|
||||
.set_permission_profile_from_session_snapshot(
|
||||
PermissionProfileSnapshot::from_session_snapshot(
|
||||
permission_profile.clone(),
|
||||
active_permission_profile.clone(),
|
||||
),
|
||||
)
|
||||
{
|
||||
tracing::warn!(
|
||||
error = %err,
|
||||
profile_id,
|
||||
"failed to set selected permission profile on app config"
|
||||
);
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to set permission profile `{profile_id}`: {err}"
|
||||
));
|
||||
return false;
|
||||
}
|
||||
if let Some(reviewer) = approvals_reviewer {
|
||||
config.approvals_reviewer = reviewer;
|
||||
}
|
||||
config.permissions.network = network.clone();
|
||||
self.config = config;
|
||||
|
||||
if let Some(policy) = approval_policy {
|
||||
self.runtime_approval_policy_override = Some(policy);
|
||||
self.chat_widget.set_approval_policy(policy);
|
||||
}
|
||||
if let Err(err) = self.chat_widget.set_permission_profile_with_active_profile(
|
||||
permission_profile.clone(),
|
||||
active_permission_profile.clone(),
|
||||
) {
|
||||
tracing::warn!(
|
||||
error = %err,
|
||||
profile_id,
|
||||
"failed to set selected permission profile on chat config"
|
||||
);
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to set permission profile `{profile_id}`: {err}"
|
||||
));
|
||||
return false;
|
||||
}
|
||||
if let Some(reviewer) = approvals_reviewer {
|
||||
self.chat_widget.set_approvals_reviewer(reviewer);
|
||||
}
|
||||
self.chat_widget.set_permission_network(network);
|
||||
self.runtime_permission_profile_override =
|
||||
Some(RuntimePermissionProfileOverride::from_config(&self.config));
|
||||
self.sync_active_thread_permission_settings_to_cached_session()
|
||||
.await;
|
||||
self.app_event_tx
|
||||
.send(AppEvent::CodexOp(AppCommand::override_turn_context(
|
||||
/*cwd*/ None,
|
||||
approval_policy,
|
||||
approvals_reviewer,
|
||||
Some(permission_profile.clone()),
|
||||
active_permission_profile,
|
||||
/*windows_sandbox_level*/ None,
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
/*service_tier*/ None,
|
||||
/*collaboration_mode*/ None,
|
||||
/*personality*/ None,
|
||||
)));
|
||||
self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
history_cell::new_info_event(
|
||||
format!("Permissions updated to {display_label}"),
|
||||
/*hint*/ None,
|
||||
),
|
||||
)));
|
||||
true
|
||||
}
|
||||
|
||||
pub(super) async fn refresh_in_memory_config_from_disk(&mut self) -> Result<()> {
|
||||
let mut config = self
|
||||
.rebuild_config_for_cwd(self.chat_widget.config_ref().cwd.to_path_buf())
|
||||
@@ -73,13 +230,25 @@ impl App {
|
||||
"Failed to carry forward approval policy override: {err}"
|
||||
));
|
||||
}
|
||||
if let Some(profile) = self.runtime_permission_profile_override.as_ref()
|
||||
&& let Err(err) = config.permissions.set_permission_profile(profile.clone())
|
||||
{
|
||||
tracing::warn!(%err, "failed to carry forward permission profile override");
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to carry forward permission profile override: {err}"
|
||||
));
|
||||
if let Some(profile_override) = self.runtime_permission_profile_override.as_ref() {
|
||||
match config
|
||||
.permissions
|
||||
.set_permission_profile_from_session_snapshot(
|
||||
PermissionProfileSnapshot::from_session_snapshot(
|
||||
profile_override.permission_profile.clone(),
|
||||
profile_override.active_permission_profile.clone(),
|
||||
),
|
||||
) {
|
||||
Ok(()) => {
|
||||
config.permissions.network = profile_override.network.clone();
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(%err, "failed to carry forward permission profile override");
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to carry forward permission profile override: {err}"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,8 +510,9 @@ impl App {
|
||||
self.chat_widget
|
||||
.add_error_message(format!("Failed to enable Auto-review: {err}"));
|
||||
}
|
||||
if let Some(permission_profile) = permission_profile_override_value {
|
||||
self.runtime_permission_profile_override = Some(permission_profile);
|
||||
if permission_profile_override.is_some() {
|
||||
self.runtime_permission_profile_override =
|
||||
Some(RuntimePermissionProfileOverride::from_config(&self.config));
|
||||
}
|
||||
|
||||
if approval_policy_override.is_some()
|
||||
|
||||
@@ -800,18 +800,24 @@ impl App {
|
||||
AppEvent::OpenFullAccessConfirmation {
|
||||
preset,
|
||||
return_to_permissions,
|
||||
profile_selection,
|
||||
} => {
|
||||
self.chat_widget
|
||||
.open_full_access_confirmation(preset, return_to_permissions);
|
||||
self.chat_widget.open_full_access_confirmation(
|
||||
preset,
|
||||
return_to_permissions,
|
||||
profile_selection,
|
||||
);
|
||||
}
|
||||
AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset,
|
||||
profile_selection,
|
||||
sample_paths,
|
||||
extra_count,
|
||||
failed_scan,
|
||||
} => {
|
||||
self.chat_widget.open_world_writable_warning_confirmation(
|
||||
preset,
|
||||
profile_selection,
|
||||
sample_paths,
|
||||
extra_count,
|
||||
failed_scan,
|
||||
@@ -848,10 +854,17 @@ impl App {
|
||||
self.launch_external_editor(tui).await;
|
||||
}
|
||||
}
|
||||
AppEvent::OpenWindowsSandboxEnablePrompt { preset } => {
|
||||
self.chat_widget.open_windows_sandbox_enable_prompt(preset);
|
||||
AppEvent::OpenWindowsSandboxEnablePrompt {
|
||||
preset,
|
||||
profile_selection,
|
||||
} => {
|
||||
self.chat_widget
|
||||
.open_windows_sandbox_enable_prompt(preset, profile_selection);
|
||||
}
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt { preset } => {
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt {
|
||||
preset,
|
||||
profile_selection,
|
||||
} => {
|
||||
self.session_telemetry.counter(
|
||||
"codex.windows_sandbox.fallback_prompt_shown",
|
||||
/*inc*/ 1,
|
||||
@@ -866,12 +879,30 @@ impl App {
|
||||
);
|
||||
}
|
||||
self.chat_widget
|
||||
.open_windows_sandbox_fallback_prompt(preset);
|
||||
.open_windows_sandbox_fallback_prompt(preset, profile_selection);
|
||||
}
|
||||
AppEvent::BeginWindowsSandboxElevatedSetup { preset } => {
|
||||
AppEvent::BeginWindowsSandboxElevatedSetup {
|
||||
preset,
|
||||
profile_selection,
|
||||
} => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let permission_profile = preset.permission_profile.clone();
|
||||
let permission_profile = match self
|
||||
.permission_profile_for_windows_setup(&preset, profile_selection.as_ref())
|
||||
.await
|
||||
{
|
||||
Ok(permission_profile) => permission_profile,
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
error = %err,
|
||||
"failed to resolve permission profile for elevated Windows sandbox setup"
|
||||
);
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to prepare Windows sandbox for the selected permission profile: {err}"
|
||||
));
|
||||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
};
|
||||
let policy_cwd = self.config.cwd.clone();
|
||||
let command_cwd = policy_cwd.clone();
|
||||
let env_map: std::collections::HashMap<String, String> =
|
||||
@@ -887,6 +918,7 @@ impl App {
|
||||
tx.send(AppEvent::EnableWindowsSandboxForAgentMode {
|
||||
preset,
|
||||
mode: WindowsSandboxEnableMode::Elevated,
|
||||
profile_selection,
|
||||
});
|
||||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
@@ -903,7 +935,10 @@ impl App {
|
||||
);
|
||||
})
|
||||
else {
|
||||
tx.send(AppEvent::OpenWindowsSandboxFallbackPrompt { preset });
|
||||
tx.send(AppEvent::OpenWindowsSandboxFallbackPrompt {
|
||||
preset,
|
||||
profile_selection,
|
||||
});
|
||||
return Ok(AppRunControl::Continue);
|
||||
};
|
||||
tokio::task::spawn_blocking(move || {
|
||||
@@ -924,6 +959,7 @@ impl App {
|
||||
AppEvent::EnableWindowsSandboxForAgentMode {
|
||||
preset: preset.clone(),
|
||||
mode: WindowsSandboxEnableMode::Elevated,
|
||||
profile_selection: profile_selection.clone(),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -955,7 +991,10 @@ impl App {
|
||||
error = %err,
|
||||
"failed to run elevated Windows sandbox setup"
|
||||
);
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt { preset }
|
||||
AppEvent::OpenWindowsSandboxFallbackPrompt {
|
||||
preset,
|
||||
profile_selection,
|
||||
}
|
||||
}
|
||||
};
|
||||
tx.send(event);
|
||||
@@ -963,13 +1002,31 @@ impl App {
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = preset;
|
||||
let _ = (preset, profile_selection);
|
||||
}
|
||||
}
|
||||
AppEvent::BeginWindowsSandboxLegacySetup { preset } => {
|
||||
AppEvent::BeginWindowsSandboxLegacySetup {
|
||||
preset,
|
||||
profile_selection,
|
||||
} => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let permission_profile = preset.permission_profile.clone();
|
||||
let permission_profile = match self
|
||||
.permission_profile_for_windows_setup(&preset, profile_selection.as_ref())
|
||||
.await
|
||||
{
|
||||
Ok(permission_profile) => permission_profile,
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
error = %err,
|
||||
"failed to resolve permission profile for legacy Windows sandbox setup"
|
||||
);
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to prepare Windows sandbox for the selected permission profile: {err}"
|
||||
));
|
||||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
};
|
||||
let policy_cwd = self.config.cwd.clone();
|
||||
let command_cwd = policy_cwd.clone();
|
||||
let env_map: std::collections::HashMap<String, String> =
|
||||
@@ -988,7 +1045,10 @@ impl App {
|
||||
);
|
||||
})
|
||||
else {
|
||||
tx.send(AppEvent::OpenWindowsSandboxFallbackPrompt { preset });
|
||||
tx.send(AppEvent::OpenWindowsSandboxFallbackPrompt {
|
||||
preset,
|
||||
profile_selection,
|
||||
});
|
||||
return Ok(AppRunControl::Continue);
|
||||
};
|
||||
tokio::task::spawn_blocking(move || {
|
||||
@@ -1014,12 +1074,13 @@ impl App {
|
||||
tx.send(AppEvent::EnableWindowsSandboxForAgentMode {
|
||||
preset,
|
||||
mode: WindowsSandboxEnableMode::Legacy,
|
||||
profile_selection,
|
||||
});
|
||||
});
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = preset;
|
||||
let _ = (preset, profile_selection);
|
||||
}
|
||||
}
|
||||
AppEvent::BeginWindowsSandboxGrantReadRoot { path } => {
|
||||
@@ -1082,7 +1143,11 @@ impl App {
|
||||
));
|
||||
}
|
||||
},
|
||||
AppEvent::EnableWindowsSandboxForAgentMode { preset, mode } => {
|
||||
AppEvent::EnableWindowsSandboxForAgentMode {
|
||||
preset,
|
||||
mode,
|
||||
profile_selection,
|
||||
} => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
self.chat_widget.clear_windows_sandbox_setup_status();
|
||||
@@ -1142,11 +1207,40 @@ impl App {
|
||||
self.app_event_tx.send(
|
||||
AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset: Some(preset.clone()),
|
||||
profile_selection: profile_selection.clone(),
|
||||
sample_paths,
|
||||
extra_count,
|
||||
failed_scan,
|
||||
},
|
||||
);
|
||||
} else if let Some(selection) = profile_selection {
|
||||
self.app_event_tx.send(AppEvent::CodexOp(
|
||||
AppCommand::override_turn_context(
|
||||
/*cwd*/ None,
|
||||
/*approval_policy*/ None,
|
||||
/*approvals_reviewer*/ None,
|
||||
/*permission_profile*/ None,
|
||||
/*active_permission_profile*/ None,
|
||||
#[cfg(target_os = "windows")]
|
||||
Some(windows_sandbox_level),
|
||||
/*model*/ None,
|
||||
/*effort*/ None,
|
||||
/*summary*/ None,
|
||||
/*service_tier*/ None,
|
||||
/*collaboration_mode*/ None,
|
||||
/*personality*/ None,
|
||||
),
|
||||
));
|
||||
self.apply_permission_profile_selection(selection).await;
|
||||
let _ = mode;
|
||||
self.chat_widget.add_plain_history_lines(vec![
|
||||
Line::from(vec!["• ".dim(), "Sandbox ready".into()]),
|
||||
Line::from(vec![
|
||||
" ".into(),
|
||||
"Codex can now safely edit files and execute commands in your computer"
|
||||
.dark_gray(),
|
||||
]),
|
||||
]);
|
||||
} else {
|
||||
self.app_event_tx.send(AppEvent::CodexOp(
|
||||
AppCommand::override_turn_context(
|
||||
@@ -1196,7 +1290,7 @@ impl App {
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = (preset, mode);
|
||||
let _ = (preset, mode, profile_selection);
|
||||
}
|
||||
}
|
||||
AppEvent::PersistModelSelection { model, effort } => {
|
||||
@@ -1460,7 +1554,7 @@ impl App {
|
||||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
self.runtime_permission_profile_override =
|
||||
Some(self.config.permissions.permission_profile().clone());
|
||||
Some(RuntimePermissionProfileOverride::from_config(&self.config));
|
||||
self.sync_active_thread_permission_settings_to_cached_session()
|
||||
.await;
|
||||
|
||||
@@ -1496,6 +1590,9 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
AppEvent::SelectPermissionProfile(selection) => {
|
||||
self.apply_permission_profile_selection(selection).await;
|
||||
}
|
||||
AppEvent::UpdateApprovalsReviewer(policy) => {
|
||||
self.config.approvals_reviewer = policy;
|
||||
self.chat_widget.set_approvals_reviewer(policy);
|
||||
|
||||
@@ -47,6 +47,7 @@ impl App {
|
||||
fn send_world_writable_scan_failed(tx: &AppEventSender) {
|
||||
tx.send(AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset: None,
|
||||
profile_selection: None,
|
||||
sample_paths: Vec::new(),
|
||||
extra_count: 0usize,
|
||||
failed_scan: true,
|
||||
|
||||
@@ -80,6 +80,7 @@ use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::CollaborationModeMask;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::models::ActivePermissionProfile;
|
||||
@@ -1627,6 +1628,91 @@ async fn reset_memories_clears_local_memory_directories() -> Result<()> {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn apply_permission_profile_selection_preserves_loader_overrides() -> Result<()> {
|
||||
let (mut app, mut app_event_rx, _op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
let selected_config = codex_home.path().join("work.config.toml");
|
||||
std::fs::write(
|
||||
&selected_config,
|
||||
r#"
|
||||
default_permissions = "locked-down"
|
||||
|
||||
[permissions.locked-down.filesystem]
|
||||
":minimal" = "read"
|
||||
"#,
|
||||
)?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf().abs();
|
||||
app.loader_overrides.user_config_path = Some(selected_config.abs());
|
||||
app.harness_overrides.sandbox_mode = Some(SandboxMode::WorkspaceWrite);
|
||||
app.harness_overrides.permission_profile = Some(PermissionProfile::workspace_write());
|
||||
|
||||
assert!(
|
||||
app.apply_permission_profile_selection(PermissionProfileSelection {
|
||||
profile_id: "locked-down".to_string(),
|
||||
approval_policy: None,
|
||||
approvals_reviewer: None,
|
||||
display_label: "locked-down".to_string(),
|
||||
})
|
||||
.await
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
app.config
|
||||
.permissions
|
||||
.active_permission_profile()
|
||||
.as_ref()
|
||||
.map(|profile| profile.id.as_str()),
|
||||
Some("locked-down")
|
||||
);
|
||||
assert_eq!(
|
||||
app.chat_widget
|
||||
.config_ref()
|
||||
.permissions
|
||||
.active_permission_profile()
|
||||
.as_ref()
|
||||
.map(|profile| profile.id.as_str()),
|
||||
Some("locked-down")
|
||||
);
|
||||
assert_eq!(
|
||||
app.runtime_permission_profile_override,
|
||||
Some(RuntimePermissionProfileOverride::from_config(&app.config))
|
||||
);
|
||||
let op = match app_event_rx.try_recv() {
|
||||
Ok(AppEvent::CodexOp(op)) => op,
|
||||
other => panic!("expected CodexOp event, got {other:?}"),
|
||||
};
|
||||
assert_eq!(
|
||||
op,
|
||||
Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: None,
|
||||
approvals_reviewer: None,
|
||||
permission_profile: Some(app.config.permissions.permission_profile().clone()),
|
||||
active_permission_profile: app.config.permissions.active_permission_profile(),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
service_tier: None,
|
||||
collaboration_mode: None,
|
||||
personality: None,
|
||||
}
|
||||
);
|
||||
let cell = match app_event_rx.try_recv() {
|
||||
Ok(AppEvent::InsertHistoryCell(cell)) => cell,
|
||||
other => panic!("expected InsertHistoryCell event, got {other:?}"),
|
||||
};
|
||||
let rendered = cell
|
||||
.display_lines(/*width*/ 120)
|
||||
.into_iter()
|
||||
.map(|line| line.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(rendered.contains("Permissions updated to locked-down"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_feature_flags_enabling_guardian_selects_auto_review() -> Result<()> {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
@@ -1687,7 +1773,7 @@ async fn update_feature_flags_enabling_guardian_selects_auto_review() -> Result<
|
||||
assert_eq!(app.runtime_approval_policy_override, None);
|
||||
assert_eq!(
|
||||
app.runtime_permission_profile_override,
|
||||
Some(auto_review.permission_profile())
|
||||
Some(RuntimePermissionProfileOverride::from_config(&app.config))
|
||||
);
|
||||
assert_eq!(
|
||||
op_rx.try_recv(),
|
||||
|
||||
@@ -591,7 +591,9 @@ impl App {
|
||||
let permissions_override = Self::turn_permissions_override_from_config(
|
||||
config,
|
||||
active_permission_profile.as_ref(),
|
||||
self.runtime_permission_profile_override.as_ref(),
|
||||
self.runtime_permission_profile_override
|
||||
.as_ref()
|
||||
.map(|profile| &profile.permission_profile),
|
||||
);
|
||||
app_server
|
||||
.turn_start(
|
||||
|
||||
Reference in new issue
Block a user