Skip to main content

lash_remote_protocol/
processes.rs

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