From 68ea51b3c7530bc44b37d2ac6072c9988f46caf0 Mon Sep 17 00:00:00 2001 From: Winston Howes Date: Thu, 14 May 2026 12:43:15 -0700 Subject: [PATCH] Refresh credentialed routes during session --- codex-rs/core/src/session/mod.rs | 70 ++++++++++++++++++++++- codex-rs/core/src/session/session.rs | 2 +- codex-rs/core/src/session/tests.rs | 8 ++- codex-rs/core/src/session/turn_context.rs | 1 + codex-rs/core/src/state/service.rs | 3 +- 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/codex-rs/core/src/session/mod.rs b/codex-rs/core/src/session/mod.rs index c7cf9bc2308..21c127bfc2e 100644 --- a/codex-rs/core/src/session/mod.rs +++ b/codex-rs/core/src/session/mod.rs @@ -1009,7 +1009,8 @@ impl Session { return; } }; - let spec = match spec.with_credentialed_routes(&self.services.credentialed_routes) { + let credentialed_routes = self.services.credentialed_routes.read().await.clone(); + let spec = match spec.with_credentialed_routes(&credentialed_routes) { Ok(spec) => spec, Err(err) => { warn!( @@ -1033,6 +1034,63 @@ impl Session { } } + async fn refresh_credentialed_routes_for_managed_proxy(&self) { + let Some(started_proxy) = self.services.network_proxy.as_ref() else { + return; + }; + let (chatgpt_base_url, permission_profile, spec) = { + let state = self.state.lock().await; + let session_configuration = &state.session_configuration; + let Some(spec) = session_configuration + .original_config_do_not_use + .permissions + .network + .as_ref() + else { + return; + }; + ( + session_configuration + .original_config_do_not_use + .chatgpt_base_url + .clone(), + session_configuration.permission_profile(), + spec.clone(), + ) + }; + let auth = self.services.auth_manager.auth().await; + let credentialed_routes = + crate::credentialed_routes::load_for_session(&chatgpt_base_url, auth.as_ref()).await; + let previous_routes = self.services.credentialed_routes.read().await.clone(); + if credentialed_routes.routes == previous_routes.routes + && credentialed_routes.proxy_url == previous_routes.proxy_url + { + return; + } + + let Ok(_refresh_guard) = self.managed_network_proxy_refresh_lock.acquire().await else { + error!("managed network proxy refresh semaphore closed"); + return; + }; + let current_exec_policy = self.services.exec_policy.current(); + let spec = match spec + .recompute_for_permission_profile(&permission_profile) + .and_then(|spec| spec.with_credentialed_routes(&credentialed_routes)) + .and_then(|spec| spec.with_exec_policy_network_rules(current_exec_policy.as_ref())) + { + Ok(spec) => spec, + Err(err) => { + warn!("failed to rebuild managed network proxy for credentialed routes: {err}"); + return; + } + }; + if let Err(err) = spec.apply_to_started_proxy(started_proxy).await { + warn!("failed to refresh managed network proxy for credentialed routes: {err}"); + return; + } + *self.services.credentialed_routes.write().await = credentialed_routes; + } + #[cfg(test)] pub(crate) async fn codex_home(&self) -> AbsolutePathBuf { let state = self.state.lock().await; @@ -1471,6 +1529,8 @@ impl Session { ) { self.services.hooks.store(Arc::new(hooks)); } + drop(state); + self.refresh_credentialed_routes_for_managed_proxy().await; } fn emit_config_changed_contributors( @@ -2665,8 +2725,12 @@ impl Session { .render(), ); } - if let Some(credentialed_routes_instructions) = - self.services.credentialed_routes.developer_instructions() + if let Some(credentialed_routes_instructions) = self + .services + .credentialed_routes + .read() + .await + .developer_instructions() { developer_sections.push(credentialed_routes_instructions); } diff --git a/codex-rs/core/src/session/session.rs b/codex-rs/core/src/session/session.rs index 1bc306ce455..a72394cfa84 100644 --- a/codex-rs/core/src/session/session.rs +++ b/codex-rs/core/src/session/session.rs @@ -871,7 +871,7 @@ impl Session { thread_extension_data, agent_control, network_proxy, - credentialed_routes, + credentialed_routes: RwLock::new(credentialed_routes), network_approval: Arc::clone(&network_approval), state_db: state_db_ctx.clone(), live_thread: live_thread_init.as_ref().cloned(), diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index c0cf11408f3..db82ac0701a 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -4132,7 +4132,9 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) { thread_extension_data: codex_extension_api::ExtensionData::new(thread_id.to_string()), agent_control, network_proxy: None, - credentialed_routes: crate::credentialed_routes::CredentialedRoutesSessionConfig::default(), + credentialed_routes: RwLock::new( + crate::credentialed_routes::CredentialedRoutesSessionConfig::default(), + ), network_approval: Arc::clone(&network_approval), state_db: None, live_thread: None, @@ -5985,7 +5987,9 @@ where thread_extension_data: codex_extension_api::ExtensionData::new(thread_id.to_string()), agent_control, network_proxy: None, - credentialed_routes: crate::credentialed_routes::CredentialedRoutesSessionConfig::default(), + credentialed_routes: RwLock::new( + crate::credentialed_routes::CredentialedRoutesSessionConfig::default(), + ), network_approval: Arc::clone(&network_approval), state_db: state_db.clone(), live_thread: None, diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index c646b5833df..7fbe22d2b62 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -757,6 +757,7 @@ impl Session { mcp_connection_manager .set_permission_profile(session_configuration.permission_profile()); } + self.refresh_credentialed_routes_for_managed_proxy().await; let model_info = self .services diff --git a/codex-rs/core/src/state/service.rs b/codex-rs/core/src/state/service.rs index fe0f77828e7..25d497b0621 100644 --- a/codex-rs/core/src/state/service.rs +++ b/codex-rs/core/src/state/service.rs @@ -66,7 +66,8 @@ pub(crate) struct SessionServices { pub(crate) thread_extension_data: ExtensionData, pub(crate) agent_control: AgentControl, pub(crate) network_proxy: Option, - pub(crate) credentialed_routes: crate::credentialed_routes::CredentialedRoutesSessionConfig, + pub(crate) credentialed_routes: + RwLock, pub(crate) network_approval: Arc, pub(crate) state_db: Option, pub(crate) live_thread: Option,