1use serde::{Deserialize, Serialize};
21use serde_json::Value;
22
23use crate::message::{MessageIdentity, SessionMessage, SessionMessageDelta};
24use crate::session::{SessionInfo, SessionStatus};
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28#[serde(tag = "type")]
29pub enum DaemonMessage {
30 #[serde(rename = "auth.ok")]
31 AuthOk(AuthOkMsg),
32
33 #[serde(rename = "response.ok", rename_all = "camelCase")]
34 ResponseOk {
35 request_id: String,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
37 data: Option<Value>,
38 },
39
40 #[serde(rename = "response.error", rename_all = "camelCase")]
41 ResponseError {
42 request_id: String,
43 error: String,
44 code: ErrorCode,
45 },
46
47 #[serde(rename = "session.list.result", rename_all = "camelCase")]
48 SessionListResult {
49 request_id: String,
50 sessions: Vec<SessionInfo>,
51 },
52
53 #[serde(rename = "models.list.result", rename_all = "camelCase")]
54 ModelsListResult {
55 request_id: String,
56 models: Vec<ModelInfo>,
57 live: bool,
59 #[serde(default)]
62 provider: Option<String>,
63 },
64
65 #[serde(rename = "session.message")]
66 SessionMessage(SessionMessage),
67
68 #[serde(rename = "session.message.delta")]
69 SessionMessageDelta(SessionMessageDelta),
70
71 #[serde(rename = "session.status_change", rename_all = "camelCase")]
72 SessionStatusChange {
73 session_id: String,
74 status: SessionStatus,
75 timestamp: String,
76 },
77
78 #[serde(rename = "session.info_update", rename_all = "camelCase")]
79 SessionInfoUpdate {
80 session: SessionInfo,
81 timestamp: String,
82 },
83
84 #[serde(rename = "scrollback.replay", rename_all = "camelCase")]
85 ScrollbackReplay {
86 session_id: String,
87 messages: Vec<SessionMessage>,
88 #[serde(default)]
91 tail: Option<bool>,
92 #[serde(default)]
94 has_more: Option<bool>,
95 },
96
97 #[serde(rename = "scrollback.page.result", rename_all = "camelCase")]
100 ScrollbackPageResult {
101 request_id: String,
102 session_id: String,
103 messages: Vec<SessionMessage>,
104 has_more: bool,
105 source: String,
108 },
109
110 #[serde(rename = "session.search.result", rename_all = "camelCase")]
111 SessionSearchResult {
112 request_id: String,
113 query: String,
114 sessions: Vec<SessionSearchHit>,
115 workspace_id: String,
116 limit: u32,
117 },
118
119 #[serde(rename = "claude.config.result", rename_all = "camelCase")]
120 ClaudeConfigResult {
121 request_id: String,
122 workdir: String,
123 agents: Vec<ClaudeConfigAgent>,
124 skills: Vec<ClaudeConfigSkill>,
125 mcp_servers: Vec<ClaudeConfigMcpServer>,
126 hooks: Vec<ClaudeConfigHook>,
127 },
128
129 #[serde(rename = "session.export.result", rename_all = "camelCase")]
130 SessionExportResult {
131 request_id: String,
132 manifest: SessionExportManifest,
133 payload: SessionExportPayload,
134 },
135
136 #[serde(rename = "session.import.result", rename_all = "camelCase")]
137 SessionImportResult {
138 request_id: String,
139 new_session_id: String,
140 imported_messages: u32,
141 imported_episodes: u32,
142 imported_turns: u32,
143 pinned_files_written: u32,
144 warnings: Vec<String>,
145 },
146
147 #[serde(rename = "session.ui_request", rename_all = "camelCase")]
154 SessionUiRequest(SessionUiRequestMsg),
155
156 #[serde(rename = "session.ui_resolved", rename_all = "camelCase")]
159 SessionUiResolved {
160 session_id: String,
161 request_id: String,
162 reason: UiResolvedReason,
163 timestamp: String,
164 },
165
166 #[serde(rename = "session.commands.result", rename_all = "camelCase")]
170 SessionCommandsResult {
171 request_id: String,
172 session_id: String,
173 provider_id: String,
174 commands: Vec<ProviderCommand>,
175 },
176
177 #[serde(other)]
179 Unknown,
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
184#[serde(rename_all = "snake_case")]
185pub enum UiRequestMethod {
186 Select,
187 Confirm,
188 Input,
189 Editor,
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
195#[serde(rename_all = "snake_case")]
196pub enum UiResolvedReason {
197 Answered,
198 Cancelled,
199 Timeout,
200 Interrupted,
201 #[serde(other)]
202 Other,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct SessionUiRequestMsg {
209 pub session_id: String,
210 pub request_id: String,
212 pub method: UiRequestMethod,
213 pub title: String,
214 #[serde(default, skip_serializing_if = "Option::is_none")]
215 pub message: Option<String>,
216 #[serde(default, skip_serializing_if = "Option::is_none")]
218 pub options: Option<Vec<String>>,
219 #[serde(default, skip_serializing_if = "Option::is_none")]
220 pub placeholder: Option<String>,
221 #[serde(default, skip_serializing_if = "Option::is_none")]
222 pub prefill: Option<String>,
223 #[serde(default, skip_serializing_if = "Option::is_none")]
225 pub timeout_ms: Option<u64>,
226 pub timestamp: String,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(rename_all = "camelCase")]
232pub struct ProviderCommand {
233 pub name: String,
235 #[serde(default, skip_serializing_if = "Option::is_none")]
236 pub description: Option<String>,
237 #[serde(default, skip_serializing_if = "Option::is_none")]
240 pub source: Option<String>,
241 #[serde(default, skip_serializing_if = "Option::is_none")]
242 pub argument_hint: Option<String>,
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
247#[serde(rename_all = "snake_case")]
248pub enum ClaudeConfigScope {
249 Global,
250 Workdir,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
254#[serde(rename_all = "camelCase")]
255pub struct ClaudeConfigAgent {
256 pub name: String,
257 pub description: Option<String>,
258 pub path: String,
259 pub scope: ClaudeConfigScope,
260 #[serde(default, skip_serializing_if = "Option::is_none")]
261 pub tools: Option<Vec<String>>,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
265#[serde(rename_all = "camelCase")]
266pub struct ClaudeConfigSkill {
267 pub name: String,
268 pub description: Option<String>,
269 pub path: String,
270 pub scope: ClaudeConfigScope,
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
274#[serde(rename_all = "camelCase")]
275pub struct ClaudeConfigMcpServer {
276 pub name: String,
277 pub scope: ClaudeConfigScope,
278 pub path: String,
279 pub command: Option<String>,
280 pub args: Vec<String>,
281 pub env_keys: Vec<String>,
282 pub url: Option<String>,
283 #[serde(rename = "type")]
284 pub server_type: Option<String>,
285 #[serde(default, skip_serializing_if = "Option::is_none")]
287 pub header_keys: Option<Vec<String>>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291#[serde(rename_all = "camelCase")]
292pub struct SessionExportManifest {
293 pub exported_at: String,
294 pub session: SessionExportMetaSlim,
295 pub workdir: SessionExportWorkdir,
296 pub counts: SessionExportCounts,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
300#[serde(rename_all = "camelCase")]
301pub struct SessionExportMetaSlim {
302 pub id: String,
303 pub name: String,
304 pub created_at: String,
305 #[serde(default, skip_serializing_if = "Option::is_none")]
306 pub model: Option<String>,
307 #[serde(default, skip_serializing_if = "Option::is_none")]
308 pub mode: Option<String>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312#[serde(rename_all = "camelCase")]
313pub struct SessionExportWorkdir {
314 pub alias: String,
315 pub alias_source: String,
316 pub original_absolute: String,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
320#[serde(rename_all = "camelCase")]
321pub struct SessionExportCounts {
322 pub messages: u32,
323 pub episodes: u32,
324 pub turns: u32,
325 pub pinned_files: u32,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
329#[serde(tag = "kind", rename_all = "camelCase")]
330pub enum SessionExportPayload {
331 Inline {
332 bundle: serde_json::Value,
333 size_bytes: u64,
334 },
335 File {
336 path: String,
337 size_bytes: u64,
338 },
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
342#[serde(rename_all = "camelCase")]
343pub struct ClaudeConfigHook {
344 pub event: String,
345 pub scope: ClaudeConfigScope,
346 pub path: String,
347 pub matcher: Option<String>,
348 pub kind: String,
349 pub command: String,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
355#[serde(rename_all = "camelCase")]
356pub struct ModelInfo {
357 pub value: String,
359 pub display_name: String,
361 #[serde(default, skip_serializing_if = "Option::is_none")]
363 pub description: Option<String>,
364 #[serde(default, skip_serializing_if = "Option::is_none")]
366 pub is_default: Option<bool>,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize)]
371#[serde(rename_all = "camelCase")]
372pub struct AuthOkMsg {
373 pub identity: MessageIdentity,
374 pub scopes: Vec<String>,
375 #[serde(default, skip_serializing_if = "Option::is_none")]
379 pub protocol_version: Option<u32>,
380 #[serde(default, skip_serializing_if = "Option::is_none")]
384 pub capabilities: Option<Vec<String>>,
385 #[serde(default, skip_serializing_if = "Option::is_none")]
389 pub providers: Option<Vec<String>>,
390}
391
392#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
393#[serde(rename_all = "snake_case")]
394pub enum ErrorCode {
395 Unauthorized,
396 Forbidden,
397 NotFound,
398 InvalidRequest,
399 RateLimited,
400 Internal,
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
405#[serde(rename_all = "camelCase")]
406pub struct SessionSearchHit {
407 pub session_id: String,
408 pub session_name: String,
409 pub workdir: String,
410 pub match_count: u32,
411 pub first_match_at: i64,
412 pub last_match_at: i64,
413 pub aggregate_score: f64,
414 pub snippets: Vec<SessionSearchSnippet>,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
418#[serde(rename_all = "camelCase")]
419pub struct SessionSearchSnippet {
420 pub episode_id: String,
421 pub kind: SearchSnippetKind,
422 #[serde(default, skip_serializing_if = "Option::is_none")]
423 pub tool_name: Option<String>,
424 pub summary: String,
425 pub excerpt: String,
426 pub created_at: i64,
427 pub score: f64,
428 pub file_paths: Vec<String>,
429}
430
431#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
432#[serde(rename_all = "snake_case")]
433pub enum SearchSnippetKind {
434 UserTurn,
435 AssistantTurn,
436 ToolCall,
437 Error,
438}