1use std::collections::HashMap;
21
22use serde::{Deserialize, Serialize};
23use serde_json::Value;
24
25use crate::message::{MessageIdentity, SessionMessage, SessionMessageDelta};
26use crate::session::{SessionInfo, SessionStatus};
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30#[serde(tag = "type")]
31pub enum DaemonMessage {
32 #[serde(rename = "auth.ok")]
33 AuthOk(AuthOkMsg),
34
35 #[serde(rename = "response.ok", rename_all = "camelCase")]
36 ResponseOk {
37 request_id: String,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
39 data: Option<Value>,
40 },
41
42 #[serde(rename = "response.error", rename_all = "camelCase")]
43 ResponseError {
44 request_id: String,
45 error: String,
46 code: ErrorCode,
47 },
48
49 #[serde(rename = "session.list.result", rename_all = "camelCase")]
50 SessionListResult {
51 request_id: String,
52 sessions: Vec<SessionInfo>,
53 },
54
55 #[serde(rename = "models.list.result", rename_all = "camelCase")]
56 ModelsListResult {
57 request_id: String,
58 models: Vec<ModelInfo>,
59 live: bool,
61 #[serde(default)]
64 provider: Option<String>,
65 },
66
67 #[serde(rename = "session.message")]
68 SessionMessage(SessionMessage),
69
70 #[serde(rename = "session.message.delta")]
71 SessionMessageDelta(SessionMessageDelta),
72
73 #[serde(rename = "session.status_change", rename_all = "camelCase")]
74 SessionStatusChange {
75 session_id: String,
76 status: SessionStatus,
77 timestamp: String,
78 },
79
80 #[serde(rename = "session.info_update", rename_all = "camelCase")]
81 SessionInfoUpdate {
82 session: SessionInfo,
83 timestamp: String,
84 },
85
86 #[serde(rename = "scrollback.replay", rename_all = "camelCase")]
87 ScrollbackReplay {
88 session_id: String,
89 messages: Vec<SessionMessage>,
90 #[serde(default)]
93 tail: Option<bool>,
94 #[serde(default)]
96 has_more: Option<bool>,
97 },
98
99 #[serde(rename = "scrollback.page.result", rename_all = "camelCase")]
102 ScrollbackPageResult {
103 request_id: String,
104 session_id: String,
105 messages: Vec<SessionMessage>,
106 has_more: bool,
107 source: String,
110 },
111
112 #[serde(rename = "session.search.result", rename_all = "camelCase")]
113 SessionSearchResult {
114 request_id: String,
115 query: String,
116 sessions: Vec<SessionSearchHit>,
117 workspace_id: String,
118 limit: u32,
119 },
120
121 #[serde(rename = "claude.config.result", rename_all = "camelCase")]
122 ClaudeConfigResult {
123 request_id: String,
124 workdir: String,
125 agents: Vec<ClaudeConfigAgent>,
126 skills: Vec<ClaudeConfigSkill>,
127 mcp_servers: Vec<ClaudeConfigMcpServer>,
128 hooks: Vec<ClaudeConfigHook>,
129 },
130
131 #[serde(rename = "session.export.result", rename_all = "camelCase")]
132 SessionExportResult {
133 request_id: String,
134 manifest: SessionExportManifest,
135 payload: SessionExportPayload,
136 },
137
138 #[serde(rename = "session.import.result", rename_all = "camelCase")]
139 SessionImportResult {
140 request_id: String,
141 new_session_id: String,
142 imported_messages: u32,
143 imported_episodes: u32,
144 imported_turns: u32,
145 pinned_files_written: u32,
146 warnings: Vec<String>,
147 },
148
149 #[serde(rename = "session.ui_request", rename_all = "camelCase")]
156 SessionUiRequest(SessionUiRequestMsg),
157
158 #[serde(rename = "session.ui_resolved", rename_all = "camelCase")]
161 SessionUiResolved {
162 session_id: String,
163 request_id: String,
164 reason: UiResolvedReason,
165 timestamp: String,
166 },
167
168 #[serde(rename = "session.commands.result", rename_all = "camelCase")]
172 SessionCommandsResult {
173 request_id: String,
174 session_id: String,
175 provider_id: String,
176 commands: Vec<ProviderCommand>,
177 },
178
179 #[serde(rename = "settings.schema.result", rename_all = "camelCase")]
181 SettingsSchemaResult {
182 request_id: String,
183 manifest: SettingsManifest,
184 },
185
186 #[serde(rename = "settings.get.result", rename_all = "camelCase")]
188 SettingsGetResult {
189 request_id: String,
190 snapshot: SettingsSnapshot,
191 },
192
193 #[serde(rename = "settings.set.result", rename_all = "camelCase")]
195 SettingsSetResult {
196 request_id: String,
197 ok: bool,
198 snapshot: SettingsSnapshot,
199 errors: Vec<SettingError>,
200 restart_required: bool,
201 },
202
203 #[serde(other)]
205 Unknown,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
214#[serde(rename_all = "camelCase")]
215pub struct SettingsManifest {
216 pub version: u32,
217 pub tabs: Vec<SettingsTab>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct SettingsTab {
223 pub id: String,
224 pub title: String,
225 #[serde(default, skip_serializing_if = "Option::is_none")]
226 pub icon: Option<String>,
227 #[serde(default, skip_serializing_if = "Option::is_none")]
228 pub description: Option<String>,
229 pub groups: Vec<SettingsGroup>,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
233#[serde(rename_all = "camelCase")]
234pub struct SettingsGroup {
235 pub id: String,
236 pub title: String,
237 #[serde(default, skip_serializing_if = "Option::is_none")]
238 pub description: Option<String>,
239 pub fields: Vec<SettingField>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct SettingField {
245 pub key: String,
246 pub label: String,
247 #[serde(default)]
248 pub help: String,
249 pub kind: String,
251 pub backing: String,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub path: Option<String>,
255 #[serde(default, skip_serializing_if = "Option::is_none")]
256 pub env_var: Option<String>,
257 #[serde(default, skip_serializing_if = "Option::is_none")]
258 pub default: Option<Value>,
259 #[serde(default, skip_serializing_if = "Option::is_none")]
260 pub options: Option<Vec<SettingOption>>,
261 #[serde(default, skip_serializing_if = "Option::is_none")]
262 pub min: Option<f64>,
263 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub max: Option<f64>,
265 #[serde(default, skip_serializing_if = "Option::is_none")]
266 pub placeholder: Option<String>,
267 #[serde(default)]
268 pub advanced: bool,
269 #[serde(default)]
270 pub secret: bool,
271 #[serde(default)]
273 pub applies: String,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize)]
277#[serde(rename_all = "camelCase")]
278pub struct SettingOption {
279 pub value: String,
280 pub label: String,
281 #[serde(default, skip_serializing_if = "Option::is_none")]
282 pub description: Option<String>,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
286#[serde(rename_all = "camelCase")]
287pub struct SettingsSnapshot {
288 pub values: HashMap<String, SettingState>,
290 pub secrets: HashMap<String, SecretStatus>,
292 pub config_path: String,
293 pub env_path: String,
294 #[serde(default)]
298 pub mcp_servers: Vec<McpServerStatus>,
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize)]
305#[serde(rename_all = "camelCase")]
306pub struct McpServerStatus {
307 pub name: String,
308 pub transport: String,
310 pub trust: String,
312 pub scope: String,
314 #[serde(default)]
316 pub backends: Option<Vec<String>>,
317 pub enabled: bool,
318 pub builtin: bool,
320 pub health: String,
322 pub tool_count: u32,
323 pub tools: Vec<String>,
324 #[serde(default, skip_serializing_if = "Option::is_none")]
325 pub error: Option<String>,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
329#[serde(rename_all = "camelCase")]
330pub struct SettingState {
331 pub value: Value,
332 pub source: String,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
337#[serde(rename_all = "camelCase")]
338pub struct SecretStatus {
339 pub set: bool,
340 pub source: String,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
345#[serde(rename_all = "camelCase")]
346pub struct SettingError {
347 pub key: String,
348 pub message: String,
349}
350
351#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
353#[serde(rename_all = "snake_case")]
354pub enum UiRequestMethod {
355 Select,
356 Confirm,
357 Input,
358 Editor,
359}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
364#[serde(rename_all = "snake_case")]
365pub enum UiResolvedReason {
366 Answered,
367 Cancelled,
368 Timeout,
369 Interrupted,
370 #[serde(other)]
371 Other,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376#[serde(rename_all = "camelCase")]
377pub struct SessionUiRequestMsg {
378 pub session_id: String,
379 pub request_id: String,
381 pub method: UiRequestMethod,
382 pub title: String,
383 #[serde(default, skip_serializing_if = "Option::is_none")]
384 pub message: Option<String>,
385 #[serde(default, skip_serializing_if = "Option::is_none")]
387 pub options: Option<Vec<String>>,
388 #[serde(default, skip_serializing_if = "Option::is_none")]
389 pub placeholder: Option<String>,
390 #[serde(default, skip_serializing_if = "Option::is_none")]
391 pub prefill: Option<String>,
392 #[serde(default, skip_serializing_if = "Option::is_none")]
394 pub timeout_ms: Option<u64>,
395 pub timestamp: String,
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
400#[serde(rename_all = "camelCase")]
401pub struct ProviderCommand {
402 pub name: String,
404 #[serde(default, skip_serializing_if = "Option::is_none")]
405 pub description: Option<String>,
406 #[serde(default, skip_serializing_if = "Option::is_none")]
409 pub source: Option<String>,
410 #[serde(default, skip_serializing_if = "Option::is_none")]
411 pub argument_hint: Option<String>,
412}
413
414#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
416#[serde(rename_all = "snake_case")]
417pub enum ClaudeConfigScope {
418 Global,
419 Workdir,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize)]
423#[serde(rename_all = "camelCase")]
424pub struct ClaudeConfigAgent {
425 pub name: String,
426 pub description: Option<String>,
427 pub path: String,
428 pub scope: ClaudeConfigScope,
429 #[serde(default, skip_serializing_if = "Option::is_none")]
430 pub tools: Option<Vec<String>>,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
434#[serde(rename_all = "camelCase")]
435pub struct ClaudeConfigSkill {
436 pub name: String,
437 pub description: Option<String>,
438 pub path: String,
439 pub scope: ClaudeConfigScope,
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize)]
443#[serde(rename_all = "camelCase")]
444pub struct ClaudeConfigMcpServer {
445 pub name: String,
446 pub scope: ClaudeConfigScope,
447 pub path: String,
448 pub command: Option<String>,
449 pub args: Vec<String>,
450 pub env_keys: Vec<String>,
451 pub url: Option<String>,
452 #[serde(rename = "type")]
453 pub server_type: Option<String>,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
456 pub header_keys: Option<Vec<String>>,
457}
458
459#[derive(Debug, Clone, Serialize, Deserialize)]
460#[serde(rename_all = "camelCase")]
461pub struct SessionExportManifest {
462 pub exported_at: String,
463 pub session: SessionExportMetaSlim,
464 pub workdir: SessionExportWorkdir,
465 pub counts: SessionExportCounts,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
469#[serde(rename_all = "camelCase")]
470pub struct SessionExportMetaSlim {
471 pub id: String,
472 pub name: String,
473 pub created_at: String,
474 #[serde(default, skip_serializing_if = "Option::is_none")]
475 pub model: Option<String>,
476 #[serde(default, skip_serializing_if = "Option::is_none")]
477 pub mode: Option<String>,
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize)]
481#[serde(rename_all = "camelCase")]
482pub struct SessionExportWorkdir {
483 pub alias: String,
484 pub alias_source: String,
485 pub original_absolute: String,
486}
487
488#[derive(Debug, Clone, Serialize, Deserialize)]
489#[serde(rename_all = "camelCase")]
490pub struct SessionExportCounts {
491 pub messages: u32,
492 pub episodes: u32,
493 pub turns: u32,
494 pub pinned_files: u32,
495}
496
497#[derive(Debug, Clone, Serialize, Deserialize)]
498#[serde(tag = "kind", rename_all = "camelCase")]
499pub enum SessionExportPayload {
500 Inline {
501 bundle: serde_json::Value,
502 size_bytes: u64,
503 },
504 File {
505 path: String,
506 size_bytes: u64,
507 },
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
511#[serde(rename_all = "camelCase")]
512pub struct ClaudeConfigHook {
513 pub event: String,
514 pub scope: ClaudeConfigScope,
515 pub path: String,
516 pub matcher: Option<String>,
517 pub kind: String,
518 pub command: String,
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize)]
524#[serde(rename_all = "camelCase")]
525pub struct ModelInfo {
526 pub value: String,
528 pub display_name: String,
530 #[serde(default, skip_serializing_if = "Option::is_none")]
532 pub description: Option<String>,
533 #[serde(default, skip_serializing_if = "Option::is_none")]
535 pub is_default: Option<bool>,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
540#[serde(rename_all = "camelCase")]
541pub struct AuthOkMsg {
542 pub identity: MessageIdentity,
543 pub scopes: Vec<String>,
544 #[serde(default, skip_serializing_if = "Option::is_none")]
548 pub protocol_version: Option<u32>,
549 #[serde(default, skip_serializing_if = "Option::is_none")]
553 pub capabilities: Option<Vec<String>>,
554 #[serde(default, skip_serializing_if = "Option::is_none")]
558 pub providers: Option<Vec<String>>,
559}
560
561#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
562#[serde(rename_all = "snake_case")]
563pub enum ErrorCode {
564 Unauthorized,
565 Forbidden,
566 NotFound,
567 InvalidRequest,
568 RateLimited,
569 Internal,
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize)]
574#[serde(rename_all = "camelCase")]
575pub struct SessionSearchHit {
576 pub session_id: String,
577 pub session_name: String,
578 pub workdir: String,
579 pub match_count: u32,
580 pub first_match_at: i64,
581 pub last_match_at: i64,
582 pub aggregate_score: f64,
583 pub snippets: Vec<SessionSearchSnippet>,
584}
585
586#[derive(Debug, Clone, Serialize, Deserialize)]
587#[serde(rename_all = "camelCase")]
588pub struct SessionSearchSnippet {
589 pub episode_id: String,
590 pub kind: SearchSnippetKind,
591 #[serde(default, skip_serializing_if = "Option::is_none")]
592 pub tool_name: Option<String>,
593 pub summary: String,
594 pub excerpt: String,
595 pub created_at: i64,
596 pub score: f64,
597 pub file_paths: Vec<String>,
598}
599
600#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
601#[serde(rename_all = "snake_case")]
602pub enum SearchSnippetKind {
603 UserTurn,
604 AssistantTurn,
605 ToolCall,
606 Error,
607}