Skip to main content

agent_sdk_core/package/
capability.rs

1//! Capability identity, projection, executable routes, and readiness records. Use
2//! this module when a runtime package exposes callable or discoverable behavior.
3//! Projection helpers are pure and must not execute tools.
4//!
5use core::fmt;
6
7use serde::{Deserialize, Deserializer, Serialize, de::Error as DeError};
8
9use crate::{
10    domain::{
11        AdapterRef, AgentError, IdValidationError, PolicyKind, PolicyRef, PrivacyClass, SourceRef,
12    },
13    ids::validate_identifier,
14};
15
16#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
17#[serde(transparent)]
18/// Describes the capability id portion of a runtime package snapshot.
19/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
20pub struct CapabilityId(String);
21
22impl CapabilityId {
23    /// Creates a new package::capability value with explicit
24    /// caller-provided inputs. This constructor is data-only and
25    /// performs no I/O or external side effects.
26    ///
27    /// # Panics
28    ///
29    /// Panics if constructor invariants fail, such as invalid identifier
30    /// text or constructor-specific bounds. Use a fallible constructor such as
31    /// `try_new` when one is available for untrusted input.
32    pub fn new(value: impl Into<String>) -> Self {
33        Self::try_new(value).expect("CapabilityId must be valid")
34    }
35
36    /// Creates a new package::capability value after validation.
37    /// Returns an SDK error instead of panicking when the identifier or
38    /// input does not satisfy the contract.
39    pub fn try_new(value: impl Into<String>) -> Result<Self, IdValidationError> {
40        let value = value.into();
41        validate_identifier(&value)?;
42        Ok(Self(value))
43    }
44
45    /// Returns this value as str. The accessor is side-effect free and
46    /// keeps ownership with the caller.
47    pub fn as_str(&self) -> &str {
48        &self.0
49    }
50}
51
52impl From<&str> for CapabilityId {
53    fn from(value: &str) -> Self {
54        Self::new(value)
55    }
56}
57
58impl<'de> Deserialize<'de> for CapabilityId {
59    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60    where
61        D: Deserializer<'de>,
62    {
63        let value = String::deserialize(deserializer)?;
64        Self::try_new(value).map_err(D::Error::custom)
65    }
66}
67
68impl fmt::Debug for CapabilityId {
69    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70        formatter.write_str("CapabilityId(redacted)")
71    }
72}
73
74impl fmt::Display for CapabilityId {
75    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
76        formatter.write_str("CapabilityId(redacted)")
77    }
78}
79
80#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
81#[serde(transparent)]
82/// Describes the capability namespace portion of a runtime package snapshot.
83/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
84pub struct CapabilityNamespace(String);
85
86impl CapabilityNamespace {
87    /// Creates a new package::capability value with explicit
88    /// caller-provided inputs. This constructor is data-only and
89    /// performs no I/O or external side effects.
90    ///
91    /// # Panics
92    ///
93    /// Panics if constructor invariants fail, such as invalid identifier
94    /// text or constructor-specific bounds. Use a fallible constructor such as
95    /// `try_new` when one is available for untrusted input.
96    pub fn new(value: impl Into<String>) -> Self {
97        Self::try_new(value).expect("CapabilityNamespace must be valid")
98    }
99
100    /// Creates a new package::capability value after validation.
101    /// Returns an SDK error instead of panicking when the identifier or
102    /// input does not satisfy the contract.
103    pub fn try_new(value: impl Into<String>) -> Result<Self, IdValidationError> {
104        let value = value.into();
105        validate_identifier(&value)?;
106        Ok(Self(value))
107    }
108
109    /// Returns this value as str. The accessor is side-effect free and
110    /// keeps ownership with the caller.
111    pub fn as_str(&self) -> &str {
112        &self.0
113    }
114}
115
116impl<'de> Deserialize<'de> for CapabilityNamespace {
117    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
118    where
119        D: Deserializer<'de>,
120    {
121        let value = String::deserialize(deserializer)?;
122        Self::try_new(value).map_err(D::Error::custom)
123    }
124}
125
126impl fmt::Debug for CapabilityNamespace {
127    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
128        formatter.write_str("CapabilityNamespace(redacted)")
129    }
130}
131
132#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
133#[serde(transparent)]
134/// Describes the capability version portion of a runtime package snapshot.
135/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
136pub struct CapabilityVersion(String);
137
138impl CapabilityVersion {
139    /// Creates a new package::capability value with explicit
140    /// caller-provided inputs. This constructor is data-only and
141    /// performs no I/O or external side effects.
142    ///
143    /// # Panics
144    ///
145    /// Panics if constructor invariants fail, such as invalid identifier
146    /// text or constructor-specific bounds. Use a fallible constructor such as
147    /// `try_new` when one is available for untrusted input.
148    pub fn new(value: impl Into<String>) -> Self {
149        Self::try_new(value).expect("CapabilityVersion must be valid")
150    }
151
152    /// Creates a new package::capability value after validation.
153    /// Returns an SDK error instead of panicking when the identifier or
154    /// input does not satisfy the contract.
155    pub fn try_new(value: impl Into<String>) -> Result<Self, IdValidationError> {
156        let value = value.into();
157        validate_identifier(&value)?;
158        Ok(Self(value))
159    }
160
161    /// Returns this value as str. The accessor is side-effect free and
162    /// keeps ownership with the caller.
163    pub fn as_str(&self) -> &str {
164        &self.0
165    }
166}
167
168impl<'de> Deserialize<'de> for CapabilityVersion {
169    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170    where
171        D: Deserializer<'de>,
172    {
173        let value = String::deserialize(deserializer)?;
174        Self::try_new(value).map_err(D::Error::custom)
175    }
176}
177
178impl fmt::Debug for CapabilityVersion {
179    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
180        formatter.write_str("CapabilityVersion(redacted)")
181    }
182}
183
184#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
185#[serde(transparent)]
186/// Describes the executor ref portion of a runtime package snapshot.
187/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
188pub struct ExecutorRef(String);
189
190impl ExecutorRef {
191    /// Creates a new package::capability value with explicit
192    /// caller-provided inputs. This constructor is data-only and
193    /// performs no I/O or external side effects.
194    ///
195    /// # Panics
196    ///
197    /// Panics if constructor invariants fail, such as invalid identifier
198    /// text or constructor-specific bounds. Use a fallible constructor such as
199    /// `try_new` when one is available for untrusted input.
200    pub fn new(value: impl Into<String>) -> Self {
201        Self::try_new(value).expect("ExecutorRef must be valid")
202    }
203
204    /// Creates a new package::capability value after validation.
205    /// Returns an SDK error instead of panicking when the identifier or
206    /// input does not satisfy the contract.
207    pub fn try_new(value: impl Into<String>) -> Result<Self, IdValidationError> {
208        let value = value.into();
209        validate_identifier(&value)?;
210        Ok(Self(value))
211    }
212
213    /// Returns this value as str. The accessor is side-effect free and
214    /// keeps ownership with the caller.
215    pub fn as_str(&self) -> &str {
216        &self.0
217    }
218}
219
220impl<'de> Deserialize<'de> for ExecutorRef {
221    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
222    where
223        D: Deserializer<'de>,
224    {
225        let value = String::deserialize(deserializer)?;
226        Self::try_new(value).map_err(D::Error::custom)
227    }
228}
229
230impl fmt::Debug for ExecutorRef {
231    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
232        formatter.write_str("ExecutorRef(redacted)")
233    }
234}
235
236#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
237#[serde(rename_all = "snake_case")]
238/// Enumerates the finite capability kind cases.
239/// Serialized names are part of the SDK contract; update fixtures when variants change.
240pub enum CapabilityKind {
241    /// Use this variant when the contract needs to represent tool; selecting it has no side effect by itself.
242    Tool,
243    /// Use this variant when the contract needs to represent mcp tool; selecting it has no side effect by itself.
244    McpTool,
245    /// Use this variant when the contract needs to represent mcp resource; selecting it has no side effect by itself.
246    McpResource,
247    /// Use this variant when the contract needs to represent tool discovery candidate; selecting it has no side effect by itself.
248    ToolDiscoveryCandidate,
249    /// Use this variant when the contract needs to represent agent as tool; selecting it has no side effect by itself.
250    AgentAsTool,
251    /// Use this variant when the contract needs to represent extension action; selecting it has no side effect by itself.
252    ExtensionAction,
253    /// Use this variant when the contract needs to represent stream control; selecting it has no side effect by itself.
254    StreamControl,
255    /// Use this variant when the contract needs to represent realtime action; selecting it has no side effect by itself.
256    RealtimeAction,
257}
258
259impl CapabilityKind {
260    /// Reports whether this value is reserved. The check is pure and
261    /// does not mutate SDK or host state.
262    pub fn is_reserved(&self) -> bool {
263        !matches!(self, Self::Tool)
264    }
265
266    /// Returns owner role for the current value.
267    /// This is a read-only or data-construction helper unless the method body explicitly calls
268    /// a port or store.
269    pub fn owner_role(&self) -> &'static str {
270        match self {
271            Self::Tool => "02-runtime-package-p0-fake-tool",
272            Self::McpTool | Self::McpResource | Self::ToolDiscoveryCandidate => {
273                "04-tools-approval-toolpacks"
274            }
275            Self::AgentAsTool => "07-subagents-coordination",
276            Self::ExtensionAction => "08-extension-sdk-packaging",
277            Self::StreamControl | Self::RealtimeAction => "05-streaming-realtime-rules",
278        }
279    }
280}
281
282#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
283#[serde(rename_all = "snake_case")]
284/// Enumerates the finite capability source kind cases.
285/// Serialized names are part of the SDK contract; update fixtures when variants change.
286pub enum CapabilitySourceKind {
287    /// Use this variant when the contract needs to represent sdk built in; selecting it has no side effect by itself.
288    SdkBuiltIn,
289    /// Use this variant when the contract needs to represent host provided; selecting it has no side effect by itself.
290    HostProvided,
291    /// Use this variant when the contract needs to represent tool pack; selecting it has no side effect by itself.
292    ToolPack,
293    /// Use this variant when the contract needs to represent mcp server; selecting it has no side effect by itself.
294    McpServer,
295    /// Use this variant when the contract needs to represent extension; selecting it has no side effect by itself.
296    Extension,
297    /// Use this variant when the contract needs to represent subagent; selecting it has no side effect by itself.
298    Subagent,
299    /// Use this variant when the contract needs to represent discovery index; selecting it has no side effect by itself.
300    DiscoveryIndex,
301    /// Use this variant when the contract needs to represent test only fake; selecting it has no side effect by itself.
302    TestOnlyFake,
303}
304
305#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
306/// Describes the capability source portion of a runtime package snapshot.
307/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
308pub struct CapabilitySource {
309    /// Kind/category for this record, capability, event, or detected
310    /// resource.
311    pub kind: CapabilitySourceKind,
312    /// Typed source reference that records where this item originated.
313    pub source_ref: SourceRef,
314    #[serde(skip_serializing_if = "Option::is_none")]
315    /// Typed adapter ref reference. Resolving or executing it is a separate
316    /// policy-gated step.
317    pub adapter_ref: Option<AdapterRef>,
318}
319
320impl CapabilitySource {
321    /// Builds the test fake value.
322    /// This is data construction and performs no I/O, journal append, event publication, or
323    /// process work.
324    pub fn test_fake(source_ref: SourceRef) -> Self {
325        Self {
326            kind: CapabilitySourceKind::TestOnlyFake,
327            source_ref,
328            adapter_ref: None,
329        }
330    }
331}
332
333#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
334#[serde(rename_all = "snake_case")]
335/// Enumerates the finite capability visibility cases.
336/// Serialized names are part of the SDK contract; update fixtures when variants change.
337pub enum CapabilityVisibility {
338    /// Use this variant when the contract needs to represent active; selecting it has no side effect by itself.
339    Active,
340    /// Use this variant when the contract needs to represent hidden; selecting it has no side effect by itself.
341    Hidden,
342}
343
344#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
345#[serde(rename_all = "snake_case")]
346/// Enumerates the finite projection mode cases.
347/// Serialized names are part of the SDK contract; update fixtures when variants change.
348pub enum ProjectionMode {
349    /// Use this variant when the contract needs to represent not projected; selecting it has no side effect by itself.
350    NotProjected,
351    /// Use this variant when the contract needs to represent descriptor only; selecting it has no side effect by itself.
352    DescriptorOnly,
353    /// Use this variant when the contract needs to represent provider tool schema; selecting it has no side effect by itself.
354    ProviderToolSchema {
355        /// Typed schema ref reference. Resolving or executing it is a
356        /// separate policy-gated step.
357        schema_ref: PackageSidecarRef,
358    },
359    /// Use this variant when the contract needs to represent produces context items; selecting it has no side effect by itself.
360    ProducesContextItems {
361        /// Kinds this capability is allowed to produce.
362        /// Package validation uses the list to reject undeclared context item kinds.
363        allowed_kinds: Vec<String>,
364    },
365    /// Use this variant when the contract needs to represent projects context refs; selecting it has no side effect by itself.
366    ProjectsContextRefs {
367        /// Reference kinds this capability is allowed to project.
368        /// Projection validation uses the list to reject undeclared reference kinds.
369        allowed_ref_kinds: Vec<String>,
370    },
371}
372
373impl ProjectionMode {
374    /// Reports whether this value is projected. The check is pure and
375    /// does not mutate SDK or host state.
376    pub fn is_projected(&self) -> bool {
377        !matches!(self, Self::NotProjected)
378    }
379}
380
381#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
382/// Describes the package sidecar ref portion of a runtime package snapshot.
383/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
384pub struct PackageSidecarRef {
385    /// Identifier for the typed package sidecar.
386    pub sidecar_id: String,
387    /// Kind/category for this record, capability, event, or detected
388    /// resource.
389    pub kind: String,
390    /// Version string for this capability, package, or protocol surface.
391    /// Use it for compatibility checks during package or adapter resolution.
392    pub version: String,
393    #[serde(skip_serializing_if = "Option::is_none")]
394    /// Stable hash for the bytes or canonical payload used for stale checks
395    /// and fingerprints.
396    pub content_hash: Option<String>,
397}
398
399impl PackageSidecarRef {
400    /// Creates a new package::capability value with explicit
401    /// caller-provided inputs. This constructor is data-only and
402    /// performs no I/O or external side effects.
403    pub fn new(
404        sidecar_id: impl Into<String>,
405        kind: impl Into<String>,
406        version: impl Into<String>,
407    ) -> Self {
408        Self {
409            sidecar_id: sidecar_id.into(),
410            kind: kind.into(),
411            version: version.into(),
412            content_hash: None,
413        }
414    }
415}
416
417#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
418#[serde(rename_all = "snake_case")]
419/// Enumerates the finite capability readiness status cases.
420/// Serialized names are part of the SDK contract; update fixtures when variants change.
421pub enum CapabilityReadinessStatus {
422    /// Use this variant when the contract needs to represent active; selecting it has no side effect by itself.
423    Active,
424    /// Use this variant when the contract needs to represent reserved inactive; selecting it has no side effect by itself.
425    ReservedInactive,
426}
427
428#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
429/// Describes the capability readiness portion of a runtime package snapshot.
430/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
431pub struct CapabilityReadiness {
432    /// Finite status for this record or lifecycle stage.
433    pub status: CapabilityReadinessStatus,
434    /// Implementation owner responsible for this capability surface.
435    /// Use it to route follow-up work and validation ownership.
436    pub owner_role: String,
437    #[serde(skip_serializing_if = "Option::is_none")]
438    /// Sidecar contract that carries typed capability data.
439    /// Package validation uses it to connect capability specs to executable sidecars.
440    pub typed_sidecar_contract: Option<String>,
441    #[serde(default)]
442    /// Deterministic fingerprint fields used for stale checks, package
443    /// evidence, or replay comparisons.
444    pub fingerprint_fields: Vec<String>,
445    #[serde(default)]
446    /// Event kinds emitted by this capability or feature.
447    /// Use them to keep event fixtures and subscriptions aligned with the public contract.
448    pub emitted_events: Vec<String>,
449    #[serde(default)]
450    /// Journal record kinds produced by this capability or feature.
451    /// Use them to keep replay and recovery fixtures aligned with the public contract.
452    pub journal_records: Vec<String>,
453    #[serde(default)]
454    /// Acceptance tests that prove the capability contract is implemented.
455    /// Use them as release-readiness evidence before marking the capability active.
456    pub acceptance_tests: Vec<String>,
457}
458
459impl CapabilityReadiness {
460    /// Returns active tool for the current value.
461    /// This is a read-only or data-construction helper unless the method body explicitly calls
462    /// a port or store.
463    pub fn active_tool() -> Self {
464        Self {
465            status: CapabilityReadinessStatus::Active,
466            owner_role: CapabilityKind::Tool.owner_role().to_string(),
467            typed_sidecar_contract: Some(
468                "capability.tool.schema_ref.executor_ref.policy_ref".to_string(),
469            ),
470            fingerprint_fields: vec![
471                "capability_id".to_string(),
472                "kind".to_string(),
473                "namespace".to_string(),
474                "version".to_string(),
475                "projection".to_string(),
476                "executor_ref".to_string(),
477                "policy_ref".to_string(),
478                "sidecar_refs".to_string(),
479                "source".to_string(),
480            ],
481            emitted_events: vec![
482                "capability_loaded".to_string(),
483                "tool_requested".to_string(),
484                "tool_completed".to_string(),
485            ],
486            journal_records: vec![
487                "package_catalog_snapshot".to_string(),
488                "package_delta".to_string(),
489                "tool_execution_intent".to_string(),
490                "tool_execution_result".to_string(),
491            ],
492            acceptance_tests: vec![
493                "provider_visible_capability_requires_executor_and_policy_refs".to_string(),
494                "projection_and_execution_hashes_match".to_string(),
495            ],
496        }
497    }
498
499    /// Returns reserved for the current value.
500    /// This is a read-only or data-construction helper unless the method body explicitly calls
501    /// a port or store.
502    pub fn reserved(kind: &CapabilityKind) -> Self {
503        Self {
504            status: CapabilityReadinessStatus::ReservedInactive,
505            owner_role: kind.owner_role().to_string(),
506            typed_sidecar_contract: None,
507            fingerprint_fields: Vec::new(),
508            emitted_events: Vec::new(),
509            journal_records: Vec::new(),
510            acceptance_tests: Vec::new(),
511        }
512    }
513
514    /// Reports whether this value is active. The check is pure and does
515    /// not mutate SDK or host state.
516    pub fn is_active(&self) -> bool {
517        matches!(self.status, CapabilityReadinessStatus::Active)
518    }
519}
520
521#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
522/// Describes the capability spec portion of a runtime package snapshot.
523/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
524pub struct CapabilitySpec {
525    /// Stable capability identifier used for package projection and
526    /// executable routing.
527    pub capability_id: CapabilityId,
528    /// Kind/category for this record, capability, event, or detected
529    /// resource.
530    pub kind: CapabilityKind,
531    /// Source label or ref for this item; it is metadata and does not fetch
532    /// content by itself.
533    pub source: CapabilitySource,
534    /// Namespace that groups this capability or identifier.
535    /// Use it to avoid collisions between packages, hosts, and extensions.
536    pub namespace: CapabilityNamespace,
537    /// Version string for this capability, package, or protocol surface.
538    /// Use it for compatibility checks during package or adapter resolution.
539    pub version: CapabilityVersion,
540    /// Visibility class for the capability or result.
541    /// Discovery and projection use it to decide what callers or models can see.
542    pub visibility: CapabilityVisibility,
543    /// Projection controls for exposing data to a provider or subscriber.
544    /// Use it to keep provider-visible data separate from private SDK state.
545    pub projection: ProjectionMode,
546    #[serde(skip_serializing_if = "Option::is_none")]
547    /// Typed executor ref reference. Resolving or executing it is a separate
548    /// policy-gated step.
549    pub executor_ref: Option<ExecutorRef>,
550    /// Policy reference that must be resolved by the host or runtime before
551    /// execution.
552    pub policy_ref: PolicyRef,
553    #[serde(default, skip_serializing_if = "Vec::is_empty")]
554    /// References to typed package sidecars needed by this capability.
555    pub sidecar_refs: Vec<PackageSidecarRef>,
556    #[serde(skip_serializing_if = "Option::is_none")]
557    /// Typed isolation ref reference. Resolving or executing it is a separate
558    /// policy-gated step.
559    pub isolation_ref: Option<PackageSidecarRef>,
560    /// Privacy class used for projection, telemetry, and raw-content access
561    /// decisions.
562    pub privacy: PrivacyClass,
563    /// Readiness state for a capability or package feature.
564    /// Launch and package validation use it to distinguish active, reserved, and blocked
565    /// surfaces.
566    pub readiness: CapabilityReadiness,
567}
568
569impl CapabilitySpec {
570    /// Returns fake tool for the current value.
571    /// This is a read-only or data-construction helper unless the method body explicitly calls
572    /// a port or store.
573    pub fn fake_tool(
574        capability_id: impl Into<CapabilityId>,
575        name: impl Into<String>,
576        schema_ref: PackageSidecarRef,
577        executor_ref: ExecutorRef,
578        policy_ref: PolicyRef,
579        source: SourceRef,
580    ) -> Self {
581        let name = name.into();
582        Self {
583            capability_id: capability_id.into(),
584            kind: CapabilityKind::Tool,
585            source: CapabilitySource::test_fake(source),
586            namespace: CapabilityNamespace::new(format!("tool.{name}")),
587            version: CapabilityVersion::new("v1"),
588            visibility: CapabilityVisibility::Active,
589            projection: ProjectionMode::ProviderToolSchema { schema_ref },
590            executor_ref: Some(executor_ref),
591            policy_ref,
592            sidecar_refs: Vec::new(),
593            isolation_ref: None,
594            privacy: PrivacyClass::ContentRefsOnly,
595            readiness: CapabilityReadiness::active_tool(),
596        }
597    }
598
599    /// Builds the reserved inactive value with the documented defaults.
600    /// This is data-only and does not perform I/O, call host ports, append journals, publish
601    /// events, or start processes.
602    pub fn reserved_inactive(
603        capability_id: impl Into<CapabilityId>,
604        kind: CapabilityKind,
605        policy_ref: PolicyRef,
606        source: SourceRef,
607    ) -> Self {
608        let owner = kind.owner_role().replace('-', ".");
609        Self {
610            capability_id: capability_id.into(),
611            kind: kind.clone(),
612            source: CapabilitySource::test_fake(source),
613            namespace: CapabilityNamespace::new(format!("reserved.{owner}")),
614            version: CapabilityVersion::new("reserved"),
615            visibility: CapabilityVisibility::Hidden,
616            projection: ProjectionMode::NotProjected,
617            executor_ref: None,
618            policy_ref,
619            sidecar_refs: Vec::new(),
620            isolation_ref: None,
621            privacy: PrivacyClass::Internal,
622            readiness: CapabilityReadiness::reserved(&kind),
623        }
624    }
625
626    /// Computes or returns provider visible for the package::capability
627    /// contract without external I/O or side effects.
628    pub fn provider_visible(&self) -> bool {
629        self.visibility == CapabilityVisibility::Active && self.projection.is_projected()
630    }
631
632    /// Returns whether executable applies for this state.
633    /// This is data-only and does not perform I/O, call host ports, append journals, publish
634    /// events, or start processes.
635    pub fn executable(&self) -> bool {
636        self.executor_ref.is_some()
637    }
638
639    /// Computes or returns project for provider for the package::capability
640    /// contract without external I/O or side effects.
641    pub fn project_for_provider(&self) -> Result<Option<ProviderCapabilityProjection>, AgentError> {
642        self.validate()?;
643        if !self.provider_visible() {
644            return Ok(None);
645        }
646        Ok(Some(ProviderCapabilityProjection {
647            capability_id: self.capability_id.clone(),
648            namespace: self.namespace.clone(),
649            projection: self.projection.clone(),
650            policy_ref: self.policy_ref.clone(),
651        }))
652    }
653
654    /// Computes or returns executable route for the package::capability
655    /// contract without external I/O or side effects.
656    pub fn executable_route(&self) -> Result<Option<ExecutableCapabilityRoute>, AgentError> {
657        self.validate()?;
658        let Some(executor_ref) = self.executor_ref.clone() else {
659            return Ok(None);
660        };
661        Ok(Some(ExecutableCapabilityRoute {
662            capability_id: self.capability_id.clone(),
663            executor_ref,
664            policy_ref: self.policy_ref.clone(),
665        }))
666    }
667
668    /// Validates the package::capability invariants and returns a typed
669    /// error on failure. Validation is pure and does not perform I/O,
670    /// dispatch, journal appends, or adapter calls.
671    pub fn validate(&self) -> Result<(), AgentError> {
672        if self.policy_ref.as_str().is_empty() {
673            return Err(AgentError::missing_required_field("capability.policy_ref"));
674        }
675        if self.kind.is_reserved() && self.readiness.is_active() {
676            return Err(AgentError::contract_violation(format!(
677                "reserved capability {} cannot be active until its owner supplies sidecar, fingerprint, event, journal, and acceptance-test evidence",
678                self.capability_id.as_str()
679            )));
680        }
681        if self.kind.is_reserved() && self.projection.is_projected() {
682            return Err(AgentError::contract_violation(format!(
683                "reserved capability {} cannot be projected while inactive",
684                self.capability_id.as_str()
685            )));
686        }
687        if self.kind.is_reserved() && self.executor_ref.is_some() {
688            return Err(AgentError::contract_violation(format!(
689                "reserved capability {} cannot execute while inactive",
690                self.capability_id.as_str()
691            )));
692        }
693        if self.provider_visible() && self.executor_ref.is_none() {
694            return Err(AgentError::missing_required_field(
695                "provider_visible_capability.executor_ref",
696            ));
697        }
698        if self.provider_visible() && self.policy_ref.kind == PolicyKind::Host {
699            return Err(AgentError::contract_violation(
700                "provider-visible capability must use an explicit non-host policy ref",
701            ));
702        }
703        Ok(())
704    }
705}
706
707#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
708/// Describes the provider capability projection portion of a runtime package snapshot.
709/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
710pub struct ProviderCapabilityProjection {
711    /// Stable capability identifier used for package projection and
712    /// executable routing.
713    pub capability_id: CapabilityId,
714    /// Namespace that groups this capability or identifier.
715    /// Use it to avoid collisions between packages, hosts, and extensions.
716    pub namespace: CapabilityNamespace,
717    /// Projection controls for exposing data to a provider or subscriber.
718    /// Use it to keep provider-visible data separate from private SDK state.
719    pub projection: ProjectionMode,
720    /// Policy reference that must be resolved by the host or runtime before
721    /// execution.
722    pub policy_ref: PolicyRef,
723}
724
725#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
726/// Describes the executable capability route portion of a runtime package snapshot.
727/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
728pub struct ExecutableCapabilityRoute {
729    /// Stable capability identifier used for package projection and
730    /// executable routing.
731    pub capability_id: CapabilityId,
732    /// Typed executor ref reference. Resolving or executing it is a separate
733    /// policy-gated step.
734    pub executor_ref: ExecutorRef,
735    /// Policy reference that must be resolved by the host or runtime before
736    /// execution.
737    pub policy_ref: PolicyRef,
738}