diff --git a/codex-rs/app-server/src/request_processors/config_processor.rs b/codex-rs/app-server/src/request_processors/config_processor.rs index 8043675e0d..78d756fd69 100644 --- a/codex-rs/app-server/src/request_processors/config_processor.rs +++ b/codex-rs/app-server/src/request_processors/config_processor.rs @@ -47,7 +47,6 @@ use codex_login::AuthManager; use codex_model_provider::create_model_provider; use codex_plugin::PluginId; use codex_protocol::config_types::WebSearchMode; -use codex_protocol::protocol::Op; use serde_json::json; use std::path::PathBuf; @@ -384,9 +383,18 @@ impl ConfigRequestProcessor { let Ok(thread) = self.thread_manager.get_thread(thread_id).await else { continue; }; - if let Err(err) = thread.submit(Op::ReloadUserConfig).await { - tracing::warn!("failed to request user config reload: {err}"); - } + let cwd = thread.config_snapshot().await.cwd.to_path_buf(); + let next_config = match self.load_latest_config(Some(cwd)).await { + Ok(config) => config, + Err(err) => { + tracing::warn!( + "failed to rebuild thread config for runtime refresh: {}", + err.message + ); + continue; + } + }; + thread.refresh_runtime_config(next_config).await; } } diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 75c8e8c9b2..11015acbef 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -1160,39 +1160,6 @@ async fn reload_user_config_layer_updates_effective_apps_config() { assert_eq!(app.destructive_enabled, Some(false)); } -#[tokio::test] -async fn refresh_runtime_config_updates_effective_apps_config() { - let (session, _turn_context) = make_session_and_context().await; - let codex_home = session.codex_home().await; - std::fs::create_dir_all(&codex_home).expect("create codex home"); - std::fs::write( - codex_home.join(CONFIG_TOML_FILE), - "[apps.calendar]\nenabled = false\ndestructive_enabled = false\n", - ) - .expect("write user config"); - - let next_config = load_latest_config_for_session(&session).await; - session.refresh_runtime_config(next_config).await; - - let config = session.get_config().await; - let apps_toml = config - .config_layer_stack - .effective_config() - .as_table() - .and_then(|table| table.get("apps")) - .cloned() - .expect("apps table"); - let apps = codex_config::types::AppsConfigToml::deserialize(apps_toml) - .expect("deserialize apps config"); - let app = apps - .apps - .get("calendar") - .expect("calendar app config exists"); - - assert!(!app.enabled); - assert_eq!(app.destructive_enabled, Some(false)); -} - #[tokio::test] async fn reload_user_config_layer_refreshes_hooks() -> anyhow::Result<()> { let session = make_session_with_config(|config| { @@ -1298,13 +1265,18 @@ disabled_tools = [ } #[tokio::test] -async fn refresh_runtime_config_updates_effective_tool_suggest_config() { +async fn refresh_runtime_config_updates_runtime_refreshable_fields_and_keeps_session_static_settings() + { let (session, _turn_context) = make_session_and_context().await; let codex_home = session.codex_home().await; std::fs::create_dir_all(&codex_home).expect("create codex home"); std::fs::write( codex_home.join(CONFIG_TOML_FILE), - r#"[tool_suggest] + r#"[apps.calendar] +enabled = false +destructive_enabled = false + +[tool_suggest] disabled_tools = [ { type = "connector", id = " calendar " }, { type = "plugin", id = "slack@openai-curated" }, @@ -1313,31 +1285,30 @@ disabled_tools = [ ) .expect("write user config"); - let next_config = load_latest_config_for_session(&session).await; - session.refresh_runtime_config(next_config).await; - - let config = session.get_config().await; - assert_eq!( - config.tool_suggest.disabled_tools, - vec![ - ToolSuggestDisabledTool::connector("calendar"), - ToolSuggestDisabledTool::plugin("slack@openai-curated"), - ] - ); -} - -#[tokio::test] -async fn refresh_runtime_config_keeps_session_static_settings() { - let (session, _turn_context) = make_session_and_context().await; let original = session.get_config().await; - let mut next_config = (*original).clone(); + let mut next_config = load_latest_config_for_session(&session).await; next_config.model = Some("gpt-5.4".to_string()); next_config.notify = Some(vec!["echo".to_string()]); - next_config.tool_suggest.disabled_tools = vec![ToolSuggestDisabledTool::connector("calendar")]; session.refresh_runtime_config(next_config).await; let config = session.get_config().await; + let apps_toml = config + .config_layer_stack + .effective_config() + .as_table() + .and_then(|table| table.get("apps")) + .cloned() + .expect("apps table"); + let apps = codex_config::types::AppsConfigToml::deserialize(apps_toml) + .expect("deserialize apps config"); + let app = apps + .apps + .get("calendar") + .expect("calendar app config exists"); + + assert!(!app.enabled); + assert_eq!(app.destructive_enabled, Some(false)); assert_eq!(config.model, original.model); assert_eq!(config.notify, original.notify); assert_eq!(