Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion codex-rs/tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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,

Expand Down Expand Up @@ -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;
Expand Down
188 changes: 179 additions & 9 deletions codex-rs/tui/src/app/config_persistence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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()
Comment thread
viyatb-oai marked this conversation as resolved.
.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())
Expand Down Expand Up @@ -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}"
));
}
}
}
}

Expand Down Expand Up @@ -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()
Expand Down
Loading
Loading