Skip to main content

lash_remote_protocol/protocol/
processes.rs

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