Skip to main content

lash_remote_protocol/protocol/
processes.rs

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