Skip to main content

agent_sdk_core/ports/
isolation.rs

1//! Host adapter boundaries for the SDK core. Use these traits and registries when
2//! hosts provide providers, journals, sinks, tools, isolation, extensions, telemetry,
3//! or subscriptions. Implementations may perform external side effects and must honor
4//! policy, redaction, idempotency, and replay contracts. This file contains the
5//! isolation portion of that contract.
6//!
7use std::{
8    collections::BTreeMap,
9    sync::{Arc, Mutex},
10};
11
12use serde::{Deserialize, Serialize};
13
14use crate::{
15    domain::{AgentError, AgentErrorKind, ContentRef, RetryClassification},
16    package_isolation::{
17        CleanupPlanRef, ExecutionEnvironment, ImageRef, ImageRequest, IsolatedProcessRef,
18        IsolatedProcessSpec, IsolationAdapterSessionRef, IsolationCapabilityReportRef,
19        IsolationCapabilitySet, IsolationClass, IsolationRuntimeRef, IsolationSessionRef,
20        IsolationTrustRequirement, MountRef, NetworkNamespaceRef, PreparedEnvironmentRef,
21        ProcessIoStreamRef, ProcessStatsSnapshotRef, ReclaimTicketRef, RootfsRef, RootfsRequest,
22        SecretMountRef,
23    },
24};
25
26/// Port or behavior contract for isolation runtime. Implementors should
27/// preserve policy, redaction, idempotency, and replay expectations
28/// from the surrounding module. Implementations may perform side
29/// effects only as described by the trait methods.
30pub trait IsolationRuntime: Send + Sync {
31    /// Returns runtime ref for the current value.
32    /// This is a read-only or data-construction helper unless the method body explicitly calls
33    /// a port or store.
34    fn runtime_ref(&self) -> &IsolationRuntimeRef;
35
36    /// Returns capability report for the current value.
37    /// This is a read-only or data-construction helper unless the method body explicitly calls
38    /// a port or store.
39    fn capability_report(&self) -> Result<IsolationCapabilityReport, AgentError>;
40
41    /// Prepares or selects an isolation session for the request.
42    /// Implementations allocate or select an isolation session for the request; they may mutate
43    /// adapter sandbox state but must not start the process.
44    fn prepare_session(
45        &self,
46        request: SessionPrepareRequest,
47    ) -> Result<IsolationSessionRef, AgentError>;
48
49    /// Resolves an image request into adapter-specific image metadata.
50    /// Implementations resolve isolation planning data; only process-start methods may launch a
51    /// process.
52    fn resolve_image(&self, request: ImageResolveRequest) -> Result<ImageResolution, AgentError>;
53
54    /// Prepares the root filesystem for the isolation session.
55    /// Implementations materialize or select the requested root filesystem and may touch host
56    /// storage or image caches; they must not start the process.
57    fn prepare_rootfs(&self, request: RootfsPrepareRequest) -> Result<RootfsRef, AgentError>;
58
59    /// Resolves requested mounts into a mount plan for the environment.
60    /// Implementations resolve isolation planning data; only process-start methods may launch a
61    /// process.
62    fn resolve_mounts(&self, request: MountResolveRequest) -> Result<MountPlan, AgentError>;
63
64    /// Configures or selects the network namespace for the environment.
65    /// Implementations resolve isolation planning data; only process-start methods may launch a
66    /// process.
67    fn configure_network(
68        &self,
69        request: NetworkPrepareRequest,
70    ) -> Result<NetworkNamespaceRef, AgentError>;
71
72    /// Prepares secret mounts or handles for the environment.
73    /// Implementations materialize secret mounts or handles for the isolated environment and
74    /// must not return raw secret values.
75    fn prepare_secrets(
76        &self,
77        request: SecretPrepareRequest,
78    ) -> Result<SecretMaterializationPlan, AgentError>;
79
80    /// Combines prepared isolation pieces into an executable environment.
81    /// Implementations combine prepared rootfs, mounts, network, and secrets into an executable
82    /// environment handle; they must not start the process.
83    fn prepare_environment(
84        &self,
85        request: EnvironmentPrepareRequest,
86    ) -> Result<PreparedEnvironmentRef, AgentError>;
87
88    /// Starts the prepared isolated process through the host adapter.
89    /// Implementations may launch a process or container and return process
90    /// handles, but journal intent/result recording stays with the runtime.
91    fn start_process(&self, request: ProcessStartRequest)
92    -> Result<ProcessStartResult, AgentError>;
93
94    /// Reads or writes one bounded I/O frame for an isolated process.
95    /// Implementations may touch host process streams, but must preserve
96    /// redaction and return stream refs or bounded data according to policy.
97    fn stream_io(&self, request: ProcessIoRequest) -> Result<ProcessIoFrame, AgentError>;
98
99    /// Sends a control signal to an already-started isolated process.
100    /// Implementations return the observed signal result and leave lifecycle
101    /// journal evidence to the runtime.
102    fn signal_process(
103        &self,
104        request: ProcessSignalRequest,
105    ) -> Result<ProcessSignalResult, AgentError>;
106
107    /// Collects statistics for an already-started isolated process.
108    /// Implementations may query host process/container state and must return
109    /// bounded metadata rather than raw process output.
110    fn collect_stats(
111        &self,
112        request: ProcessStatsRequest,
113    ) -> Result<ProcessStatsSnapshot, AgentError>;
114
115    /// Cleans up adapter-owned isolation resources for a finished process.
116    /// Implementations may remove sessions, mounts, namespaces, or reclaim
117    /// tickets selected by the cleanup request.
118    fn cleanup(&self, request: CleanupRequest) -> Result<CleanupResult, AgentError>;
119
120    /// Transfers ownership of isolation resources according to a detach plan.
121    /// Implementations may leave processes or resources running under a reclaim
122    /// ticket, but must not silently discard cleanup responsibility.
123    fn detach(&self, request: DetachTransferRequest) -> Result<DetachTransferResult, AgentError>;
124
125    /// Reclaims resources that were previously detached from runtime ownership.
126    /// Implementations may stop processes or remove resources referenced by the
127    /// reclaim ticket and must report any cleanup failure for repair.
128    fn reclaim(&self, request: ReclaimRequest) -> Result<ReclaimResult, AgentError>;
129}
130
131#[derive(Clone, Default)]
132/// Carries isolation runtime registry data across a host-port boundary.
133/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
134pub struct IsolationRuntimeRegistry {
135    runtimes: BTreeMap<String, Arc<dyn IsolationRuntime>>,
136}
137
138impl IsolationRuntimeRegistry {
139    /// Creates a new ports::isolation value with explicit
140    /// caller-provided inputs. This constructor is data-only and
141    /// performs no I/O or external side effects.
142    pub fn new() -> Self {
143        Self::default()
144    }
145
146    /// Adds data to this in-memory ports::isolation collection. It does not
147    /// perform external I/O, execute tools, or append journals.
148    pub fn register(&mut self, runtime: Arc<dyn IsolationRuntime>) -> Result<(), AgentError> {
149        let runtime_ref = runtime.runtime_ref().as_str().to_string();
150        if runtime_ref.is_empty() {
151            return Err(AgentError::missing_required_field(
152                "isolation_runtime.runtime_ref",
153            ));
154        }
155        self.runtimes.insert(runtime_ref, runtime);
156        Ok(())
157    }
158
159    /// Looks up an entry in this local store without registry or runtime work.
160    /// This reads the in-memory isolation runtime registry and does not call the adapter.
161    pub fn get(&self, runtime_ref: &IsolationRuntimeRef) -> Option<Arc<dyn IsolationRuntime>> {
162        self.runtimes.get(runtime_ref.as_str()).cloned()
163    }
164
165    /// Returns first for the current value.
166    /// This is a read-only or data-construction helper unless the method body explicitly calls
167    /// a port or store.
168    pub fn first(&self) -> Option<Arc<dyn IsolationRuntime>> {
169        self.runtimes.values().next().cloned()
170    }
171
172    /// Reports whether this value is empty. The check is pure and does
173    /// not mutate SDK or host state.
174    pub fn is_empty(&self) -> bool {
175        self.runtimes.is_empty()
176    }
177}
178
179#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
180/// Carries isolation capability report data across a host-port boundary.
181/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
182pub struct IsolationCapabilityReport {
183    /// Typed report ref reference. Resolving or executing it is a separate
184    /// policy-gated step.
185    pub report_ref: IsolationCapabilityReportRef,
186    /// Typed adapter ref reference. Resolving or executing it is a separate
187    /// policy-gated step.
188    pub adapter_ref: IsolationRuntimeRef,
189    /// Kind discriminator for adapter kind.
190    /// Use it to route finite match arms without parsing display text.
191    pub adapter_kind: IsolationRuntimeKind,
192    /// Version string for this capability, package, or protocol surface.
193    /// Use it for compatibility checks during package or adapter resolution.
194    pub adapter_version: String,
195    /// Capability version advertised by the provider or package.
196    /// Use it to match compatible feature contracts during package resolution.
197    pub capability_version: String,
198    /// Health used by this record or request.
199    pub health: IsolationRuntimeHealth,
200    #[serde(default, skip_serializing_if = "Vec::is_empty")]
201    /// Classification selectors for supported classes.
202    /// Policy and projection paths use them for finite routing decisions.
203    pub supported_classes: Vec<IsolationClass>,
204    /// Platform used by this record or request.
205    pub platform: PlatformReport,
206    /// Capabilities frozen into the package or returned by an adapter health
207    /// check.
208    pub capabilities: IsolationCapabilitySet,
209    /// Trust class used when deciding whether context or capabilities may be
210    /// admitted.
211    pub trust: IsolationTrustRequirement,
212    #[serde(default, skip_serializing_if = "Vec::is_empty")]
213    /// Collection of unsupported requirements values.
214    /// Ordering and membership should be treated as part of the serialized contract when
215    /// relevant.
216    pub unsupported_requirements: Vec<String>,
217    /// Retention class for referenced content or records.
218    /// Stores and telemetry sinks use it to decide how long evidence may be kept.
219    pub log_retention: crate::domain::RetentionClass,
220}
221
222impl IsolationCapabilityReport {
223    /// Returns sandbox for the current value.
224    /// This is a read-only or data-construction helper unless the method body explicitly calls
225    /// a port or store.
226    pub fn sandbox(adapter_ref: impl Into<IsolationRuntimeRef>) -> Self {
227        let adapter_ref = adapter_ref.into();
228        Self {
229            report_ref: IsolationCapabilityReportRef::new(format!(
230                "report.{}",
231                adapter_ref.as_str()
232            )),
233            adapter_ref,
234            adapter_kind: IsolationRuntimeKind::HostProvided,
235            adapter_version: "fake.v1".to_string(),
236            capability_version: "isolation.capability.v1".to_string(),
237            health: IsolationRuntimeHealth::Healthy,
238            supported_classes: vec![IsolationClass::Sandbox],
239            platform: PlatformReport::portable_fake(),
240            capabilities: IsolationCapabilitySet::default().with_all([
241                crate::IsolationCapability::ReadOnlyRoot,
242                crate::IsolationCapability::NoNetworkGuarantee,
243                crate::IsolationCapability::Cleanup,
244                crate::IsolationCapability::ProcessTimeout,
245                crate::IsolationCapability::ProcessSignal,
246                crate::IsolationCapability::IoRedaction,
247                crate::IsolationCapability::ContentRefIo,
248                crate::IsolationCapability::ProcessStats,
249                crate::IsolationCapability::SecretIsolation,
250            ]),
251            trust: IsolationTrustRequirement::local_dedicated(),
252            unsupported_requirements: Vec::new(),
253            log_retention: crate::domain::RetentionClass::RunScoped,
254        }
255    }
256
257    /// Returns host process for the current value.
258    /// This is a read-only or data-construction helper unless the method body explicitly calls
259    /// a port or store.
260    pub fn host_process(adapter_ref: impl Into<IsolationRuntimeRef>) -> Self {
261        let adapter_ref = adapter_ref.into();
262        Self {
263            report_ref: IsolationCapabilityReportRef::new(format!(
264                "report.{}",
265                adapter_ref.as_str()
266            )),
267            adapter_ref,
268            adapter_kind: IsolationRuntimeKind::TestOnlyFake,
269            adapter_version: "fake.host.v1".to_string(),
270            capability_version: "isolation.capability.host.v1".to_string(),
271            health: IsolationRuntimeHealth::Healthy,
272            supported_classes: vec![IsolationClass::HostProcess],
273            platform: PlatformReport::portable_fake(),
274            capabilities: IsolationCapabilitySet::default().with_all([
275                crate::IsolationCapability::ProcessTimeout,
276                crate::IsolationCapability::IoRedaction,
277            ]),
278            trust: IsolationTrustRequirement::any(),
279            unsupported_requirements: Vec::new(),
280            log_retention: crate::domain::RetentionClass::RunScoped,
281        }
282    }
283
284    /// Returns unsupported for the current value.
285    /// This is a read-only or data-construction helper unless the method body explicitly calls
286    /// a port or store.
287    pub fn unsupported(
288        adapter_ref: impl Into<IsolationRuntimeRef>,
289        missing: impl IntoIterator<Item = impl Into<String>>,
290    ) -> Self {
291        let mut report = Self::sandbox(adapter_ref);
292        let missing = missing.into_iter().map(Into::into).collect::<Vec<_>>();
293        report.health = IsolationRuntimeHealth::UnsupportedHost {
294            missing_prerequisites: missing.clone(),
295        };
296        report.unsupported_requirements = missing;
297        report
298    }
299}
300
301#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
302#[serde(rename_all = "snake_case")]
303/// Enumerates the finite isolation runtime kind cases.
304/// Serialized names are part of the SDK contract; update fixtures when variants change.
305pub enum IsolationRuntimeKind {
306    /// Use this variant when the contract needs to represent host provided; selecting it has no side effect by itself.
307    HostProvided,
308    /// Use this variant when the contract needs to represent host process; selecting it has no side effect by itself.
309    HostProcess,
310    /// Use this variant when the contract needs to represent os sandbox; selecting it has no side effect by itself.
311    OsSandbox,
312    /// Use this variant when the contract needs to represent container; selecting it has no side effect by itself.
313    Container,
314    /// Use this variant when the contract needs to represent lightweight vm; selecting it has no side effect by itself.
315    LightweightVm,
316    /// Use this variant when the contract needs to represent remote sandbox; selecting it has no side effect by itself.
317    RemoteSandbox,
318    /// Use this variant when the contract needs to represent test only fake; selecting it has no side effect by itself.
319    TestOnlyFake,
320}
321
322#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
323#[serde(rename_all = "snake_case")]
324/// Enumerates the finite isolation runtime health cases.
325/// Serialized names are part of the SDK contract; update fixtures when variants change.
326pub enum IsolationRuntimeHealth {
327    /// Use this variant when the contract needs to represent healthy; selecting it has no side effect by itself.
328    Healthy,
329    /// Use this variant when the contract needs to represent degraded; selecting it has no side effect by itself.
330    Degraded {
331        /// Redacted explanation for a denial, failure, status, or package
332        /// delta.
333        reason: String,
334    },
335    /// Use this variant when the contract needs to represent unsupported host; selecting it has no side effect by itself.
336    UnsupportedHost {
337        /// Collection of missing prerequisites values.
338        /// Ordering and membership should be treated as part of the serialized contract when
339        /// relevant.
340        missing_prerequisites: Vec<String>,
341    },
342}
343
344#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
345/// Carries platform report data across a host-port boundary.
346/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
347pub struct PlatformReport {
348    /// Platform used by this record or request.
349    pub platform: String,
350    /// Os used by this record or request.
351    pub os: String,
352    /// Cpu architecture used by this record or request.
353    pub cpu_architecture: String,
354    /// Whether emulation supported is enabled.
355    /// Policy, validation, or routing code uses this flag to choose the explicit behavior.
356    pub emulation_supported: bool,
357}
358
359impl PlatformReport {
360    /// Returns portable fake for the current value.
361    /// This is a read-only or data-construction helper unless the method body explicitly calls
362    /// a port or store.
363    pub fn portable_fake() -> Self {
364        Self {
365            platform: "portable-test".to_string(),
366            os: "test-os".to_string(),
367            cpu_architecture: "test-arch".to_string(),
368            emulation_supported: false,
369        }
370    }
371}
372
373#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
374/// Carries session prepare request data across a host-port boundary.
375/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
376pub struct SessionPrepareRequest {
377    /// Environment used by this record or request.
378    pub environment: ExecutionEnvironment,
379}
380
381#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
382/// Carries image resolve request data across a host-port boundary.
383/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
384pub struct ImageResolveRequest {
385    /// Image used by this record or request.
386    pub image: ImageRequest,
387}
388
389#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
390/// Carries image resolution data across a host-port boundary.
391/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
392pub struct ImageResolution {
393    /// Typed image ref reference. Resolving or executing it is a separate
394    /// policy-gated step.
395    pub image_ref: ImageRef,
396    /// Digest used by this record or request.
397    pub digest: String,
398    /// Optional redacted credential alias value.
399    /// When absent, callers should use the documented default or skip that optional behavior.
400    pub redacted_credential_alias: Option<String>,
401}
402
403#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
404/// Carries rootfs prepare request data across a host-port boundary.
405/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
406pub struct RootfsPrepareRequest {
407    /// Rootfs used by this record or request.
408    pub rootfs: RootfsRequest,
409}
410
411#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
412/// Carries mount resolve request data across a host-port boundary.
413/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
414pub struct MountResolveRequest {
415    /// Environment used by this record or request.
416    pub environment: ExecutionEnvironment,
417}
418
419#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
420/// Carries mount plan data across a host-port boundary.
421/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
422pub struct MountPlan {
423    #[serde(default, skip_serializing_if = "Vec::is_empty")]
424    /// Collection of mounts values.
425    /// Ordering and membership should be treated as part of the serialized contract when
426    /// relevant.
427    pub mounts: Vec<MountRef>,
428    #[serde(default, skip_serializing_if = "Vec::is_empty")]
429    /// Collection of expanded exposure audits values.
430    /// Ordering and membership should be treated as part of the serialized contract when
431    /// relevant.
432    pub expanded_exposure_audits: Vec<String>,
433}
434
435#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
436/// Carries network prepare request data across a host-port boundary.
437/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
438pub struct NetworkPrepareRequest {
439    /// Environment used by this record or request.
440    pub environment: ExecutionEnvironment,
441}
442
443#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
444/// Carries secret prepare request data across a host-port boundary.
445/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
446pub struct SecretPrepareRequest {
447    /// Environment used by this record or request.
448    pub environment: ExecutionEnvironment,
449}
450
451#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
452/// Carries secret materialization plan data across a host-port boundary.
453/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
454pub struct SecretMaterializationPlan {
455    #[serde(default, skip_serializing_if = "Vec::is_empty")]
456    /// Typed secret mount refs references. Resolving them is separate from
457    /// constructing this record.
458    pub secret_mount_refs: Vec<SecretMountRef>,
459    /// Whether teardown required is enabled.
460    /// Policy, validation, or routing code uses this flag to choose the explicit behavior.
461    pub teardown_required: bool,
462}
463
464#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
465/// Carries environment prepare request data across a host-port boundary.
466/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
467pub struct EnvironmentPrepareRequest {
468    /// Environment used by this record or request.
469    pub environment: ExecutionEnvironment,
470}
471
472#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
473/// Carries process start request data across a host-port boundary.
474/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
475pub struct ProcessStartRequest {
476    /// Environment used by this record or request.
477    pub environment: ExecutionEnvironment,
478    /// Process used by this record or request.
479    pub process: IsolatedProcessSpec,
480    /// Effect intent used by this record or request.
481    pub effect_intent: crate::EffectIntent,
482}
483
484#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
485/// Carries process start result data across a host-port boundary.
486/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
487pub struct ProcessStartResult {
488    /// Typed process ref reference. Resolving or executing it is a separate
489    /// policy-gated step.
490    pub process_ref: IsolatedProcessRef,
491    /// Typed adapter session ref reference. Resolving or executing it is a
492    /// separate policy-gated step.
493    pub adapter_session_ref: Option<IsolationAdapterSessionRef>,
494    /// Terminal status used by this record or request.
495    pub terminal_status: crate::EffectTerminalStatus,
496    /// Stable external operation id used for typed lineage, lookup, or
497    /// dedupe.
498    pub external_operation_id: Option<String>,
499    #[serde(default, skip_serializing_if = "Vec::is_empty")]
500    /// Collection of io frames values.
501    /// Ordering and membership should be treated as part of the serialized contract when
502    /// relevant.
503    pub io_frames: Vec<ProcessIoFrame>,
504    /// Redacted human-readable summary safe for events, telemetry, and logs.
505    pub redacted_summary: String,
506}
507
508#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
509/// Carries process io request data across a host-port boundary.
510/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
511pub struct ProcessIoRequest {
512    /// Typed process ref reference. Resolving or executing it is a separate
513    /// policy-gated step.
514    pub process_ref: IsolatedProcessRef,
515    /// Stream used by this record or request.
516    pub stream: ProcessIoStream,
517}
518
519#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
520/// Carries process io frame data across a host-port boundary.
521/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
522pub struct ProcessIoFrame {
523    /// Typed stream ref reference. Resolving or executing it is a separate
524    /// policy-gated step.
525    pub stream_ref: ProcessIoStreamRef,
526    /// Stream used by this record or request.
527    pub stream: ProcessIoStream,
528    /// Cursor identifying a replay, export, or subscription position.
529    /// Use it to resume without widening the original scope.
530    pub cursor: u64,
531    /// Count of byte items observed or included in this record.
532    pub byte_count: u64,
533    /// Stable hash for the bytes or canonical payload used for stale checks
534    /// and fingerprints.
535    pub content_hash: String,
536    #[serde(default, skip_serializing_if = "Vec::is_empty")]
537    /// Content references associated with this record; resolving them is a
538    /// separate policy-gated step.
539    pub content_refs: Vec<ContentRef>,
540    /// Raw content or raw-content control for this value.
541    /// Use it only when policy explicitly allows raw content capture or delivery.
542    pub raw_content_present: bool,
543    /// Whether output was shortened by byte, item, page, archive, or parser
544    /// limits.
545    pub truncated: bool,
546    /// Redacted human-readable summary safe for events, telemetry, and logs.
547    pub redacted_summary: String,
548}
549
550#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
551#[serde(rename_all = "snake_case")]
552/// Enumerates the finite process io stream cases.
553/// Serialized names are part of the SDK contract; update fixtures when variants change.
554pub enum ProcessIoStream {
555    /// Use this variant when the contract needs to represent stdin; selecting it has no side effect by itself.
556    Stdin,
557    /// Use this variant when the contract needs to represent stdout; selecting it has no side effect by itself.
558    Stdout,
559    /// Use this variant when the contract needs to represent stderr; selecting it has no side effect by itself.
560    Stderr,
561}
562
563#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
564/// Carries process signal request data across a host-port boundary.
565/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
566pub struct ProcessSignalRequest {
567    /// Typed process ref reference. Resolving or executing it is a separate
568    /// policy-gated step.
569    pub process_ref: IsolatedProcessRef,
570    /// Signal used by this record or request.
571    pub signal: ProcessSignal,
572    /// Grace period in milliseconds before termination or cleanup escalates.
573    pub grace_ms: u64,
574}
575
576#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
577/// Carries process signal result data across a host-port boundary.
578/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
579pub struct ProcessSignalResult {
580    /// Typed process ref reference. Resolving or executing it is a separate
581    /// policy-gated step.
582    pub process_ref: IsolatedProcessRef,
583    /// Signal used by this record or request.
584    pub signal: ProcessSignal,
585    /// Whether delivered is enabled.
586    /// Policy, validation, or routing code uses this flag to choose the explicit behavior.
587    pub delivered: bool,
588    /// Redacted human-readable summary safe for events, telemetry, and logs.
589    pub redacted_summary: String,
590}
591
592#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
593#[serde(rename_all = "snake_case")]
594/// Enumerates the finite process signal cases.
595/// Serialized names are part of the SDK contract; update fixtures when variants change.
596pub enum ProcessSignal {
597    /// Use this variant when the contract needs to represent interrupt; selecting it has no side effect by itself.
598    Interrupt,
599    /// Use this variant when the contract needs to represent terminate; selecting it has no side effect by itself.
600    Terminate,
601    /// Use this variant when the contract needs to represent kill; selecting it has no side effect by itself.
602    Kill,
603}
604
605#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
606/// Carries process stats request data across a host-port boundary.
607/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
608pub struct ProcessStatsRequest {
609    /// Typed process ref reference. Resolving or executing it is a separate
610    /// policy-gated step.
611    pub process_ref: IsolatedProcessRef,
612}
613
614#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
615/// Carries process stats snapshot data across a host-port boundary.
616/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
617pub struct ProcessStatsSnapshot {
618    /// Typed snapshot ref reference. Resolving or executing it is a separate
619    /// policy-gated step.
620    pub snapshot_ref: ProcessStatsSnapshotRef,
621    /// Typed process ref reference. Resolving or executing it is a separate
622    /// policy-gated step.
623    pub process_ref: IsolatedProcessRef,
624    /// Time value in milliseconds for cpu millis.
625    /// Use it for timeout, ordering, or diagnostic calculations.
626    pub cpu_millis: Option<u64>,
627    /// memory bytes used for bounds checks, summaries, or truncation
628    /// evidence.
629    pub memory_bytes: Option<u64>,
630    /// Count of process items observed or included in this record.
631    pub process_count: Option<u32>,
632    /// filesystem bytes used for bounds checks, summaries, or truncation
633    /// evidence.
634    pub filesystem_bytes: Option<u64>,
635    /// network bytes used for bounds checks, summaries, or truncation
636    /// evidence.
637    pub network_bytes: Option<u64>,
638    /// Process exit status when the process reported one.
639    pub exit_code: Option<i32>,
640    /// Redacted human-readable summary safe for events, telemetry, and logs.
641    pub redacted_summary: String,
642}
643
644#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
645/// Carries cleanup request data across a host-port boundary.
646/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
647pub struct CleanupRequest {
648    /// Environment used by this record or request.
649    pub environment: ExecutionEnvironment,
650    /// Typed cleanup plan ref reference. Resolving or executing it is a
651    /// separate policy-gated step.
652    pub cleanup_plan_ref: CleanupPlanRef,
653}
654
655#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
656/// Carries cleanup result data across a host-port boundary.
657/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
658pub struct CleanupResult {
659    /// Typed cleanup plan ref reference. Resolving or executing it is a
660    /// separate policy-gated step.
661    pub cleanup_plan_ref: CleanupPlanRef,
662    /// Finite status for this record or lifecycle stage.
663    pub status: CleanupStatus,
664    /// Stable external operation id used for typed lineage, lookup, or
665    /// dedupe.
666    pub external_operation_id: Option<String>,
667    /// Redacted human-readable summary safe for events, telemetry, and logs.
668    pub redacted_summary: String,
669}
670
671#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
672#[serde(rename_all = "snake_case")]
673/// Enumerates the finite cleanup status cases.
674/// Serialized names are part of the SDK contract; update fixtures when variants change.
675pub enum CleanupStatus {
676    /// Use this variant when the contract needs to represent completed; selecting it has no side effect by itself.
677    Completed,
678    /// Use this variant when the contract needs to represent repair needed; selecting it has no side effect by itself.
679    RepairNeeded,
680}
681
682#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
683/// Carries detach transfer request data across a host-port boundary.
684/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
685pub struct DetachTransferRequest {
686    /// Environment used by this record or request.
687    pub environment: ExecutionEnvironment,
688}
689
690#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
691/// Carries detach transfer result data across a host-port boundary.
692/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
693pub struct DetachTransferResult {
694    /// Typed host ack ref reference. Resolving or executing it is a separate
695    /// policy-gated step.
696    pub host_ack_ref: String,
697    /// Redacted human-readable summary safe for events, telemetry, and logs.
698    pub redacted_summary: String,
699}
700
701#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
702/// Carries reclaim request data across a host-port boundary.
703/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
704pub struct ReclaimRequest {
705    /// Typed ticket ref reference. Resolving or executing it is a separate
706    /// policy-gated step.
707    pub ticket_ref: ReclaimTicketRef,
708}
709
710#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
711/// Carries reclaim result data across a host-port boundary.
712/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
713pub struct ReclaimResult {
714    /// Typed ticket ref reference. Resolving or executing it is a separate
715    /// policy-gated step.
716    pub ticket_ref: ReclaimTicketRef,
717    /// Finite status for this record or lifecycle stage.
718    pub status: CleanupStatus,
719    /// Redacted human-readable summary safe for events, telemetry, and logs.
720    pub redacted_summary: String,
721}
722
723#[derive(Clone, Debug, Default)]
724/// Carries isolation runtime call log data across a host-port boundary.
725/// Constructing the value does not call the host; the port method that receives it documents any adapter, network, or storage effect.
726pub struct IsolationRuntimeCallLog {
727    calls: Arc<Mutex<Vec<String>>>,
728}
729
730impl IsolationRuntimeCallLog {
731    /// Operates on in-memory or journal-derived ports::isolation state for
732    /// diagnostics and repair evidence. It does not create a second run loop
733    /// or product workflow owner.
734    pub fn push(&self, call: impl Into<String>) {
735        self.calls
736            .lock()
737            .expect("isolation call log")
738            .push(call.into());
739    }
740
741    /// Operates on in-memory or journal-derived ports::isolation state for
742    /// diagnostics and repair evidence. It does not create a second run loop
743    /// or product workflow owner.
744    pub fn calls(&self) -> Vec<String> {
745        self.calls.lock().expect("isolation call log").clone()
746    }
747
748    /// Operates on in-memory or journal-derived ports::isolation state for
749    /// diagnostics and repair evidence. It does not create a second run loop
750    /// or product workflow owner.
751    pub fn count_matching(&self, call: &str) -> usize {
752        self.calls()
753            .into_iter()
754            .filter(|entry| entry == call)
755            .count()
756    }
757}
758
759/// Builds the isolation host configuration needed value.
760/// This is data construction and performs no I/O, journal append, event publication, or process
761pub fn isolation_host_configuration_needed(message: impl Into<String>) -> AgentError {
762    AgentError::new(
763        AgentErrorKind::IsolationFailure,
764        RetryClassification::HostConfigurationNeeded,
765        message,
766    )
767}