Generalize skill locator aliases across providers (#37488)

## What changed

- Compact executor and orchestrator skill locators under metadata pressure, using provider-specific `e` and `o` aliases alongside host `r` aliases.
- Derive alias roots from executor discovery paths and orchestrator namespaces while preserving host plugin root handling.
- Centralize alias ordering, deduplication, and longest-prefix matching across skill providers.

## Testing

- Cover alias planning, cross-platform executor roots, and host plugin discovery.

GitOrigin-RevId: e7d15bd3891c08ae20170e96c4ad45c76d353707
This commit is contained in:
rphilizaire-openai
2026-08-07 19:04:39 +00:00
committed by copyberry
parent 27e4a05cd3
commit ce22ea9712
12 changed files with 458 additions and 218 deletions
+55
View File
@@ -0,0 +1,55 @@
use std::collections::HashSet;
struct AliasRoot {
name: String,
value: String,
}
pub(crate) struct AliasPlan {
roots: Vec<AliasRoot>,
}
impl AliasPlan {
pub(crate) fn build(prefix: &str, candidates: &[&str]) -> Option<Self> {
let mut roots = Vec::new();
let mut seen = HashSet::new();
for &root in candidates {
if !seen.insert(root) {
continue;
}
let index = roots.len();
roots.push(AliasRoot {
name: format!("{prefix}{index}"),
value: root.to_string(),
});
}
(!roots.is_empty()).then_some(Self { roots })
}
pub(crate) fn shorten(&self, locator: &str) -> Option<String> {
self.roots
.iter()
.filter_map(|root| {
let suffix = locator
.strip_prefix(root.value.trim_end_matches('/'))?
.strip_prefix('/')?;
Some((root, suffix))
})
.max_by_key(|(root, _)| root.value.len())
.map(|(root, suffix)| format!("{}/{suffix}", root.name))
}
pub(crate) fn root_lines(&self) -> Vec<String> {
self.roots
.iter()
.map(|root| format!("- `{}` = `{}`", root.name, root.value))
.collect()
}
}
#[cfg(test)]
#[path = "aliases_tests.rs"]
mod tests;
+73
View File
@@ -0,0 +1,73 @@
use pretty_assertions::assert_eq;
use super::AliasPlan;
#[test]
fn assigns_aliases_in_first_seen_order() {
let roots = [
"/skills/beta",
"/skills/alpha",
"/skills/beta",
"/skills/alpha",
];
let plan = AliasPlan::build("r", &roots).expect("alias plan should build");
assert_eq!(
vec!["- `r0` = `/skills/beta`", "- `r1` = `/skills/alpha`"],
plan.root_lines()
);
}
#[test]
fn reuses_aliases_for_duplicate_roots() {
let root = "/skills/shared";
let plan = AliasPlan::build("r", &[root, root, root]).expect("alias plan should build");
assert_eq!(vec!["- `r0` = `/skills/shared`"], plan.root_lines());
assert_eq!(
Some("r0/alpha/SKILL.md".to_string()),
plan.shorten("/skills/shared/alpha/SKILL.md")
);
assert_eq!(
Some("r0/beta/SKILL.md".to_string()),
plan.shorten("/skills/shared/beta/SKILL.md")
);
}
#[test]
fn shortens_filesystem_and_skill_resource_locators() {
for (prefix, root, locator, expected) in [
(
"r",
"/skills/shared",
"/skills/shared/alpha/SKILL.md",
"r0/alpha/SKILL.md",
),
(
"e",
"skill://executor/workspace/skills",
"skill://executor/workspace/skills/alpha/SKILL.md",
"e0/alpha/SKILL.md",
),
("o", "skill://plugin", "skill://plugin/alpha", "o0/alpha"),
] {
let plan = AliasPlan::build(prefix, &[root, root]).expect("alias plan should build");
assert_eq!(Some(expected.to_string()), plan.shorten(locator));
}
}
#[test]
fn rejects_unknown_roots_and_locators_outside_the_root() {
let root = "/skills/shared";
let plan = AliasPlan::build("r", &[root, root]).expect("alias plan should build");
assert_eq!(None, plan.shorten("/skills/other/alpha"));
assert_eq!(None, plan.shorten("/skills/shared-other/alpha"));
assert_eq!(None, plan.shorten(root));
}
#[test]
fn returns_none_when_no_roots_are_provided() {
assert!(AliasPlan::build("r", &[]).is_none());
}
+13 -13
View File
@@ -178,8 +178,8 @@ pub struct SkillCatalogEntry {
pub short_description: Option<String>,
pub main_prompt: SkillResourceId,
pub display_path: Option<String>,
display_path_root: Option<String>,
display_path_root_order: Option<usize>,
alias_root: Option<String>,
alias_root_order: Option<usize>,
prompt_scope: Option<SkillScope>,
pub dependencies: Option<SkillDependencies>,
pub enabled: bool,
@@ -202,8 +202,8 @@ impl SkillCatalogEntry {
short_description: None,
main_prompt,
display_path: None,
display_path_root: None,
display_path_root_order: None,
alias_root: None,
alias_root_order: None,
prompt_scope: None,
dependencies: None,
enabled: true,
@@ -221,14 +221,14 @@ impl SkillCatalogEntry {
self
}
/// Sets the shared filesystem prefix that may be compacted in model-visible paths.
pub fn with_display_path_root(mut self, display_path_root: impl Into<String>) -> Self {
self.display_path_root = Some(display_path_root.into());
/// Sets the shared locator prefix that may be compacted in model-visible skill catalogs.
pub fn with_alias_root(mut self, alias_root: impl Into<String>) -> Self {
self.alias_root = Some(alias_root.into());
self
}
pub(crate) fn with_display_path_root_order(mut self, display_path_root_order: usize) -> Self {
self.display_path_root_order = Some(display_path_root_order);
pub(crate) fn with_alias_root_order(mut self, alias_root_order: usize) -> Self {
self.alias_root_order = Some(alias_root_order);
self
}
@@ -262,12 +262,12 @@ impl SkillCatalogEntry {
.unwrap_or_else(|| self.main_prompt.as_str())
}
pub(crate) fn display_path_root(&self) -> Option<&str> {
self.display_path_root.as_deref()
pub(crate) fn alias_root(&self) -> Option<&str> {
self.alias_root.as_deref()
}
pub(crate) fn display_path_root_order(&self) -> Option<usize> {
self.display_path_root_order
pub(crate) fn alias_root_order(&self) -> Option<usize> {
self.alias_root_order
}
pub(crate) fn prompt_scope(&self) -> Option<SkillScope> {
+68
View File
@@ -0,0 +1,68 @@
use std::collections::HashMap;
use std::path::Component;
use std::path::Path;
use std::path::PathBuf;
use crate::catalog::SkillCatalogEntry;
pub(crate) fn shared_host_alias_roots(entries: &[&SkillCatalogEntry]) -> Vec<String> {
let mut plugin_version_skill_counts = HashMap::new();
for root in entries.iter().filter_map(|entry| entry.alias_root()) {
let Some(plugin_version) = plugin_version_base(Path::new(root)) else {
continue;
};
let count = plugin_version_skill_counts
.entry(plugin_version)
.or_insert(0usize);
*count = count.saturating_add(1);
}
entries
.iter()
.filter_map(|entry| entry.alias_root())
.map(|root| {
let root = Path::new(root);
let shared_root = match plugin_version_base(root) {
Some(plugin_version)
if plugin_version_skill_counts
.get(&plugin_version)
.copied()
.unwrap_or_default()
<= 1 =>
{
plugin_marketplace_base(root).unwrap_or_else(|| root.to_path_buf())
}
Some(_) | None => root.to_path_buf(),
};
shared_root.to_string_lossy().replace('\\', "/")
})
.collect()
}
fn plugin_marketplace_base(path: &Path) -> Option<PathBuf> {
let mut candidate = path;
while let Some(parent) = candidate.parent() {
if parent.file_name()?.to_str()? == "cache"
&& parent.parent()?.file_name()?.to_str()? == "plugins"
{
return Some(candidate.to_path_buf());
}
candidate = parent;
}
None
}
fn plugin_version_base(path: &Path) -> Option<PathBuf> {
let marketplace_base = plugin_marketplace_base(path)?;
let mut relative_components = path.strip_prefix(&marketplace_base).ok()?.components();
let plugin = match relative_components.next()? {
Component::Normal(plugin) => plugin,
_ => return None,
};
let version = match relative_components.next()? {
Component::Normal(version) => version,
_ => return None,
};
Some(marketplace_base.join(plugin).join(version))
}
+2
View File
@@ -1,9 +1,11 @@
mod aliases;
pub mod catalog;
mod catalog_prompt;
mod config;
mod dynamic_skill_selector;
mod extension;
mod fragments;
mod host_aliases;
mod host_roots;
mod host_service;
mod host_snapshot;
+12 -2
View File
@@ -89,6 +89,7 @@ impl SkillProvider for ExecutorSkillProvider {
&skill,
authority.clone(),
selected_root_id,
path,
environment_id,
/*instructions*/ None,
));
@@ -172,8 +173,10 @@ impl ExecutorSkillProvider {
let mut catalog = SkillCatalog::default();
for root in snapshot.roots() {
let selected_root_id = &root.selected_root.id;
let CapabilityRootLocation::Environment { environment_id, .. } =
&root.selected_root.location;
let CapabilityRootLocation::Environment {
environment_id,
path,
} = &root.selected_root.location;
let discovery = match &root.result {
Ok(discovery) => discovery.as_ref(),
Err(error) => {
@@ -193,6 +196,7 @@ impl ExecutorSkillProvider {
&skill.metadata,
authority.clone(),
selected_root_id,
path,
environment_id,
Some(skill.instructions),
));
@@ -206,10 +210,15 @@ fn catalog_entry_from_skill(
skill: &EnvironmentSkillMetadata,
authority: SkillAuthority,
selected_root_id: &str,
selected_root_path: &PathUri,
environment_id: &str,
instructions: Option<String>,
) -> SkillCatalogEntry {
let handle_prefix = format!("skill://{selected_root_id}/");
let alias_root = format!(
"{handle_prefix}{}",
normalized_environment_path(selected_root_path).trim_start_matches('/')
);
let normalized_main_path = normalized_environment_path(&skill.path_to_skills_md);
let normalized_package_path = skill.path_to_skills_md.parent().map_or_else(
|| normalized_main_path.clone(),
@@ -245,6 +254,7 @@ fn catalog_entry_from_skill(
)
.with_short_description(skill.short_description.clone())
.with_display_path(main_resource)
.with_alias_root(alias_root)
.with_dependencies(skill.dependencies.clone());
if skill.allows_implicit_invocation() {
+2 -2
View File
@@ -112,9 +112,9 @@ fn catalog_from_outcome(outcome: &SkillLoadOutcome) -> SkillCatalog {
entry = entry.with_display_path(discovery_path.to_string_lossy().replace('\\', "/"));
}
if let Some(root) = outcome.skill_root_for_path(&skill.path_to_skills_md) {
entry = entry.with_display_path_root(root.to_string_lossy().replace('\\', "/"));
entry = entry.with_alias_root(root.to_string_lossy().replace('\\', "/"));
if let Some(root_order) = root_order_by_path.get(root.as_path()) {
entry = entry.with_display_path_root_order(*root_order);
entry = entry.with_alias_root_order(*root_order);
}
}
catalog.push_entry(entry);
@@ -48,7 +48,7 @@ async fn host_catalog_entries_carry_their_render_metadata() -> Result<(), Box<dy
assert_eq!(catalog.entries.len(), 1);
assert_eq!(
(
catalog.entries[0].display_path_root(),
catalog.entries[0].alias_root(),
catalog.entries[0].prompt_scope(),
),
(
@@ -96,7 +96,7 @@ async fn host_catalog_preserves_symlinked_skill_discovery_paths()
(
catalog.entries[0].main_prompt.as_str(),
catalog.entries[0].display_path.as_deref(),
catalog.entries[0].display_path_root(),
catalog.entries[0].alias_root(),
),
(
canonical_path.to_string_lossy().as_ref(),
@@ -246,6 +246,7 @@ impl SkillProvider for OrchestratorSkillProvider {
fn catalog_entry_from_resource(resource: &Resource) -> Option<SkillCatalogEntry> {
let uri = validated_skill_uri(resource.uri.as_str(), MAX_SKILL_PACKAGE_URI_CHARS)?;
let namespace = uri.strip_prefix("skill://")?.split_once('/')?.0;
let meta = resource.meta.as_ref()?.as_object()?;
let allow_implicit_invocation = meta
.get("allow_implicit_invocation")
@@ -271,7 +272,8 @@ fn catalog_entry_from_resource(resource: &Resource) -> Option<SkillCatalogEntry>
description,
SkillResourceId::new(main_prompt),
)
.with_display_path(uri);
.with_display_path(uri)
.with_alias_root(format!("skill://{namespace}"));
Some(if allow_implicit_invocation {
entry
+59 -156
View File
@@ -1,20 +1,17 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::collections::HashSet;
use std::path::Component;
use std::path::Path;
use std::path::PathBuf;
use codex_core_skills::MAX_SKILL_PROMPT_BYTES;
use codex_protocol::protocol::SkillScope;
use codex_utils_string::approx_token_count;
use codex_utils_string::take_bytes_at_char_boundary;
use crate::aliases::AliasPlan;
use crate::catalog::SkillCatalog;
use crate::catalog::SkillCatalogEntry;
use crate::catalog::SkillSourceKind;
use crate::catalog_prompt::render_available_skills_body;
use crate::fragments::AvailableSkillsInstructions;
use crate::host_aliases::shared_host_alias_roots;
const DEFAULT_SKILL_METADATA_CHAR_BUDGET: usize = 8_000;
const SKILL_METADATA_CONTEXT_WINDOW_PERCENT: usize = 2;
@@ -693,12 +690,14 @@ fn build_aliased_combined_catalog(
host_entries: &[&SkillCatalogEntry],
budget: SkillMetadataBudget,
) -> Option<CombinedAvailableSkillsRender> {
let plan = build_alias_plan(host_entries, budget)?;
if plan.table_cost >= budget.limit() {
let plan = build_alias_plan(host_entries)?;
let skill_root_lines = plan.root_lines();
let table_cost = aliased_metadata_overhead_cost(budget, &skill_root_lines);
if table_cost >= budget.limit() {
return None;
}
let adjusted_limit = budget.limit().saturating_sub(plan.table_cost);
let adjusted_limit = budget.limit().saturating_sub(table_cost);
let adjusted_budget = match budget {
SkillMetadataBudget::Tokens(_) => SkillMetadataBudget::Tokens(adjusted_limit),
SkillMetadataBudget::Characters(_) => SkillMetadataBudget::Characters(adjusted_limit),
@@ -717,7 +716,7 @@ fn build_aliased_combined_catalog(
SkillLine::with_locator(
entry,
SkillCatalogRenderPolicy::CoreCompatible,
render_skill_path_with_aliases(entry, &plan),
render_skill_locator_with_aliases(entry, &plan),
)
})
.collect();
@@ -726,7 +725,7 @@ fn build_aliased_combined_catalog(
orchestrator_lines,
host_lines,
adjusted_budget,
plan.skill_root_lines,
skill_root_lines,
))
}
@@ -904,47 +903,12 @@ fn available_skills_fragment(
.into_fragment(include_skills_usage_instructions)
}
struct AliasPlan {
skill_root_lines: Vec<String>,
alias_root_by_display_root: HashMap<String, String>,
root_aliases: HashMap<String, String>,
table_cost: usize,
}
fn build_aliased_catalog(
entries: &[&SkillCatalogEntry],
policy: SkillCatalogRenderPolicy,
budget: SkillMetadataBudget,
) -> Option<RenderedCatalog> {
let plan = build_alias_plan(entries, budget)?;
if plan.table_cost >= budget.limit() {
return None;
}
let adjusted_limit = budget.limit().saturating_sub(plan.table_cost);
let adjusted_budget = match budget {
SkillMetadataBudget::Tokens(_) => SkillMetadataBudget::Tokens(adjusted_limit),
SkillMetadataBudget::Characters(_) => SkillMetadataBudget::Characters(adjusted_limit),
};
let skill_lines = entries
.iter()
.map(|entry| {
SkillLine::with_locator(entry, policy, render_skill_path_with_aliases(entry, &plan))
})
.collect();
Some(render_catalog(
skill_lines,
adjusted_budget,
plan.skill_root_lines,
policy,
))
}
fn build_alias_plan(
entries: &[&SkillCatalogEntry],
budget: SkillMetadataBudget,
) -> Option<AliasPlan> {
// The shared alias prompt only describes host filesystem skills.
// Alias instructions currently describe only host filesystem skills.
if entries
.iter()
.any(|entry| entry.authority.kind != SkillSourceKind::Host)
@@ -952,132 +916,71 @@ fn build_alias_plan(
return None;
}
let plugin_version_skill_counts = plugin_version_skill_counts_for_entries(entries);
let mut alias_root_by_display_root = HashMap::new();
let mut alias_roots = Vec::new();
let mut seen = HashSet::new();
let mut alias_ordered_entries = entries.to_vec();
alias_ordered_entries
.sort_by_key(|entry| entry.display_path_root_order().unwrap_or(usize::MAX));
for entry in alias_ordered_entries {
if entry.authority.kind != SkillSourceKind::Host {
continue;
}
let Some(display_root) = entry.display_path_root() else {
continue;
};
let alias_root =
alias_root_for_display_root(Path::new(display_root), &plugin_version_skill_counts)
.to_string_lossy()
.replace('\\', "/");
alias_root_by_display_root.insert(display_root.to_string(), alias_root.clone());
if seen.insert(alias_root.clone()) {
alias_roots.push(alias_root);
}
}
if alias_roots.is_empty() {
let plan = build_alias_plan(entries)?;
let skill_root_lines = plan.root_lines();
let table_cost = aliased_metadata_overhead_cost(budget, &skill_root_lines);
if table_cost >= budget.limit() {
return None;
}
let root_aliases = alias_roots
let adjusted_limit = budget.limit().saturating_sub(table_cost);
let adjusted_budget = match budget {
SkillMetadataBudget::Tokens(_) => SkillMetadataBudget::Tokens(adjusted_limit),
SkillMetadataBudget::Characters(_) => SkillMetadataBudget::Characters(adjusted_limit),
};
let skill_lines = entries
.iter()
.enumerate()
.map(|(index, root)| (root.clone(), format!("r{index}")))
.map(|entry| {
SkillLine::with_locator(
entry,
policy,
render_skill_locator_with_aliases(entry, &plan),
)
})
.collect();
let skill_root_lines = alias_roots
.iter()
.enumerate()
.map(|(index, root)| format!("- `r{index}` = `{root}`"))
.collect::<Vec<_>>();
let table_cost = aliased_metadata_overhead_cost(budget, &skill_root_lines);
Some(AliasPlan {
Some(render_catalog(
skill_lines,
adjusted_budget,
skill_root_lines,
alias_root_by_display_root,
root_aliases,
table_cost,
})
policy,
))
}
fn plugin_version_skill_counts_for_entries(
entries: &[&SkillCatalogEntry],
) -> HashMap<PathBuf, usize> {
let mut counts = HashMap::new();
for root in entries.iter().filter_map(|entry| {
(entry.authority.kind == SkillSourceKind::Host)
.then(|| entry.display_path_root())
.flatten()
}) {
if let Some(plugin_version_base) = plugin_version_base(Path::new(root)) {
let count = counts.entry(plugin_version_base).or_insert(0usize);
*count = count.saturating_add(1);
}
fn build_alias_plan(entries: &[&SkillCatalogEntry]) -> Option<AliasPlan> {
let source = &entries.first()?.authority.kind;
if entries.iter().any(|entry| &entry.authority.kind != source) {
return None;
}
counts
}
fn alias_root_for_display_root(
root: &Path,
plugin_version_skill_counts: &HashMap<PathBuf, usize>,
) -> PathBuf {
let Some(plugin_version_base) = plugin_version_base(root) else {
return root.to_path_buf();
let prefix = match source {
SkillSourceKind::Host => "r",
SkillSourceKind::Executor => "e",
SkillSourceKind::Orchestrator => "o",
SkillSourceKind::Custom(_) => return None,
};
let skill_count = plugin_version_skill_counts
.get(&plugin_version_base)
.copied()
.unwrap_or_default();
if skill_count > 1 {
root.to_path_buf()
} else {
plugin_marketplace_base(root).unwrap_or_else(|| root.to_path_buf())
}
}
fn plugin_marketplace_base(path: &Path) -> Option<PathBuf> {
let mut candidate = path;
while let Some(parent) = candidate.parent() {
if parent.file_name()?.to_str()? == "cache"
&& parent.parent()?.file_name()?.to_str()? == "plugins"
{
return Some(candidate.to_path_buf());
}
candidate = parent;
}
None
}
fn plugin_version_base(path: &Path) -> Option<PathBuf> {
let marketplace_base = plugin_marketplace_base(path)?;
let mut relative_components = path.strip_prefix(&marketplace_base).ok()?.components();
let plugin = match relative_components.next()? {
Component::Normal(plugin) => plugin,
_ => return None,
let mut alias_ordered_entries = entries.to_vec();
alias_ordered_entries.sort_by_key(|entry| entry.alias_root_order().unwrap_or(usize::MAX));
let roots = match source {
SkillSourceKind::Host => shared_host_alias_roots(&alias_ordered_entries),
SkillSourceKind::Executor | SkillSourceKind::Orchestrator => alias_ordered_entries
.iter()
.filter_map(|entry| entry.alias_root())
.map(str::to_string)
.collect(),
SkillSourceKind::Custom(_) => return None,
};
let version = match relative_components.next()? {
Component::Normal(version) => version,
_ => return None,
};
Some(marketplace_base.join(plugin).join(version))
let roots = roots.iter().map(String::as_str).collect::<Vec<_>>();
AliasPlan::build(prefix, &roots)
}
fn render_skill_path_with_aliases(entry: &SkillCatalogEntry, plan: &AliasPlan) -> String {
if entry.authority.kind != SkillSourceKind::Host {
fn render_skill_locator_with_aliases(entry: &SkillCatalogEntry, plan: &AliasPlan) -> String {
if entry.alias_root().is_none() {
return entry.rendered_path().to_string();
}
let Some(display_root) = entry.display_path_root() else {
return entry.rendered_path().to_string();
};
let Some(alias_root) = plan.alias_root_by_display_root.get(display_root) else {
return entry.rendered_path().to_string();
};
let Some(alias) = plan.root_aliases.get(alias_root) else {
return entry.rendered_path().to_string();
};
let Ok(relative_path) = Path::new(entry.rendered_path()).strip_prefix(alias_root) else {
return entry.rendered_path().to_string();
};
let relative_path = relative_path.to_string_lossy().replace('\\', "/");
format!("{alias}/{relative_path}")
let locator = entry.rendered_path().replace('\\', "/");
plan.shorten(&locator)
.unwrap_or_else(|| entry.rendered_path().to_string())
}
fn aliased_metadata_overhead_cost(
+77 -42
View File
@@ -200,10 +200,10 @@ fn path_aliases_are_not_used_without_budget_pressure() {
entries: vec![
entry("alpha", "Alpha skill.", /*short_description*/ None)
.with_display_path(format!("{root}/alpha/SKILL.md"))
.with_display_path_root(root),
.with_alias_root(root),
entry("beta", "Beta skill.", /*short_description*/ None)
.with_display_path(format!("{root}/beta/SKILL.md"))
.with_display_path_root(root),
.with_alias_root(root),
],
warnings: Vec::new(),
};
@@ -232,7 +232,7 @@ fn path_aliases_retain_every_skill_under_budget_pressure() {
let name = format!("shared-root-skill-{index}");
entry(&name, "Description.", /*short_description*/ None)
.with_display_path(format!("{root}/skill-{index}/SKILL.md"))
.with_display_path_root(root)
.with_alias_root(root)
})
.collect::<Vec<_>>();
let catalog = SkillCatalog {
@@ -240,17 +240,17 @@ fn path_aliases_retain_every_skill_under_budget_pressure() {
warnings: Vec::new(),
};
let visible_entries = catalog.entries.iter().collect::<Vec<_>>();
let plan = build_alias_plan(
&visible_entries,
let plan = build_alias_plan(&visible_entries).expect("alias plan should build");
let table_cost = aliased_metadata_overhead_cost(
SkillMetadataBudget::Characters(usize::MAX),
)
.expect("alias plan should build");
let alias_minimum = visible_entries.iter().fold(plan.table_cost, |cost, entry| {
&plan.root_lines(),
);
let alias_minimum = visible_entries.iter().fold(table_cost, |cost, entry| {
cost.saturating_add(
SkillLine::with_locator(
entry,
SkillCatalogRenderPolicy::ExtensionCompatible,
render_skill_path_with_aliases(entry, &plan),
render_skill_locator_with_aliases(entry, &plan),
)
.minimum_cost(SkillMetadataBudget::Characters(usize::MAX)),
)
@@ -330,9 +330,9 @@ async fn host_alias_roots_follow_core_discovery_order() -> Result<(), Box<dyn st
.await?;
let mut entries = catalog.entries.iter().collect::<Vec<_>>();
SkillCatalogRenderPolicy::CoreCompatible.order_entries(&mut entries);
let actual = build_alias_plan(&entries, SkillMetadataBudget::Characters(usize::MAX))
let actual = build_alias_plan(&entries)
.expect("alias plan should build")
.skill_root_lines;
.root_lines();
std::fs::remove_dir_all(parent)?;
assert_eq!(
@@ -359,7 +359,7 @@ fn mixed_catalogs_keep_absolute_authority_aware_rendering_under_budget_pressure(
let name = format!("host-skill-{index}");
entry(&name, "Description.", /*short_description*/ None)
.with_display_path(format!("{root}/skill-{index}/SKILL.md"))
.with_display_path_root(root)
.with_alias_root(root)
})
.collect::<Vec<_>>();
entries.push(
@@ -384,13 +384,7 @@ fn mixed_catalogs_keep_absolute_authority_aware_rendering_under_budget_pressure(
)
});
assert!(
build_alias_plan(
&visible_entries,
SkillMetadataBudget::Characters(usize::MAX),
)
.is_none()
);
assert!(build_alias_plan(&visible_entries).is_none());
let fragment = available_skills_fragment(
&catalog,
@@ -488,7 +482,7 @@ fn mixed_catalog_prefers_executor_inclusion_over_total_aliased_inclusion() {
.map(|name| {
entry(name, "", /*short_description*/ None)
.with_display_path(format!("{root}/{name}/SKILL.md"))
.with_display_path_root(root.as_str())
.with_alias_root(root.as_str())
})
.collect(),
warnings: Vec::new(),
@@ -563,38 +557,79 @@ fn mixed_catalog_prefers_executor_inclusion_over_total_aliased_inclusion() {
assert_eq!(host.skill_root_lines, Vec::<String>::new());
}
#[test]
fn singleton_plugin_versions_share_the_marketplace_alias_root() {
let github_root = "/Users/test/.codex/plugins/cache/openai-curated/github/hash123/skills";
let slack_root = "/Users/test/.codex/plugins/cache/openai-curated/slack/hash456/skills";
let entries = [
entry("github", "GitHub skill.", /*short_description*/ None)
.with_display_path(format!("{github_root}/github/SKILL.md"))
.with_display_path_root(github_root),
entry("slack", "Slack skill.", /*short_description*/ None)
.with_display_path(format!("{slack_root}/slack/SKILL.md"))
.with_display_path_root(slack_root),
];
let visible_entries = entries.iter().collect::<Vec<_>>();
let plan = build_alias_plan(
&visible_entries,
SkillMetadataBudget::Characters(usize::MAX),
#[tokio::test]
async fn singleton_plugin_versions_share_the_marketplace_alias_root()
-> Result<(), Box<dyn std::error::Error>> {
let unique = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let parent = std::env::temp_dir().join(format!(
"codex-skills-extension-marketplace-alias-{}-{unique}",
std::process::id()
));
let marketplace_path = parent.join("plugins/cache/openai-curated");
let mut roots = Vec::new();
for (plugin, version) in [("github", "hash123"), ("slack", "hash456")] {
let root = marketplace_path.join(plugin).join(version).join("skills");
let skill_dir = root.join(plugin);
std::fs::create_dir_all(&skill_dir)?;
std::fs::write(
skill_dir.join("SKILL.md"),
format!("---\nname: {plugin}\ndescription: {plugin} skill.\n---\n"),
)?;
roots.push(HostSkillRoot::host(
AbsolutePathBuf::try_from(std::fs::canonicalize(root)?)?,
SkillScope::User,
Arc::clone(&LOCAL_FS),
));
}
let marketplace_root = dunce::canonicalize(&marketplace_path)?
.to_string_lossy()
.replace('\\', "/");
let outcome = load_and_merge_host_skill_roots(
roots,
&Semaphore::new(/*permits*/ 2),
/*restriction_product*/ None,
/*plugin_skill_snapshots*/ None,
)
.expect("alias plan should build");
.await;
let catalog = HostSkillProvider::new()
.list(SkillListQuery {
turn_id: "turn-1".to_string(),
executor_roots: Vec::new(),
resolved_executor_roots: Vec::new(),
host_snapshot: Some(Arc::new(HostSkillsSnapshot::new(Arc::new(outcome)))),
include_host_skills: true,
include_bundled_skills: false,
include_orchestrator_skills: false,
mcp_resources: None,
executor_capability_discovery: None,
})
.await?;
let entries = catalog.entries.iter().collect::<Vec<_>>();
let plan = build_alias_plan(&entries).expect("alias plan should build");
let github = entries
.iter()
.find(|entry| entry.name == "github")
.ok_or("GitHub skill should be discovered")?;
let slack = entries
.iter()
.find(|entry| entry.name == "slack")
.ok_or("Slack skill should be discovered")?;
assert_eq!(
plan.skill_root_lines,
vec!["- `r0` = `/Users/test/.codex/plugins/cache/openai-curated`".to_string()]
plan.root_lines(),
vec![format!("- `r0` = `{marketplace_root}`")]
);
assert_eq!(
render_skill_path_with_aliases(&entries[0], &plan),
render_skill_locator_with_aliases(github, &plan),
"r0/github/hash123/skills/github/SKILL.md"
);
assert_eq!(
render_skill_path_with_aliases(&entries[1], &plan),
render_skill_locator_with_aliases(slack, &plan),
"r0/slack/hash456/skills/slack/SKILL.md"
);
std::fs::remove_dir_all(parent)?;
Ok(())
}
#[test]
@@ -8,10 +8,14 @@ use codex_config::ConfigLayerEntry;
use codex_config::ConfigLayerSource;
use codex_config::ConfigLayerStack;
use codex_config::ConfigRequirementsToml;
use codex_exec_server::CapabilityRootDiscovery;
use codex_exec_server::CapabilityTextFile;
use codex_exec_server::CopyOptions;
use codex_exec_server::CreateDirectoryOptions;
use codex_exec_server::DiscoveredSkillFiles;
use codex_exec_server::EnvironmentManager;
use codex_exec_server::ExecutorCapabilityDiscoveryCache;
use codex_exec_server::ExecutorCapabilityDiscoverySnapshot;
use codex_exec_server::ExecutorFileSystem;
use codex_exec_server::ExecutorFileSystemFuture;
use codex_exec_server::FileMetadata;
@@ -29,6 +33,8 @@ use codex_skills_extension::ExecutorSkillProvider;
use codex_skills_extension::HostSkillsLoadInput;
use codex_skills_extension::HostSkillsService;
use codex_skills_extension::catalog::SkillAuthority;
use codex_skills_extension::catalog::SkillCatalog;
use codex_skills_extension::catalog::SkillCatalogEntry;
use codex_skills_extension::catalog::SkillPackageId;
use codex_skills_extension::catalog::SkillResourceId;
use codex_skills_extension::catalog::SkillSourceKind;
@@ -367,6 +373,92 @@ async fn selected_root_id_distinguishes_identical_executor_paths() {
std::fs::remove_dir_all(test_root).expect("remove skill directory");
}
#[tokio::test]
async fn executor_discovery_preserves_posix_and_windows_locator_alias_roots() {
let provider = ExecutorSkillProvider::new_with_restriction_product(
Arc::new(EnvironmentManager::default_for_tests()),
/*restriction_product*/ None,
);
for (root_uri, alias_root) in [
(
"file:///workspace/skills",
"skill://cross-platform-root/workspace/skills",
),
(
"file:///C:/workspace/skills",
"skill://cross-platform-root/C:/workspace/skills",
),
] {
let root = PathUri::parse(root_uri).expect("executor root URI");
let skill_path = root.join("skill/SKILL.md").expect("executor skill URI");
let selected_root = SelectedCapabilityRoot {
id: "cross-platform-root".to_string(),
location: CapabilityRootLocation::Environment {
environment_id: "remote".to_string(),
path: root.clone(),
},
};
let discovery = ExecutorCapabilityDiscoverySnapshot::new(
std::slice::from_ref(&selected_root),
vec![Ok(Arc::new(CapabilityRootDiscovery {
id: selected_root.id.clone(),
path: root,
plugin: None,
skills: vec![DiscoveredSkillFiles {
instructions: CapabilityTextFile {
path: skill_path.clone(),
contents: SKILL_CONTENTS.to_string(),
},
metadata: None,
}],
namespace_manifests: Vec::new(),
warnings: Vec::new(),
error: None,
}))],
HashMap::new(),
);
let catalog = provider
.list(SkillListQuery {
turn_id: "turn-1".to_string(),
executor_roots: vec![selected_root],
resolved_executor_roots: Vec::new(),
host_snapshot: None,
include_host_skills: false,
include_bundled_skills: true,
include_orchestrator_skills: false,
mcp_resources: None,
executor_capability_discovery: Some(discovery),
})
.await
.expect("list executor skills");
let resource = format!("{alias_root}/skill/SKILL.md");
assert_eq!(
catalog,
SkillCatalog {
entries: vec![
SkillCatalogEntry::new(
SkillPackageId(format!("{alias_root}/skill")),
SkillAuthority::new(SkillSourceKind::Executor, "cross-platform-root"),
"synthetic",
"Synthetic executor skill.",
SkillResourceId::environment_with_contents(
resource.clone(),
"remote",
skill_path,
SKILL_CONTENTS.to_string(),
),
)
.with_display_path(resource)
.with_alias_root(alias_root)
],
warnings: Vec::new(),
}
);
}
}
#[tokio::test]
async fn executor_discovery_routes_produce_equivalent_catalog_metadata() {
let id = NEXT_TEST_ROOT_ID.fetch_add(1, Ordering::Relaxed);