Skip to main content

lash_remote_protocol/protocol/
processes.rs

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