use codex_core_skills::model::SkillDependencies; use codex_utils_absolute_path::AbsolutePathBuf; /// Source authority that owns a skill package and must be used to read it. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum SkillSourceKind { /// Codex-hosted skills, including bundled, user, repo, plugin-installed, /// and downloaded/materialized remote skills. Host, /// Skills owned by an execution environment. Executor, /// Skills owned by the orchestrator rather than an execution environment. Orchestrator, /// Extension-private source kind for future providers that do not fit an /// existing transport category. Custom(String), } impl SkillSourceKind { pub fn custom(kind: impl Into) -> Self { Self::Custom(kind.into()) } fn as_str(&self) -> &str { match self { Self::Host => "host", Self::Executor => "executor", Self::Orchestrator => "orchestrator", Self::Custom(kind) => kind, } } } impl std::fmt::Display for SkillSourceKind { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.as_str().fmt(formatter) } } /// Opaque authority identity for list/read routing. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct SkillAuthority { pub kind: SkillSourceKind, pub id: String, } impl SkillAuthority { pub fn new(kind: SkillSourceKind, id: impl Into) -> Self { Self { kind, id: id.into(), } } } /// Opaque package id. Callers should not parse local paths out of this value. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct SkillPackageId(pub String); /// Opaque resource id inside a skill package, optionally bound to the /// environment path that owns its contents. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct SkillResourceId { id: String, environment_path: Option, } impl SkillResourceId { pub fn new(id: impl Into) -> Self { Self { id: id.into(), environment_path: None, } } pub fn environment( id: impl Into, environment_id: impl Into, path: AbsolutePathBuf, ) -> Self { Self { id: id.into(), environment_path: Some(EnvironmentSkillResource { environment_id: environment_id.into(), path, }), } } pub fn as_str(&self) -> &str { &self.id } pub(crate) fn environment_path(&self) -> Option<(&str, &AbsolutePathBuf)> { self.environment_path .as_ref() .map(|resource| (resource.environment_id.as_str(), &resource.path)) } } #[derive(Clone, Debug, PartialEq, Eq, Hash)] struct EnvironmentSkillResource { environment_id: String, path: AbsolutePathBuf, } /// Metadata shown in the always-visible skills catalog. #[derive(Clone, Debug, PartialEq, Eq)] pub struct SkillCatalogEntry { pub id: SkillPackageId, pub authority: SkillAuthority, pub name: String, pub description: String, pub short_description: Option, pub main_prompt: SkillResourceId, pub display_path: Option, pub dependencies: Option, pub enabled: bool, pub prompt_visible: bool, } impl SkillCatalogEntry { pub fn new( id: SkillPackageId, authority: SkillAuthority, name: impl Into, description: impl Into, main_prompt: SkillResourceId, ) -> Self { Self { id, authority, name: name.into(), description: description.into(), short_description: None, main_prompt, display_path: None, dependencies: None, enabled: true, prompt_visible: true, } } pub fn with_short_description(mut self, short_description: Option) -> Self { self.short_description = short_description; self } pub fn with_display_path(mut self, display_path: impl Into) -> Self { self.display_path = Some(display_path.into()); self } pub fn with_dependencies(mut self, dependencies: Option) -> Self { self.dependencies = dependencies; self } pub fn disabled(mut self) -> Self { self.enabled = false; self } pub fn hidden_from_prompt(mut self) -> Self { self.prompt_visible = false; self } pub(crate) fn rendered_path(&self) -> &str { self.display_path .as_deref() .unwrap_or_else(|| self.main_prompt.as_str()) } } /// Merged catalog for one turn. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SkillCatalog { pub entries: Vec, pub warnings: Vec, } impl SkillCatalog { pub fn extend(&mut self, other: SkillCatalog) { for entry in other.entries { self.push_entry(entry); } self.warnings.extend(other.warnings); } pub fn push_entry(&mut self, entry: SkillCatalogEntry) { if self .entries .iter() .any(|existing| existing.authority == entry.authority && existing.id == entry.id) { return; } self.entries.push(entry); } } /// Contents returned after resolving a skill resource through its owner. #[derive(Clone, Debug, PartialEq, Eq)] pub struct SkillReadResult { pub resource: SkillResourceId, pub contents: String, } /// Search results for a package whose files are not readable through ordinary /// executor filesystem access. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SkillSearchResult { pub matches: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SkillSearchMatch { pub resource: SkillResourceId, pub title: String, pub snippet: String, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct SkillProviderError { pub message: String, } impl SkillProviderError { pub fn new(message: impl Into) -> Self { Self { message: message.into(), } } } impl std::fmt::Display for SkillProviderError { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.message.fmt(formatter) } } impl std::error::Error for SkillProviderError {} pub type SkillProviderResult = Result;