Move host skill prompt injection into the skills extension (#37503)

## What changed

- Add `HostSkillsSnapshot::load_skill_prompts` so the skills extension owns reading and rendering selected host skills.
- Keep explicit invocation telemetry, unreadable-skill warnings, plugin prompt ordering, and suppression of host prompts superseded by provider prompts.
- Remove the legacy prompt injection and fragment implementation from `codex-core-skills`.

## Testing

- Cover prompt truncation without an installed skills extension, skill/plugin ordering, selective provider supersession, unreadable skills, and contextual fragment detection.

GitOrigin-RevId: 9c8b84ad54b90d174abc287ab49cc5a231e9e1aa
This commit is contained in:
felixxia-oai
2026-08-07 21:04:16 +00:00
committed by copyberry
parent 8e4b10446e
commit beac16cccd
22 changed files with 553 additions and 299 deletions
+29 -28
View File
@@ -3,8 +3,6 @@ use std::collections::HashSet;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use crate::SkillInjections;
use crate::build_skill_injections;
use crate::client::ModelClientSession;
use crate::client_common::Prompt;
use crate::client_common::ResponseEvent;
@@ -24,9 +22,6 @@ use crate::hook_runtime::reject_pending_input;
use crate::hook_runtime::run_legacy_after_agent_hook;
use crate::hook_runtime::run_pending_session_start_hooks;
use crate::hook_runtime::run_turn_stop_hooks;
use crate::injection::ToolMentionKind;
use crate::injection::app_id_from_path;
use crate::injection::tool_kind_for_path;
use crate::mcp_skill_dependencies::maybe_prompt_and_install_mcp_dependencies;
use crate::mentions::build_connector_slug_counts;
use crate::mentions::build_skill_name_counts;
@@ -44,6 +39,7 @@ use crate::session::TurnInput;
use crate::session::session::Session;
use crate::session::step_context::StepContext;
use crate::session::turn_context::TurnContext;
use crate::skills::emit_explicit_skill_invocations;
use crate::stream_events_utils::HandleOutputCtx;
use crate::stream_events_utils::TurnItemContributorPolicy;
use crate::stream_events_utils::finalize_non_tool_response_item;
@@ -74,7 +70,6 @@ use codex_analytics::build_track_events_context;
use codex_async_utils::OrCancelExt;
use codex_connectors::AppToolPolicyEvaluator;
use codex_core_plugins::RecommendedPluginCandidatesInput;
use codex_core_skills::injection::InjectedHostSkillPrompts;
use codex_extension_api::ExtensionData;
use codex_extension_api::TurnInputContext;
use codex_extension_api::TurnInputEnvironment;
@@ -111,6 +106,11 @@ use codex_protocol::protocol::SafetyBufferingEvent;
use codex_protocol::protocol::TurnDiffEvent;
use codex_protocol::protocol::WarningEvent;
use codex_protocol::user_input::UserInput;
use codex_skills::ToolMentionKind;
use codex_skills::app_id_from_path;
use codex_skills::tool_kind_for_path;
use codex_skills_extension::HostSkillPrompts;
use codex_skills_extension::InjectedHostSkillPrompts;
use codex_tools::DiscoverableTool;
use codex_tools::ToolName;
use codex_tools::filter_request_plugin_install_discoverable_tools_for_client;
@@ -791,27 +791,26 @@ async fn build_skills_and_plugins(
let injected_host_skill_prompts = turn_context
.extension_data
.get::<InjectedHostSkillPrompts>();
let SkillInjections {
items: skill_injections,
warnings: skill_warnings,
} = build_skill_injections(
let HostSkillPrompts {
fragments,
injected: injected_host_skills,
warnings: host_skill_warnings,
} = skills_snapshot.load_skill_prompts(&mentioned_skills).await;
emit_explicit_skill_invocations(
sess,
turn_context,
&mentioned_skills,
Some(skills_outcome),
Some(&turn_context.session_telemetry),
&sess.services.analytics_events_client,
&injected_host_skills,
tracking.clone(),
)
.await;
for message in skill_warnings {
);
for message in host_skill_warnings {
sess.send_event(turn_context, EventMsg::Warning(WarningEvent { message }))
.await;
}
let skill_items: Vec<ResponseItem> = skill_injections
.iter()
.map(|skill| ContextualUserFragment::into(crate::context::SkillInstructions::from(skill)))
.collect();
let skill_items = fragments
.into_iter()
.map(ContextualUserFragment::into_boxed_response_item)
.collect::<Vec<_>>();
let skill_connector_ids = collect_explicit_app_ids_from_skill_items(
&skill_items,
&available_connectors,
@@ -849,12 +848,14 @@ async fn build_skills_and_plugins(
}
}
let mut injection_items: Vec<ResponseItem> = match injected_host_skill_prompts {
Some(injected_host_skill_prompts) => skill_injections
.iter()
.filter(|skill| !injected_host_skill_prompts.contains_path(&skill.path))
.map(|skill| {
ContextualUserFragment::into(crate::context::SkillInstructions::from(skill))
let mut injection_items = match injected_host_skill_prompts {
Some(injected_host_skill_prompts) => skill_items
.into_iter()
.zip(injected_host_skills.iter())
.filter_map(|(item, skill)| {
(!injected_host_skill_prompts
.contains_path(&skill.path_to_skills_md.to_string_lossy()))
.then_some(item)
})
.collect(),
None => skill_items,