diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 3cd3c57dcb..c5147a19d6 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -443,6 +443,11 @@ impl PersonalityCatalogCache { catalog } + fn clear(&mut self) { + self.catalogs.clear(); + self.lru.clear(); + } + fn touch(&mut self, cwd: &Path) { if let Some(index) = self.lru.iter().position(|entry| entry.as_path() == cwd) { self.lru.remove(index); @@ -664,6 +669,10 @@ impl CodexMessageProcessor { .unwrap_or_default() } + async fn clear_personality_catalog_cache(&self) { + self.personality_catalogs.lock().await.clear(); + } + async fn personality_catalog_for_cwd( &self, cwd: &AbsolutePathBuf, @@ -673,12 +682,13 @@ impl CodexMessageProcessor { return Ok(catalog); } + let cloud_requirements = self.current_cloud_requirements(); let config_layer_stack = load_config_layers_state( &self.config.codex_home, Some(cwd.clone()), cli_overrides, LoaderOverrides::default(), - CloudRequirementsLoader::default(), + cloud_requirements, ) .await .map_err(|err| config_load_error(&err))?; @@ -1268,6 +1278,7 @@ impl CodexMessageProcessor { let active_login = self.active_login.clone(); let auth_manager = self.auth_manager.clone(); let cloud_requirements = self.cloud_requirements.clone(); + let personality_catalogs = self.personality_catalogs.clone(); let chatgpt_base_url = self.config.chatgpt_base_url.clone(); let codex_home = self.config.codex_home.clone(); let cli_overrides = self.current_cli_overrides(); @@ -1306,6 +1317,7 @@ impl CodexMessageProcessor { chatgpt_base_url, codex_home, ); + personality_catalogs.lock().await.clear(); sync_default_client_residency_requirement( &cli_overrides, cloud_requirements.as_ref(), @@ -1385,6 +1397,7 @@ impl CodexMessageProcessor { let active_login = self.active_login.clone(); let auth_manager = self.auth_manager.clone(); let cloud_requirements = self.cloud_requirements.clone(); + let personality_catalogs = self.personality_catalogs.clone(); let chatgpt_base_url = self.config.chatgpt_base_url.clone(); let codex_home = self.config.codex_home.clone(); let cli_overrides = self.current_cli_overrides(); @@ -1420,6 +1433,7 @@ impl CodexMessageProcessor { chatgpt_base_url, codex_home, ); + personality_catalogs.lock().await.clear(); sync_default_client_residency_requirement( &cli_overrides, cloud_requirements.as_ref(), @@ -1560,6 +1574,7 @@ impl CodexMessageProcessor { self.config.chatgpt_base_url.clone(), self.config.codex_home.clone(), ); + self.clear_personality_catalog_cache().await; let cli_overrides = self.current_cli_overrides(); sync_default_client_residency_requirement(&cli_overrides, self.cloud_requirements.as_ref()) .await; diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 328dcaa941..fd405a5cc9 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -5849,8 +5849,7 @@ impl ChatWidget { .config .personality .clone() - .filter(|_| self.config.features.enabled(Feature::Personality)) - .filter(|personality| !personality.is_none()); + .filter(|_| self.config.features.enabled(Feature::Personality)); let service_tier = self.config.service_tier.map(Some); let op = AppCommand::user_turn( items, diff --git a/codex-rs/tui/src/chatwidget/tests/plan_mode.rs b/codex-rs/tui/src/chatwidget/tests/plan_mode.rs index e3f779c26e..668093db2b 100644 --- a/codex-rs/tui/src/chatwidget/tests/plan_mode.rs +++ b/codex-rs/tui/src/chatwidget/tests/plan_mode.rs @@ -1365,6 +1365,23 @@ async fn user_turn_includes_personality_from_config() { } } +#[tokio::test] +async fn user_turn_preserves_explicit_none_personality_from_config() { + let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.2-codex")).await; + chat.set_feature_enabled(Feature::Personality, /*enabled*/ true); + chat.thread_id = Some(ThreadId::new()); + chat.set_model("gpt-5.2-codex"); + chat.set_personality(Personality::none()); + + chat.bottom_pane + .set_composer_text("hello".to_string(), Vec::new(), Vec::new()); + chat.handle_key_event(KeyEvent::from(KeyCode::Enter)); + match next_submit_op(&mut op_rx) { + Op::UserTurn { personality, .. } if personality == Some(Personality::none()) => {} + other => panic!("expected Op::UserTurn with none personality, got {other:?}"), + } +} + #[tokio::test] async fn plan_update_renders_history_cell() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;