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
5 changes: 5 additions & 0 deletions codex-rs/tui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ use codex_app_server_protocol::Turn;
use codex_app_server_protocol::TurnError as AppServerTurnError;
use codex_app_server_protocol::TurnStatus;
use codex_app_server_protocol::WriteStatus;
use codex_config::CloudConfigBundleLoader;
use codex_config::ConfigLayerStackOrdering;
use codex_config::LoaderOverrides;
use codex_config::types::ApprovalsReviewer;
Expand Down Expand Up @@ -490,6 +491,7 @@ pub(crate) struct App {
cli_kv_overrides: Vec<(String, TomlValue)>,
harness_overrides: ConfigOverrides,
loader_overrides: LoaderOverrides,
cloud_config_bundle: CloudConfigBundleLoader,
runtime_approval_policy_override: Option<AskForApproval>,
runtime_permission_profile_override: Option<RuntimePermissionProfileOverride>,

Expand Down Expand Up @@ -720,6 +722,7 @@ impl App {
cli_kv_overrides: Vec<(String, TomlValue)>,
harness_overrides: ConfigOverrides,
loader_overrides: LoaderOverrides,
cloud_config_bundle: CloudConfigBundleLoader,
initial_prompt: Option<String>,
initial_images: Vec<PathBuf>,
session_selection: SessionSelection,
Expand Down Expand Up @@ -754,6 +757,7 @@ impl App {
&mut config,
&cli_kv_overrides,
&harness_overrides,
&cloud_config_bundle,
entered_trust_nux,
)
.await?;
Expand Down Expand Up @@ -1003,6 +1007,7 @@ See the Codex keymap documentation for supported actions and examples."
cli_kv_overrides,
harness_overrides,
loader_overrides,
cloud_config_bundle,
runtime_approval_policy_override: None,
runtime_permission_profile_override: None,
file_search,
Expand Down
66 changes: 64 additions & 2 deletions codex-rs/tui/src/app/config_persistence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ impl App {
.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());
.loader_overrides(self.loader_overrides.clone())
.cloud_config_bundle(self.cloud_config_bundle.clone());
build_config_on_runtime_worker(
builder,
format!("Failed to rebuild config for cwd {cwd_display}"),
Expand All @@ -55,7 +56,8 @@ impl App {
.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());
.loader_overrides(self.loader_overrides.clone())
.cloud_config_bundle(self.cloud_config_bundle.clone());
build_config_on_runtime_worker(
builder,
format!("Failed to rebuild config for permission profile {profile_id}"),
Expand Down Expand Up @@ -1123,6 +1125,66 @@ mod tests {
Ok(())
}

// Regression coverage for `/new` and `/clear`: cloud requirements
// must survive the config refresh that runs before thread transitions.
#[tokio::test]
async fn refresh_in_memory_config_from_disk_keeps_cloud_requirements_for_thread_transitions()
-> Result<()> {
let mut app = make_test_app().await;
let codex_home = tempdir()?;
let required_policy = codex_protocol::protocol::AskForApproval::Never;
let cloud_config_bundle =
codex_config::test_support::CloudConfigBundleFixture::loader_with_enterprise_requirement(
r#"allowed_approval_policies = ["never"]"#,
);

let config = ConfigBuilder::default()
.codex_home(codex_home.path().to_path_buf())
.loader_overrides(LoaderOverrides::without_managed_config_for_tests())
.cloud_config_bundle(cloud_config_bundle.clone())
.build()
.await?;
app.config = config;
app.cloud_config_bundle = cloud_config_bundle;
let app_id = "unit_test_cloud_requirements_reload_marker";
std::fs::write(
codex_home.path().join("config.toml"),
format!(
r#"
[apps.{app_id}]
enabled = false
"#
),
)?;

let assert_cloud_requirements = |app: &App| {
let config = app.fresh_session_config();
assert_eq!(
config
.config_layer_stack
.requirements_toml()
.allowed_approval_policies
.clone(),
Some(vec![required_policy])
);
assert_eq!(config.permissions.approval_policy.value(), required_policy);
};

assert_cloud_requirements(&app);
assert_eq!(app_enabled_in_effective_config(&app.config, app_id), None);

// This is the fallible reload that the best-effort `/new`, `/clear`,
// `/fork`, side-conversation, and session-picker paths wrap.
app.refresh_in_memory_config_from_disk().await?;

assert_eq!(
app_enabled_in_effective_config(&app.config, app_id),
Some(false)
);
assert_cloud_requirements(&app);
Ok(())
}

#[tokio::test]
async fn refresh_in_memory_config_from_disk_best_effort_keeps_current_config_on_error()
-> Result<()> {
Expand Down
1 change: 1 addition & 0 deletions codex-rs/tui/src/app/test_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub(super) async fn make_test_app() -> App {
cli_kv_overrides: Vec::new(),
harness_overrides: ConfigOverrides::default(),
loader_overrides: LoaderOverrides::without_managed_config_for_tests(),
cloud_config_bundle: CloudConfigBundleLoader::default(),
runtime_approval_policy_override: None,
runtime_permission_profile_override: None,
file_search,
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/tui/src/app/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3945,6 +3945,7 @@ async fn make_test_app() -> App {
cli_kv_overrides: Vec::new(),
harness_overrides: ConfigOverrides::default(),
loader_overrides: LoaderOverrides::without_managed_config_for_tests(),
cloud_config_bundle: CloudConfigBundleLoader::default(),
runtime_approval_policy_override: None,
runtime_permission_profile_override: None,
file_search,
Expand Down Expand Up @@ -4009,6 +4010,7 @@ async fn make_test_app_with_channels() -> (
cli_kv_overrides: Vec::new(),
harness_overrides: ConfigOverrides::default(),
loader_overrides: LoaderOverrides::without_managed_config_for_tests(),
cloud_config_bundle: CloudConfigBundleLoader::default(),
runtime_approval_policy_override: None,
runtime_permission_profile_override: None,
file_search,
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/tui/src/external_agent_config_migration_startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::legacy_core::config::edit::ConfigEditsBuilder;
use crate::tui;
use codex_app_server_protocol::ExternalAgentConfigDetectParams;
use codex_app_server_protocol::ExternalAgentConfigMigrationItem;
use codex_config::CloudConfigBundleLoader;
use codex_features::Feature;
use color_eyre::eyre::Result;
use color_eyre::eyre::WrapErr;
Expand Down Expand Up @@ -248,6 +249,7 @@ pub(crate) async fn handle_external_agent_config_migration_prompt_if_needed(
config: &mut Config,
cli_kv_overrides: &[(String, TomlValue)],
harness_overrides: &ConfigOverrides,
cloud_config_bundle: &CloudConfigBundleLoader,
entered_trust_nux: bool,
) -> Result<ExternalAgentConfigMigrationStartupOutcome> {
if !should_show_external_agent_config_migration_prompt(config, entered_trust_nux) {
Expand Down Expand Up @@ -321,6 +323,7 @@ pub(crate) async fn handle_external_agent_config_migration_prompt_if_needed(
.codex_home(config.codex_home.to_path_buf())
.cli_overrides(cli_kv_overrides.to_vec())
.harness_overrides(harness_overrides.clone())
.cloud_config_bundle(cloud_config_bundle.clone())
.build()
.await
.wrap_err("Failed to reload config after external agent migration")?;
Expand Down
1 change: 1 addition & 0 deletions codex-rs/tui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,7 @@ async fn run_ratatui_app(
cli_kv_overrides.clone(),
overrides.clone(),
loader_overrides.clone(),
cloud_config_bundle,
prompt,
images,
session_selection,
Expand Down
Loading