From f2d9bccbdef4f2dfa2a063c5e3f09f68ab8530a0 Mon Sep 17 00:00:00 2001 From: felixxia-oai Date: Mon, 14 Sep 2026 18:43:20 +0000 Subject: [PATCH] Remove Guardian subagent-spawner plumbing (#45491) ## What changed - Remove `AgentSpawner` and `AgentSpawnFuture` from the extension API, along with the Guardian wrapper, thread lifecycle context, and app-server injection plumbing. - Define `InternalSessionSpawnFuture` directly as a boxed future instead of aliasing `AgentSpawnFuture`. - Raise the workspace `rustls` minimum version to `0.23.45`. GitOrigin-RevId: b7319dee41bfb869479afeb7555a6f050c4d00a5 --- codex-rs/Cargo.toml | 2 +- codex-rs/app-server/src/extensions.rs | 38 +--------- codex-rs/app-server/src/mcp_refresh.rs | 32 ++++---- codex-rs/app-server/src/message_processor.rs | 32 ++++---- .../extension-api/src/capabilities/agent.rs | 38 ---------- .../src/capabilities/internal_session.rs | 8 +- .../ext/extension-api/src/capabilities/mod.rs | 3 - codex-rs/ext/extension-api/src/lib.rs | 2 - .../ext/extension-api/tests/capabilities.rs | 29 -------- codex-rs/ext/guardian-v2/src/lib.rs | 73 +------------------ 10 files changed, 39 insertions(+), 218 deletions(-) delete mode 100644 codex-rs/ext/extension-api/src/capabilities/agent.rs diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 788afe6e80..21d86ee677 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -430,7 +430,7 @@ reqwest = { version = "0.12", features = ["cookies"] } rmcp = { version = "=3.2.0", default-features = false } runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" } rustix = { version = "1.1.4", features = ["net"] } -rustls = { version = "0.23", default-features = false, features = [ +rustls = { version = "0.23.45", default-features = false, features = [ "aws_lc_rs", "std", ] } diff --git a/codex-rs/app-server/src/extensions.rs b/codex-rs/app-server/src/extensions.rs index 46af9c9a9c..0751c40d1a 100644 --- a/codex-rs/app-server/src/extensions.rs +++ b/codex-rs/app-server/src/extensions.rs @@ -8,13 +8,9 @@ use codex_app_server_protocol::ThreadGoal; use codex_app_server_protocol::ThreadGoalUpdatedNotification; use codex_app_server_protocol::ThreadQueueChangedNotification; use codex_app_server_protocol::WarningNotification; -use codex_core::NewThread; -use codex_core::StartThreadOptions; use codex_core::ThreadManager; use codex_core::config::Config; use codex_exec_server::EnvironmentManager; -use codex_extension_api::AgentSpawnFuture; -use codex_extension_api::AgentSpawner; use codex_extension_api::ExtensionEventSink; use codex_extension_api::ExtensionRegistry; use codex_extension_api::ExtensionRegistryBuilder; @@ -25,7 +21,6 @@ use codex_goal_extension::GoalService; use codex_http_client::HttpClientFactory; use codex_login::AuthManager; use codex_protocol::ThreadId; -use codex_protocol::error::CodexErr; use codex_protocol::protocol::Event; use codex_protocol::protocol::EventMsg; use codex_queue_extension::QueuedItemService; @@ -52,13 +47,9 @@ pub(crate) struct ThreadExtensionDependencies { pub(crate) turn_start_admission: Option>, } -pub(crate) fn thread_extensions( - guardian_agent_spawner: S, +pub(crate) fn thread_extensions( dependencies: ThreadExtensionDependencies, -) -> Arc> -where - S: AgentSpawner + 'static, -{ +) -> Arc> { let ThreadExtensionDependencies { event_sink, auth_manager, @@ -101,12 +92,7 @@ where git_attribution_base_url, http_client_factory, ); - codex_guardian_v2::install( - &mut builder, - guardian_agent_spawner, - auth_manager.clone(), - thread_manager, - ); + codex_guardian_v2::install(&mut builder, auth_manager.clone(), thread_manager); codex_memories_extension::install(&mut builder, codex_otel::global()); codex_mcp_extension::install(&mut builder); codex_mcp_extension::install_executor_plugins(&mut builder, environment_manager); @@ -309,24 +295,6 @@ impl ExtensionEventSink for AppServerExtensionEventSink { } } -pub(crate) fn guardian_agent_spawner( - thread_manager: Weak, -) -> impl AgentSpawner { - move |forked_from_thread_id: ThreadId, - options: StartThreadOptions| - -> AgentSpawnFuture<'static, NewThread, CodexErr> { - let thread_manager = thread_manager.clone(); - Box::pin(async move { - let thread_manager = thread_manager.upgrade().ok_or_else(|| { - CodexErr::UnsupportedOperation("thread manager dropped".to_string()) - })?; - thread_manager - .spawn_subagent(forked_from_thread_id, options) - .await - }) - } -} - #[cfg(test)] mod tests { use codex_protocol::protocol::ThreadGoal as CoreThreadGoal; diff --git a/codex-rs/app-server/src/mcp_refresh.rs b/codex-rs/app-server/src/mcp_refresh.rs index f2c446b3a4..2953580378 100644 --- a/codex-rs/app-server/src/mcp_refresh.rs +++ b/codex-rs/app-server/src/mcp_refresh.rs @@ -65,7 +65,6 @@ async fn load_refresh_config( mod tests { use super::*; use crate::extensions::ThreadExtensionDependencies; - use crate::extensions::guardian_agent_spawner; use crate::extensions::thread_extensions; use codex_arg0::Arg0DispatchPaths; use codex_config::CloudConfigBundleLoader; @@ -326,23 +325,20 @@ enabled = false codex_core::CodexAppsToolsCache::default(), SessionSource::Exec, Arc::clone(&environment_manager), - thread_extensions( - guardian_agent_spawner(thread_manager.clone()), - ThreadExtensionDependencies { - event_sink: Arc::new(NoopExtensionEventSink), - auth_manager: auth_manager.clone(), - state_db: Some(state_db.clone()), - analytics_events_client: codex_analytics::AnalyticsEventsClient::disabled(), - thread_manager: thread_manager.clone(), - goal_service: Arc::new(codex_goal_extension::GoalService::new()), - environment_manager: Arc::clone(&environment_manager), - executor_skill_provider: Arc::clone(&executor_skill_provider), - git_attribution_base_url: good_config.chatgpt_base_url.clone(), - http_client_factory: good_config.http_client_factory(), - queue_service: None, - turn_start_admission: None, - }, - ), + thread_extensions(ThreadExtensionDependencies { + event_sink: Arc::new(NoopExtensionEventSink), + auth_manager: auth_manager.clone(), + state_db: Some(state_db.clone()), + analytics_events_client: codex_analytics::AnalyticsEventsClient::disabled(), + thread_manager: thread_manager.clone(), + goal_service: Arc::new(codex_goal_extension::GoalService::new()), + environment_manager: Arc::clone(&environment_manager), + executor_skill_provider: Arc::clone(&executor_skill_provider), + git_attribution_base_url: good_config.chatgpt_base_url.clone(), + http_client_factory: good_config.http_client_factory(), + queue_service: None, + turn_start_admission: None, + }), Arc::new(CodexHomeUserInstructionsProvider::new( good_config.codex_home.clone(), )), diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 5798a5f602..f08d34ee44 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -13,7 +13,6 @@ use crate::error_code::invalid_params; use crate::error_code::invalid_request; use crate::extensions::ThreadExtensionDependencies; use crate::extensions::app_server_extension_event_sink; -use crate::extensions::guardian_agent_spawner; use crate::extensions::thread_extensions; use crate::external_agent_migration::ExternalAgentConfigRequestProcessor; use crate::external_agent_migration::ExternalAgentConfigRequestProcessorArgs; @@ -335,23 +334,20 @@ impl MessageProcessor { codex_core::CodexAppsToolsCache::default(), session_source, environment_manager, - thread_extensions( - guardian_agent_spawner(thread_manager.clone()), - ThreadExtensionDependencies { - event_sink: Arc::clone(&extension_event_sink), - auth_manager: auth_manager.clone(), - state_db: state_db.clone(), - analytics_events_client: analytics_events_client.clone(), - thread_manager: thread_manager.clone(), - goal_service: Arc::clone(&goal_service), - environment_manager: Arc::clone(&environment_manager_for_extensions), - executor_skill_provider: Arc::clone(&executor_skill_provider), - git_attribution_base_url: config.chatgpt_base_url.clone(), - http_client_factory: config.http_client_factory(), - queue_service: queue_service.clone(), - turn_start_admission: Some(Arc::clone(&turn_start_admission)), - }, - ), + thread_extensions(ThreadExtensionDependencies { + event_sink: Arc::clone(&extension_event_sink), + auth_manager: auth_manager.clone(), + state_db: state_db.clone(), + analytics_events_client: analytics_events_client.clone(), + thread_manager: thread_manager.clone(), + goal_service: Arc::clone(&goal_service), + environment_manager: Arc::clone(&environment_manager_for_extensions), + executor_skill_provider: Arc::clone(&executor_skill_provider), + git_attribution_base_url: config.chatgpt_base_url.clone(), + http_client_factory: config.http_client_factory(), + queue_service: queue_service.clone(), + turn_start_admission: Some(Arc::clone(&turn_start_admission)), + }), Arc::new(CodexHomeUserInstructionsProvider::new( config.codex_home.clone(), )), diff --git a/codex-rs/ext/extension-api/src/capabilities/agent.rs b/codex-rs/ext/extension-api/src/capabilities/agent.rs deleted file mode 100644 index 3ecdfdf5fd..0000000000 --- a/codex-rs/ext/extension-api/src/capabilities/agent.rs +++ /dev/null @@ -1,38 +0,0 @@ -use std::future::Future; -use std::pin::Pin; - -use codex_protocol::ThreadId; - -/// Future returned by one injected subagent-spawn helper. -pub type AgentSpawnFuture<'a, T, E> = Pin> + Send + 'a>>; - -/// Constructor-injected host helper for extensions that need to spawn subagents. -/// -/// The extension owns the request shape and resulting handle types. The host -/// provides the implementation when it constructs the extension. -pub trait AgentSpawner: Send + Sync { - type Spawned; - type Error; - - fn spawn_subagent<'a>( - &'a self, - forked_from_thread_id: ThreadId, - request: R, - ) -> AgentSpawnFuture<'a, Self::Spawned, Self::Error>; -} - -impl AgentSpawner for F -where - F: Fn(ThreadId, R) -> AgentSpawnFuture<'static, S, E> + Send + Sync, -{ - type Spawned = S; - type Error = E; - - fn spawn_subagent<'a>( - &'a self, - forked_from_thread_id: ThreadId, - request: R, - ) -> AgentSpawnFuture<'a, Self::Spawned, Self::Error> { - self(forked_from_thread_id, request) - } -} diff --git a/codex-rs/ext/extension-api/src/capabilities/internal_session.rs b/codex-rs/ext/extension-api/src/capabilities/internal_session.rs index 756dbd550b..c7a69ae745 100644 --- a/codex-rs/ext/extension-api/src/capabilities/internal_session.rs +++ b/codex-rs/ext/extension-api/src/capabilities/internal_session.rs @@ -1,9 +1,11 @@ +use std::future::Future; +use std::pin::Pin; + use codex_protocol::ThreadId; -use super::agent::AgentSpawnFuture; - /// Future returned by one host-owned internal-session spawning request. -pub type InternalSessionSpawnFuture<'a, T, E> = AgentSpawnFuture<'a, T, E>; +pub type InternalSessionSpawnFuture<'a, T, E> = + Pin> + Send + 'a>>; /// Constructor-injected host helper for extensions that need private internal sessions. /// diff --git a/codex-rs/ext/extension-api/src/capabilities/mod.rs b/codex-rs/ext/extension-api/src/capabilities/mod.rs index deb5cba723..cdca29d0ac 100644 --- a/codex-rs/ext/extension-api/src/capabilities/mod.rs +++ b/codex-rs/ext/extension-api/src/capabilities/mod.rs @@ -1,12 +1,9 @@ -mod agent; mod conversation_history; mod events; mod internal_session; mod metrics; mod response_items; -pub use agent::AgentSpawnFuture; -pub use agent::AgentSpawner; pub use conversation_history::ConversationHistorySnapshot; pub use events::ExtensionEventSink; pub use events::ExtensionWarning; diff --git a/codex-rs/ext/extension-api/src/lib.rs b/codex-rs/ext/extension-api/src/lib.rs index d92e5c6a65..d57743c529 100644 --- a/codex-rs/ext/extension-api/src/lib.rs +++ b/codex-rs/ext/extension-api/src/lib.rs @@ -8,8 +8,6 @@ mod user_instructions; pub use session_isolation::SessionIsolation; -pub use capabilities::AgentSpawnFuture; -pub use capabilities::AgentSpawner; pub use capabilities::ConversationHistorySnapshot; pub use capabilities::ExtensionEventSink; pub use capabilities::ExtensionMetrics; diff --git a/codex-rs/ext/extension-api/tests/capabilities.rs b/codex-rs/ext/extension-api/tests/capabilities.rs index 16114102cc..b26011f15b 100644 --- a/codex-rs/ext/extension-api/tests/capabilities.rs +++ b/codex-rs/ext/extension-api/tests/capabilities.rs @@ -1,8 +1,6 @@ use std::sync::Arc; use std::sync::Mutex; -use codex_extension_api::AgentSpawnFuture; -use codex_extension_api::AgentSpawner; use codex_extension_api::InternalSessionSpawnFuture; use codex_extension_api::InternalSessionSpawner; use codex_extension_api::NoopResponseItemInjector; @@ -30,33 +28,6 @@ async fn noop_response_item_injector_returns_original_items() { assert_eq!(returned_items, items); } -#[tokio::test] -async fn closure_agent_spawner_forwards_arguments_and_result() { - let calls = Arc::new(Mutex::new(Vec::new())); - let recorded_calls = Arc::clone(&calls); - let spawner = move |thread_id: ThreadId, - request: String| - -> AgentSpawnFuture<'static, usize, &'static str> { - recorded_calls - .lock() - .expect("agent spawn calls lock") - .push((thread_id, request.clone())); - Box::pin(async move { Ok(request.len()) }) - }; - let thread_id = - ThreadId::from_string("11111111-1111-4111-8111-111111111111").expect("valid thread id"); - - let spawned = spawner - .spawn_subagent(thread_id, "delegate this".to_string()) - .await; - - assert_eq!(spawned, Ok(13)); - assert_eq!( - calls.lock().expect("agent spawn calls lock").as_slice(), - [(thread_id, "delegate this".to_string())] - ); -} - #[tokio::test] async fn closure_internal_session_spawner_forwards_arguments_and_result() { let calls = Arc::new(Mutex::new(Vec::new())); diff --git a/codex-rs/ext/guardian-v2/src/lib.rs b/codex-rs/ext/guardian-v2/src/lib.rs index c1463a64ff..4c6e3feaa0 100644 --- a/codex-rs/ext/guardian-v2/src/lib.rs +++ b/codex-rs/ext/guardian-v2/src/lib.rs @@ -3,89 +3,20 @@ use std::sync::Weak; use codex_core::ThreadManager; use codex_core::config::Config; -use codex_extension_api::AgentSpawnFuture; -use codex_extension_api::AgentSpawner; -use codex_extension_api::ExtensionFuture; use codex_extension_api::ExtensionRegistryBuilder; -use codex_extension_api::ThreadLifecycleContributor; -use codex_extension_api::ThreadStartInput; use codex_login::AuthManager; -use codex_protocol::ThreadId; mod async_scorer; mod sync_reviewer; pub use sync_reviewer::GuardianExtension as GuardianReviewerExtension; -/// Guardian extension dependencies supplied by the host at construction time. -#[derive(Clone, Debug)] -pub struct GuardianExtension { - agent_spawner: S, -} - -impl GuardianExtension { - /// Creates a guardian extension with its host-provided agent spawn helper. - pub fn new(agent_spawner: S) -> Self { - Self { agent_spawner } - } - - /// Delegates one guardian-owned subagent spawn request to the host helper. - pub fn spawn_subagent<'a, R>( - &'a self, - forked_from_thread_id: ThreadId, - request: R, - ) -> AgentSpawnFuture<'a, >::Spawned, >::Error> - where - S: AgentSpawner, - { - self.agent_spawner - .spawn_subagent(forked_from_thread_id, request) - } -} - -/// Thread-local guardian state captured when the host starts a thread. -#[derive(Clone, Copy, Debug)] -pub struct GuardianThreadContext { - forked_from_thread_id: ThreadId, -} - -impl GuardianThreadContext { - /// Returns the thread that future guardian subagents should fork from by default. - pub fn forked_from_thread_id(&self) -> ThreadId { - self.forked_from_thread_id - } -} - -impl ThreadLifecycleContributor for GuardianExtension -where - S: Send + Sync, -{ - fn on_thread_start<'a>( - &'a self, - input: ThreadStartInput<'a, Config>, - ) -> ExtensionFuture<'a, ()> { - Box::pin(async move { - let Ok(forked_from_thread_id) = ThreadId::from_string(input.thread_store.level_id()) - else { - return; - }; - input.thread_store.insert(GuardianThreadContext { - forked_from_thread_id, - }); - }) - } -} - /// Installs the guardian contributors into the extension registry. -pub fn install( +pub fn install( registry: &mut ExtensionRegistryBuilder, - agent_spawner: S, auth_manager: Arc, thread_manager: Weak, -) where - S: Send + Sync + 'static, -{ - registry.thread_lifecycle_contributor(Arc::new(GuardianExtension::new(agent_spawner))); +) { async_scorer::install(registry, auth_manager, thread_manager.clone()); sync_reviewer::install(registry, thread_manager); }