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}