1use super::*;
2
3#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub struct SwarmMemberRecord {
35 pub session_id: String,
36 pub handle: String,
37}
38
39#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct SessionWidget {
70 pub capability: String,
71 pub item: FrontendWidget,
72}
73
74#[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#[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#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct WorkspaceInfo {
135 pub id: String,
136 pub path: PathBuf,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
141pub struct GitStatus {
142 pub current_branch: String,
143 pub branches: Vec<String>,
144}
145
146#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
166#[serde(rename_all = "snake_case")]
167pub enum WorkspaceFileScope {
168 Modified,
169 All,
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
174pub struct WorkspaceFileRecord {
175 pub path: String,
176 pub size: u64,
177}
178
179#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
197pub struct VersionedAgentConfig {
198 pub revision: u64,
199 pub config: AgentComposition,
200}
201
202#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
215#[serde(rename_all = "snake_case")]
216pub enum ExtensionKind {
217 Skill,
218 Plugin,
219}
220
221#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253#[serde(deny_unknown_fields)]
254pub struct ProviderConfig {
255 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
270#[serde(rename_all = "snake_case")]
271pub enum ProviderEndpointAuth {
272 ProviderDefault,
273 Credentialless,
274}
275
276#[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#[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#[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#[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#[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#[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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
374#[serde(rename_all = "snake_case")]
375pub enum ProviderAuthKind {
376 ApiKey,
377 DeviceCode,
378}
379
380#[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 #[must_use]
391 pub fn enabled(&self, id: &str) -> bool {
392 self.enabled.contains(id)
393 }
394
395 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 #[must_use]
407 pub fn setting(&self, middleware: &str, setting: &str) -> Option<&FrontendSettingValue> {
408 self.settings.get(middleware)?.get(setting)
409 }
410
411 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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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}