mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat: add extension turn-input contributors (#25959)
## Disclaimer Do not use for now ## Why Extensions can already contribute prompt fragments and request same-turn item injection, but there was no host-owned hook for contributing structured `ResponseItem`s while Codex is assembling a new turn's initial model input. This change adds that seam so extensions can attach turn-local input that depends on the submitted user input and resolved turn environments without routing through prompt text or late injection. ## What changed - add `TurnInputContributor` to `codex_extension_api` and export the new `TurnInputContext` / `TurnInputEnvironment` types it receives - teach `ExtensionRegistry` to register and expose turn-input contributors alongside the existing extension hooks - call registered turn-input contributors from `core/src/session/turn.rs` while building the initial injected input for a turn, then append their returned `ResponseItem`s after the skill and plugin injections
This commit is contained in:
@@ -69,6 +69,8 @@ use codex_analytics::InvocationType;
|
||||
use codex_analytics::TurnResolvedConfigFact;
|
||||
use codex_analytics::build_track_events_context;
|
||||
use codex_async_utils::OrCancelExt;
|
||||
use codex_extension_api::TurnInputContext;
|
||||
use codex_extension_api::TurnInputEnvironment;
|
||||
use codex_features::Feature;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_git_utils::get_git_repo_root_with_fs;
|
||||
@@ -513,6 +515,9 @@ async fn build_skills_and_plugins(
|
||||
};
|
||||
let skills_outcome = turn_context.turn_skills.outcome.as_ref();
|
||||
let connector_slug_counts = build_connector_slug_counts(&available_connectors);
|
||||
let extension_injection_items =
|
||||
build_extension_turn_input_items(sess, turn_context, &user_input, cancellation_token)
|
||||
.await?;
|
||||
let skill_name_counts_lower =
|
||||
build_skill_name_counts(&skills_outcome.skills, &skills_outcome.disabled_paths).1;
|
||||
let mentioned_skills = collect_explicit_skill_mentions(
|
||||
@@ -588,9 +593,57 @@ async fn build_skills_and_plugins(
|
||||
|
||||
let mut injection_items = skill_items;
|
||||
injection_items.extend(plugin_items);
|
||||
injection_items.extend(extension_injection_items);
|
||||
Some((injection_items, explicitly_enabled_connectors))
|
||||
}
|
||||
|
||||
async fn build_extension_turn_input_items(
|
||||
sess: &Arc<Session>,
|
||||
turn_context: &TurnContext,
|
||||
user_input: &[UserInput],
|
||||
cancellation_token: &CancellationToken,
|
||||
) -> Option<Vec<ResponseItem>> {
|
||||
let contributors = sess.services.extensions.turn_input_contributors().to_vec();
|
||||
if contributors.is_empty() {
|
||||
return Some(Vec::new());
|
||||
}
|
||||
|
||||
let environments = turn_context
|
||||
.environments
|
||||
.turn_environments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, environment)| TurnInputEnvironment {
|
||||
environment_id: environment.environment_id.clone(),
|
||||
cwd: environment.cwd.as_path().to_path_buf(),
|
||||
is_primary: index == 0,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let input = TurnInputContext {
|
||||
turn_id: turn_context.sub_id.to_string(),
|
||||
user_input: user_input.to_vec(),
|
||||
environments,
|
||||
};
|
||||
|
||||
let mut items = Vec::new();
|
||||
for contributor in contributors {
|
||||
let contributed_items = contributor
|
||||
.contribute(
|
||||
input.clone(),
|
||||
&sess.services.session_extension_data,
|
||||
&sess.services.thread_extension_data,
|
||||
turn_context.extension_data.as_ref(),
|
||||
)
|
||||
.or_cancel(cancellation_token)
|
||||
.await
|
||||
.ok()?;
|
||||
items.extend(contributed_items);
|
||||
}
|
||||
|
||||
Some(items)
|
||||
}
|
||||
|
||||
async fn track_turn_resolved_config_analytics(
|
||||
sess: &Session,
|
||||
turn_context: &TurnContext,
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::protocol::ReviewDecision;
|
||||
use codex_protocol::protocol::TokenUsageInfo;
|
||||
use codex_tools::ToolCall;
|
||||
@@ -12,6 +13,7 @@ use crate::ExtensionData;
|
||||
mod prompt;
|
||||
mod thread_lifecycle;
|
||||
mod tool_lifecycle;
|
||||
mod turn_input;
|
||||
mod turn_lifecycle;
|
||||
|
||||
pub use prompt::PromptFragment;
|
||||
@@ -25,6 +27,8 @@ pub use tool_lifecycle::ToolCallSource;
|
||||
pub use tool_lifecycle::ToolFinishInput;
|
||||
pub use tool_lifecycle::ToolLifecycleFuture;
|
||||
pub use tool_lifecycle::ToolStartInput;
|
||||
pub use turn_input::TurnInputContext;
|
||||
pub use turn_input::TurnInputEnvironment;
|
||||
pub use turn_lifecycle::TurnAbortInput;
|
||||
pub use turn_lifecycle::TurnErrorInput;
|
||||
pub use turn_lifecycle::TurnStartInput;
|
||||
@@ -85,6 +89,25 @@ pub trait TurnLifecycleContributor: Send + Sync {
|
||||
async fn on_turn_error(&self, _input: TurnErrorInput<'_>) {}
|
||||
}
|
||||
|
||||
/// WARNING: DO NOT USE YET
|
||||
/// Extension contribution that can add turn-local model input.
|
||||
///
|
||||
/// Implementations should resolve only the model-visible input they own and
|
||||
/// must preserve authority boundaries for external resources. Expensive or
|
||||
/// host-specific dependencies belong on the extension value installed by the
|
||||
/// host, not in this input.
|
||||
#[async_trait::async_trait]
|
||||
pub trait TurnInputContributor: Send + Sync {
|
||||
/// Returns additional model input items for one submitted turn.
|
||||
async fn contribute(
|
||||
&self,
|
||||
input: TurnInputContext,
|
||||
session_store: &ExtensionData,
|
||||
thread_store: &ExtensionData,
|
||||
turn_store: &ExtensionData,
|
||||
) -> Vec<ResponseItem>;
|
||||
}
|
||||
|
||||
/// Contributor for host-owned configuration changes.
|
||||
///
|
||||
/// Implementations should treat the supplied values as immutable before/after
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_protocol::user_input::UserInput;
|
||||
|
||||
/// Host-owned turn environment summary visible to turn-input contributors.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TurnInputEnvironment {
|
||||
/// Stable host environment id used to route executor-scoped capabilities.
|
||||
pub environment_id: String,
|
||||
/// Effective working directory for this turn in the environment.
|
||||
pub cwd: PathBuf,
|
||||
/// Whether this is the primary environment for the turn.
|
||||
pub is_primary: bool,
|
||||
}
|
||||
|
||||
/// Turn facts supplied before the host records turn-local model input items.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TurnInputContext {
|
||||
/// Stable host-owned turn identifier.
|
||||
pub turn_id: String,
|
||||
/// User input submitted for this turn.
|
||||
pub user_input: Vec<UserInput>,
|
||||
/// Resolved turn environments, in host priority order.
|
||||
pub environments: Vec<TurnInputEnvironment>,
|
||||
}
|
||||
@@ -10,6 +10,7 @@ pub use capabilities::NoopExtensionEventSink;
|
||||
pub use capabilities::NoopResponseItemInjector;
|
||||
pub use capabilities::ResponseItemInjectionFuture;
|
||||
pub use capabilities::ResponseItemInjector;
|
||||
pub use codex_protocol::models::ResponseItem;
|
||||
pub use codex_tools::ConversationHistory;
|
||||
pub use codex_tools::ExtensionTurnItem;
|
||||
pub use codex_tools::FunctionCallError;
|
||||
@@ -46,6 +47,9 @@ pub use contributors::ToolLifecycleFuture;
|
||||
pub use contributors::ToolStartInput;
|
||||
pub use contributors::TurnAbortInput;
|
||||
pub use contributors::TurnErrorInput;
|
||||
pub use contributors::TurnInputContext;
|
||||
pub use contributors::TurnInputContributor;
|
||||
pub use contributors::TurnInputEnvironment;
|
||||
pub use contributors::TurnItemContributor;
|
||||
pub use contributors::TurnLifecycleContributor;
|
||||
pub use contributors::TurnStartInput;
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::ThreadLifecycleContributor;
|
||||
use crate::TokenUsageContributor;
|
||||
use crate::ToolContributor;
|
||||
use crate::ToolLifecycleContributor;
|
||||
use crate::TurnInputContributor;
|
||||
use crate::TurnItemContributor;
|
||||
use crate::TurnLifecycleContributor;
|
||||
|
||||
@@ -23,6 +24,7 @@ pub struct ExtensionRegistryBuilder<C: Sync> {
|
||||
config_contributors: Vec<Arc<dyn ConfigContributor<C>>>,
|
||||
token_usage_contributors: Vec<Arc<dyn TokenUsageContributor>>,
|
||||
context_contributors: Vec<Arc<dyn ContextContributor>>,
|
||||
turn_input_contributors: Vec<Arc<dyn TurnInputContributor>>,
|
||||
tool_contributors: Vec<Arc<dyn ToolContributor>>,
|
||||
tool_lifecycle_contributors: Vec<Arc<dyn ToolLifecycleContributor>>,
|
||||
turn_item_contributors: Vec<Arc<dyn TurnItemContributor>>,
|
||||
@@ -39,6 +41,7 @@ impl<C: Sync> Default for ExtensionRegistryBuilder<C> {
|
||||
token_usage_contributors: Vec::new(),
|
||||
approval_review_contributors: Vec::new(),
|
||||
context_contributors: Vec::new(),
|
||||
turn_input_contributors: Vec::new(),
|
||||
tool_contributors: Vec::new(),
|
||||
tool_lifecycle_contributors: Vec::new(),
|
||||
turn_item_contributors: Vec::new(),
|
||||
@@ -98,6 +101,11 @@ impl<C: Sync> ExtensionRegistryBuilder<C> {
|
||||
self.context_contributors.push(contributor);
|
||||
}
|
||||
|
||||
/// Registers one turn-input contributor.
|
||||
pub fn turn_input_contributor(&mut self, contributor: Arc<dyn TurnInputContributor>) {
|
||||
self.turn_input_contributors.push(contributor);
|
||||
}
|
||||
|
||||
/// Registers one native tool contributor.
|
||||
pub fn tool_contributor(&mut self, contributor: Arc<dyn ToolContributor>) {
|
||||
self.tool_contributors.push(contributor);
|
||||
@@ -123,6 +131,7 @@ impl<C: Sync> ExtensionRegistryBuilder<C> {
|
||||
token_usage_contributors: self.token_usage_contributors,
|
||||
approval_review_contributors: self.approval_review_contributors,
|
||||
context_contributors: self.context_contributors,
|
||||
turn_input_contributors: self.turn_input_contributors,
|
||||
tool_contributors: self.tool_contributors,
|
||||
tool_lifecycle_contributors: self.tool_lifecycle_contributors,
|
||||
turn_item_contributors: self.turn_item_contributors,
|
||||
@@ -138,6 +147,7 @@ pub struct ExtensionRegistry<C: Sync> {
|
||||
config_contributors: Vec<Arc<dyn ConfigContributor<C>>>,
|
||||
token_usage_contributors: Vec<Arc<dyn TokenUsageContributor>>,
|
||||
context_contributors: Vec<Arc<dyn ContextContributor>>,
|
||||
turn_input_contributors: Vec<Arc<dyn TurnInputContributor>>,
|
||||
tool_contributors: Vec<Arc<dyn ToolContributor>>,
|
||||
tool_lifecycle_contributors: Vec<Arc<dyn ToolLifecycleContributor>>,
|
||||
turn_item_contributors: Vec<Arc<dyn TurnItemContributor>>,
|
||||
@@ -195,6 +205,11 @@ impl<C: Sync> ExtensionRegistry<C> {
|
||||
&self.context_contributors
|
||||
}
|
||||
|
||||
/// Returns the registered turn-input contributors.
|
||||
pub fn turn_input_contributors(&self) -> &[Arc<dyn TurnInputContributor>] {
|
||||
&self.turn_input_contributors
|
||||
}
|
||||
|
||||
/// Returns the registered native tool contributors.
|
||||
pub fn tool_contributors(&self) -> &[Arc<dyn ToolContributor>] {
|
||||
&self.tool_contributors
|
||||
|
||||
Reference in New Issue
Block a user