Skip to main content

lash_remote_protocol/
processes.rs

1//! Process lifecycle envelopes: start/cancel/signal/await/list requests and
2//! results, process records and summaries, event semantics, execution
3//! environments, and runtime invocation provenance.
4
5use std::collections::BTreeMap;
6
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10use crate::prompt::RemotePromptLayer;
11use crate::registry_errors::{RemoteProtocolError, require_non_empty};
12use crate::tools::RemoteToolOutputContract;
13use crate::turn_input::RemoteTurnInput;
14use crate::turn_result::RemoteCausalRef;
15use crate::{REMOTE_PROTOCOL_VERSION, ensure_protocol_version};
16
17#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
18pub struct RemoteSessionScope {
19    pub session_id: String,
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub agent_frame_id: Option<String>,
22}
23
24impl RemoteSessionScope {
25    pub fn new(session_id: impl Into<String>) -> Self {
26        Self {
27            session_id: session_id.into(),
28            agent_frame_id: None,
29        }
30    }
31
32    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
33        require_non_empty(type_name, "session_id", &self.session_id)?;
34        if let Some(agent_frame_id) = &self.agent_frame_id {
35            require_non_empty(type_name, "agent_frame_id", agent_frame_id)?;
36        }
37        Ok(())
38    }
39}
40
41#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, JsonSchema)]
42#[serde(transparent)]
43pub struct RemoteProcessExecutionEnvRef(String);
44
45impl RemoteProcessExecutionEnvRef {
46    pub const PREFIX: &'static str = "process-env:sha256:";
47
48    pub fn parse(value: impl Into<String>) -> Result<Self, RemoteProtocolError> {
49        let value = value.into();
50        if is_canonical_process_execution_env_ref(&value) {
51            Ok(Self(value))
52        } else {
53            Err(RemoteProtocolError::InvalidEnvelope {
54                type_name: "RemoteProcessExecutionEnvRef",
55                message: "env_ref must match `process-env:sha256:<64 lowercase hex>`".to_string(),
56            })
57        }
58    }
59
60    pub fn as_str(&self) -> &str {
61        &self.0
62    }
63
64    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
65        if is_canonical_process_execution_env_ref(&self.0) {
66            Ok(())
67        } else {
68            Err(RemoteProtocolError::InvalidEnvelope {
69                type_name,
70                message: "env_ref must match `process-env:sha256:<64 lowercase hex>`".to_string(),
71            })
72        }
73    }
74}
75
76impl std::fmt::Display for RemoteProcessExecutionEnvRef {
77    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        formatter.write_str(&self.0)
79    }
80}
81
82impl std::str::FromStr for RemoteProcessExecutionEnvRef {
83    type Err = RemoteProtocolError;
84
85    fn from_str(value: &str) -> Result<Self, Self::Err> {
86        Self::parse(value)
87    }
88}
89
90impl<'de> serde::Deserialize<'de> for RemoteProcessExecutionEnvRef {
91    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
92    where
93        D: serde::Deserializer<'de>,
94    {
95        let value = String::deserialize(deserializer)?;
96        Self::parse(value).map_err(serde::de::Error::custom)
97    }
98}
99
100fn is_canonical_process_execution_env_ref(value: &str) -> bool {
101    let Some(digest) = value.strip_prefix(RemoteProcessExecutionEnvRef::PREFIX) else {
102        return false;
103    };
104    digest.len() == 64
105        && digest
106            .bytes()
107            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
108}
109
110#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
111#[serde(tag = "type", rename_all = "snake_case")]
112pub enum RemoteProcessOriginator {
113    Host {
114        #[serde(default, skip_serializing_if = "Option::is_none")]
115        scope: Option<String>,
116    },
117    Session {
118        scope: RemoteSessionScope,
119    },
120}
121
122impl RemoteProcessOriginator {
123    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
124        match self {
125            Self::Host { .. } => Ok(()),
126            Self::Session { scope } => scope.validate(type_name),
127        }
128    }
129}
130
131#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
132pub struct RemoteProcessProvenance {
133    pub originator: RemoteProcessOriginator,
134    #[serde(default, skip_serializing_if = "Option::is_none")]
135    pub caused_by: Option<RemoteCausalRef>,
136}
137
138impl RemoteProcessProvenance {
139    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
140        self.originator.validate(type_name)
141    }
142}
143
144#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
145pub struct RemoteProcessDefinitionIdentity {
146    #[serde(default)]
147    pub value: serde_json::Value,
148}
149
150impl RemoteProcessDefinitionIdentity {
151    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
152        if self.value.is_null() {
153            return Err(RemoteProtocolError::InvalidEnvelope {
154                type_name,
155                message: "definition value cannot be null".to_string(),
156            });
157        }
158        Ok(())
159    }
160}
161
162#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
163pub struct RemoteProcessIdentity {
164    pub kind: String,
165    #[serde(default, skip_serializing_if = "Option::is_none")]
166    pub label: Option<String>,
167    #[serde(default, skip_serializing_if = "Option::is_none")]
168    pub definition: Option<RemoteProcessDefinitionIdentity>,
169}
170
171impl RemoteProcessIdentity {
172    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
173        require_non_empty(type_name, "identity.kind", &self.kind)?;
174        if let Some(definition) = &self.definition {
175            definition.validate(type_name)?;
176        }
177        Ok(())
178    }
179}
180
181#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
182pub struct RemoteProcessHandleDescriptor {
183    #[serde(default, skip_serializing_if = "Option::is_none")]
184    pub kind: Option<String>,
185    #[serde(default, skip_serializing_if = "Option::is_none")]
186    pub label: Option<String>,
187}
188
189impl RemoteProcessHandleDescriptor {
190    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
191        if let Some(kind) = &self.kind {
192            require_non_empty(type_name, "descriptor.kind", kind)?;
193        }
194        if let Some(label) = &self.label {
195            require_non_empty(type_name, "descriptor.label", label)?;
196        }
197        Ok(())
198    }
199}
200
201#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
202pub struct RemoteProcessStartGrant {
203    pub session_scope: RemoteSessionScope,
204    pub descriptor: RemoteProcessHandleDescriptor,
205}
206
207impl RemoteProcessStartGrant {
208    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
209        self.session_scope.validate(type_name)?;
210        self.descriptor.validate(type_name)
211    }
212}
213
214#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
215#[serde(tag = "type", rename_all = "snake_case")]
216#[allow(clippy::large_enum_variant)]
217pub enum RemoteProcessInput {
218    ToolCall {
219        #[serde(default)]
220        prepared_tool_call: serde_json::Value,
221    },
222    Engine {
223        kind: String,
224        #[serde(default)]
225        payload: serde_json::Value,
226    },
227    SessionTurn {
228        #[serde(default)]
229        create_request: serde_json::Value,
230        turn_input: RemoteTurnInput,
231        #[serde(default, skip_serializing_if = "RemoteToolOutputContract::is_static")]
232        output_contract: RemoteToolOutputContract,
233    },
234    External {
235        #[serde(default)]
236        metadata: serde_json::Value,
237    },
238}
239
240impl RemoteProcessInput {
241    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
242        match self {
243            Self::ToolCall {
244                prepared_tool_call: _,
245            } => Ok(()),
246            Self::Engine { kind, payload: _ } => require_non_empty(type_name, "kind", kind),
247            Self::SessionTurn {
248                create_request: _,
249                turn_input,
250                output_contract,
251            } => {
252                turn_input.validate()?;
253                match output_contract {
254                    RemoteToolOutputContract::Static => Ok(()),
255                    RemoteToolOutputContract::FromInputSchema {
256                        input_field,
257                        default_schema: _,
258                    } => require_non_empty(type_name, "output_contract.input_field", input_field),
259                }
260            }
261            Self::External { metadata: _ } => Ok(()),
262        }
263    }
264}
265
266#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
267#[serde(rename_all = "snake_case")]
268pub enum RemoteProcessLifecycleStatus {
269    #[default]
270    Running,
271    Completed,
272    Failed,
273    Cancelled,
274    Abandoned,
275}
276
277impl RemoteProcessLifecycleStatus {
278    pub fn is_terminal(self) -> bool {
279        !matches!(self, Self::Running)
280    }
281}
282
283#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
284#[serde(tag = "state", rename_all = "snake_case")]
285pub enum RemoteProcessStatus {
286    #[default]
287    Running,
288    Completed {
289        await_output: RemoteProcessAwaitOutput,
290    },
291    Failed {
292        await_output: RemoteProcessAwaitOutput,
293    },
294    Cancelled {
295        await_output: RemoteProcessAwaitOutput,
296    },
297    Abandoned {
298        await_output: RemoteProcessAwaitOutput,
299    },
300}
301
302#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
303#[serde(tag = "type", rename_all = "snake_case")]
304pub enum RemoteProcessAwaitOutput {
305    Success {
306        value: serde_json::Value,
307        #[serde(default, skip_serializing_if = "Option::is_none")]
308        control: Option<serde_json::Value>,
309    },
310    Failure {
311        class: RemoteToolFailureClass,
312        code: String,
313        message: String,
314        #[serde(default, skip_serializing_if = "Option::is_none")]
315        raw: Option<serde_json::Value>,
316        #[serde(default, skip_serializing_if = "Option::is_none")]
317        control: Option<serde_json::Value>,
318    },
319    Cancelled {
320        message: String,
321        #[serde(default, skip_serializing_if = "Option::is_none")]
322        raw: Option<serde_json::Value>,
323        #[serde(default, skip_serializing_if = "Option::is_none")]
324        control: Option<serde_json::Value>,
325    },
326    Abandoned {
327        evidence: RemoteAbandonEvidence,
328        #[serde(default, skip_serializing_if = "Option::is_none")]
329        control: Option<serde_json::Value>,
330    },
331}
332
333impl RemoteProcessAwaitOutput {
334    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
335        match self {
336            Self::Success { .. } | Self::Abandoned { .. } => Ok(()),
337            Self::Failure { code, message, .. } => {
338                require_non_empty(type_name, "await_output.code", code)?;
339                require_non_empty(type_name, "await_output.message", message)
340            }
341            Self::Cancelled { message, .. } => {
342                require_non_empty(type_name, "await_output.message", message)
343            }
344        }
345    }
346}
347
348#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
349#[serde(rename_all = "snake_case")]
350pub enum RemoteToolFailureClass {
351    InvalidRequest,
352    Unavailable,
353    PermissionDenied,
354    Timeout,
355    Execution,
356    External,
357    ResourceLimit,
358    Internal,
359}
360
361#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
362pub struct RemoteProcessExternalRef {
363    pub backend: String,
364    pub id: String,
365    #[serde(default, skip_serializing_if = "Option::is_none")]
366    pub metadata: Option<serde_json::Value>,
367}
368
369impl RemoteProcessExternalRef {
370    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
371        require_non_empty(type_name, "external_ref.backend", &self.backend)?;
372        require_non_empty(type_name, "external_ref.id", &self.id)
373    }
374}
375
376#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
377pub struct RemoteProcessWaitState {
378    pub kind: RemoteProcessWaitKind,
379    pub since_ms: u64,
380}
381
382#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
383#[serde(tag = "kind", rename_all = "snake_case")]
384pub enum RemoteProcessWaitKind {
385    Signal {
386        name: String,
387        event_type: String,
388        key: String,
389        ordinal: u64,
390    },
391}
392
393impl RemoteProcessWaitState {
394    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
395        match &self.kind {
396            RemoteProcessWaitKind::Signal {
397                name,
398                event_type,
399                key,
400                ordinal,
401            } => {
402                require_non_empty(type_name, "wait.name", name)?;
403                require_non_empty(type_name, "wait.event_type", event_type)?;
404                require_non_empty(type_name, "wait.key", key)?;
405                if *ordinal == 0 {
406                    return Err(RemoteProtocolError::InvalidEnvelope {
407                        type_name,
408                        message: "wait ordinal must be non-zero".to_string(),
409                    });
410                }
411                Ok(())
412            }
413        }
414    }
415}
416
417#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
418pub struct RemoteProcessSummary {
419    #[serde(rename = "__handle__")]
420    pub handle_type: String,
421    pub id: String,
422    pub process_id: String,
423    pub descriptor: RemoteProcessHandleDescriptor,
424    #[serde(default, skip_serializing_if = "Option::is_none")]
425    pub definition: Option<RemoteProcessDefinitionIdentity>,
426    pub status: RemoteProcessLifecycleStatus,
427}
428
429impl RemoteProcessSummary {
430    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
431        require_non_empty(type_name, "handle_type", &self.handle_type)?;
432        require_non_empty(type_name, "id", &self.id)?;
433        require_non_empty(type_name, "process_id", &self.process_id)?;
434        self.descriptor.validate(type_name)?;
435        if let Some(definition) = &self.definition {
436            definition.validate(type_name)?;
437        }
438        Ok(())
439    }
440}
441
442#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
443pub struct RemoteProcessRecord {
444    pub process_id: String,
445    pub input: RemoteProcessInput,
446    pub disposition: RemoteRecoveryDisposition,
447    pub identity: RemoteProcessIdentity,
448    #[serde(default, skip_serializing_if = "Vec::is_empty")]
449    pub event_types: Vec<RemoteProcessEventType>,
450    pub provenance: RemoteProcessProvenance,
451    #[serde(default, skip_serializing_if = "Option::is_none")]
452    pub env_ref: Option<RemoteProcessExecutionEnvRef>,
453    #[serde(default, skip_serializing_if = "Option::is_none")]
454    pub wake_target: Option<RemoteSessionScope>,
455    pub created_at_ms: u64,
456    pub updated_at_ms: u64,
457    #[serde(default, skip_serializing_if = "Option::is_none")]
458    pub external_ref: Option<RemoteProcessExternalRef>,
459    #[serde(default, skip_serializing_if = "Option::is_none")]
460    pub first_started: Option<RemoteProcessStarted>,
461    #[serde(default, skip_serializing_if = "Option::is_none")]
462    pub abandon_request: Option<RemoteAbandonRequest>,
463    #[serde(default, skip_serializing_if = "Option::is_none")]
464    pub wait: Option<RemoteProcessWaitState>,
465    #[serde(default)]
466    pub status: RemoteProcessStatus,
467}
468
469impl RemoteProcessRecord {
470    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
471        require_non_empty(type_name, "process_id", &self.process_id)?;
472        self.input.validate(type_name)?;
473        self.identity.validate(type_name)?;
474        for event_type in &self.event_types {
475            event_type.validate(type_name)?;
476        }
477        self.provenance.validate(type_name)?;
478        if let Some(env_ref) = &self.env_ref {
479            env_ref.validate(type_name)?;
480        }
481        if let Some(wake_target) = &self.wake_target {
482            wake_target.validate(type_name)?;
483        }
484        if let Some(external_ref) = &self.external_ref {
485            external_ref.validate(type_name)?;
486        }
487        if let Some(wait) = &self.wait {
488            wait.validate(type_name)?;
489        }
490        match &self.status {
491            RemoteProcessStatus::Running => Ok(()),
492            RemoteProcessStatus::Completed { await_output }
493            | RemoteProcessStatus::Failed { await_output }
494            | RemoteProcessStatus::Cancelled { await_output }
495            | RemoteProcessStatus::Abandoned { await_output } => await_output.validate(type_name),
496        }
497    }
498}
499
500#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
501pub struct RemoteProcessWorkSnapshot {
502    pub protocol_version: u32,
503    pub session_id: String,
504    #[serde(default)]
505    pub visible_process_ids: Vec<String>,
506    #[serde(default)]
507    pub items: Vec<RemoteProcessWorkItem>,
508}
509
510impl RemoteProcessWorkSnapshot {
511    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
512        ensure_protocol_version(self.protocol_version)?;
513        require_non_empty("RemoteProcessWorkSnapshot", "session_id", &self.session_id)?;
514        for process_id in &self.visible_process_ids {
515            require_non_empty(
516                "RemoteProcessWorkSnapshot",
517                "visible_process_ids",
518                process_id,
519            )?;
520        }
521        for item in &self.items {
522            item.validate("RemoteProcessWorkSnapshot")?;
523        }
524        Ok(())
525    }
526}
527
528#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
529pub struct RemoteProcessWorkItem {
530    pub process: RemoteObservedProcess,
531    pub descriptor: RemoteProcessHandleDescriptor,
532    #[serde(default)]
533    pub events: Vec<RemoteObservedProcessEvent>,
534    pub kind: String,
535    pub label: String,
536}
537
538impl RemoteProcessWorkItem {
539    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
540        self.process.validate(type_name)?;
541        self.descriptor.validate(type_name)?;
542        for event in &self.events {
543            event.validate(type_name)?;
544        }
545        require_non_empty(type_name, "kind", &self.kind)?;
546        require_non_empty(type_name, "label", &self.label)
547    }
548}
549
550#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
551pub struct RemoteObservedProcess {
552    pub process_id: String,
553    pub graph_key: String,
554    pub kind: String,
555    pub identity: RemoteProcessIdentity,
556    pub lifecycle: RemoteProcessLifecycleStatus,
557    pub status_label: String,
558    pub terminal: bool,
559    pub disposition: RemoteRecoveryDisposition,
560    #[serde(default, skip_serializing_if = "Option::is_none")]
561    pub error: Option<String>,
562    pub created_at_ms: u64,
563    pub updated_at_ms: u64,
564    #[serde(default, skip_serializing_if = "Option::is_none")]
565    pub first_started: Option<RemoteProcessStarted>,
566    #[serde(default, skip_serializing_if = "Option::is_none")]
567    pub lease_holder: Option<serde_json::Value>,
568    #[serde(default, skip_serializing_if = "Option::is_none")]
569    pub lease_expires_at_ms: Option<u64>,
570    #[serde(default, skip_serializing_if = "Option::is_none")]
571    pub abandon_request: Option<RemoteAbandonRequest>,
572    pub input: RemoteProcessInput,
573    pub originator: RemoteProcessOriginator,
574    #[serde(default, skip_serializing_if = "Option::is_none")]
575    pub env_ref: Option<RemoteProcessExecutionEnvRef>,
576    #[serde(default, skip_serializing_if = "Option::is_none")]
577    pub wake_target: Option<RemoteSessionScope>,
578    #[serde(default, skip_serializing_if = "Option::is_none")]
579    pub caused_by: Option<RemoteCausalRef>,
580    #[serde(default, skip_serializing_if = "Option::is_none")]
581    pub external_ref: Option<RemoteProcessExternalRef>,
582    #[serde(default, skip_serializing_if = "Option::is_none")]
583    pub wait: Option<RemoteProcessWaitState>,
584    #[serde(default, skip_serializing_if = "Option::is_none")]
585    pub child_session_id: Option<String>,
586    pub label: String,
587}
588
589impl RemoteObservedProcess {
590    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
591        require_non_empty(type_name, "process_id", &self.process_id)?;
592        require_non_empty(type_name, "graph_key", &self.graph_key)?;
593        require_non_empty(type_name, "kind", &self.kind)?;
594        self.identity.validate(type_name)?;
595        require_non_empty(type_name, "status_label", &self.status_label)?;
596        self.input.validate(type_name)?;
597        self.originator.validate(type_name)?;
598        if let Some(env_ref) = &self.env_ref {
599            env_ref.validate(type_name)?;
600        }
601        if let Some(wake_target) = &self.wake_target {
602            wake_target.validate(type_name)?;
603        }
604        if let Some(external_ref) = &self.external_ref {
605            external_ref.validate(type_name)?;
606        }
607        if let Some(wait) = &self.wait {
608            wait.validate(type_name)?;
609        }
610        if let Some(child_session_id) = &self.child_session_id {
611            require_non_empty(type_name, "child_session_id", child_session_id)?;
612        }
613        require_non_empty(type_name, "label", &self.label)
614    }
615}
616
617#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
618pub struct RemoteObservedProcessEvent {
619    pub sequence: u64,
620    pub event_type: String,
621    pub occurred_at_ms: u64,
622    #[serde(default)]
623    pub payload: serde_json::Value,
624}
625
626impl RemoteObservedProcessEvent {
627    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
628        require_non_empty(type_name, "event_type", &self.event_type)
629    }
630}
631
632#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
633pub struct RemoteProcessEvent {
634    pub process_id: String,
635    pub sequence: u64,
636    pub event_type: String,
637    #[serde(default)]
638    pub payload: serde_json::Value,
639    #[serde(default, skip_serializing_if = "Option::is_none")]
640    pub invocation: Option<RemoteRuntimeInvocation>,
641    #[serde(default)]
642    pub semantics: RemoteProcessEventSemantics,
643    pub occurred_at_ms: u64,
644}
645
646impl RemoteProcessEvent {
647    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
648        require_non_empty(type_name, "process_id", &self.process_id)?;
649        require_non_empty(type_name, "event_type", &self.event_type)?;
650        if let Some(invocation) = &self.invocation {
651            invocation.validate(type_name)?;
652        }
653        self.semantics.validate(type_name)
654    }
655}
656
657#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
658pub struct RemoteProcessEventType {
659    pub name: String,
660    #[serde(default)]
661    pub payload_schema: serde_json::Value,
662    #[serde(default)]
663    pub semantics: RemoteProcessEventSemanticsSpec,
664}
665
666impl RemoteProcessEventType {
667    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
668        require_non_empty(type_name, "event_type.name", &self.name)?;
669        self.semantics.validate(type_name)
670    }
671}
672
673#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
674pub struct RemoteProcessEventSemanticsSpec {
675    #[serde(default, skip_serializing_if = "Option::is_none")]
676    pub terminal: Option<RemoteProcessTerminalSpec>,
677    #[serde(default, skip_serializing_if = "Option::is_none")]
678    pub wake: Option<RemoteProcessWakeSpec>,
679}
680
681impl RemoteProcessEventSemanticsSpec {
682    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
683        if let Some(terminal) = &self.terminal {
684            terminal.validate(type_name)?;
685        }
686        if let Some(wake) = &self.wake {
687            wake.validate(type_name)?;
688        }
689        Ok(())
690    }
691}
692
693#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
694pub struct RemoteProcessTerminalSpec {
695    pub state: RemoteProcessTerminalState,
696    #[serde(default, skip_serializing_if = "Option::is_none")]
697    pub await_output: Option<RemoteProcessValueSelector>,
698}
699
700impl RemoteProcessTerminalSpec {
701    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
702        if let Some(await_output) = &self.await_output {
703            await_output.validate(type_name)?;
704        }
705        Ok(())
706    }
707}
708
709#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
710pub struct RemoteProcessWakeSpec {
711    #[serde(default, skip_serializing_if = "Option::is_none")]
712    pub when: Option<RemoteProcessValueSelector>,
713    pub input: RemoteProcessValueSelector,
714    #[serde(default)]
715    pub dedupe_key: RemoteProcessWakeDedupeKey,
716}
717
718impl RemoteProcessWakeSpec {
719    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
720        if let Some(when) = &self.when {
721            when.validate(type_name)?;
722        }
723        self.input.validate(type_name)?;
724        self.dedupe_key.validate(type_name)
725    }
726}
727
728#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
729pub struct RemoteProcessEventSemantics {
730    #[serde(default, skip_serializing_if = "Option::is_none")]
731    pub terminal: Option<RemoteProcessTerminalSemantics>,
732    #[serde(default, skip_serializing_if = "Option::is_none")]
733    pub wake: Option<RemoteProcessWake>,
734}
735
736impl RemoteProcessEventSemantics {
737    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
738        if let Some(terminal) = &self.terminal {
739            terminal.await_output.validate(type_name)?;
740        }
741        if let Some(wake) = &self.wake {
742            wake.validate(type_name)?;
743        }
744        Ok(())
745    }
746}
747
748#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
749#[serde(rename_all = "snake_case")]
750pub enum RemoteProcessTerminalState {
751    Completed,
752    Failed,
753    Cancelled,
754    Abandoned,
755}
756
757/// Wire mirror of the producer-declared recovery contract (ADR 0019).
758#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
759#[serde(rename_all = "snake_case")]
760pub enum RemoteRecoveryDisposition {
761    Rerunnable,
762    OwnerBound,
763    ExternallyOwned,
764}
765
766/// Wire mirror of the writer that established an Abandoned terminal.
767#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
768#[serde(rename_all = "snake_case")]
769pub enum RemoteAbandonWriter {
770    OwnerDrain,
771    Sweep,
772    ReconciledRequest,
773}
774
775/// Wire mirror of Abandoned-terminal evidence. The dead/lapsed owner identity is
776/// carried opaquely (the wire has no lease-owner mirror), matching how other
777/// nested core types ride the wire as JSON.
778#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
779pub struct RemoteAbandonEvidence {
780    pub writer: RemoteAbandonWriter,
781    #[serde(default, skip_serializing_if = "Option::is_none")]
782    pub owner: Option<serde_json::Value>,
783    pub epoch_ms: u64,
784}
785
786/// Wire mirror of the durable execution-started fact.
787#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
788pub struct RemoteProcessStarted {
789    #[serde(default)]
790    pub owner: serde_json::Value,
791    pub started_at_ms: u64,
792}
793
794/// Wire mirror of the pending Abandon Request marker.
795#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
796pub struct RemoteAbandonRequest {
797    pub requested_by: String,
798    pub requested_at_ms: u64,
799    #[serde(default, skip_serializing_if = "Option::is_none")]
800    pub reason: Option<String>,
801}
802
803#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
804pub struct RemoteProcessTerminalSemantics {
805    pub state: RemoteProcessTerminalState,
806    pub await_output: RemoteProcessAwaitOutput,
807}
808
809#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
810pub struct RemoteProcessWake {
811    pub input: String,
812    pub dedupe_key: String,
813}
814
815impl RemoteProcessWake {
816    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
817        require_non_empty(type_name, "wake.input", &self.input)?;
818        require_non_empty(type_name, "wake.dedupe_key", &self.dedupe_key)
819    }
820}
821
822#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
823#[serde(rename_all = "snake_case")]
824pub enum RemoteProcessWakeDedupeKey {
825    #[default]
826    EventIdentity,
827    Selector(RemoteProcessValueSelector),
828    Const(String),
829}
830
831impl RemoteProcessWakeDedupeKey {
832    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
833        match self {
834            Self::EventIdentity => Ok(()),
835            Self::Selector(selector) => selector.validate(type_name),
836            Self::Const(value) => require_non_empty(type_name, "wake.dedupe_key.const", value),
837        }
838    }
839}
840
841#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
842#[serde(rename_all = "snake_case")]
843pub enum RemoteProcessValueSelector {
844    Payload,
845    Pointer(String),
846    Const(serde_json::Value),
847    Template {
848        template: String,
849        #[serde(default)]
850        fields: BTreeMap<String, RemoteProcessValueSelector>,
851    },
852    Present(String),
853}
854
855impl RemoteProcessValueSelector {
856    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
857        match self {
858            Self::Payload | Self::Const(_) => Ok(()),
859            Self::Pointer(pointer) => require_non_empty(type_name, "selector.pointer", pointer),
860            Self::Template { template, fields } => {
861                require_non_empty(type_name, "selector.template", template)?;
862                for (name, selector) in fields {
863                    require_non_empty(type_name, "selector.field", name)?;
864                    selector.validate(type_name)?;
865                }
866                Ok(())
867            }
868            Self::Present(pointer) => require_non_empty(type_name, "selector.present", pointer),
869        }
870    }
871}
872
873#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
874pub struct RemoteRuntimeInvocation {
875    pub scope: RemoteRuntimeScope,
876    pub subject: RemoteRuntimeSubject,
877    #[serde(default, skip_serializing_if = "Option::is_none")]
878    pub caused_by: Option<RemoteCausalRef>,
879    #[serde(default, skip_serializing_if = "Option::is_none")]
880    pub replay: Option<RemoteRuntimeReplay>,
881}
882
883impl RemoteRuntimeInvocation {
884    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
885        self.scope.validate(type_name)?;
886        self.subject.validate(type_name)?;
887        if let Some(replay) = &self.replay {
888            require_non_empty(type_name, "replay.key", &replay.key)?;
889        }
890        Ok(())
891    }
892}
893
894#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
895pub struct RemoteRuntimeScope {
896    pub session_id: String,
897    #[serde(default, skip_serializing_if = "Option::is_none")]
898    pub turn_id: Option<String>,
899    #[serde(default, skip_serializing_if = "Option::is_none")]
900    pub turn_index: Option<usize>,
901    #[serde(default, skip_serializing_if = "Option::is_none")]
902    pub protocol_iteration: Option<usize>,
903}
904
905impl RemoteRuntimeScope {
906    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
907        require_non_empty(type_name, "runtime_scope.session_id", &self.session_id)?;
908        if let Some(turn_id) = &self.turn_id {
909            require_non_empty(type_name, "runtime_scope.turn_id", turn_id)?;
910        }
911        Ok(())
912    }
913}
914
915#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
916pub struct RemoteRuntimeReplay {
917    pub key: String,
918}
919
920#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
921#[serde(tag = "type", rename_all = "snake_case")]
922pub enum RemoteRuntimeSubject {
923    Effect {
924        effect_id: String,
925        kind: RemoteRuntimeEffectKind,
926    },
927    Process {
928        process_id: String,
929    },
930    ProcessEvent {
931        process_id: String,
932        sequence: u64,
933        event_type: String,
934    },
935    TriggerOccurrence {
936        occurrence_id: String,
937    },
938    SessionNode {
939        node_id: String,
940    },
941}
942
943impl RemoteRuntimeSubject {
944    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
945        match self {
946            Self::Effect { effect_id, .. } => {
947                require_non_empty(type_name, "runtime_subject.effect_id", effect_id)
948            }
949            Self::Process { process_id } => {
950                require_non_empty(type_name, "runtime_subject.process_id", process_id)
951            }
952            Self::ProcessEvent {
953                process_id,
954                event_type,
955                ..
956            } => {
957                require_non_empty(type_name, "runtime_subject.process_id", process_id)?;
958                require_non_empty(type_name, "runtime_subject.event_type", event_type)
959            }
960            Self::TriggerOccurrence { occurrence_id } => {
961                require_non_empty(type_name, "runtime_subject.occurrence_id", occurrence_id)
962            }
963            Self::SessionNode { node_id } => {
964                require_non_empty(type_name, "runtime_subject.node_id", node_id)
965            }
966        }
967    }
968}
969
970#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
971#[serde(rename_all = "snake_case")]
972pub enum RemoteRuntimeEffectKind {
973    LlmCall,
974    Direct,
975    ToolAttempt,
976    ToolBatch,
977    Process,
978    ExecCode,
979    Checkpoint,
980    SyncExecutionEnvironment,
981    Sleep,
982    AwaitEvent,
983    DurableStep,
984}
985
986#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
987#[serde(deny_unknown_fields)]
988pub struct RemoteProcessPluginOptions {
989    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
990    pub plugins: BTreeMap<String, serde_json::Value>,
991}
992
993fn default_remote_context_window_tokens() -> usize {
994    1
995}
996
997#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
998#[serde(deny_unknown_fields)]
999pub struct RemoteProcessModelLimits {
1000    #[serde(default = "default_remote_context_window_tokens")]
1001    pub context_window_tokens: usize,
1002    #[serde(default, skip_serializing_if = "Option::is_none")]
1003    pub output_token_capacity: Option<usize>,
1004}
1005
1006impl Default for RemoteProcessModelLimits {
1007    fn default() -> Self {
1008        Self {
1009            context_window_tokens: default_remote_context_window_tokens(),
1010            output_token_capacity: None,
1011        }
1012    }
1013}
1014
1015#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1016#[serde(deny_unknown_fields)]
1017pub struct RemoteProcessModelSpec {
1018    #[serde(default)]
1019    pub id: String,
1020    #[serde(default, skip_serializing_if = "Option::is_none")]
1021    pub variant: Option<String>,
1022    #[serde(
1023        default,
1024        skip_serializing_if = "crate::llm::RemoteModelCapability::is_empty"
1025    )]
1026    pub capability: crate::llm::RemoteModelCapability,
1027    #[serde(default)]
1028    pub limits: RemoteProcessModelLimits,
1029}
1030
1031#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
1032#[serde(deny_unknown_fields)]
1033pub struct RemoteProcessExecutionPolicy {
1034    #[serde(default)]
1035    pub model: RemoteProcessModelSpec,
1036    #[serde(default)]
1037    pub provider_id: String,
1038    #[serde(default, skip_serializing_if = "Option::is_none")]
1039    pub session_id: Option<String>,
1040    #[serde(default)]
1041    pub autonomous: bool,
1042    #[serde(default, skip_serializing_if = "Option::is_none")]
1043    pub max_turns: Option<usize>,
1044    #[serde(default, skip_serializing_if = "RemotePromptLayer::is_empty")]
1045    pub prompt: RemotePromptLayer,
1046}
1047
1048#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
1049#[serde(deny_unknown_fields)]
1050pub struct RemoteProcessExecutionEnvSpec {
1051    #[serde(default, skip_serializing_if = "RemoteProcessPluginOptions::is_empty")]
1052    pub plugin_options: RemoteProcessPluginOptions,
1053    #[serde(
1054        default,
1055        skip_serializing_if = "RemoteProcessExecutionPolicy::is_empty"
1056    )]
1057    pub policy: RemoteProcessExecutionPolicy,
1058}
1059
1060impl RemoteProcessPluginOptions {
1061    pub fn is_empty(&self) -> bool {
1062        self.plugins.is_empty()
1063    }
1064}
1065
1066impl RemoteProcessExecutionPolicy {
1067    pub fn is_empty(&self) -> bool {
1068        self == &Self::default()
1069    }
1070}
1071
1072impl RemoteProcessExecutionEnvSpec {
1073    pub fn is_empty(&self) -> bool {
1074        self.plugin_options.is_empty() && self.policy.is_empty()
1075    }
1076
1077    pub fn validate(&self, type_name: &'static str) -> Result<(), RemoteProtocolError> {
1078        if self.policy.model.limits.context_window_tokens == 0 {
1079            return Err(RemoteProtocolError::InvalidEnvelope {
1080                type_name,
1081                message:
1082                    "env_spec.policy.model.limits.context_window_tokens must be greater than zero"
1083                        .to_string(),
1084            });
1085        }
1086        if self
1087            .policy
1088            .model
1089            .limits
1090            .output_token_capacity
1091            .is_some_and(|value| value == 0)
1092        {
1093            return Err(RemoteProtocolError::InvalidEnvelope {
1094                type_name,
1095                message:
1096                    "env_spec.policy.model.limits.output_token_capacity must be greater than zero"
1097                        .to_string(),
1098            });
1099        }
1100        Ok(())
1101    }
1102}
1103
1104#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1105pub struct RemotePersistProcessEnvRequest {
1106    pub protocol_version: u32,
1107    pub env_spec: RemoteProcessExecutionEnvSpec,
1108}
1109
1110impl RemotePersistProcessEnvRequest {
1111    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1112        ensure_protocol_version(self.protocol_version)?;
1113        self.env_spec.validate("RemotePersistProcessEnvRequest")
1114    }
1115}
1116
1117#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1118pub struct RemotePersistProcessEnvResult {
1119    pub protocol_version: u32,
1120    pub env_ref: RemoteProcessExecutionEnvRef,
1121}
1122
1123impl RemotePersistProcessEnvResult {
1124    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1125        ensure_protocol_version(self.protocol_version)?;
1126        self.env_ref.validate("RemotePersistProcessEnvResult")
1127    }
1128}
1129
1130#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1131pub struct RemoteProcessStartRequest {
1132    pub protocol_version: u32,
1133    pub id: String,
1134    pub input: RemoteProcessInput,
1135    pub disposition: RemoteRecoveryDisposition,
1136    #[serde(default, skip_serializing_if = "Option::is_none")]
1137    pub env_spec: Option<RemoteProcessExecutionEnvSpec>,
1138    pub originator: RemoteProcessOriginator,
1139    #[serde(default, skip_serializing_if = "Option::is_none")]
1140    pub wake_target: Option<RemoteSessionScope>,
1141    #[serde(default, skip_serializing_if = "Option::is_none")]
1142    pub grant: Option<RemoteProcessStartGrant>,
1143    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1144    pub event_types: Vec<RemoteProcessEventType>,
1145}
1146
1147impl RemoteProcessStartRequest {
1148    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1149        ensure_protocol_version(self.protocol_version)?;
1150        require_non_empty("RemoteProcessStartRequest", "id", &self.id)?;
1151        self.input.validate("RemoteProcessStartRequest")?;
1152        if let Some(env_spec) = &self.env_spec {
1153            env_spec.validate("RemoteProcessStartRequest")?;
1154        }
1155        if let RemoteProcessInput::SessionTurn { turn_input, .. } = &self.input
1156            && turn_input.protocol_version != self.protocol_version
1157        {
1158            return Err(RemoteProtocolError::MismatchedNestedProtocolVersion {
1159                parent: "RemoteProcessStartRequest",
1160                child: "input.turn_input",
1161                parent_version: self.protocol_version,
1162                child_version: turn_input.protocol_version,
1163            });
1164        }
1165        self.originator.validate("RemoteProcessStartRequest")?;
1166        if let Some(wake_target) = &self.wake_target {
1167            wake_target.validate("RemoteProcessStartRequest")?;
1168        }
1169        if let Some(grant) = &self.grant {
1170            grant.validate("RemoteProcessStartRequest")?;
1171        }
1172        for event_type in &self.event_types {
1173            event_type.validate("RemoteProcessStartRequest")?;
1174        }
1175        Ok(())
1176    }
1177}
1178
1179#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1180pub struct RemoteProcessStartResult {
1181    pub protocol_version: u32,
1182    pub record: RemoteProcessRecord,
1183    #[serde(default, skip_serializing_if = "Option::is_none")]
1184    pub summary: Option<RemoteProcessSummary>,
1185}
1186
1187impl RemoteProcessStartResult {
1188    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1189        ensure_protocol_version(self.protocol_version)?;
1190        self.record.validate("RemoteProcessStartResult")?;
1191        if let Some(summary) = &self.summary {
1192            summary.validate("RemoteProcessStartResult")?;
1193        }
1194        Ok(())
1195    }
1196}
1197
1198#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1199#[serde(rename_all = "snake_case")]
1200pub enum RemoteProcessStatusFilter {
1201    #[default]
1202    Running,
1203    Completed,
1204    Failed,
1205    Cancelled,
1206    Abandoned,
1207    Any,
1208}
1209
1210#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1211pub struct RemoteProcessListFilter {
1212    pub protocol_version: u32,
1213    #[serde(default, skip_serializing_if = "Option::is_none")]
1214    pub definition: Option<RemoteProcessDefinitionIdentity>,
1215    #[serde(default)]
1216    pub status: RemoteProcessStatusFilter,
1217    #[serde(default, skip_serializing_if = "Option::is_none")]
1218    pub waiting: Option<bool>,
1219    #[serde(default, skip_serializing_if = "Option::is_none")]
1220    pub originator_scope_id: Option<String>,
1221    #[serde(default, skip_serializing_if = "Option::is_none")]
1222    pub identity_kind: Option<String>,
1223    #[serde(default, skip_serializing_if = "Option::is_none")]
1224    pub identity_label: Option<String>,
1225    #[serde(default, skip_serializing_if = "Option::is_none")]
1226    pub caused_by_occurrence_id: Option<String>,
1227    #[serde(default, skip_serializing_if = "Option::is_none")]
1228    pub caused_by_subscription_id: Option<String>,
1229    #[serde(default, skip_serializing_if = "Option::is_none")]
1230    pub created_at_start_ms: Option<u64>,
1231    #[serde(default, skip_serializing_if = "Option::is_none")]
1232    pub created_at_end_ms: Option<u64>,
1233}
1234
1235impl Default for RemoteProcessListFilter {
1236    fn default() -> Self {
1237        Self {
1238            protocol_version: REMOTE_PROTOCOL_VERSION,
1239            definition: None,
1240            status: RemoteProcessStatusFilter::Running,
1241            waiting: None,
1242            originator_scope_id: None,
1243            identity_kind: None,
1244            identity_label: None,
1245            caused_by_occurrence_id: None,
1246            caused_by_subscription_id: None,
1247            created_at_start_ms: None,
1248            created_at_end_ms: None,
1249        }
1250    }
1251}
1252
1253impl RemoteProcessListFilter {
1254    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1255        ensure_protocol_version(self.protocol_version)?;
1256        if let Some(definition) = &self.definition {
1257            definition.validate("RemoteProcessListFilter")?;
1258        }
1259        Ok(())
1260    }
1261}
1262
1263#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1264pub struct RemoteProcessListResponse {
1265    pub protocol_version: u32,
1266    #[serde(default)]
1267    pub records: Vec<RemoteObservedProcess>,
1268}
1269
1270impl RemoteProcessListResponse {
1271    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1272        ensure_protocol_version(self.protocol_version)?;
1273        for record in &self.records {
1274            record.validate("RemoteProcessListResponse")?;
1275        }
1276        Ok(())
1277    }
1278}
1279
1280#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1281pub struct RemoteProcessCancelRequest {
1282    pub protocol_version: u32,
1283    pub process_id: String,
1284    #[serde(default, skip_serializing_if = "Option::is_none")]
1285    pub reason: Option<String>,
1286}
1287
1288impl RemoteProcessCancelRequest {
1289    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1290        ensure_protocol_version(self.protocol_version)?;
1291        require_non_empty("RemoteProcessCancelRequest", "process_id", &self.process_id)?;
1292        if let Some(reason) = &self.reason {
1293            require_non_empty("RemoteProcessCancelRequest", "reason", reason)?;
1294        }
1295        Ok(())
1296    }
1297}
1298
1299#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1300pub struct RemoteProcessCancelResult {
1301    pub protocol_version: u32,
1302    pub process_id: String,
1303    pub status: RemoteProcessLifecycleStatus,
1304    #[serde(default, skip_serializing_if = "Option::is_none")]
1305    pub record: Option<RemoteProcessRecord>,
1306}
1307
1308impl RemoteProcessCancelResult {
1309    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1310        ensure_protocol_version(self.protocol_version)?;
1311        require_non_empty("RemoteProcessCancelResult", "process_id", &self.process_id)?;
1312        if let Some(record) = &self.record {
1313            record.validate("RemoteProcessCancelResult")?;
1314        }
1315        Ok(())
1316    }
1317}
1318
1319#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1320pub struct RemoteProcessSignalRequest {
1321    pub protocol_version: u32,
1322    pub process_id: String,
1323    pub signal_name: String,
1324    pub signal_id: String,
1325    #[serde(default)]
1326    pub payload: serde_json::Value,
1327    #[serde(default, skip_serializing_if = "Option::is_none")]
1328    pub replay_key: Option<String>,
1329    #[serde(default, skip_serializing_if = "Option::is_none")]
1330    pub wake_target_scope: Option<RemoteSessionScope>,
1331}
1332
1333impl RemoteProcessSignalRequest {
1334    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1335        ensure_protocol_version(self.protocol_version)?;
1336        require_non_empty("RemoteProcessSignalRequest", "process_id", &self.process_id)?;
1337        require_non_empty(
1338            "RemoteProcessSignalRequest",
1339            "signal_name",
1340            &self.signal_name,
1341        )?;
1342        require_non_empty("RemoteProcessSignalRequest", "signal_id", &self.signal_id)?;
1343        if let Some(replay_key) = &self.replay_key {
1344            require_non_empty("RemoteProcessSignalRequest", "replay_key", replay_key)?;
1345        }
1346        if let Some(scope) = &self.wake_target_scope {
1347            scope.validate("RemoteProcessSignalRequest")?;
1348        }
1349        Ok(())
1350    }
1351}
1352
1353#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1354pub struct RemoteProcessSignalResult {
1355    pub protocol_version: u32,
1356    pub event: RemoteProcessEvent,
1357}
1358
1359impl RemoteProcessSignalResult {
1360    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1361        ensure_protocol_version(self.protocol_version)?;
1362        self.event.validate("RemoteProcessSignalResult")
1363    }
1364}
1365
1366#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1367pub struct RemoteProcessAwaitRequest {
1368    pub protocol_version: u32,
1369    pub process_id: String,
1370}
1371
1372impl RemoteProcessAwaitRequest {
1373    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1374        ensure_protocol_version(self.protocol_version)?;
1375        require_non_empty("RemoteProcessAwaitRequest", "process_id", &self.process_id)
1376    }
1377}
1378
1379#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1380pub struct RemoteProcessAwaitResult {
1381    pub protocol_version: u32,
1382    pub process_id: String,
1383    pub output: RemoteProcessAwaitOutput,
1384}
1385
1386impl RemoteProcessAwaitResult {
1387    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1388        ensure_protocol_version(self.protocol_version)?;
1389        require_non_empty("RemoteProcessAwaitResult", "process_id", &self.process_id)?;
1390        self.output.validate("RemoteProcessAwaitResult")
1391    }
1392}
1393
1394#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1395pub struct RemoteProcessEventsRequest {
1396    pub protocol_version: u32,
1397    pub process_id: String,
1398    #[serde(default)]
1399    pub after_sequence: u64,
1400}
1401
1402impl RemoteProcessEventsRequest {
1403    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1404        ensure_protocol_version(self.protocol_version)?;
1405        require_non_empty("RemoteProcessEventsRequest", "process_id", &self.process_id)
1406    }
1407}
1408
1409#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)]
1410pub struct RemoteProcessEventsResponse {
1411    pub protocol_version: u32,
1412    pub process_id: String,
1413    #[serde(default)]
1414    pub events: Vec<RemoteProcessEvent>,
1415}
1416
1417impl RemoteProcessEventsResponse {
1418    pub fn validate(&self) -> Result<(), RemoteProtocolError> {
1419        ensure_protocol_version(self.protocol_version)?;
1420        require_non_empty(
1421            "RemoteProcessEventsResponse",
1422            "process_id",
1423            &self.process_id,
1424        )?;
1425        for event in &self.events {
1426            event.validate("RemoteProcessEventsResponse")?;
1427        }
1428        Ok(())
1429    }
1430}