Attribute executor skill invocations to plugins (#39309)

## What changed

- Carry selected plugin identities from MCP discovery into per-turn extension data, limited to capability roots that are ready for the turn.
- Annotate executor skill catalog entries with the matching plugin ID and `user` scope.
- Include that attribution in `skill_invocation` analytics for both explicit and implicit executor skill invocations.

## Testing

- Extend the executor skills app-server test to verify plugin ID, skill scope, and invocation type for explicit and implicit analytics events.

GitOrigin-RevId: 9345600ae29bcb8eb894fffd2d6f7b05f22ebf74
This commit is contained in:
rphilizaire-openai authored and copyberry committed 2026-08-18 22:53:31 +00:00
1 parent c97bd2dcb5
commit 280d56b1d8
17 files changed
+129 -18

No files matched your search

+12
View File
@@ -13,6 +13,8 @@ use codex_extension_api::ExtensionDataInit;
use codex_extension_api::ExtensionRegistry;
use codex_extension_api::McpServerContribution;
use codex_extension_api::McpServerContributionContext;
use codex_extension_api::SelectedPluginIdentity;
use codex_extension_api::SelectedPluginSnapshot;
use codex_login::CodexAuth;
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
use codex_mcp::EffectiveMcpServer;
@@ -35,6 +37,7 @@ const LEGACY_CODEX_APPS_REGISTRATION_ID: &str = "legacy_codex_apps";
pub(crate) struct McpRuntimeProjection {
pub(crate) config: McpConfig,
pub(crate) plugins_available: bool,
pub(crate) selected_plugins: SelectedPluginSnapshot,
}
pub(crate) struct McpThreadIdentity<'a> {
@@ -143,6 +146,7 @@ impl McpManager {
let mut selected_plugin_available = false;
let mut selected_plugin_connector_sources = Vec::new();
let mut selected_plugin_registrations = Vec::new();
let mut selected_plugins = Vec::new();
let mut overlays = Vec::new();
// A contributor can emit multiple ordered actions, so order each action globally rather
// than enumerating contributors.
@@ -173,11 +177,16 @@ impl McpManager {
),
),
McpServerContribution::SelectedPluginPackage {
selected_root_id,
plugin_id,
plugin_display_name,
connector_ids,
} => {
selected_plugin_available = true;
selected_plugins.push(SelectedPluginIdentity {
selected_root_id,
plugin_id: plugin_id.clone(),
});
if !connector_ids.is_empty() {
selected_plugin_connector_sources.push(
PluginConnectorSource::from_connector_ids(
@@ -265,6 +274,9 @@ impl McpManager {
McpRuntimeProjection {
config: mcp_config,
plugins_available,
selected_plugins: SelectedPluginSnapshot {
plugins: selected_plugins,
},
}
}
+4 -1
View File
@@ -259,6 +259,7 @@ impl Session {
executor_capability_discovery.as_deref(),
)
.await;
let selected_plugins = mcp_projection.selected_plugins.clone();
let input = self.build_mcp_runtime_input(
&desired,
mcp_projection,
@@ -269,7 +270,9 @@ impl Session {
input.mcp_servers.contains_key(CODEX_APPS_MCP_SERVER_NAME),
"unknown MCP server '{CODEX_APPS_MCP_SERVER_NAME}'"
);
self.services.mcp_runtime.replace_fresh(input).await
let refreshed = self.services.mcp_runtime.replace_fresh(input).await;
self.services.thread_extension_data.insert(selected_plugins);
refreshed
}
pub(super) fn mark_mcp_runtime_dirty(&self) {
+3
View File
@@ -141,6 +141,7 @@ impl Session {
ready_selected_capability_roots: &[SelectedCapabilityRoot],
elicitation_reviewer: Option<ElicitationReviewerHandle>,
) {
let selected_plugins = mcp_projection.selected_plugins.clone();
let input = self.build_mcp_runtime_input(
desired,
mcp_projection,
@@ -148,6 +149,7 @@ impl Session {
elicitation_reviewer,
);
self.services.mcp_runtime.replace(input).await;
self.services.thread_extension_data.insert(selected_plugins);
}
pub(super) fn build_mcp_runtime_input(
@@ -161,6 +163,7 @@ impl Session {
let McpRuntimeProjection {
mut config,
plugins_available,
selected_plugins: _,
} = mcp_projection;
config.approval_policy = desired.config.permissions.approval_policy.clone();
config.permission_profile = desired.config.permissions.effective_permission_profile();
+13
View File
@@ -3193,6 +3193,19 @@ impl Session {
}
.or_cancel(cancellation_token)
.await?;
let mut selected_plugins = self
.services
.thread_extension_data
.get::<codex_extension_api::SelectedPluginSnapshot>()
.map(|snapshot| snapshot.as_ref().clone())
.unwrap_or_default();
selected_plugins.plugins.retain(|plugin| {
ready_selected_capability_roots
.iter()
.any(|root| root.id == plugin.selected_root_id)
});
extension_data.insert(selected_plugins.clone());
turn_context.extension_data.insert(selected_plugins);
let tool_router = turn::built_tools(
self.as_ref(),
turn_context.as_ref(),