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