Skip to main content

mobius_gateway/wire/
records.rs

1use super::*;
2
3/// Gateway-wide frontend-safe state sent after authentication.
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5pub struct ReadyPayload {
6    pub machine_name: String,
7    pub bots: Vec<BotRecord>,
8    pub sessions: Vec<SessionRecord>,
9    pub background_approvals: Vec<BackgroundApproval>,
10    pub swarm_attentions: Vec<SwarmAttention>,
11    pub swarms: Vec<SwarmRecord>,
12    pub providers: Vec<ProviderStatus>,
13    pub provider_instances: Vec<ProviderInstance>,
14    pub bot_defaults: Option<VersionedAgentConfig>,
15    pub models: Vec<ModelChoice>,
16    pub model_providers: BTreeMap<String, String>,
17    pub middleware_features: Vec<MiddlewareFeature>,
18    pub extensions: Vec<ExtensionRecord>,
19    pub contributions: Vec<FrontendContribution>,
20    pub max_active_sessions: usize,
21    pub session_file_limits: SessionFileLimits,
22}
23
24/// One hidden Bot conversation currently waiting for a human execution decision.
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub struct BackgroundApproval {
27    pub session_id: String,
28    pub bot_id: String,
29    pub turn_id: String,
30    pub request_id: String,
31}
32
33/// One durable Swarm Chat message awaiting an authenticated human response.
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct SwarmAttention {
36    pub swarm_id: String,
37    pub swarm_title: String,
38    pub message_id: String,
39    pub bot_id: String,
40    pub text: String,
41}
42
43/// One gateway-managed group of durable Bots.
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45pub struct SwarmRecord {
46    pub id: String,
47    pub title: String,
48    pub leader_bot_id: String,
49    pub members: Vec<SwarmMemberRecord>,
50    pub messages: Vec<SwarmMessageRecord>,
51    pub updated_at_ms: i64,
52}
53
54/// Stable identity for one Bot in a Swarm.
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56pub struct SwarmMemberRecord {
57    pub bot_id: String,
58    pub handle: String,
59}
60
61/// One retained post on a swarm's shared message board.
62#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63pub struct SwarmMessageRecord {
64    pub id: String,
65    pub sequence: u64,
66    pub author_bot_id: String,
67    pub author_handle: String,
68    pub source_session_id: String,
69    pub text: String,
70    pub created_at_ms: i64,
71    pub in_reply_to_message_id: Option<String>,
72    pub reply_depth: u8,
73}
74
75/// Gateway-managed scope selected by a human-facing capability request.
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(tag = "type", rename_all = "snake_case")]
78pub enum ContributionScope {
79    Global,
80    Swarm { id: String },
81}
82
83/// Frontend-safe state for one opened session.
84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub struct SessionReadyPayload {
86    pub latest_sequence: u64,
87    pub next_before_sequence: Option<u64>,
88    pub workspace: WorkspaceInfo,
89    pub git: Option<GitStatus>,
90    pub session: SessionConfiguredEvent,
91    pub contributions: Vec<FrontendContribution>,
92    pub widgets: Vec<SessionWidget>,
93    pub tool_count: usize,
94    pub compaction_count: u64,
95    pub context_limit_tokens: Option<i64>,
96    pub run_stats: RunStats,
97}
98
99/// One currently mounted capability widget and its owning namespace.
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
101pub struct SessionWidget {
102    pub capability: String,
103    pub item: FrontendWidget,
104}
105
106/// One visible session with gateway-owned catalog presentation metadata.
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub struct SessionRecord {
109    pub session_id: String,
110    pub session_context: mobius::protocol::SessionContext,
111    pub parent_session_id: Option<String>,
112    pub parent_sequence: Option<u64>,
113    pub sequence: u64,
114    pub first_user_message: Option<String>,
115    pub execution_stats: mobius::backend::checkpoint::ExecutionStats,
116    pub title: Option<String>,
117    pub pinned: bool,
118    pub activity: SessionActivity,
119    pub created_at: i64,
120    pub updated_at: i64,
121}
122
123/// Gateway-observed lifecycle state for one session.
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125#[serde(deny_unknown_fields)]
126pub struct SessionActivity {
127    pub state: SessionActivityState,
128    pub turn_id: Option<String>,
129    pub approval_request_id: Option<String>,
130    pub started_at: Option<i64>,
131    pub last_outcome: Option<mobius::backend::checkpoint::ExecutionOutcome>,
132    pub message: Option<String>,
133}
134
135impl Default for SessionActivity {
136    fn default() -> Self {
137        Self {
138            state: SessionActivityState::Idle,
139            turn_id: None,
140            approval_request_id: None,
141            started_at: None,
142            last_outcome: None,
143            message: None,
144        }
145    }
146}
147
148/// Current work state advertised in the session catalog.
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
150#[serde(rename_all = "snake_case")]
151pub enum SessionActivityState {
152    Idle,
153    Running,
154    AwaitingApproval,
155}
156
157/// Canonical workspace identity and path for one chat.
158#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
159pub struct WorkspaceInfo {
160    pub id: String,
161    pub path: PathBuf,
162}
163
164/// Local branch state for a Git-backed workspace.
165#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
166pub struct GitStatus {
167    pub current_branch: String,
168    pub branches: Vec<String>,
169}
170
171/// Public metadata for one SSH identity found on the gateway host.
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
173#[serde(deny_unknown_fields)]
174pub struct SshIdentityRecord {
175    pub label: String,
176    pub algorithm: String,
177    pub fingerprint: String,
178}
179
180/// One explicit Git patch selection.
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
182#[serde(rename_all = "snake_case")]
183pub enum GitDiffScope {
184    Staged,
185    Unstaged,
186    Committed,
187}
188
189/// Which openable files to include in a workspace catalog.
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
191#[serde(rename_all = "snake_case")]
192pub enum WorkspaceFileScope {
193    Modified,
194    All,
195}
196
197/// One regular file confined to the selected chat workspace.
198#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
199pub struct WorkspaceFileRecord {
200    pub path: String,
201    pub size: u64,
202}
203
204/// One bounded folder listing from the gateway host.
205#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
206pub struct DirectoryListing {
207    pub path: PathBuf,
208    pub parent: Option<PathBuf>,
209    pub entries: Vec<DirectoryEntry>,
210}
211
212/// A selectable child folder on the gateway host.
213#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
214pub struct DirectoryEntry {
215    pub name: String,
216    pub path: PathBuf,
217    pub is_directory: bool,
218}
219
220/// A frontend-safe agent composition guarded by an optimistic revision.
221#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
222pub struct VersionedAgentConfig {
223    pub revision: u64,
224    pub config: AgentComposition,
225}
226
227/// Runtime settings an authenticated client may read and replace atomically.
228#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
229#[serde(deny_unknown_fields)]
230pub struct AgentComposition {
231    pub provider: ProviderConfig,
232    pub realtime_voice: Option<String>,
233    pub middleware: MiddlewareConfig,
234    pub extensions: BTreeSet<String>,
235    pub system_prompt: String,
236    pub max_model_steps: u64,
237}
238
239/// Package format of one gateway-managed extension.
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
241#[serde(rename_all = "snake_case")]
242pub enum ExtensionKind {
243    Skill,
244    Plugin,
245}
246
247/// One executable plugin hook shown before digest-bound trust is granted.
248#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
249#[serde(deny_unknown_fields)]
250pub struct ExtensionHookRecord {
251    pub event: String,
252    pub matcher: Option<String>,
253    pub command: String,
254    pub timeout_seconds: u64,
255}
256
257/// Frontend-safe metadata for one installed extension snapshot.
258#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
259#[serde(deny_unknown_fields)]
260pub struct ExtensionRecord {
261    pub id: String,
262    pub capability: String,
263    pub kind: ExtensionKind,
264    pub name: String,
265    pub description: String,
266    pub version: Option<String>,
267    pub source: String,
268    pub reference: Option<String>,
269    pub subdirectory: Option<String>,
270    pub resolved_revision: String,
271    pub digest: String,
272    pub skills: Vec<String>,
273    pub hooks: Vec<ExtensionHookRecord>,
274    pub hooks_trusted: bool,
275}
276
277/// Provider and model settings. Credentials are resolved only on the gateway host.
278#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
279#[serde(deny_unknown_fields)]
280pub struct ProviderConfig {
281    /// Stable identity of one configured setup of `provider`. A gateway may hold
282    /// several instances of the same provider with separate credentials.
283    pub instance: String,
284    pub provider: String,
285    pub model: String,
286    #[serde(default, skip_serializing_if = "Option::is_none")]
287    pub base_url: Option<String>,
288    pub endpoint_auth: ProviderEndpointAuth,
289    #[serde(default, skip_serializing_if = "Option::is_none")]
290    pub reasoning_effort: Option<String>,
291    pub web_search: HostedWebSearch,
292}
293
294/// Authentication applied when calling one configured provider endpoint.
295#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
296#[serde(rename_all = "snake_case")]
297pub enum ProviderEndpointAuth {
298    ProviderDefault,
299    Credentialless,
300}
301
302/// Credential availability exposed without returning credential material.
303#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
304pub struct ProviderStatus {
305    pub provider: String,
306    pub label: String,
307    pub symbol: FrontendSymbol,
308    pub description: String,
309    pub model_ids_configurable: bool,
310    pub auth: ProviderAuthKind,
311    pub default_base_url: Option<String>,
312    pub default_api_key_env: Option<String>,
313    pub models: Vec<ProviderModel>,
314    pub web_search: Vec<FrontendSettingOption>,
315    pub tool_discovery: ToolDiscoveryMode,
316    pub custom_endpoint_tool_discovery: Option<ToolDiscoveryMode>,
317    pub realtime_voices: Vec<String>,
318}
319
320/// User-chosen accent for distinguishing provider instances in model selectors.
321#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
322#[serde(rename_all = "snake_case")]
323pub enum ProviderTint {
324    #[default]
325    Blue,
326    Teal,
327    Green,
328    Yellow,
329    Orange,
330    Red,
331    Purple,
332}
333
334/// One durable setup of a provider. Several may share one `provider`.
335#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
336pub struct ProviderInstance {
337    pub label: String,
338    pub tint: ProviderTint,
339    pub configured: bool,
340    #[serde(default, skip_serializing_if = "Option::is_none")]
341    pub credential_hint: Option<String>,
342    pub selection: ProviderConfig,
343    pub model_ids: Vec<String>,
344    pub reasoning_efforts: Vec<String>,
345}
346
347/// Frontend type attached to one authenticated connection.
348#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
349#[serde(rename_all = "snake_case")]
350pub enum ClientKind {
351    Cli,
352    Macos,
353    Ios,
354    Ipados,
355    GatewayDashboard,
356}
357
358/// One paired client and its current connection state.
359#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
360pub struct ClientStatus {
361    pub client_id: String,
362    pub label: String,
363    pub kinds: Vec<ClientKind>,
364    pub connections: usize,
365}
366
367impl ProviderStatus {
368    #[must_use]
369    pub fn realtime_voices(&self, base_url: Option<&str>) -> &[String] {
370        if mobius::backend::model::provider::uses_default_endpoint(
371            self.default_base_url.as_deref(),
372            base_url,
373        ) {
374            &self.realtime_voices
375        } else {
376            &[]
377        }
378    }
379
380    #[must_use]
381    pub fn configurable_base_url(&self) -> bool {
382        self.default_base_url.is_some()
383    }
384
385    #[must_use]
386    pub fn default_model(&self) -> Option<&ProviderModel> {
387        self.models.first()
388    }
389}
390
391/// One model advertised by a provider manifest.
392#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
393pub struct ProviderModel {
394    pub id: String,
395    pub label: String,
396    pub description: String,
397    pub context_window: i64,
398    pub reasoning: Vec<ReasoningChoice>,
399    pub default_reasoning: Option<String>,
400    pub tool_discovery: ToolDiscoveryMode,
401}
402
403/// One reasoning effort advertised for a provider model.
404#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
405pub struct ReasoningChoice {
406    pub id: String,
407    pub label: String,
408    pub description: String,
409}
410
411/// Frontend-safe provider authentication mechanism.
412#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
413#[serde(rename_all = "snake_case")]
414pub enum ProviderAuthKind {
415    ApiKey,
416    DeviceCode,
417}
418
419/// Enabled optional middleware IDs and their schema-backed settings.
420#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
421#[serde(deny_unknown_fields)]
422pub struct MiddlewareConfig {
423    pub(crate) enabled: BTreeSet<String>,
424    pub settings: BTreeMap<String, BTreeMap<String, FrontendSettingValue>>,
425}
426
427impl MiddlewareConfig {
428    /// Returns the selected policy that excludes an optional capability.
429    #[must_use]
430    pub fn disabled_by<'a>(
431        &self,
432        features: &'a [mobius::protocol::MiddlewareFeature],
433        id: &str,
434    ) -> Option<&'a str> {
435        features
436            .iter()
437            .filter(|feature| feature.required || self.enabled(&feature.id))
438            .find_map(|feature| {
439                feature.settings.iter().find_map(|setting| {
440                    let mobius::protocol::FrontendSettingKind::Select { options, .. } =
441                        &setting.kind
442                    else {
443                        return None;
444                    };
445                    let Some(FrontendSettingValue::String(value)) =
446                        self.setting(&feature.id, &setting.id)
447                    else {
448                        return None;
449                    };
450                    options
451                        .iter()
452                        .find(|option| {
453                            option.value == *value
454                                && option.disables.iter().any(|disabled| disabled == id)
455                        })
456                        .map(|option| option.label.as_str())
457                })
458            })
459    }
460
461    /// Applies exclusions advertised by the currently selected policies.
462    pub fn reconcile(&mut self, features: &[mobius::protocol::MiddlewareFeature]) {
463        let excluded = self
464            .enabled
465            .iter()
466            .filter(|id| self.disabled_by(features, id).is_some())
467            .cloned()
468            .collect::<Vec<_>>();
469        for id in excluded {
470            self.enabled.remove(&id);
471        }
472    }
473
474    /// Returns whether one advertised optional middleware is enabled.
475    #[must_use]
476    pub fn enabled(&self, id: &str) -> bool {
477        self.enabled.contains(id)
478    }
479
480    /// Updates one advertised optional middleware before gateway validation.
481    pub fn set_enabled(&mut self, id: impl Into<String>, enabled: bool) {
482        let id = id.into();
483        if enabled {
484            self.enabled.insert(id);
485        } else {
486            self.enabled.remove(&id);
487        }
488    }
489
490    /// Returns one advertised middleware setting.
491    #[must_use]
492    pub fn setting(&self, middleware: &str, setting: &str) -> Option<&FrontendSettingValue> {
493        self.settings.get(middleware)?.get(setting)
494    }
495
496    /// Sets or clears one advertised middleware setting before gateway validation.
497    pub fn set_setting(
498        &mut self,
499        middleware: impl Into<String>,
500        setting: impl Into<String>,
501        value: Option<FrontendSettingValue>,
502    ) {
503        let middleware = middleware.into();
504        let setting = setting.into();
505        if let Some(value) = value {
506            self.settings
507                .entry(middleware)
508                .or_default()
509                .insert(setting, value);
510        } else if let Some(settings) = self.settings.get_mut(&middleware) {
511            settings.remove(&setting);
512            if settings.is_empty() {
513                self.settings.remove(&middleware);
514            }
515        }
516    }
517
518    pub(crate) fn entries(&self) -> impl Iterator<Item = &str> {
519        self.enabled.iter().map(String::as_str)
520    }
521}
522
523/// Capability-rendered preview whose inner events remain provider-neutral.
524#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
525pub struct RenderedPreview {
526    pub id: String,
527    pub title: String,
528    pub subtitle: String,
529    pub page_id: String,
530    pub update: FrontendPreviewUpdate,
531    pub events: Vec<RenderedEvent>,
532    pub next: Option<Op>,
533}
534
535/// One preview event and its capability-rendered blocks.
536#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
537pub struct RenderedEvent {
538    pub submission_id: Option<String>,
539    pub recorded_at_ms: i64,
540    pub event: EventMsg,
541    pub blocks: Vec<RenderedBlock>,
542}
543
544/// One timestamped semantic event and its deterministic presentation.
545#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
546pub struct RecordedEvent {
547    pub sequence: u64,
548    pub recorded_at_ms: i64,
549    pub event: Event,
550    pub stream_metrics: Vec<StreamMetrics>,
551    pub blocks: Vec<RenderedBlock>,
552    pub preview: Option<RenderedPreview>,
553}
554
555/// Gateway-owned profile and aggregate usage information.
556#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
557pub struct ProfileSnapshot {
558    pub user_name: Option<String>,
559    pub daily_usage: Vec<DailyUsage>,
560    pub run_stats: RunStats,
561    pub recent_run_groups: Vec<SessionRunGroup>,
562}
563
564/// Recent executions grouped under their nearest visible session.
565#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
566pub struct SessionRunGroup {
567    pub session_id: String,
568    pub title: String,
569    pub runs: Vec<RunSummary>,
570}
571
572/// Completed execution totals plus the active run, when one exists.
573#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
574pub struct RunStats {
575    #[serde(flatten)]
576    pub completed: mobius::backend::checkpoint::ExecutionStats,
577    pub active: Option<RunSummary>,
578}
579
580/// Frontend-safe summary of one completed or active user turn.
581#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
582pub struct RunSummary {
583    pub session_id: String,
584    pub submission_id: String,
585    pub turn_id: String,
586    pub started_at_ms: i64,
587    pub finished_at_ms: Option<i64>,
588    pub elapsed_ms: u64,
589    pub outcome: Option<mobius::backend::checkpoint::ExecutionOutcome>,
590    pub model_calls: u64,
591    pub tool_calls: u64,
592    pub failed_tool_calls: u64,
593    pub usage: TokenUsage,
594}
595
596/// Usage accrued during one Unix day.
597#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
598pub struct DailyUsage {
599    pub unix_day: u64,
600    pub provider: String,
601    pub usage: TokenUsage,
602}
603
604/// One durable Bot profile.
605#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
606pub struct BotRecord {
607    pub id: String,
608    pub handle: String,
609    pub name: String,
610    pub description: String,
611    pub tint: ProviderTint,
612    pub config: VersionedAgentConfig,
613}
614
615// Wire eligibility is derived from the current config, never stored as a second source of truth.
616impl Serialize for BotRecord {
617    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
618    where
619        S: serde::Serializer,
620    {
621        use serde::ser::SerializeStruct as _;
622
623        let mut record = serializer.serialize_struct("BotRecord", 7)?;
624        record.serialize_field("id", &self.id)?;
625        record.serialize_field("handle", &self.handle)?;
626        record.serialize_field("name", &self.name)?;
627        record.serialize_field("description", &self.description)?;
628        record.serialize_field("tint", &self.tint)?;
629        record.serialize_field("config", &self.config)?;
630        record.serialize_field("collaboration_enabled", &self.collaboration_enabled())?;
631        record.end()
632    }
633}
634
635impl BotRecord {
636    /// Whether this Bot permits gateway-managed peer collaboration.
637    #[must_use]
638    pub fn collaboration_enabled(&self) -> bool {
639        mobius::middleware::bots::collaboration_enabled(
640            self.config
641                .config
642                .middleware
643                .setting("bots", "collaboration"),
644        )
645    }
646}
647
648/// One Bot-owned routine.
649#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
650pub struct Routine {
651    pub id: String,
652    pub bot_id: String,
653    pub workspace: PathBuf,
654    pub instructions: String,
655    pub schedule: RoutineSchedule,
656    pub ends_at: Option<i64>,
657    pub enabled: bool,
658    pub finished: bool,
659    pub next_run_at: Option<i64>,
660}
661
662/// A user-selected scheduling rule.
663#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
664#[serde(deny_unknown_fields)]
665pub struct RoutineSchedule {
666    pub kind: RoutineScheduleKind,
667    pub at: Option<i64>,
668    pub every_seconds: Option<u64>,
669    pub expression: Option<String>,
670    pub time_zone: Option<String>,
671}
672
673/// The supported scheduling rule families.
674#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
675#[serde(rename_all = "snake_case")]
676pub enum RoutineScheduleKind {
677    Once,
678    Interval,
679    Cron,
680}
681
682/// A read-only page of a Bot routine transcript.
683#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
684pub struct RoutineRunPreview {
685    pub routine: Routine,
686    pub run: RoutineRun,
687    pub records: Vec<RecordedEvent>,
688    pub next_before_sequence: Option<u64>,
689}
690
691/// One completed or active invocation of a Bot routine.
692#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
693pub struct RoutineRun {
694    pub id: String,
695    pub routine_id: String,
696    pub bot_id: String,
697    pub started_at: i64,
698    pub finished_at: Option<i64>,
699    pub status: RoutineRunStatus,
700    pub session_id: Option<String>,
701    pub message: Option<String>,
702}
703
704/// Durable state of one routine invocation.
705#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
706#[serde(rename_all = "snake_case")]
707pub enum RoutineRunStatus {
708    Running,
709    Succeeded,
710    Failed,
711    Skipped,
712}