Skip to main content

lash_remote_protocol/
processes.rs

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