mirror of
https://github.com/openai/codex.git
synced 2026-09-15 12:08:01 +00:00
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
This commit is contained in:
@@ -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",
|
||||
] }
|
||||
|
||||
@@ -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<Arc<dyn TurnStartAdmission>>,
|
||||
}
|
||||
|
||||
pub(crate) fn thread_extensions<S>(
|
||||
guardian_agent_spawner: S,
|
||||
pub(crate) fn thread_extensions(
|
||||
dependencies: ThreadExtensionDependencies,
|
||||
) -> Arc<ExtensionRegistry<Config>>
|
||||
where
|
||||
S: AgentSpawner<StartThreadOptions, Spawned = NewThread, Error = CodexErr> + 'static,
|
||||
{
|
||||
) -> Arc<ExtensionRegistry<Config>> {
|
||||
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<ThreadManager>,
|
||||
) -> impl AgentSpawner<StartThreadOptions, Spawned = NewThread, Error = CodexErr> {
|
||||
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;
|
||||
|
||||
@@ -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(),
|
||||
)),
|
||||
|
||||
@@ -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(),
|
||||
)),
|
||||
|
||||
@@ -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<Box<dyn Future<Output = Result<T, E>> + 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<R>: 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<R, S, E, F> AgentSpawner<R> 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)
|
||||
}
|
||||
}
|
||||
@@ -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<Box<dyn Future<Output = Result<T, E>> + Send + 'a>>;
|
||||
|
||||
/// Constructor-injected host helper for extensions that need private internal sessions.
|
||||
///
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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<S> {
|
||||
agent_spawner: S,
|
||||
}
|
||||
|
||||
impl<S> GuardianExtension<S> {
|
||||
/// 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, <S as AgentSpawner<R>>::Spawned, <S as AgentSpawner<R>>::Error>
|
||||
where
|
||||
S: AgentSpawner<R>,
|
||||
{
|
||||
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<S> ThreadLifecycleContributor<Config> for GuardianExtension<S>
|
||||
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<S>(
|
||||
pub fn install(
|
||||
registry: &mut ExtensionRegistryBuilder<Config>,
|
||||
agent_spawner: S,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
thread_manager: Weak<ThreadManager>,
|
||||
) 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user