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