Move shared skill models into codex-skills (#34429)

## What changed

- Define and export skill metadata, policy, dependency, interface, and configuration rule types from `codex-skills`.
- Update core, plugin, and extension consumers to use the shared models while retaining compatibility re-exports from `codex-core-skills`.
- Share product-restriction handling between host and environment skill metadata.

## Testing

- Add unit coverage for default and explicit implicit-invocation and product policies.

GitOrigin-RevId: 9093469f8fff8e2d902dc9781235f64709827a87
This commit is contained in:
felixxia-oai authored and copyberry committed 2026-07-20 23:22:48 +00:00
1 parent 35c2278dd5
commit 56c11cf658
20 files changed
+206 -133

No files matched your search

+5
View File
@@ -2665,6 +2665,7 @@ dependencies = [
"codex-sandboxing", "codex-sandboxing",
"codex-shell-command", "codex-shell-command",
"codex-shell-escalation", "codex-shell-escalation",
"codex-skills",
"codex-state", "codex-state",
"codex-terminal-detection", "codex-terminal-detection",
"codex-test-binary-support", "codex-test-binary-support",
@@ -2777,6 +2778,7 @@ dependencies = [
"codex-otel", "codex-otel",
"codex-plugin", "codex-plugin",
"codex-protocol", "codex-protocol",
"codex-skills",
"codex-tools", "codex-tools",
"codex-utils-absolute-path", "codex-utils-absolute-path",
"codex-utils-path", "codex-utils-path",
@@ -3950,7 +3952,9 @@ dependencies = [
name = "codex-skills" name = "codex-skills"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"codex-protocol",
"codex-utils-absolute-path", "codex-utils-absolute-path",
"codex-utils-path-uri",
"include_dir", "include_dir",
"thiserror 2.0.18", "thiserror 2.0.18",
] ]
@@ -3965,6 +3969,7 @@ dependencies = [
"codex-mcp", "codex-mcp",
"codex-otel", "codex-otel",
"codex-protocol", "codex-protocol",
"codex-skills",
"codex-tools", "codex-tools",
"codex-utils-absolute-path", "codex-utils-absolute-path",
"codex-utils-path-uri", "codex-utils-path-uri",
+1
View File
@@ -28,6 +28,7 @@ codex-model-provider = { workspace = true }
codex-otel = { workspace = true } codex-otel = { workspace = true }
codex-plugin = { workspace = true } codex-plugin = { workspace = true }
codex-protocol = { workspace = true } codex-protocol = { workspace = true }
codex-skills = { workspace = true }
codex-tools = { workspace = true } codex-tools = { workspace = true }
codex-utils-absolute-path = { workspace = true } codex-utils-absolute-path = { workspace = true }
codex-utils-path = { workspace = true } codex-utils-path = { workspace = true }
+2 -2
View File
@@ -26,8 +26,6 @@ use codex_config::types::PluginMcpServerConfig;
use codex_connectors::parse_plugin_app_config; use codex_connectors::parse_plugin_app_config;
use codex_connectors::parse_plugin_app_config_value; use codex_connectors::parse_plugin_app_config_value;
use codex_core_skills::PluginSkillSnapshots; use codex_core_skills::PluginSkillSnapshots;
use codex_core_skills::SkillMetadata;
use codex_core_skills::config_rules::SkillConfigRules;
use codex_core_skills::config_rules::resolve_disabled_skill_paths; use codex_core_skills::config_rules::resolve_disabled_skill_paths;
use codex_core_skills::config_rules::skill_config_rules_from_stack; use codex_core_skills::config_rules::skill_config_rules_from_stack;
use codex_core_skills::loader::SkillRoot; use codex_core_skills::loader::SkillRoot;
@@ -44,6 +42,8 @@ use codex_plugin::app_connector_ids_from_declarations;
use codex_protocol::auth::AuthMode; use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::Product; use codex_protocol::protocol::Product;
use codex_protocol::protocol::SkillScope; use codex_protocol::protocol::SkillScope;
use codex_skills::SkillConfigRules;
use codex_skills::SkillMetadata;
use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_plugins::find_plugin_manifest_path; use codex_utils_plugins::find_plugin_manifest_path;
use serde_json::Value as JsonValue; use serde_json::Value as JsonValue;
+2 -2
View File
@@ -67,8 +67,6 @@ use codex_config::types::PluginConfig;
use codex_config::types::ToolSuggestDisabledTool; use codex_config::types::ToolSuggestDisabledTool;
use codex_config::types::ToolSuggestDiscoverableType; use codex_config::types::ToolSuggestDiscoverableType;
use codex_core_skills::PluginSkillSnapshots; use codex_core_skills::PluginSkillSnapshots;
use codex_core_skills::SkillMetadata;
use codex_core_skills::config_rules::SkillConfigRules;
use codex_core_skills::config_rules::skill_config_rules_from_stack; use codex_core_skills::config_rules::skill_config_rules_from_stack;
use codex_core_skills::loader::MAX_CONCURRENT_ROOT_SCANS; use codex_core_skills::loader::MAX_CONCURRENT_ROOT_SCANS;
use codex_hooks::plugin_hook_declarations; use codex_hooks::plugin_hook_declarations;
@@ -84,6 +82,8 @@ use codex_plugin::prompt_safe_plugin_description;
use codex_protocol::auth::AuthMode; use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::HookEventName; use codex_protocol::protocol::HookEventName;
use codex_protocol::protocol::Product; use codex_protocol::protocol::Product;
use codex_skills::SkillConfigRules;
use codex_skills::SkillMetadata;
use codex_tools::DiscoverablePluginInfo; use codex_tools::DiscoverablePluginInfo;
use codex_tools::DiscoverableTool; use codex_tools::DiscoverableTool;
use codex_tools::filter_request_plugin_install_discoverable_tools_for_client; use codex_tools::filter_request_plugin_install_discoverable_tools_for_client;
+1 -1
View File
@@ -42,13 +42,13 @@ use codex_config::types::McpServerTransportConfig;
use codex_core_skills::PluginSkillSnapshots; use codex_core_skills::PluginSkillSnapshots;
use codex_core_skills::SkillsLoadInput; use codex_core_skills::SkillsLoadInput;
use codex_core_skills::SkillsService; use codex_core_skills::SkillsService;
use codex_core_skills::config_rules::SkillConfigRules;
use codex_login::CodexAuth; use codex_login::CodexAuth;
use codex_plugin::AppDeclaration; use codex_plugin::AppDeclaration;
use codex_plugin::PluginId; use codex_plugin::PluginId;
use codex_protocol::auth::AuthMode; use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::HookEventName; use codex_protocol::protocol::HookEventName;
use codex_protocol::protocol::Product; use codex_protocol::protocol::Product;
use codex_skills::SkillConfigRules;
use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_absolute_path::test_support::PathBufExt; use codex_utils_absolute_path::test_support::PathBufExt;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
@@ -2,7 +2,6 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::RwLock;
use codex_core_skills::config_rules::SkillConfigRules;
use codex_plugin::AppDeclaration; use codex_plugin::AppDeclaration;
use codex_plugin::PluginCapabilitySummary; use codex_plugin::PluginCapabilitySummary;
use codex_plugin::PluginId; use codex_plugin::PluginId;
@@ -11,6 +10,7 @@ use codex_plugin::app_connector_ids_from_declarations;
use codex_plugin::prompt_safe_plugin_description; use codex_plugin::prompt_safe_plugin_description;
use codex_protocol::auth::AuthMode; use codex_protocol::auth::AuthMode;
use codex_protocol::protocol::Product; use codex_protocol::protocol::Product;
use codex_skills::SkillConfigRules;
use tokio::sync::Semaphore; use tokio::sync::Semaphore;
use crate::app_mcp_routing::apply_app_mcp_routing_policy; use crate::app_mcp_routing::apply_app_mcp_routing_policy;
+3 -17
View File
@@ -5,28 +5,14 @@ use codex_config::ConfigLayerStack;
use codex_config::ConfigLayerStackOrdering; use codex_config::ConfigLayerStackOrdering;
use codex_config::SkillConfig; use codex_config::SkillConfig;
use codex_config::SkillsConfig; use codex_config::SkillsConfig;
pub use codex_skills::SkillConfigRule;
pub use codex_skills::SkillConfigRuleSelector;
pub use codex_skills::SkillConfigRules;
use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBuf;
use tracing::warn; use tracing::warn;
use crate::SkillMetadata; use crate::SkillMetadata;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum SkillConfigRuleSelector {
Name(String),
Path(AbsolutePathBuf),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SkillConfigRule {
pub selector: SkillConfigRuleSelector,
pub enabled: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct SkillConfigRules {
pub entries: Vec<SkillConfigRule>,
}
pub fn skill_config_rules_from_stack(config_layer_stack: &ConfigLayerStack) -> SkillConfigRules { pub fn skill_config_rules_from_stack(config_layer_stack: &ConfigLayerStack) -> SkillConfigRules {
let mut entries = Vec::new(); let mut entries = Vec::new();
for layer in config_layer_stack.get_layers( for layer in config_layer_stack.get_layers(
+1 -32
View File
@@ -4,6 +4,7 @@ use std::io;
use codex_exec_server::CapabilityRootDiscovery; use codex_exec_server::CapabilityRootDiscovery;
use codex_exec_server::ExecutorFileSystem; use codex_exec_server::ExecutorFileSystem;
use codex_protocol::protocol::Product; use codex_protocol::protocol::Product;
pub use codex_skills::EnvironmentSkillMetadata;
use codex_utils_path_uri::PathUri; use codex_utils_path_uri::PathUri;
use futures::StreamExt; use futures::StreamExt;
@@ -36,17 +37,6 @@ struct ParsedEnvironmentSkill {
policy: Option<SkillPolicy>, policy: Option<SkillPolicy>,
} }
/// URI-native metadata for one skill owned by an execution environment.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EnvironmentSkillMetadata {
pub path_to_skills_md: PathUri,
pub name: String,
pub description: String,
pub short_description: Option<String>,
pub dependencies: Option<SkillDependencies>,
pub policy: Option<SkillPolicy>,
}
/// Parsed executor skill plus the instructions already materialized by capability discovery. /// Parsed executor skill plus the instructions already materialized by capability discovery.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct EnvironmentSkillSnapshot { pub struct EnvironmentSkillSnapshot {
@@ -60,27 +50,6 @@ pub struct EnvironmentSkillSnapshotOutcome {
pub warnings: Vec<String>, pub warnings: Vec<String>,
} }
impl EnvironmentSkillMetadata {
pub fn allows_implicit_invocation(&self) -> bool {
self.policy
.as_ref()
.and_then(|policy| policy.allow_implicit_invocation)
.unwrap_or(true)
}
fn matches_product_restriction(&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,
}
}
}
impl ParsedEnvironmentSkill { impl ParsedEnvironmentSkill {
async fn load( async fn load(
file_system: &dyn ExecutorFileSystem, file_system: &dyn ExecutorFileSystem,
+5 -72
View File
@@ -7,81 +7,14 @@ use std::sync::Arc;
use codex_exec_server::ExecutorFileSystem; use codex_exec_server::ExecutorFileSystem;
use codex_exec_server::LOCAL_FS; use codex_exec_server::LOCAL_FS;
use codex_protocol::protocol::Product; use codex_protocol::protocol::Product;
use codex_protocol::protocol::SkillScope; pub use codex_skills::SkillDependencies;
pub use codex_skills::SkillInterface;
pub use codex_skills::SkillMetadata;
pub use codex_skills::SkillPolicy;
pub use codex_skills::SkillToolDependency;
use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri; use codex_utils_path_uri::PathUri;
#[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: AbsolutePathBuf,
pub scope: SkillScope,
pub plugin_id: Option<String>,
}
impl SkillMetadata {
pub fn allows_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<AbsolutePathBuf>,
pub icon_large: Option<AbsolutePathBuf>,
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)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct SkillError { pub struct SkillError {
pub path: AbsolutePathBuf, pub path: AbsolutePathBuf,
+1
View File
@@ -63,6 +63,7 @@ codex-rollout = { workspace = true }
codex-rollout-trace = { workspace = true } codex-rollout-trace = { workspace = true }
codex-rmcp-client = { workspace = true } codex-rmcp-client = { workspace = true }
codex-sandboxing = { workspace = true } codex-sandboxing = { workspace = true }
codex-skills = { workspace = true }
codex-state = { workspace = true } codex-state = { workspace = true }
codex-terminal-detection = { workspace = true } codex-terminal-detection = { workspace = true }
codex-thread-store = { workspace = true } codex-thread-store = { workspace = true }
+2 -2
View File
@@ -12,8 +12,6 @@ use codex_utils_plugins::PluginSkillRoot;
pub use codex_core_skills::SkillError; pub use codex_core_skills::SkillError;
pub use codex_core_skills::SkillLoadOutcome; pub use codex_core_skills::SkillLoadOutcome;
pub use codex_core_skills::SkillMetadata;
pub use codex_core_skills::SkillPolicy;
pub use codex_core_skills::SkillRenderReport; pub use codex_core_skills::SkillRenderReport;
pub use codex_core_skills::SkillsLoadInput; pub use codex_core_skills::SkillsLoadInput;
pub use codex_core_skills::SkillsService; pub use codex_core_skills::SkillsService;
@@ -34,6 +32,8 @@ pub use codex_core_skills::render;
pub use codex_core_skills::render::SkillRenderSideEffects; pub use codex_core_skills::render::SkillRenderSideEffects;
pub use codex_core_skills::service; pub use codex_core_skills::service;
pub use codex_core_skills::system; pub use codex_core_skills::system;
pub use codex_skills::SkillMetadata;
pub use codex_skills::SkillPolicy;
pub(crate) fn skills_load_input_from_config( pub(crate) fn skills_load_input_from_config(
config: &Config, config: &Config,
+1
View File
@@ -19,6 +19,7 @@ codex-extension-api = { workspace = true }
codex-mcp = { workspace = true } codex-mcp = { workspace = true }
codex-otel = { workspace = true } codex-otel = { workspace = true }
codex-protocol = { workspace = true } codex-protocol = { workspace = true }
codex-skills = { workspace = true }
codex-tools = { workspace = true } codex-tools = { workspace = true }
codex-utils-path-uri = { workspace = true } codex-utils-path-uri = { workspace = true }
codex-utils-string = { workspace = true } codex-utils-string = { workspace = true }
+1 -1
View File
@@ -1,4 +1,4 @@
use codex_core_skills::model::SkillDependencies; use codex_skills::SkillDependencies;
use codex_utils_path_uri::PathUri; use codex_utils_path_uri::PathUri;
use std::sync::Arc; use std::sync::Arc;
+1 -1
View File
@@ -1,11 +1,11 @@
use std::sync::Arc; use std::sync::Arc;
use codex_core_skills::loader::EnvironmentSkillMetadata;
use codex_core_skills::loader::load_environment_skills_from_discovery; use codex_core_skills::loader::load_environment_skills_from_discovery;
use codex_core_skills::loader::load_environment_skills_from_root; use codex_core_skills::loader::load_environment_skills_from_root;
use codex_exec_server::EnvironmentManager; use codex_exec_server::EnvironmentManager;
use codex_protocol::capabilities::CapabilityRootLocation; use codex_protocol::capabilities::CapabilityRootLocation;
use codex_protocol::protocol::Product; use codex_protocol::protocol::Product;
use codex_skills::EnvironmentSkillMetadata;
use codex_utils_path_uri::PathConvention; use codex_utils_path_uri::PathConvention;
use crate::catalog::SkillAuthority; use crate::catalog::SkillAuthority;
+1 -1
View File
@@ -1,5 +1,5 @@
use codex_core_skills::SkillLoadOutcome; use codex_core_skills::SkillLoadOutcome;
use codex_core_skills::SkillMetadata; use codex_skills::SkillMetadata;
use crate::catalog::SkillAuthority; use crate::catalog::SkillAuthority;
use crate::catalog::SkillCatalog; use crate::catalog::SkillCatalog;
@@ -7,7 +7,6 @@ use std::sync::atomic::Ordering;
use codex_core_skills::HostSkillsSnapshot; use codex_core_skills::HostSkillsSnapshot;
use codex_core_skills::SKILLS_INTRO_WITH_ABSOLUTE_PATHS; use codex_core_skills::SKILLS_INTRO_WITH_ABSOLUTE_PATHS;
use codex_core_skills::SkillLoadOutcome; use codex_core_skills::SkillLoadOutcome;
use codex_core_skills::SkillMetadata;
use codex_core_skills::injection::InjectedHostSkillPrompts; use codex_core_skills::injection::InjectedHostSkillPrompts;
use codex_extension_api::ConversationHistory; use codex_extension_api::ConversationHistory;
use codex_extension_api::ExtensionData; use codex_extension_api::ExtensionData;
@@ -31,6 +30,7 @@ use codex_protocol::protocol::SkillScope;
use codex_protocol::protocol::TruncationPolicy; use codex_protocol::protocol::TruncationPolicy;
use codex_protocol::protocol::TurnEnvironmentSelection; use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_protocol::user_input::UserInput; use codex_protocol::user_input::UserInput;
use codex_skills::SkillMetadata;
use codex_skills_extension::SkillProviders; use codex_skills_extension::SkillProviders;
use codex_skills_extension::SkillsExtensionConfig; use codex_skills_extension::SkillsExtensionConfig;
use codex_skills_extension::catalog::SkillAuthority; use codex_skills_extension::catalog::SkillAuthority;
+2
View File
@@ -14,6 +14,8 @@ path = "src/lib.rs"
workspace = true workspace = true
[dependencies] [dependencies]
codex-protocol = { workspace = true }
codex-utils-absolute-path = { workspace = true } codex-utils-absolute-path = { workspace = true }
codex-utils-path-uri = { workspace = true }
include_dir = { workspace = true } include_dir = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
+12
View File
@@ -1,3 +1,15 @@
mod model;
pub use model::EnvironmentSkillMetadata;
pub use model::SkillConfigRule;
pub use model::SkillConfigRuleSelector;
pub use model::SkillConfigRules;
pub use model::SkillDependencies;
pub use model::SkillInterface;
pub use model::SkillMetadata;
pub use model::SkillPolicy;
pub use model::SkillToolDependency;
use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBuf;
use include_dir::Dir; use include_dir::Dir;
use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::DefaultHasher;
+127
View File
@@ -0,0 +1,127 @@
use codex_protocol::protocol::Product;
use codex_protocol::protocol::SkillScope;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
/// Metadata for one skill materialized on the host filesystem.
#[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 SKILL.md file that declares this skill.
pub path_to_skills_md: AbsolutePathBuf,
pub scope: SkillScope,
pub plugin_id: Option<String>,
}
impl SkillMetadata {
pub fn allows_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 {
matches_product_restriction(self.policy.as_ref(), restriction_product)
}
}
/// URI-native metadata for one skill owned by an execution environment.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EnvironmentSkillMetadata {
pub path_to_skills_md: PathUri,
pub name: String,
pub description: String,
pub short_description: Option<String>,
pub dependencies: Option<SkillDependencies>,
pub policy: Option<SkillPolicy>,
}
impl EnvironmentSkillMetadata {
pub fn allows_implicit_invocation(&self) -> bool {
self.policy
.as_ref()
.and_then(|policy| policy.allow_implicit_invocation)
.unwrap_or(true)
}
pub fn matches_product_restriction(&self, restriction_product: Option<Product>) -> bool {
matches_product_restriction(self.policy.as_ref(), restriction_product)
}
}
#[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<AbsolutePathBuf>,
pub icon_large: Option<AbsolutePathBuf>,
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, Hash, PartialOrd, Ord)]
pub enum SkillConfigRuleSelector {
Name(String),
Path(AbsolutePathBuf),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SkillConfigRule {
pub selector: SkillConfigRuleSelector,
pub enabled: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct SkillConfigRules {
pub entries: Vec<SkillConfigRule>,
}
fn matches_product_restriction(
policy: Option<&SkillPolicy>,
restriction_product: Option<Product>,
) -> bool {
match policy {
Some(policy) => {
policy.products.is_empty()
|| restriction_product
.is_some_and(|product| product.matches_product_restriction(&policy.products))
}
None => true,
}
}
#[cfg(test)]
#[path = "model_tests.rs"]
mod tests;
+36
View File
@@ -0,0 +1,36 @@
use super::*;
#[test]
fn missing_policy_allows_implicit_invocation_and_all_products() {
let skill = EnvironmentSkillMetadata {
path_to_skills_md: PathUri::parse("file:///skills/demo/SKILL.md").expect("valid skill URI"),
name: "demo".to_string(),
description: "Demo skill".to_string(),
short_description: None,
dependencies: None,
policy: None,
};
assert!(skill.allows_implicit_invocation());
assert!(skill.matches_product_restriction(/*restriction_product*/ None));
}
#[test]
fn policy_restricts_implicit_invocation_and_products() {
let policy = SkillPolicy {
allow_implicit_invocation: Some(false),
products: vec![Product::Codex],
};
let skill = EnvironmentSkillMetadata {
path_to_skills_md: PathUri::parse("file:///skills/demo/SKILL.md").expect("valid skill URI"),
name: "demo".to_string(),
description: "Demo skill".to_string(),
short_description: None,
dependencies: None,
policy: Some(policy),
};
assert!(!skill.allows_implicit_invocation());
assert!(skill.matches_product_restriction(Some(Product::Codex)));
assert!(!skill.matches_product_restriction(/*restriction_product*/ None));
}