Skip to main content

a3s_code_core/capability/
value.rs

1use std::fmt;
2use std::sync::Arc;
3
4use crate::cognitive_context::CognitiveContextSession;
5use crate::commands::SlashCommand;
6use crate::context::ContextProvider;
7use crate::hooks::HookBinding;
8use crate::mcp::McpBinding;
9use crate::skills::Skill;
10use crate::subagent::AgentDefinition;
11use crate::tools::Tool;
12
13#[cfg(feature = "dynamic-workflow")]
14use super::FlowBinding;
15use super::{CapabilityKind, KnowledgeSurfaceBinding, UiBinding};
16
17/// Closed runtime value categories accepted by the Code projection kernel.
18///
19/// Implementations inside trait-backed categories remain open, but callers
20/// cannot insert `Any` or invent a new product category. UI values carry only
21/// renderer-neutral path-free content; embedding-host authority stays outside
22/// this enum.
23#[derive(Clone)]
24pub enum CapabilityValue {
25    Tool(Arc<dyn Tool>),
26    Skill(Arc<Skill>),
27    Agent(Arc<AgentDefinition>),
28    Command(Arc<dyn SlashCommand>),
29    Hook(Arc<HookBinding>),
30    Mcp(Arc<McpBinding>),
31    #[cfg(feature = "dynamic-workflow")]
32    Flow(Arc<FlowBinding>),
33    KnowledgeSurface(Arc<KnowledgeSurfaceBinding>),
34    Knowledge(Arc<CognitiveContextSession>),
35    Ui(Arc<UiBinding>),
36    Context(Arc<dyn ContextProvider>),
37}
38
39impl CapabilityValue {
40    pub const fn kind(&self) -> CapabilityKind {
41        match self {
42            Self::Tool(_) => CapabilityKind::Tool,
43            Self::Skill(_) => CapabilityKind::Skill,
44            Self::Agent(_) => CapabilityKind::Agent,
45            Self::Command(_) => CapabilityKind::Command,
46            Self::Hook(_) => CapabilityKind::Hook,
47            Self::Mcp(_) => CapabilityKind::Mcp,
48            #[cfg(feature = "dynamic-workflow")]
49            Self::Flow(_) => CapabilityKind::Flow,
50            Self::KnowledgeSurface(_) => CapabilityKind::KnowledgeSurface,
51            Self::Knowledge(_) => CapabilityKind::Knowledge,
52            Self::Ui(_) => CapabilityKind::Ui,
53            Self::Context(_) => CapabilityKind::Context,
54        }
55    }
56
57    pub(crate) fn public_name(&self) -> Option<&str> {
58        match self {
59            Self::Tool(value) => Some(value.name()),
60            Self::Skill(value) => Some(&value.name),
61            Self::Agent(value) => Some(&value.name),
62            Self::Command(value) => Some(value.name()),
63            Self::Hook(value) => Some(&value.hook().id),
64            #[cfg(feature = "dynamic-workflow")]
65            Self::Flow(value) => Some(value.public_name()),
66            Self::Mcp(value) => Some(value.server_name()),
67            Self::KnowledgeSurface(value) => Some(value.public_name()),
68            Self::Knowledge(value) => Some(value.provider_name()),
69            Self::Ui(value) => Some(value.public_name()),
70            Self::Context(value) => Some(value.name()),
71        }
72    }
73}
74
75impl fmt::Debug for CapabilityValue {
76    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
77        let mut debug = formatter.debug_struct("CapabilityValue");
78        debug.field("kind", &self.kind());
79        if let Some(public_name) = self.public_name() {
80            debug.field("public_name", &public_name);
81        }
82        debug.finish_non_exhaustive()
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::skills::{Skill, SkillKind};
90
91    fn sample_skill(name: &str) -> CapabilityValue {
92        CapabilityValue::Skill(Arc::new(Skill {
93            name: name.to_owned(),
94            description: "coverage".to_owned(),
95            allowed_tools: None,
96            disable_model_invocation: false,
97            kind: SkillKind::Instruction,
98            content: "body".to_owned(),
99            tags: vec![],
100            version: None,
101        }))
102    }
103
104    #[test]
105    fn debug_emits_kind_and_public_name_without_body() {
106        let value = sample_skill("coverage-skill");
107        let rendered = format!("{value:?}");
108        assert!(rendered.contains("CapabilityValue"));
109        assert!(rendered.contains("Skill"));
110        assert!(rendered.contains("coverage-skill"));
111        assert!(!rendered.contains("body"));
112        assert_eq!(value.kind(), CapabilityKind::Skill);
113        assert_eq!(value.public_name(), Some("coverage-skill"));
114    }
115}