Skip to main content

agent_sdk_core/package/
mod.rs

1//! Runtime package authority for one run. Use this module to freeze provider routes,
2//! capabilities, sidecars, catalogs, policies, output sinks, and fingerprints before
3//! execution. Package builders are data-only; applying deltas returns a new snapshot
4//! rather than mutating ambient state.
5//!
6use serde::{Deserialize, Serialize};
7use sha2::{Digest, Sha256};
8
9use crate::{
10    capability::{
11        CapabilityId, CapabilityKind, CapabilitySpec, ExecutableCapabilityRoute, PackageSidecarRef,
12        ProviderCapabilityProjection,
13    },
14    domain::{
15        AgentError, AgentId, OutputSchemaId, PolicyRef, RuntimePackageId, SourceRef, TrustClass,
16    },
17    output::{OutputContract, OutputMode, OutputSchemaDialect, ProviderHintPolicy, SchemaVersion},
18};
19
20/// Public realtime namespace. Use it for the documented realtime API
21/// surface; prefer crate-root re-exports for common imports. Module
22/// items must preserve the core ownership and side-effect boundaries
23/// described in this file.
24pub mod realtime;
25/// Public stream namespace. Use it for the documented stream API
26/// surface; prefer crate-root re-exports for common imports. Module
27/// items must preserve the core ownership and side-effect boundaries
28/// described in this file.
29pub mod stream;
30/// Public subagent namespace. Use it for the documented subagent API
31/// surface; prefer crate-root re-exports for common imports. Module
32/// items must preserve the core ownership and side-effect boundaries
33/// described in this file.
34pub mod subagent;
35/// Public tool pack namespace. Use it for the documented tool pack API
36/// surface; prefer crate-root re-exports for common imports. Module
37/// items must preserve the core ownership and side-effect boundaries
38/// described in this file.
39pub mod tool_pack;
40
41pub use crate::package_isolation::IsolationRequirementSnapshot;
42pub use subagent::{
43    ChildPackageStripManifest, ChildRuntimePackage, ChildRuntimePackagePolicy,
44    ContextHandoffPolicy, DepthBudget, RouteInheritanceMode, SubagentRoutePolicy,
45    SubagentToolPolicy, build_child_runtime_package,
46};
47
48/// Constant value for the package contract. Use it to keep SDK records
49/// and tests aligned on the same stable value.
50pub const RUNTIME_PACKAGE_SCHEMA_VERSION: u16 = 1;
51/// Constant value for the package contract. Use it to keep SDK records
52/// and tests aligned on the same stable value.
53pub const RUNTIME_PACKAGE_FINGERPRINT_ALGORITHM: &str = "sha256:runtime-package-canonical-v1";
54
55#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
56/// Describes the runtime package portion of a runtime package snapshot.
57/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
58pub struct RuntimePackage {
59    /// Wire schema version used for compatibility checks.
60    pub schema_version: u16,
61    /// Runtime package identifier for the immutable per-run package snapshot.
62    pub package_id: RuntimePackageId,
63    /// Agent snapshot frozen into this package or record.
64    pub agent: AgentSnapshot,
65    /// Provider route snapshot selected for this runtime package.
66    pub provider_route: ProviderRouteSnapshot,
67    /// Provider capability hints frozen into this package snapshot.
68    pub provider_capabilities: ProviderCapabilitySnapshot,
69    #[serde(default)]
70    /// Output contracts frozen into this package or request.
71    pub output_contracts: Vec<OutputContractSnapshot>,
72    #[serde(default)]
73    /// Output sink snapshots available to this run.
74    pub output_sinks: Vec<OutputSinkSnapshot>,
75    #[serde(default)]
76    /// Capabilities frozen into the package or returned by an adapter health
77    /// check.
78    pub capabilities: Vec<CapabilitySpec>,
79    #[serde(default)]
80    /// Typed sidecar snapshots included in a package or delta.
81    pub sidecars: Vec<PackageSidecarSnapshot>,
82    #[serde(default)]
83    /// Isolation requirements frozen into the package snapshot.
84    pub isolation_requirements: Vec<IsolationRequirementSnapshot>,
85    #[serde(default)]
86    /// Catalog snapshots contributed to or returned with a runtime package
87    /// delta.
88    pub catalogs: Vec<CapabilityCatalogSnapshot>,
89    /// Child-run lifecycle policy frozen into the package snapshot.
90    pub child_lifecycle: ChildLifecyclePolicySnapshot,
91    /// Policies used by this record or request.
92    pub policies: PolicySnapshot,
93    /// Manifest describing which fields entered or were excluded from
94    /// fingerprinting.
95    pub fingerprint_manifest: FingerprintInputManifest,
96    #[serde(default, skip_serializing_if = "VolatileRuntimeFields::is_empty")]
97    /// Volatile used by this record or request.
98    pub volatile: VolatileRuntimeFields,
99}
100
101impl RuntimePackage {
102    /// Starts a builder for this package value. Building is data-only;
103    /// runtime side effects occur only when a later coordinator or host
104    /// port executes the built configuration.
105    pub fn builder(package_id: RuntimePackageId) -> RuntimePackageBuilder {
106        RuntimePackageBuilder::new(package_id)
107    }
108
109    /// Returns for agent for the current value.
110    /// This is a read-only or data-construction helper unless the method body explicitly calls
111    /// a port or store.
112    pub fn for_agent(agent_id: AgentId, agent_name: impl Into<String>) -> RuntimePackageBuilder {
113        RuntimePackageBuilder::new(RuntimePackageId::new(format!(
114            "package.{}",
115            agent_id.as_str()
116        )))
117        .agent(AgentSnapshot {
118            agent_id,
119            name: agent_name.into(),
120            default_behavior_refs: Vec::new(),
121        })
122    }
123
124    /// Computes the stable canonical snapshot for this package value.
125    /// The computation is deterministic and side-effect free so it can
126    /// be used in package, journal, or test evidence.
127    pub fn canonical_snapshot(&self) -> Result<RuntimePackageCanonicalV1, AgentError> {
128        self.validate()?;
129        Ok(RuntimePackageCanonicalV1 {
130            schema_version: self.schema_version,
131            package_id: self.package_id.clone(),
132            agent: canonical_agent(self.agent.clone()),
133            provider_route: self.provider_route.clone(),
134            provider_capabilities: self.provider_capabilities.clone(),
135            output_contracts: sorted_by_key(self.output_contracts.clone(), |item| {
136                item.schema_id.as_str().to_string()
137            }),
138            output_sinks: sorted_by_key(self.output_sinks.clone(), |item| item.sink_id.clone()),
139            capabilities: sorted_by_key(
140                self.capabilities
141                    .clone()
142                    .into_iter()
143                    .map(canonical_capability)
144                    .collect(),
145                |item| item.capability_id.as_str().to_string(),
146            ),
147            sidecars: sorted_by_key(
148                self.sidecars
149                    .clone()
150                    .into_iter()
151                    .map(canonical_sidecar)
152                    .collect(),
153                |item| item.sidecar_id.clone(),
154            ),
155            isolation_requirements: sorted_by_key(self.isolation_requirements.clone(), |item| {
156                item.requirement_ref.as_str().to_string()
157            }),
158            catalogs: sorted_by_key(
159                self.catalogs
160                    .clone()
161                    .into_iter()
162                    .map(canonical_catalog)
163                    .collect(),
164                |item| item.catalog_id.clone(),
165            ),
166            child_lifecycle: self.child_lifecycle.clone(),
167            policies: self.policies.clone_sorted(),
168            fingerprint_manifest: self.computed_fingerprint_manifest(),
169        })
170    }
171
172    /// Computes the stable fingerprint for this package value. The
173    /// computation is deterministic and side-effect free so it can be
174    /// used in package, journal, or test evidence.
175    pub fn fingerprint(&self) -> Result<RuntimePackageFingerprint, AgentError> {
176        let canonical = self.canonical_snapshot()?;
177        let preimage = serde_json::json!({
178            "algorithm": RUNTIME_PACKAGE_FINGERPRINT_ALGORITHM,
179            "canonical_schema_version": RUNTIME_PACKAGE_SCHEMA_VERSION,
180            "snapshot": canonical,
181        });
182        let bytes = serde_json::to_vec(&crate::domain::json::normalize_json_value(preimage))
183            .map_err(|error| {
184                AgentError::contract_violation(format!(
185                    "package fingerprint serialization failed: {error}"
186                ))
187            })?;
188        let digest = Sha256::digest(bytes);
189        Ok(RuntimePackageFingerprint(format!(
190            "{RUNTIME_PACKAGE_FINGERPRINT_ALGORITHM}:{}",
191            hex_lower(&digest)
192        )))
193    }
194
195    /// Validates the package invariants and returns a typed error on
196    /// failure. Validation is pure and does not perform I/O, dispatch,
197    /// journal appends, or adapter calls.
198    pub fn validate(&self) -> Result<(), AgentError> {
199        if self.schema_version != RUNTIME_PACKAGE_SCHEMA_VERSION {
200            return Err(AgentError::contract_violation(format!(
201                "unsupported runtime package schema version {}",
202                self.schema_version
203            )));
204        }
205        if self.provider_route.route_id.is_empty() {
206            return Err(AgentError::missing_required_field(
207                "provider_route.route_id",
208            ));
209        }
210        if self.provider_route.model_id.is_empty() {
211            return Err(AgentError::missing_required_field(
212                "provider_route.model_id",
213            ));
214        }
215
216        for capability in &self.capabilities {
217            capability.validate()?;
218        }
219        for output_contract in &self.output_contracts {
220            output_contract.validate()?;
221        }
222        for isolation_requirement in &self.isolation_requirements {
223            isolation_requirement.validate()?;
224        }
225
226        let projected_ids = self
227            .provider_tool_specs()?
228            .into_iter()
229            .map(|projection| projection.capability_id)
230            .collect::<Vec<_>>();
231        let executable_ids = self
232            .executable_routes()?
233            .into_iter()
234            .map(|route| route.capability_id)
235            .collect::<Vec<_>>();
236        for projected_id in projected_ids {
237            if !executable_ids.contains(&projected_id) {
238                return Err(AgentError::contract_violation(format!(
239                    "projected capability {} has no executable route in the same runtime package",
240                    projected_id.as_str()
241                )));
242            }
243        }
244
245        Ok(())
246    }
247
248    /// Returns provider tool specs for the current value.
249    /// This is a read-only or data-construction helper unless the method body explicitly calls
250    /// a port or store.
251    pub fn provider_tool_specs(&self) -> Result<Vec<ProviderCapabilityProjection>, AgentError> {
252        self.capabilities
253            .iter()
254            .filter_map(|capability| capability.project_for_provider().transpose())
255            .collect()
256    }
257
258    /// Returns executable routes for the current value.
259    /// This is a read-only or data-construction helper unless the method body explicitly calls
260    /// a port or store.
261    pub fn executable_routes(&self) -> Result<Vec<ExecutableCapabilityRoute>, AgentError> {
262        self.capabilities
263            .iter()
264            .filter_map(|capability| capability.executable_route().transpose())
265            .collect()
266    }
267
268    /// Returns sidecar for the current value.
269    /// This is a read-only or data-construction helper unless the method body explicitly calls
270    /// a port or store.
271    pub fn sidecar(&self, sidecar_id: &str) -> Option<&PackageSidecarSnapshot> {
272        self.sidecars
273            .iter()
274            .find(|sidecar| sidecar.sidecar_id == sidecar_id)
275    }
276
277    /// Returns this value with its output contract setting replaced.
278    /// The method follows builder-style data construction and does not
279    /// execute external work.
280    pub fn with_output_contract(
281        mut self,
282        output_contract: &OutputContract,
283    ) -> Result<Self, AgentError> {
284        output_contract.validate_shape()?;
285        let snapshot = OutputContractSnapshot::from(output_contract);
286        self.output_contracts
287            .retain(|existing| existing.schema_id != snapshot.schema_id);
288        self.output_contracts.push(snapshot);
289        self.fingerprint_manifest = self.computed_fingerprint_manifest();
290        self.validate()?;
291        Ok(self)
292    }
293
294    /// Returns catalog for the current value.
295    /// This is a read-only or data-construction helper unless the method body explicitly calls
296    /// a port or store.
297    pub fn catalog(&self, catalog_id: &str) -> Option<&CapabilityCatalogSnapshot> {
298        self.catalogs
299            .iter()
300            .find(|catalog| catalog.catalog_id == catalog_id)
301    }
302
303    /// Validates a package delta against this snapshot and returns a new
304    /// runtime package. The method is pure with respect to the existing
305    /// package and does not mutate ambient registries or execute activated
306    /// capabilities.
307    pub fn apply_delta(&self, delta: PackageDelta) -> Result<Self, AgentError> {
308        if delta.previous_fingerprint != self.fingerprint()? {
309            return Err(AgentError::contract_violation(
310                "package delta previous fingerprint does not match current package",
311            ));
312        }
313
314        let mut next = self.clone();
315        for capability_id in delta.deactivated_capability_ids {
316            next.capabilities
317                .retain(|capability| capability.capability_id != capability_id);
318        }
319        next.capabilities.extend(delta.activated_capabilities);
320        next.catalogs.extend(delta.catalogs);
321        next.sidecars.extend(delta.sidecars);
322        next.fingerprint_manifest = next.computed_fingerprint_manifest();
323        next.validate()?;
324        Ok(next)
325    }
326
327    /// Returns conformance report for the current value.
328    /// This is a read-only or data-construction helper unless the method body explicitly calls
329    /// a port or store.
330    pub fn conformance_report(&self) -> Result<RuntimePackageConformanceReport, AgentError> {
331        let fingerprint = self.fingerprint()?;
332        Ok(RuntimePackageConformanceReport {
333            fingerprint,
334            provider_projection_count: self.provider_tool_specs()?.len(),
335            executable_route_count: self.executable_routes()?.len(),
336            reserved_inactive_count: self
337                .capabilities
338                .iter()
339                .filter(|capability| capability.kind.is_reserved())
340                .count(),
341            catalog_count: self.catalogs.len(),
342            sidecar_count: self.sidecars.len(),
343        })
344    }
345
346    fn computed_fingerprint_manifest(&self) -> FingerprintInputManifest {
347        FingerprintInputManifest {
348            algorithm: RUNTIME_PACKAGE_FINGERPRINT_ALGORITHM.to_string(),
349            canonical_schema_version: RUNTIME_PACKAGE_SCHEMA_VERSION,
350            readiness_profile: self.fingerprint_manifest.readiness_profile.clone(),
351            included_groups: vec![
352                FingerprintInputGroup::Agent,
353                FingerprintInputGroup::ProviderRoute,
354                FingerprintInputGroup::OutputContracts,
355                FingerprintInputGroup::OutputSinks,
356                FingerprintInputGroup::Capabilities,
357                FingerprintInputGroup::Sidecars,
358                FingerprintInputGroup::IsolationRequirements,
359                FingerprintInputGroup::Catalogs,
360                FingerprintInputGroup::ChildLifecycle,
361                FingerprintInputGroup::Policies,
362            ],
363            excluded_groups: vec![
364                FingerprintExclusionGroup::RunIds,
365                FingerprintExclusionGroup::EventIds,
366                FingerprintExclusionGroup::Timestamps,
367                FingerprintExclusionGroup::AdapterHealth,
368                FingerprintExclusionGroup::TemporaryPaths,
369                FingerprintExclusionGroup::TelemetrySinkHealth,
370            ],
371            reserved_feature_status: self
372                .capabilities
373                .iter()
374                .filter(|capability| capability.kind.is_reserved())
375                .map(|capability| ReservedFeatureFingerprintStatus {
376                    capability_id: capability.capability_id.clone(),
377                    kind: capability.kind.clone(),
378                    owner_role: capability.readiness.owner_role.clone(),
379                    status: "reserved_inactive".to_string(),
380                    reason: "owner workstream has not supplied sidecar, fingerprint, event, journal, and acceptance-test evidence".to_string(),
381                })
382                .collect::<Vec<_>>()
383                .canonicalized_reserved_status(),
384        }
385    }
386}
387
388#[derive(Clone, Debug)]
389/// Describes the runtime package builder portion of a runtime package snapshot.
390/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
391pub struct RuntimePackageBuilder {
392    package: RuntimePackage,
393}
394
395impl RuntimePackageBuilder {
396    /// Creates a new package value with explicit caller-provided
397    /// inputs. This constructor is data-only and performs no I/O or
398    /// external side effects.
399    pub fn new(package_id: RuntimePackageId) -> Self {
400        let package = RuntimePackage {
401            schema_version: RUNTIME_PACKAGE_SCHEMA_VERSION,
402            package_id,
403            agent: AgentSnapshot {
404                agent_id: AgentId::new("agent.default"),
405                name: "agent".to_string(),
406                default_behavior_refs: Vec::new(),
407            },
408            provider_route: ProviderRouteSnapshot::new("provider.fake", "model.fake"),
409            provider_capabilities: ProviderCapabilitySnapshot {
410                capability_version: "provider.capabilities.v1".to_string(),
411                realtime_capability_version: None,
412            },
413            output_contracts: Vec::new(),
414            output_sinks: Vec::new(),
415            capabilities: Vec::new(),
416            sidecars: Vec::new(),
417            isolation_requirements: Vec::new(),
418            catalogs: Vec::new(),
419            child_lifecycle: ChildLifecyclePolicySnapshot::safe_defaults(),
420            policies: PolicySnapshot::default(),
421            fingerprint_manifest: FingerprintInputManifest::p0_text(),
422            volatile: VolatileRuntimeFields::default(),
423        };
424        Self { package }
425    }
426
427    /// Returns agent for the current value.
428    /// This is a read-only or data-construction helper unless the method body explicitly calls
429    /// a port or store.
430    pub fn agent(mut self, agent: AgentSnapshot) -> Self {
431        self.package.agent = agent;
432        self
433    }
434
435    /// Returns provider route for the current value.
436    /// This is a read-only or data-construction helper unless the method body explicitly calls
437    /// a port or store.
438    pub fn provider_route(mut self, provider_route: ProviderRouteSnapshot) -> Self {
439        self.package.provider_route = provider_route;
440        self
441    }
442
443    /// Returns output contract for the current value.
444    /// This is a read-only or data-construction helper unless the method body explicitly calls
445    /// a port or store.
446    pub fn output_contract(mut self, output_contract: OutputContractSnapshot) -> Self {
447        self.package.output_contracts.push(output_contract);
448        self
449    }
450
451    /// Returns output sink for the current value.
452    /// This is a read-only or data-construction helper unless the method body explicitly calls
453    /// a port or store.
454    pub fn output_sink(mut self, output_sink: OutputSinkSnapshot) -> Self {
455        self.package.output_sinks.push(output_sink);
456        self
457    }
458
459    /// Returns capability for the current value.
460    /// This is a read-only or data-construction helper unless the method body explicitly calls
461    /// a port or store.
462    pub fn capability(mut self, capability: CapabilitySpec) -> Self {
463        self.package.capabilities.push(capability);
464        self
465    }
466
467    /// Returns sidecar for the current value.
468    /// This is a read-only or data-construction helper unless the method body explicitly calls
469    /// a port or store.
470    pub fn sidecar(mut self, sidecar: PackageSidecarSnapshot) -> Self {
471        self.package.sidecars.push(sidecar);
472        self
473    }
474
475    /// Returns catalog for the current value.
476    /// This is a read-only or data-construction helper unless the method body explicitly calls
477    /// a port or store.
478    pub fn catalog(mut self, catalog: CapabilityCatalogSnapshot) -> Self {
479        self.package.catalogs.push(catalog);
480        self
481    }
482
483    /// Returns isolation requirement for the current value.
484    /// This is a read-only or data-construction helper unless the method body explicitly calls
485    /// a port or store.
486    pub fn isolation_requirement(mut self, snapshot: IsolationRequirementSnapshot) -> Self {
487        self.package.isolation_requirements.push(snapshot);
488        self
489    }
490
491    /// Returns policy for the current value.
492    /// This is a read-only or data-construction helper unless the method body explicitly calls
493    /// a port or store.
494    pub fn policy(mut self, policy_ref: PolicyRef) -> Self {
495        self.package.policies.policy_refs.push(policy_ref);
496        self
497    }
498
499    /// Finishes builder validation and returns the configured value.
500    /// This is data-only unless the surrounding builder explicitly
501    /// documents adapter or store access.
502    pub fn build(mut self) -> Result<RuntimePackage, AgentError> {
503        self.package.fingerprint_manifest = self.package.computed_fingerprint_manifest();
504        self.package.validate()?;
505        Ok(self.package)
506    }
507}
508
509#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
510/// Describes the runtime package canonical v1 portion of a runtime package snapshot.
511/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
512pub struct RuntimePackageCanonicalV1 {
513    /// Wire schema version used for compatibility checks.
514    pub schema_version: u16,
515    /// Runtime package identifier for the immutable per-run package snapshot.
516    pub package_id: RuntimePackageId,
517    /// Agent snapshot frozen into this package or record.
518    pub agent: AgentSnapshot,
519    /// Provider route snapshot selected for this runtime package.
520    pub provider_route: ProviderRouteSnapshot,
521    /// Provider capability hints frozen into this package snapshot.
522    pub provider_capabilities: ProviderCapabilitySnapshot,
523    /// Output contracts frozen into this package or request.
524    pub output_contracts: Vec<OutputContractSnapshot>,
525    /// Output sink snapshots available to this run.
526    pub output_sinks: Vec<OutputSinkSnapshot>,
527    /// Capabilities frozen into the package or returned by an adapter health
528    /// check.
529    pub capabilities: Vec<CapabilitySpec>,
530    /// Typed sidecar snapshots included in a package or delta.
531    pub sidecars: Vec<PackageSidecarSnapshot>,
532    /// Isolation requirements frozen into the package snapshot.
533    pub isolation_requirements: Vec<IsolationRequirementSnapshot>,
534    /// Catalog snapshots contributed to or returned with a runtime package
535    /// delta.
536    pub catalogs: Vec<CapabilityCatalogSnapshot>,
537    /// Child-run lifecycle policy frozen into the package snapshot.
538    pub child_lifecycle: ChildLifecyclePolicySnapshot,
539    /// Policies used by this record or request.
540    pub policies: PolicySnapshot,
541    /// Manifest describing which fields entered or were excluded from
542    /// fingerprinting.
543    pub fingerprint_manifest: FingerprintInputManifest,
544}
545
546#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
547/// Describes the runtime package fingerprint portion of a runtime package snapshot.
548/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
549pub struct RuntimePackageFingerprint(pub String);
550
551impl RuntimePackageFingerprint {
552    /// Returns this value as str. The accessor is side-effect free and
553    /// keeps ownership with the caller.
554    pub fn as_str(&self) -> &str {
555        &self.0
556    }
557}
558
559#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
560/// Describes the agent snapshot portion of a runtime package snapshot.
561/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
562pub struct AgentSnapshot {
563    /// Agent identifier used for lineage, filtering, and ownership checks.
564    pub agent_id: AgentId,
565    /// Human-readable or protocol-visible name for this SDK item.
566    pub name: String,
567    #[serde(default)]
568    /// Typed default behavior refs references. Resolving them is separate
569    /// from constructing this record.
570    pub default_behavior_refs: Vec<PolicyRef>,
571}
572
573#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
574/// Describes the provider route snapshot portion of a runtime package snapshot.
575/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
576pub struct ProviderRouteSnapshot {
577    /// Stable route id used for typed lineage, lookup, or dedupe.
578    pub route_id: String,
579    /// Stable model id used for typed lineage, lookup, or dedupe.
580    pub model_id: String,
581    #[serde(skip_serializing_if = "Option::is_none")]
582    /// Policy reference governing provider projection or calls.
583    pub provider_policy_ref: Option<PolicyRef>,
584}
585
586impl ProviderRouteSnapshot {
587    /// Creates a new package value with explicit caller-provided
588    /// inputs. This constructor is data-only and performs no I/O or
589    /// external side effects.
590    pub fn new(route_id: impl Into<String>, model_id: impl Into<String>) -> Self {
591        Self {
592            route_id: route_id.into(),
593            model_id: model_id.into(),
594            provider_policy_ref: None,
595        }
596    }
597}
598
599#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
600/// Describes the provider capability snapshot portion of a runtime package snapshot.
601/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
602pub struct ProviderCapabilitySnapshot {
603    /// Capability version advertised by the provider or package.
604    /// Use it to match compatible feature contracts during package resolution.
605    pub capability_version: String,
606    #[serde(skip_serializing_if = "Option::is_none")]
607    /// Optional realtime capability version advertised by the package.
608    /// Package resolution can use it to match compatible realtime sidecars and adapters.
609    pub realtime_capability_version: Option<String>,
610}
611
612#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
613/// Describes the output contract snapshot portion of a runtime package snapshot.
614/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
615pub struct OutputContractSnapshot {
616    /// Stable schema id used for typed lineage, lookup, or dedupe.
617    pub schema_id: OutputSchemaId,
618    /// Wire schema version used for compatibility checks.
619    pub schema_version: SchemaVersion,
620    /// Deterministic schema fingerprint used for stale checks, package
621    /// evidence, or replay comparisons.
622    pub schema_fingerprint: String,
623    /// Schema dialect used to interpret the output schema.
624    /// Validators use it to select the supported JSON-schema subset and compatibility rules.
625    pub dialect: OutputSchemaDialect,
626    /// Mode that selects how this operation or contract should behave.
627    /// Callers use it to choose the explicit execution path instead of relying on hidden
628    /// defaults.
629    pub mode: OutputMode,
630    /// Typed validation policy ref reference. Resolving or executing it is a
631    /// separate policy-gated step.
632    pub validation_policy_ref: PolicyRef,
633    /// Typed repair policy ref reference. Resolving or executing it is a
634    /// separate policy-gated step.
635    pub repair_policy_ref: PolicyRef,
636    /// Version of the local validator contract used for this output policy.
637    /// Use it to keep validation and replay behavior stable across releases.
638    pub local_validator_version: String,
639    /// Policy for provider-side structured-output hints.
640    /// Hints may guide prompting but cannot replace SDK-owned validation.
641    pub provider_hint_policy: ProviderHintPolicy,
642    /// Validation policy applied before output is accepted as typed data.
643    /// It controls validator selection, bounds, failure visibility, and local validation
644    /// behavior.
645    pub validation: crate::output::ValidationPolicy,
646    /// Repair policy used after structured output validation fails.
647    /// It controls whether repair is attempted and which policy gates must approve it.
648    pub repair: crate::output::RepairPolicy,
649    /// Retry budget for validation, repair, or adapter attempts.
650    /// Runtimes use it to stop bounded loops deterministically.
651    pub retry_budget: crate::output::RetryBudget,
652    /// Content-capture policy that governs raw content, summaries, redaction, and retention.
653    /// Projection, telemetry, and delivery paths must honor it before exposing content.
654    pub content_policy: crate::policy::ContentCapturePolicy,
655    /// Provider-facing projection hint for structured output requests.
656    /// It can guide model prompting but does not replace local validation policy.
657    pub projection_hint: crate::output::OutputProjectionHint,
658}
659
660impl OutputContractSnapshot {
661    /// Validates the package invariants and returns a typed error on
662    /// failure. Validation is pure and does not perform I/O, dispatch,
663    /// journal appends, or adapter calls.
664    pub fn validate(&self) -> Result<(), AgentError> {
665        if !self.schema_fingerprint.starts_with("sha256:") {
666            return Err(AgentError::contract_violation(
667                "output contract snapshot schema_fingerprint must be sha256-prefixed",
668            ));
669        }
670        Ok(())
671    }
672}
673
674impl From<&OutputContract> for OutputContractSnapshot {
675    fn from(contract: &OutputContract) -> Self {
676        Self {
677            schema_id: contract.schema_id.clone(),
678            schema_version: contract.schema_version,
679            schema_fingerprint: contract.schema_fingerprint().as_str().to_string(),
680            dialect: contract.dialect.clone(),
681            mode: contract.mode.clone(),
682            validation_policy_ref: contract.validation.validator_ref_policy(),
683            repair_policy_ref: contract.repair.repair_adapter_ref_policy(),
684            local_validator_version: contract.validation.validator_ref.as_str().to_string(),
685            provider_hint_policy: contract.projection_hint.provider_hint_policy.clone(),
686            validation: contract.validation.clone(),
687            repair: contract.repair.clone(),
688            retry_budget: contract.retry_budget.clone(),
689            content_policy: contract.content_policy.clone(),
690            projection_hint: contract.projection_hint.clone(),
691        }
692    }
693}
694
695#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
696/// Describes the output sink snapshot portion of a runtime package snapshot.
697/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
698pub struct OutputSinkSnapshot {
699    /// Stable sink id used for typed lineage, lookup, or dedupe.
700    pub sink_id: String,
701    /// Typed delivery policy ref reference. Resolving or executing it is a
702    /// separate policy-gated step.
703    pub delivery_policy_ref: PolicyRef,
704    /// Dedupe policy or key for a side-effecting operation.
705    /// Replay and repair use it to avoid sending or executing the same effect twice.
706    pub dedupe_policy: String,
707    /// Capability version advertised by an output sink.
708    /// Delivery policy uses it to confirm that the sink can receive the requested payload
709    /// shape.
710    pub sink_capability_version: String,
711}
712
713#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
714/// Describes the package sidecar snapshot portion of a runtime package snapshot.
715/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
716pub struct PackageSidecarSnapshot {
717    /// Identifier for the typed package sidecar.
718    pub sidecar_id: String,
719    /// Kind/category for this record, capability, event, or detected
720    /// resource.
721    pub kind: String,
722    /// Version string for this capability, package, or protocol surface.
723    /// Use it for compatibility checks during package or adapter resolution.
724    pub version: String,
725    /// References associated with refs.
726    /// Resolve them through the appropriate registry or content store before using referenced
727    /// data.
728    pub refs: Vec<PackageSidecarRef>,
729    /// Policy references that govern admission, projection, execution, or
730    /// delivery.
731    pub policy_refs: Vec<PolicyRef>,
732    /// Stable hash for the bytes or canonical payload used for stale checks
733    /// and fingerprints.
734    pub content_hash: String,
735}
736
737#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
738/// Describes the child lifecycle policy snapshot portion of a runtime package snapshot.
739/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
740pub struct ChildLifecyclePolicySnapshot {
741    /// Policy reference that must be resolved by the host or runtime before
742    /// execution.
743    pub policy_ref: PolicyRef,
744    /// Manual cancellation policy for child or run lifecycle.
745    /// Use it to decide whether cancellation cascades, detaches, or requires explicit cleanup.
746    pub manual_cancel: String,
747    /// Typed detach policy ref reference. Resolving or executing it is a
748    /// separate policy-gated step.
749    pub detach_policy_ref: PolicyRef,
750    /// cleanup timeout ms duration in milliseconds.
751    pub cleanup_timeout_ms: u64,
752}
753
754impl ChildLifecyclePolicySnapshot {
755    /// Returns an updated value with safe defaults configured.
756    /// This is data-only and does not perform I/O, call host ports, append journals, publish
757    /// events, or start processes.
758    pub fn safe_defaults() -> Self {
759        Self {
760            policy_ref: PolicyRef::with_kind(
761                crate::domain::PolicyKind::RuntimePackage,
762                "policy.child.safe-defaults",
763            ),
764            manual_cancel: "cancel_agent_owned_children".to_string(),
765            detach_policy_ref: PolicyRef::with_kind(
766                crate::domain::PolicyKind::RuntimePackage,
767                "policy.detach.deny-by-default",
768            ),
769            cleanup_timeout_ms: 30_000,
770        }
771    }
772}
773
774#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
775/// Describes the policy snapshot portion of a runtime package snapshot.
776/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
777pub struct PolicySnapshot {
778    #[serde(default)]
779    /// Policy references that govern admission, projection, execution, or
780    /// delivery.
781    pub policy_refs: Vec<PolicyRef>,
782}
783
784impl PolicySnapshot {
785    fn clone_sorted(&self) -> Self {
786        Self {
787            policy_refs: sorted_policy_refs(self.policy_refs.clone()),
788        }
789    }
790}
791
792#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
793/// Describes the capability catalog snapshot portion of a runtime package snapshot.
794/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
795pub struct CapabilityCatalogSnapshot {
796    /// Stable catalog id used for typed lineage, lookup, or dedupe.
797    pub catalog_id: String,
798    /// Kind discriminator for source kind.
799    /// Use it to route finite match arms without parsing display text.
800    pub source_kind: crate::capability::CapabilitySourceKind,
801    /// Typed source reference that records where this item originated.
802    pub source_ref: SourceRef,
803    #[serde(skip_serializing_if = "Option::is_none")]
804    /// Version string for this capability, package, or protocol surface.
805    /// Use it for compatibility checks during package or adapter resolution.
806    pub version: Option<String>,
807    #[serde(skip_serializing_if = "Option::is_none")]
808    /// Stable hash for the bytes or canonical payload used for stale checks
809    /// and fingerprints.
810    pub content_hash: Option<String>,
811    /// Trust state used by this record or request.
812    pub trust_state: TrustClass,
813    /// Typed activation policy ref reference. Resolving or executing it is a
814    /// separate policy-gated step.
815    pub activation_policy_ref: PolicyRef,
816    #[serde(default)]
817    /// Candidate capabilities, tools, resources, or package entries exposed
818    /// for host-approved selection.
819    pub candidates: Vec<CapabilityId>,
820}
821
822#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
823/// Describes the package delta portion of a runtime package snapshot.
824/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
825pub struct PackageDelta {
826    /// Fingerprint of the package snapshot that a delta was computed against.
827    pub previous_fingerprint: RuntimePackageFingerprint,
828    /// Source that requested this package delta, activation, or side effect.
829    pub requested_by: SourceRef,
830    /// Redacted explanation for a denial, failure, status, or package delta.
831    pub reason: String,
832    #[serde(default)]
833    /// Capabilities that a package delta proposes to add to the next
834    /// snapshot.
835    pub activated_capabilities: Vec<CapabilitySpec>,
836    #[serde(default)]
837    /// Capability identifiers that a package delta proposes to remove from
838    /// the next snapshot.
839    pub deactivated_capability_ids: Vec<CapabilityId>,
840    #[serde(default)]
841    /// Catalog snapshots contributed to or returned with a runtime package
842    /// delta.
843    pub catalogs: Vec<CapabilityCatalogSnapshot>,
844    #[serde(default)]
845    /// Typed sidecar snapshots included in a package or delta.
846    pub sidecars: Vec<PackageSidecarSnapshot>,
847}
848
849#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
850/// Describes the runtime package conformance report portion of a runtime package snapshot.
851/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
852pub struct RuntimePackageConformanceReport {
853    /// Deterministic fingerprint for package, event, telemetry, or validation
854    /// evidence.
855    pub fingerprint: RuntimePackageFingerprint,
856    /// Count of provider projection items observed or included in this
857    /// record.
858    pub provider_projection_count: usize,
859    /// Count of executable route items observed or included in this record.
860    pub executable_route_count: usize,
861    /// Count of reserved inactive items observed or included in this record.
862    pub reserved_inactive_count: usize,
863    /// Count of catalog items observed or included in this record.
864    pub catalog_count: usize,
865    /// Count of sidecar items observed or included in this record.
866    pub sidecar_count: usize,
867}
868
869#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
870/// Describes the fingerprint input manifest portion of a runtime package snapshot.
871/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
872pub struct FingerprintInputManifest {
873    /// Algorithm name used for hashing or fingerprint generation.
874    pub algorithm: String,
875    /// Version of the canonical schema format used for fingerprinting.
876    /// Use it to detect incompatible fingerprint inputs.
877    pub canonical_schema_version: u16,
878    /// Readiness state for a capability or package feature.
879    /// Launch and package validation use it to distinguish active, reserved, and blocked
880    /// surfaces.
881    pub readiness_profile: ReadinessProfile,
882    /// Fingerprint input groups included for this readiness profile.
883    pub included_groups: Vec<FingerprintInputGroup>,
884    /// Fingerprint input groups deliberately excluded for this readiness
885    /// profile.
886    pub excluded_groups: Vec<FingerprintExclusionGroup>,
887    /// Readiness status for reserved feature capabilities.
888    pub reserved_feature_status: Vec<ReservedFeatureFingerprintStatus>,
889}
890
891impl FingerprintInputManifest {
892    /// Returns p0 text for the current value.
893    /// This is a read-only or data-construction helper unless the method body explicitly calls
894    /// a port or store.
895    pub fn p0_text() -> Self {
896        Self {
897            algorithm: RUNTIME_PACKAGE_FINGERPRINT_ALGORITHM.to_string(),
898            canonical_schema_version: RUNTIME_PACKAGE_SCHEMA_VERSION,
899            readiness_profile: ReadinessProfile::P0Text,
900            included_groups: Vec::new(),
901            excluded_groups: Vec::new(),
902            reserved_feature_status: Vec::new(),
903        }
904    }
905}
906
907#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
908#[serde(rename_all = "snake_case")]
909/// Enumerates the finite readiness profile cases.
910/// Serialized names are part of the SDK contract; update fixtures when variants change.
911pub enum ReadinessProfile {
912    /// Use this variant when the contract needs to represent p0 text; selecting it has no side effect by itself.
913    P0Text,
914    /// Use this variant when the contract needs to represent p1 typed output; selecting it has no side effect by itself.
915    P1TypedOutput,
916    /// Use this variant when the contract needs to represent p2 side effects; selecting it has no side effect by itself.
917    P2SideEffects,
918}
919
920#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
921#[serde(rename_all = "snake_case")]
922/// Enumerates the finite fingerprint input group cases.
923/// Serialized names are part of the SDK contract; update fixtures when variants change.
924pub enum FingerprintInputGroup {
925    /// Use this variant when the contract needs to represent agent; selecting it has no side effect by itself.
926    Agent,
927    /// Use this variant when the contract needs to represent provider route; selecting it has no side effect by itself.
928    ProviderRoute,
929    /// Use this variant when the contract needs to represent output contracts; selecting it has no side effect by itself.
930    OutputContracts,
931    /// Use this variant when the contract needs to represent output sinks; selecting it has no side effect by itself.
932    OutputSinks,
933    /// Use this variant when the contract needs to represent capabilities; selecting it has no side effect by itself.
934    Capabilities,
935    /// Use this variant when the contract needs to represent sidecars; selecting it has no side effect by itself.
936    Sidecars,
937    /// Use this variant when the contract needs to represent isolation requirements; selecting it has no side effect by itself.
938    IsolationRequirements,
939    /// Use this variant when the contract needs to represent catalogs; selecting it has no side effect by itself.
940    Catalogs,
941    /// Use this variant when the contract needs to represent child lifecycle; selecting it has no side effect by itself.
942    ChildLifecycle,
943    /// Use this variant when the contract needs to represent policies; selecting it has no side effect by itself.
944    Policies,
945}
946
947#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
948#[serde(rename_all = "snake_case")]
949/// Enumerates the finite fingerprint exclusion group cases.
950/// Serialized names are part of the SDK contract; update fixtures when variants change.
951pub enum FingerprintExclusionGroup {
952    /// Use this variant when the contract needs to represent run ids; selecting it has no side effect by itself.
953    RunIds,
954    /// Use this variant when the contract needs to represent event ids; selecting it has no side effect by itself.
955    EventIds,
956    /// Use this variant when the contract needs to represent timestamps; selecting it has no side effect by itself.
957    Timestamps,
958    /// Use this variant when the contract needs to represent adapter health; selecting it has no side effect by itself.
959    AdapterHealth,
960    /// Use this variant when the contract needs to represent temporary paths; selecting it has no side effect by itself.
961    TemporaryPaths,
962    /// Use this variant when the contract needs to represent telemetry sink health; selecting it has no side effect by itself.
963    TelemetrySinkHealth,
964}
965
966#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
967/// Describes the reserved feature fingerprint status portion of a runtime package snapshot.
968/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
969pub struct ReservedFeatureFingerprintStatus {
970    /// Stable capability identifier used for package projection and
971    /// executable routing.
972    pub capability_id: CapabilityId,
973    /// Kind/category for this record, capability, event, or detected
974    /// resource.
975    pub kind: CapabilityKind,
976    /// Implementation owner responsible for this capability surface.
977    /// Use it to route follow-up work and validation ownership.
978    pub owner_role: String,
979    /// Finite status for this record or lifecycle stage.
980    pub status: String,
981    /// Redacted explanation for a denial, failure, status, or package delta.
982    pub reason: String,
983}
984
985#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
986/// Describes the volatile runtime fields portion of a runtime package snapshot.
987/// Use it when package authors or tests need explicit package configuration; validation and activation happen in package/runtime coordinators.
988pub struct VolatileRuntimeFields {
989    #[serde(skip_serializing_if = "Option::is_none")]
990    /// Run identifier used for lineage, filtering, replay, and dedupe.
991    pub run_id: Option<String>,
992    #[serde(skip_serializing_if = "Option::is_none")]
993    /// Event identifier used to correlate live events with journal or replay
994    /// evidence.
995    pub event_id: Option<String>,
996    #[serde(skip_serializing_if = "Option::is_none")]
997    /// timestamp ms duration in milliseconds.
998    pub timestamp_ms: Option<u64>,
999    #[serde(skip_serializing_if = "Option::is_none")]
1000    /// Adapter health snapshot used to decide whether host support is
1001    /// available.
1002    pub adapter_health: Option<String>,
1003    #[serde(skip_serializing_if = "Option::is_none")]
1004    /// Temporary host path used only for diagnostics or adapter handoff; it
1005    /// should not become durable SDK authority.
1006    pub temporary_path: Option<String>,
1007}
1008
1009impl VolatileRuntimeFields {
1010    fn is_empty(&self) -> bool {
1011        self.run_id.is_none()
1012            && self.event_id.is_none()
1013            && self.timestamp_ms.is_none()
1014            && self.adapter_health.is_none()
1015            && self.temporary_path.is_none()
1016    }
1017}
1018
1019fn sorted_by_key<T, F>(mut items: Vec<T>, mut key: F) -> Vec<T>
1020where
1021    F: FnMut(&T) -> String,
1022{
1023    items.sort_by_key(|item| key(item));
1024    items
1025}
1026
1027fn canonical_agent(mut agent: AgentSnapshot) -> AgentSnapshot {
1028    agent.default_behavior_refs = sorted_policy_refs(agent.default_behavior_refs);
1029    agent
1030}
1031
1032fn canonical_capability(mut capability: CapabilitySpec) -> CapabilitySpec {
1033    capability.projection = canonical_projection(capability.projection);
1034    capability.sidecar_refs = sorted_sidecar_refs(capability.sidecar_refs);
1035    capability
1036}
1037
1038fn canonical_projection(
1039    projection: crate::capability::ProjectionMode,
1040) -> crate::capability::ProjectionMode {
1041    match projection {
1042        crate::capability::ProjectionMode::ProducesContextItems { mut allowed_kinds } => {
1043            allowed_kinds.sort();
1044            crate::capability::ProjectionMode::ProducesContextItems { allowed_kinds }
1045        }
1046        crate::capability::ProjectionMode::ProjectsContextRefs {
1047            mut allowed_ref_kinds,
1048        } => {
1049            allowed_ref_kinds.sort();
1050            crate::capability::ProjectionMode::ProjectsContextRefs { allowed_ref_kinds }
1051        }
1052        other => other,
1053    }
1054}
1055
1056fn canonical_sidecar(mut sidecar: PackageSidecarSnapshot) -> PackageSidecarSnapshot {
1057    sidecar.refs = sorted_sidecar_refs(sidecar.refs);
1058    sidecar.policy_refs = sorted_policy_refs(sidecar.policy_refs);
1059    sidecar
1060}
1061
1062fn canonical_catalog(mut catalog: CapabilityCatalogSnapshot) -> CapabilityCatalogSnapshot {
1063    catalog.candidates = sorted_by_key(catalog.candidates, |capability| {
1064        capability.as_str().to_string()
1065    });
1066    catalog
1067}
1068
1069fn sorted_policy_refs(policy_refs: Vec<PolicyRef>) -> Vec<PolicyRef> {
1070    sorted_by_key(policy_refs, policy_ref_key)
1071}
1072
1073fn sorted_sidecar_refs(sidecar_refs: Vec<PackageSidecarRef>) -> Vec<PackageSidecarRef> {
1074    sorted_by_key(sidecar_refs, sidecar_ref_key)
1075}
1076
1077fn policy_ref_key(policy: &PolicyRef) -> String {
1078    format!(
1079        "{:?}:{}:{}",
1080        policy.kind,
1081        policy.as_str(),
1082        policy.version.as_deref().unwrap_or("")
1083    )
1084}
1085
1086fn sidecar_ref_key(sidecar: &PackageSidecarRef) -> String {
1087    format!(
1088        "{}:{}:{}:{}",
1089        sidecar.sidecar_id,
1090        sidecar.kind,
1091        sidecar.version,
1092        sidecar.content_hash.as_deref().unwrap_or("")
1093    )
1094}
1095
1096trait CanonicalReservedStatus {
1097    fn canonicalized_reserved_status(self) -> Self;
1098}
1099
1100impl CanonicalReservedStatus for Vec<ReservedFeatureFingerprintStatus> {
1101    fn canonicalized_reserved_status(mut self) -> Self {
1102        self.sort_by_key(|status| status.capability_id.as_str().to_string());
1103        self
1104    }
1105}
1106
1107fn hex_lower(bytes: &[u8]) -> String {
1108    let mut output = String::with_capacity(bytes.len() * 2);
1109    for byte in bytes {
1110        output.push_str(&format!("{byte:02x}"));
1111    }
1112    output
1113}