mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat: guardian as an extension (contributors part) (#22216)
Part 1 of guardian as extension. This bind all the logic to spawn another agent from an extension and it adds `ThreadId` in the start thread collaborator
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
mod agent;
|
||||
|
||||
pub use agent::AgentSpawnFuture;
|
||||
pub use agent::AgentSpawner;
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::future::Future;
|
||||
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_tool_api::ToolBundle;
|
||||
|
||||
@@ -10,10 +11,16 @@ mod prompt;
|
||||
pub use prompt::PromptFragment;
|
||||
pub use prompt::PromptSlot;
|
||||
|
||||
/// Contributor that receives host-owned thread-start input before later
|
||||
/// contributors read from extension stores.
|
||||
/// Contributor that receives the live thread id and host-owned thread-start
|
||||
/// input before later contributors read from extension stores.
|
||||
pub trait ThreadStartContributor<C>: Send + Sync {
|
||||
fn contribute(&self, input: &C, session_store: &ExtensionData, thread_store: &ExtensionData);
|
||||
fn contribute(
|
||||
&self,
|
||||
thread_id: ThreadId,
|
||||
input: &C,
|
||||
session_store: &ExtensionData,
|
||||
thread_store: &ExtensionData,
|
||||
);
|
||||
}
|
||||
|
||||
/// Extension contribution that adds prompt fragments during prompt assembly.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
mod capabilities;
|
||||
mod contributors;
|
||||
mod registry;
|
||||
mod state;
|
||||
|
||||
pub use capabilities::AgentSpawnFuture;
|
||||
pub use capabilities::AgentSpawner;
|
||||
pub use codex_tool_api::FunctionToolSpec;
|
||||
pub use codex_tool_api::ToolBundle;
|
||||
pub use codex_tool_api::ToolCall;
|
||||
|
||||
@@ -16,6 +16,7 @@ workspace = true
|
||||
codex-core = { workspace = true }
|
||||
codex-extension-api = { workspace = true }
|
||||
codex-features = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_extension_api::ExtensionRegistryBuilder;
|
||||
use codex_extension_api::PromptFragment;
|
||||
use codex_extension_api::ThreadStartContributor;
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::ThreadId;
|
||||
|
||||
const DEFAULT_ATTRIBUTION_VALUE: &str = "Codex <noreply@openai.com>";
|
||||
|
||||
@@ -42,6 +43,7 @@ struct GitAttributionConfig {
|
||||
impl ThreadStartContributor<Config> for GitAttributionExtension {
|
||||
fn contribute(
|
||||
&self,
|
||||
_thread_id: ThreadId,
|
||||
config: &Config,
|
||||
_session_store: &ExtensionData,
|
||||
thread_store: &ExtensionData,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
load("//:defs.bzl", "codex_rust_crate")
|
||||
|
||||
codex_rust_crate(
|
||||
name = "guardian",
|
||||
crate_name = "codex_guardian",
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
name = "codex-guardian"
|
||||
version.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "codex_guardian"
|
||||
path = "src/lib.rs"
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codex-core = { workspace = true }
|
||||
codex-extension-api = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
@@ -0,0 +1,73 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_core::config::Config;
|
||||
use codex_extension_api::AgentSpawnFuture;
|
||||
use codex_extension_api::AgentSpawner;
|
||||
use codex_extension_api::ExtensionData;
|
||||
use codex_extension_api::ExtensionRegistryBuilder;
|
||||
use codex_extension_api::ThreadStartContributor;
|
||||
use codex_protocol::ThreadId;
|
||||
|
||||
/// 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> ThreadStartContributor<Config> for GuardianExtension<S>
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
fn contribute(
|
||||
&self,
|
||||
thread_id: ThreadId,
|
||||
_input: &Config,
|
||||
_session_store: &ExtensionData,
|
||||
thread_store: &ExtensionData,
|
||||
) {
|
||||
thread_store.insert(GuardianThreadContext {
|
||||
forked_from_thread_id: thread_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Installs the guardian contributors into the extension registry.
|
||||
pub fn install<S>(registry: &mut ExtensionRegistryBuilder<Config>, agent_spawner: S)
|
||||
where
|
||||
S: Send + Sync + 'static,
|
||||
{
|
||||
registry.thread_start_contributor(Arc::new(GuardianExtension::new(agent_spawner)));
|
||||
}
|
||||
Reference in New Issue
Block a user