mirror of
https://github.com/openai/codex.git
synced 2026-05-29 15:30:22 +00:00
feat: add extension event sink capability (#23293)
## Why Extensions can already expose typed contributions and receive host capabilities such as `AgentSpawner`, but they do not have a typed way to send protocol events back through the host. Extensions that need to surface progress or status should not have to own persistence, ordering, transport fanout, or logging decisions themselves. ## What - Add `ExtensionEventSink`, a host-provided fire-and-forget sink for `codex_protocol::protocol::Event`. - Add `NoopExtensionEventSink` so hosts that do not expose extension event emission keep the existing empty-registry behavior. - Store the sink on `ExtensionRegistryBuilder` / `ExtensionRegistry`, with `with_event_sink(...)` and `event_sink()` accessors, and re-export the new capability from `codex-extension-api`. ## Testing - Not run locally; PR metadata/body update only.
This commit is contained in:
19
codex-rs/ext/extension-api/src/capabilities/events.rs
Normal file
19
codex-rs/ext/extension-api/src/capabilities/events.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use codex_protocol::protocol::Event;
|
||||
|
||||
/// Host-provided fire-and-forget sink for extension-generated events.
|
||||
///
|
||||
/// Extensions construct protocol events with the correlation id appropriate for
|
||||
/// the callback they are handling, then leave persistence, ordering, transport
|
||||
/// fanout, and logging decisions to the host.
|
||||
pub trait ExtensionEventSink: Send + Sync {
|
||||
/// Queue one protocol event for host-owned delivery.
|
||||
fn emit(&self, event: Event);
|
||||
}
|
||||
|
||||
/// Event sink used when the host does not expose extension event emission.
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct NoopExtensionEventSink;
|
||||
|
||||
impl ExtensionEventSink for NoopExtensionEventSink {
|
||||
fn emit(&self, _event: Event) {}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
mod agent;
|
||||
mod events;
|
||||
|
||||
pub use agent::AgentSpawnFuture;
|
||||
pub use agent::AgentSpawner;
|
||||
pub use events::ExtensionEventSink;
|
||||
pub use events::NoopExtensionEventSink;
|
||||
|
||||
@@ -5,6 +5,8 @@ mod state;
|
||||
|
||||
pub use capabilities::AgentSpawnFuture;
|
||||
pub use capabilities::AgentSpawner;
|
||||
pub use capabilities::ExtensionEventSink;
|
||||
pub use capabilities::NoopExtensionEventSink;
|
||||
pub use codex_tools::FunctionCallError;
|
||||
pub use codex_tools::JsonToolOutput;
|
||||
pub use codex_tools::ResponsesApiTool;
|
||||
|
||||
@@ -5,6 +5,8 @@ use crate::ApprovalReviewFuture;
|
||||
use crate::ConfigContributor;
|
||||
use crate::ContextContributor;
|
||||
use crate::ExtensionData;
|
||||
use crate::ExtensionEventSink;
|
||||
use crate::NoopExtensionEventSink;
|
||||
use crate::ThreadLifecycleContributor;
|
||||
use crate::TokenUsageContributor;
|
||||
use crate::ToolContributor;
|
||||
@@ -13,6 +15,7 @@ use crate::TurnLifecycleContributor;
|
||||
|
||||
/// Mutable registry used while hosts register typed runtime contributions.
|
||||
pub struct ExtensionRegistryBuilder<C: Sync> {
|
||||
event_sink: Arc<dyn ExtensionEventSink>,
|
||||
thread_lifecycle_contributors: Vec<Arc<dyn ThreadLifecycleContributor<C>>>,
|
||||
turn_lifecycle_contributors: Vec<Arc<dyn TurnLifecycleContributor>>,
|
||||
config_contributors: Vec<Arc<dyn ConfigContributor<C>>>,
|
||||
@@ -26,6 +29,7 @@ pub struct ExtensionRegistryBuilder<C: Sync> {
|
||||
impl<C: Sync> Default for ExtensionRegistryBuilder<C> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
event_sink: Arc::new(NoopExtensionEventSink),
|
||||
thread_lifecycle_contributors: Vec::new(),
|
||||
turn_lifecycle_contributors: Vec::new(),
|
||||
config_contributors: Vec::new(),
|
||||
@@ -44,6 +48,19 @@ impl<C: Sync> ExtensionRegistryBuilder<C> {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Creates an empty registry builder with a host-provided event sink.
|
||||
pub fn with_event_sink(event_sink: Arc<dyn ExtensionEventSink>) -> Self {
|
||||
Self {
|
||||
event_sink,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the host event sink to pass into extension constructors.
|
||||
pub fn event_sink(&self) -> Arc<dyn ExtensionEventSink> {
|
||||
Arc::clone(&self.event_sink)
|
||||
}
|
||||
|
||||
/// Registers one approval-review contributor.
|
||||
pub fn approval_review_contributor(&mut self, contributor: Arc<dyn ApprovalReviewContributor>) {
|
||||
self.approval_review_contributors.push(contributor);
|
||||
@@ -90,6 +107,7 @@ impl<C: Sync> ExtensionRegistryBuilder<C> {
|
||||
/// Finishes construction and returns the immutable registry.
|
||||
pub fn build(self) -> ExtensionRegistry<C> {
|
||||
ExtensionRegistry {
|
||||
event_sink: self.event_sink,
|
||||
thread_lifecycle_contributors: self.thread_lifecycle_contributors,
|
||||
turn_lifecycle_contributors: self.turn_lifecycle_contributors,
|
||||
config_contributors: self.config_contributors,
|
||||
@@ -104,6 +122,7 @@ impl<C: Sync> ExtensionRegistryBuilder<C> {
|
||||
|
||||
/// Immutable typed registry produced after extensions are installed.
|
||||
pub struct ExtensionRegistry<C: Sync> {
|
||||
event_sink: Arc<dyn ExtensionEventSink>,
|
||||
thread_lifecycle_contributors: Vec<Arc<dyn ThreadLifecycleContributor<C>>>,
|
||||
turn_lifecycle_contributors: Vec<Arc<dyn TurnLifecycleContributor>>,
|
||||
config_contributors: Vec<Arc<dyn ConfigContributor<C>>>,
|
||||
@@ -115,6 +134,11 @@ pub struct ExtensionRegistry<C: Sync> {
|
||||
}
|
||||
|
||||
impl<C: Sync> ExtensionRegistry<C> {
|
||||
/// Returns the host event sink retained by this registry.
|
||||
pub fn event_sink(&self) -> Arc<dyn ExtensionEventSink> {
|
||||
Arc::clone(&self.event_sink)
|
||||
}
|
||||
|
||||
/// Returns the registered thread-lifecycle contributors.
|
||||
pub fn thread_lifecycle_contributors(&self) -> &[Arc<dyn ThreadLifecycleContributor<C>>] {
|
||||
&self.thread_lifecycle_contributors
|
||||
|
||||
Reference in New Issue
Block a user