mirror of
https://github.com/openai/codex.git
synced 2026-10-27 16:02:59 +00:00
Add batched executor capability discovery (#33852)
## Why Selected capability roots can contribute plugins, MCP servers, connectors, and skills. Discovering each contribution separately requires repeated access to the executor filesystem. ## What changed - Add the `capabilityRoots/discoverV1` exec-server RPC to scan selected roots and materialize recognized plugin manifests, configuration files, skill instructions, and skill metadata in one bounded request. - Add the opt-in `executor_capability_discovery` feature, with a thread-scoped cache and per-step snapshot shared by MCP and skill discovery. - Parse MCP, connector, and skill contributions from the materialized snapshot, including serving cached skill instructions without another filesystem read. ## Testing - Cover discovery limits, manifest precedence, root-local failures, cache reuse, plugin contributions, and parity with the existing environment skill loader. GitOrigin-RevId: f98fd2321cafb58c596db02da1f83c09d8eb375d
This commit is contained in:
@@ -518,6 +518,9 @@
|
||||
"exec_permission_approvals": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"executor_capability_discovery": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental_use_unified_exec_tool": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -4912,6 +4915,9 @@
|
||||
"exec_permission_approvals": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"executor_capability_discovery": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"experimental_use_unified_exec_tool": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_connectors::ConnectorRuntimeManager;
|
||||
use codex_connectors::ConnectorSnapshot;
|
||||
use codex_connectors::PluginConnectorSource;
|
||||
use codex_core_plugins::PluginsManager;
|
||||
use codex_exec_server::ExecutorCapabilityDiscoverySnapshot;
|
||||
use codex_extension_api::ExtensionData;
|
||||
use codex_extension_api::ExtensionDataInit;
|
||||
use codex_extension_api::ExtensionRegistry;
|
||||
@@ -109,6 +110,7 @@ impl McpManager {
|
||||
thread_store: &ExtensionData,
|
||||
originator: &str,
|
||||
ready_selected_capability_roots: &[SelectedCapabilityRoot],
|
||||
executor_capability_discovery: Option<&ExecutorCapabilityDiscoverySnapshot>,
|
||||
) -> McpRuntimeProjection {
|
||||
self.runtime_config_with_context(
|
||||
McpServerContributionContext::for_step(
|
||||
@@ -117,6 +119,7 @@ impl McpManager {
|
||||
thread_store,
|
||||
originator,
|
||||
ready_selected_capability_roots,
|
||||
executor_capability_discovery,
|
||||
),
|
||||
Some(originator),
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use super::*;
|
||||
use crate::mcp::McpRuntimeProjection;
|
||||
use codex_exec_server::ExecutorCapabilityDiscoveryCache;
|
||||
use codex_exec_server::ExecutorCapabilityDiscoverySnapshot;
|
||||
use codex_exec_server::MAX_SELECTED_CAPABILITY_ROOTS;
|
||||
use codex_exec_server::ResolvedSelectedCapabilityRoot;
|
||||
use codex_mcp::ElicitationReviewRequest;
|
||||
@@ -87,6 +89,9 @@ impl Session {
|
||||
.await;
|
||||
let ready_selected_capability_roots =
|
||||
Self::ready_selected_capability_roots(&selected_capability_roots);
|
||||
let executor_capability_discovery = self
|
||||
.executor_capability_discovery_for_step(config, &ready_selected_capability_roots)
|
||||
.await;
|
||||
self.services
|
||||
.mcp_manager
|
||||
.runtime_config_for_step(
|
||||
@@ -95,6 +100,7 @@ impl Session {
|
||||
&self.services.thread_extension_data,
|
||||
&originator,
|
||||
&ready_selected_capability_roots,
|
||||
executor_capability_discovery.as_deref(),
|
||||
)
|
||||
.await
|
||||
.config
|
||||
@@ -117,6 +123,7 @@ impl Session {
|
||||
turn_context: &TurnContext,
|
||||
environments: &TurnEnvironmentSnapshot,
|
||||
selected_capability_roots: &[ResolvedSelectedCapabilityRoot],
|
||||
executor_capability_discovery: Option<&ExecutorCapabilityDiscoverySnapshot>,
|
||||
) -> Arc<McpRuntimeSnapshot> {
|
||||
let ready_selected_capability_roots =
|
||||
Self::ready_selected_capability_roots(selected_capability_roots);
|
||||
@@ -141,6 +148,7 @@ impl Session {
|
||||
&self.services.thread_extension_data,
|
||||
&turn_context.originator,
|
||||
&ready_selected_capability_roots,
|
||||
executor_capability_discovery,
|
||||
)
|
||||
.await;
|
||||
let mcp_config = &mcp_projection.config;
|
||||
@@ -190,6 +198,32 @@ impl Session {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "capability_roots.snapshot_for_step",
|
||||
skip_all,
|
||||
fields(root_count = ready_selected_capability_roots.len())
|
||||
)]
|
||||
pub(crate) async fn executor_capability_discovery_for_step(
|
||||
&self,
|
||||
config: &Config,
|
||||
ready_selected_capability_roots: &[SelectedCapabilityRoot],
|
||||
) -> Option<Arc<ExecutorCapabilityDiscoverySnapshot>> {
|
||||
if !config
|
||||
.features
|
||||
.enabled(Feature::ExecutorCapabilityDiscovery)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let environment_manager = self.services.turn_environments.environment_manager();
|
||||
let cache = self
|
||||
.services
|
||||
.thread_extension_data
|
||||
.get_or_init(|| ExecutorCapabilityDiscoveryCache::new(environment_manager));
|
||||
Some(Arc::new(
|
||||
cache.snapshot(ready_selected_capability_roots).await,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn resolve_selected_capability_roots_for_step(
|
||||
&self,
|
||||
environments: &TurnEnvironmentSnapshot,
|
||||
@@ -511,6 +545,12 @@ impl Session {
|
||||
let current_runtime = self.services.latest_mcp_runtime();
|
||||
let ready_selected_capability_roots =
|
||||
current_runtime.ready_selected_capability_roots().to_vec();
|
||||
let executor_capability_discovery = self
|
||||
.executor_capability_discovery_for_step(
|
||||
&refresh_config,
|
||||
&ready_selected_capability_roots,
|
||||
)
|
||||
.await;
|
||||
let mut mcp_projection = self
|
||||
.services
|
||||
.mcp_manager
|
||||
@@ -520,6 +560,7 @@ impl Session {
|
||||
&self.services.thread_extension_data,
|
||||
&turn_context.originator,
|
||||
&ready_selected_capability_roots,
|
||||
executor_capability_discovery.as_deref(),
|
||||
)
|
||||
.await;
|
||||
mcp_projection.config.mcp_server_catalog = mcp_projection
|
||||
@@ -578,6 +619,12 @@ impl Session {
|
||||
let current_runtime = self.services.latest_mcp_runtime();
|
||||
let ready_selected_capability_roots =
|
||||
current_runtime.ready_selected_capability_roots().to_vec();
|
||||
let executor_capability_discovery = self
|
||||
.executor_capability_discovery_for_step(
|
||||
refresh_config,
|
||||
&ready_selected_capability_roots,
|
||||
)
|
||||
.await;
|
||||
let mcp_projection = self
|
||||
.services
|
||||
.mcp_manager
|
||||
@@ -587,6 +634,7 @@ impl Session {
|
||||
&self.services.thread_extension_data,
|
||||
&turn_context.originator,
|
||||
&ready_selected_capability_roots,
|
||||
executor_capability_discovery.as_deref(),
|
||||
)
|
||||
.await;
|
||||
self.refresh_mcp_servers_inner(
|
||||
@@ -613,7 +661,7 @@ impl Session {
|
||||
available
|
||||
}
|
||||
|
||||
fn ready_selected_capability_roots(
|
||||
pub(crate) fn ready_selected_capability_roots(
|
||||
selected_capability_roots: &[ResolvedSelectedCapabilityRoot],
|
||||
) -> Vec<SelectedCapabilityRoot> {
|
||||
selected_capability_roots
|
||||
|
||||
@@ -2859,17 +2859,27 @@ impl Session {
|
||||
let selected_capability_roots = self
|
||||
.resolve_selected_capability_roots_for_step(&environments)
|
||||
.await;
|
||||
let ready_selected_capability_roots =
|
||||
Self::ready_selected_capability_roots(&selected_capability_roots);
|
||||
let executor_capability_discovery = self
|
||||
.executor_capability_discovery_for_step(
|
||||
&turn_context.config,
|
||||
&ready_selected_capability_roots,
|
||||
)
|
||||
.await;
|
||||
let mcp = self
|
||||
.mcp_runtime_for_step(
|
||||
turn_context.as_ref(),
|
||||
&environments,
|
||||
&selected_capability_roots,
|
||||
executor_capability_discovery.as_deref(),
|
||||
)
|
||||
.await;
|
||||
Arc::new(StepContext::new(
|
||||
turn_context,
|
||||
environments,
|
||||
selected_capability_roots,
|
||||
executor_capability_discovery,
|
||||
mcp,
|
||||
loaded_agents_md,
|
||||
))
|
||||
|
||||
@@ -717,6 +717,7 @@ impl Session {
|
||||
thread_extension_data_for_mcp,
|
||||
&mcp_originator,
|
||||
/*ready_selected_capability_roots*/ &[],
|
||||
/*executor_capability_discovery*/ None,
|
||||
)
|
||||
.await;
|
||||
let mcp_config = &mcp_projection.config;
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::agents_md::LoadedAgentsMd;
|
||||
use crate::environment_selection::TurnEnvironmentSnapshot;
|
||||
use crate::session::McpRuntimeSnapshot;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_exec_server::ExecutorCapabilityDiscoverySnapshot;
|
||||
use codex_exec_server::ResolvedSelectedCapabilityRoot;
|
||||
use codex_mcp::ToolInfo;
|
||||
use tokio::sync::OnceCell;
|
||||
@@ -15,6 +16,8 @@ pub(crate) struct StepContext {
|
||||
pub(crate) environments: TurnEnvironmentSnapshot,
|
||||
/// Capability roots bound to ready environments in this exact step.
|
||||
pub(crate) selected_capability_roots: Vec<ResolvedSelectedCapabilityRoot>,
|
||||
/// Executor-materialized capability files shared by MCP and skills in this exact step.
|
||||
pub(crate) executor_capability_discovery: Option<Arc<ExecutorCapabilityDiscoverySnapshot>>,
|
||||
/// The exact MCP config and manager used to advertise and execute tools for this step.
|
||||
pub(crate) mcp: Arc<McpRuntimeSnapshot>,
|
||||
/// The fixed MCP tool list used for this exact sampling request.
|
||||
@@ -28,6 +31,7 @@ impl StepContext {
|
||||
turn: Arc<TurnContext>,
|
||||
environments: TurnEnvironmentSnapshot,
|
||||
selected_capability_roots: Vec<ResolvedSelectedCapabilityRoot>,
|
||||
executor_capability_discovery: Option<Arc<ExecutorCapabilityDiscoverySnapshot>>,
|
||||
mcp: Arc<McpRuntimeSnapshot>,
|
||||
loaded_agents_md: Option<Arc<LoadedAgentsMd>>,
|
||||
) -> Self {
|
||||
@@ -35,6 +39,7 @@ impl StepContext {
|
||||
turn,
|
||||
environments,
|
||||
selected_capability_roots,
|
||||
executor_capability_discovery,
|
||||
mcp,
|
||||
mcp_tool_snapshot: OnceCell::new(),
|
||||
loaded_agents_md,
|
||||
|
||||
@@ -200,6 +200,7 @@ impl StepContext {
|
||||
Arc::clone(&turn),
|
||||
environments,
|
||||
Vec::new(),
|
||||
/*executor_capability_discovery*/ None,
|
||||
crate::session::McpRuntimeSnapshot::new_uninitialized_for_test(&turn.config),
|
||||
/*loaded_agents_md*/ None,
|
||||
))
|
||||
@@ -7813,7 +7814,12 @@ async fn deferred_environment_roots_refresh_plugin_availability() {
|
||||
);
|
||||
|
||||
let new_runtime = session
|
||||
.mcp_runtime_for_step(&turn_context, &environments, &resolved_roots)
|
||||
.mcp_runtime_for_step(
|
||||
&turn_context,
|
||||
&environments,
|
||||
&resolved_roots,
|
||||
/*executor_capability_discovery*/ None,
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(!old_runtime.plugins_available());
|
||||
|
||||
@@ -77,6 +77,9 @@ impl Session {
|
||||
turn_id: turn_context.sub_id.as_str(),
|
||||
environments: &environments,
|
||||
ready_selected_capability_roots: &ready_selected_capability_roots,
|
||||
executor_capability_discovery: step_context
|
||||
.executor_capability_discovery
|
||||
.as_deref(),
|
||||
session_store: &self.services.session_extension_data,
|
||||
thread_store: &self.services.thread_extension_data,
|
||||
turn_store: turn_context.extension_data.as_ref(),
|
||||
|
||||
@@ -687,6 +687,7 @@ async fn start_thread_seeds_extension_data_for_mcp_and_lifecycle_contributors()
|
||||
&first_session.services.thread_extension_data,
|
||||
&first_originator,
|
||||
/*ready_selected_capability_roots*/ &[],
|
||||
/*executor_capability_discovery*/ None,
|
||||
)
|
||||
.await;
|
||||
let second_session = &second_thread.thread.session;
|
||||
@@ -700,6 +701,7 @@ async fn start_thread_seeds_extension_data_for_mcp_and_lifecycle_contributors()
|
||||
&second_session.services.thread_extension_data,
|
||||
&second_originator,
|
||||
/*ready_selected_capability_roots*/ &[],
|
||||
/*executor_capability_discovery*/ None,
|
||||
)
|
||||
.await;
|
||||
|
||||
|
||||
@@ -685,6 +685,7 @@ async fn environment_tools_follow_the_step_context() {
|
||||
Arc::clone(&turn),
|
||||
environments,
|
||||
Vec::new(),
|
||||
/*executor_capability_discovery*/ None,
|
||||
crate::session::McpRuntimeSnapshot::new_uninitialized_for_test(&turn.config),
|
||||
/*loaded_agents_md*/ None,
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user