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