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