mirror of
https://github.com/openai/codex.git
synced 2026-04-30 11:21:34 +03:00
## Why Skill metadata accepted a `permissions` block and stored the result on `SkillMetadata`, but that data was never consumed by runtime behavior. Leaving the dead parsing path in place makes it look like skills can widen or otherwise influence execution permissions when, in practice, declared skill permissions are ignored. This change removes that misleading surface area so the skill metadata model matches what the system actually uses. ## What changed - removed `permission_profile` and `managed_network_override` from `core-skills::SkillMetadata` - stopped parsing `permissions` from skill metadata in `core-skills/src/loader.rs` - deleted the loader tests that only exercised the removed permissions parsing path - cleaned up dependent `SkillMetadata` constructors in tests and TUI code that were only carrying `None` for those fields ## Testing - `cargo test -p codex-core-skills` - `cargo test -p codex-tui submission_prefers_selected_duplicate_skill_path` - `just argument-comment-lint`
143 lines
4.3 KiB
Rust
143 lines
4.3 KiB
Rust
use std::collections::HashMap;
|
|
use std::collections::HashSet;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use codex_protocol::protocol::Product;
|
|
use codex_protocol::protocol::SkillScope;
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub struct SkillMetadata {
|
|
pub name: String,
|
|
pub description: String,
|
|
pub short_description: Option<String>,
|
|
pub interface: Option<SkillInterface>,
|
|
pub dependencies: Option<SkillDependencies>,
|
|
pub policy: Option<SkillPolicy>,
|
|
/// Path to the SKILLS.md file that declares this skill.
|
|
pub path_to_skills_md: PathBuf,
|
|
pub scope: SkillScope,
|
|
}
|
|
|
|
impl SkillMetadata {
|
|
fn allow_implicit_invocation(&self) -> bool {
|
|
self.policy
|
|
.as_ref()
|
|
.and_then(|policy| policy.allow_implicit_invocation)
|
|
.unwrap_or(true)
|
|
}
|
|
|
|
pub fn matches_product_restriction_for_product(
|
|
&self,
|
|
restriction_product: Option<Product>,
|
|
) -> bool {
|
|
match &self.policy {
|
|
Some(policy) => {
|
|
policy.products.is_empty()
|
|
|| restriction_product.is_some_and(|product| {
|
|
product.matches_product_restriction(&policy.products)
|
|
})
|
|
}
|
|
None => true,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
|
pub struct SkillPolicy {
|
|
pub allow_implicit_invocation: Option<bool>,
|
|
// TODO: Enforce product gating in Codex skill selection/injection instead of only parsing and
|
|
// storing this metadata.
|
|
pub products: Vec<Product>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct SkillInterface {
|
|
pub display_name: Option<String>,
|
|
pub short_description: Option<String>,
|
|
pub icon_small: Option<PathBuf>,
|
|
pub icon_large: Option<PathBuf>,
|
|
pub brand_color: Option<String>,
|
|
pub default_prompt: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct SkillDependencies {
|
|
pub tools: Vec<SkillToolDependency>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct SkillToolDependency {
|
|
pub r#type: String,
|
|
pub value: String,
|
|
pub description: Option<String>,
|
|
pub transport: Option<String>,
|
|
pub command: Option<String>,
|
|
pub url: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct SkillError {
|
|
pub path: PathBuf,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct SkillLoadOutcome {
|
|
pub skills: Vec<SkillMetadata>,
|
|
pub errors: Vec<SkillError>,
|
|
pub disabled_paths: HashSet<PathBuf>,
|
|
pub(crate) implicit_skills_by_scripts_dir: Arc<HashMap<PathBuf, SkillMetadata>>,
|
|
pub(crate) implicit_skills_by_doc_path: Arc<HashMap<PathBuf, SkillMetadata>>,
|
|
}
|
|
|
|
impl SkillLoadOutcome {
|
|
pub fn is_skill_enabled(&self, skill: &SkillMetadata) -> bool {
|
|
!self.disabled_paths.contains(&skill.path_to_skills_md)
|
|
}
|
|
|
|
pub fn is_skill_allowed_for_implicit_invocation(&self, skill: &SkillMetadata) -> bool {
|
|
self.is_skill_enabled(skill) && skill.allow_implicit_invocation()
|
|
}
|
|
|
|
pub fn allowed_skills_for_implicit_invocation(&self) -> Vec<SkillMetadata> {
|
|
self.skills
|
|
.iter()
|
|
.filter(|skill| self.is_skill_allowed_for_implicit_invocation(skill))
|
|
.cloned()
|
|
.collect()
|
|
}
|
|
|
|
pub fn skills_with_enabled(&self) -> impl Iterator<Item = (&SkillMetadata, bool)> {
|
|
self.skills
|
|
.iter()
|
|
.map(|skill| (skill, self.is_skill_enabled(skill)))
|
|
}
|
|
}
|
|
|
|
pub fn filter_skill_load_outcome_for_product(
|
|
mut outcome: SkillLoadOutcome,
|
|
restriction_product: Option<Product>,
|
|
) -> SkillLoadOutcome {
|
|
outcome
|
|
.skills
|
|
.retain(|skill| skill.matches_product_restriction_for_product(restriction_product));
|
|
outcome.implicit_skills_by_scripts_dir = Arc::new(
|
|
outcome
|
|
.implicit_skills_by_scripts_dir
|
|
.iter()
|
|
.filter(|(_, skill)| skill.matches_product_restriction_for_product(restriction_product))
|
|
.map(|(path, skill)| (path.clone(), skill.clone()))
|
|
.collect(),
|
|
);
|
|
outcome.implicit_skills_by_doc_path = Arc::new(
|
|
outcome
|
|
.implicit_skills_by_doc_path
|
|
.iter()
|
|
.filter(|(_, skill)| skill.matches_product_restriction_for_product(restriction_product))
|
|
.map(|(path, skill)| (path.clone(), skill.clone()))
|
|
.collect(),
|
|
);
|
|
outcome
|
|
}
|