1use serde::{Deserialize, Serialize};
16use serde_json;
17
18use crate::session::SessionMode;
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(tag = "type")]
23pub enum ClientMessage {
24 #[serde(rename = "session.create", rename_all = "camelCase")]
25 SessionCreate {
26 id: String,
27 name: String,
28 workdir: String,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
32 provider_id: Option<String>,
33 },
34
35 #[serde(rename = "session.list", rename_all = "camelCase")]
36 SessionList { id: String },
37
38 #[serde(rename = "models.list", rename_all = "camelCase")]
41 ModelsList {
42 id: String,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
46 provider: Option<String>,
47 },
48
49 #[serde(rename = "session.attach", rename_all = "camelCase")]
50 SessionAttach { id: String, session_id: String },
51
52 #[serde(rename = "session.detach", rename_all = "camelCase")]
53 SessionDetach { id: String, session_id: String },
54
55 #[serde(rename = "session.send", rename_all = "camelCase")]
56 SessionSend {
57 id: String,
58 session_id: String,
59 text: String,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
61 attachments: Option<Vec<Attachment>>,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
63 priority: Option<SendPriority>,
64 },
65
66 #[serde(rename = "session.interrupt", rename_all = "camelCase")]
67 SessionInterrupt { id: String, session_id: String },
68
69 #[serde(rename = "session.approve", rename_all = "camelCase")]
70 SessionApprove {
71 id: String,
72 session_id: String,
73 approval_id: String,
74 approved: bool,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
80 updated_input: Option<serde_json::Value>,
81 },
82
83 #[serde(rename = "session.ui_response", rename_all = "camelCase")]
90 SessionUiResponse {
91 id: String,
92 session_id: String,
93 request_id: String,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
95 value: Option<String>,
96 #[serde(default, skip_serializing_if = "Option::is_none")]
97 confirmed: Option<bool>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 cancelled: Option<bool>,
100 },
101
102 #[serde(rename = "session.part_action", rename_all = "camelCase")]
106 SessionPartAction {
107 id: String,
108 session_id: String,
109 message_id: String,
110 action: String,
111 #[serde(default, skip_serializing_if = "Option::is_none")]
112 data: Option<serde_json::Value>,
113 },
114
115 #[serde(rename = "session.commands", rename_all = "camelCase")]
119 SessionCommands { id: String, session_id: String },
120
121 #[serde(rename = "session.destroy", rename_all = "camelCase")]
122 SessionDestroy { id: String, session_id: String },
123
124 #[serde(rename = "session.set_mode", rename_all = "camelCase")]
125 SessionSetMode {
126 id: String,
127 session_id: String,
128 mode: SessionMode,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 max_turns: Option<u32>,
131 },
132
133 #[serde(rename = "session.pin", rename_all = "camelCase")]
134 SessionPin {
135 id: String,
136 session_id: String,
137 path: String,
138 },
139
140 #[serde(rename = "session.unpin", rename_all = "camelCase")]
141 SessionUnpin {
142 id: String,
143 session_id: String,
144 path: String,
145 },
146
147 #[serde(rename = "session.rotate", rename_all = "camelCase")]
148 SessionRotate { id: String, session_id: String },
149
150 #[serde(rename = "session.search", rename_all = "camelCase")]
151 SessionSearch {
152 id: String,
153 query: String,
154 #[serde(default, skip_serializing_if = "Option::is_none")]
155 scope: Option<SearchScope>,
156 #[serde(default, skip_serializing_if = "Option::is_none")]
157 workdir: Option<String>,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
159 limit: Option<u32>,
160 },
161
162 #[serde(rename = "session.set_provider", rename_all = "camelCase")]
167 SessionSetProvider {
168 id: String,
169 session_id: String,
170 provider_id: String,
171 },
172
173 #[serde(rename = "session.fork", rename_all = "camelCase")]
180 SessionFork {
181 id: String,
182 session_id: String,
183 #[serde(default, skip_serializing_if = "Option::is_none")]
184 name: Option<String>,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
186 provider_id: Option<String>,
187 },
188
189 #[serde(rename = "scrollback.page", rename_all = "camelCase")]
193 ScrollbackPage {
194 id: String,
195 session_id: String,
196 before_message_id: String,
198 #[serde(default, skip_serializing_if = "Option::is_none")]
199 max_bytes: Option<u64>,
200 },
201
202 #[serde(rename = "session.set_model", rename_all = "camelCase")]
203 SessionSetModel {
204 id: String,
205 session_id: String,
206 model: String,
207 #[serde(default, skip_serializing_if = "Option::is_none")]
208 fallback_model: Option<Option<String>>,
209 },
210
211 #[serde(rename = "session.rename", rename_all = "camelCase")]
212 SessionRename {
213 id: String,
214 session_id: String,
215 name: String,
216 },
217
218 #[serde(rename = "claude.config", rename_all = "camelCase")]
222 ClaudeConfig { id: String, session_id: String },
223
224 #[serde(rename = "session.export", rename_all = "camelCase")]
226 SessionExport {
227 id: String,
228 session_id: String,
229 #[serde(default, skip_serializing_if = "Option::is_none")]
230 include_memory: Option<bool>,
231 #[serde(default, skip_serializing_if = "Option::is_none")]
232 include_pinned_files: Option<bool>,
233 #[serde(default, skip_serializing_if = "Option::is_none")]
234 alias_override: Option<String>,
235 #[serde(default, skip_serializing_if = "Option::is_none")]
236 to_file: Option<bool>,
237 },
238
239 #[serde(rename = "session.import", rename_all = "camelCase")]
242 SessionImport {
243 id: String,
244 source: SessionImportSource,
245 target_workdir: String,
246 #[serde(default, skip_serializing_if = "Option::is_none")]
247 name_override: Option<String>,
248 #[serde(default, skip_serializing_if = "Option::is_none")]
249 write_pinned_files: Option<bool>,
250 },
251
252 #[serde(rename = "settings.schema", rename_all = "camelCase")]
255 SettingsSchema { id: String },
256
257 #[serde(rename = "settings.get", rename_all = "camelCase")]
260 SettingsGet { id: String },
261
262 #[serde(rename = "settings.set", rename_all = "camelCase")]
266 SettingsSet {
267 id: String,
268 patches: Vec<SettingPatch>,
269 },
270}
271
272#[derive(Debug, Clone, Serialize, Deserialize)]
275#[serde(rename_all = "camelCase")]
276pub struct SettingPatch {
277 pub key: String,
278 pub value: serde_json::Value,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283#[serde(tag = "kind", rename_all = "camelCase")]
284pub enum SessionImportSource {
285 Inline { bundle: serde_json::Value },
286 File { path: String },
287}
288
289impl ClientMessage {
290 #[must_use]
293 pub fn request_id(&self) -> &str {
294 match self {
295 Self::SessionCreate { id, .. }
296 | Self::SessionList { id }
297 | Self::ModelsList { id, .. }
298 | Self::SessionAttach { id, .. }
299 | Self::SessionDetach { id, .. }
300 | Self::SessionSend { id, .. }
301 | Self::SessionInterrupt { id, .. }
302 | Self::SessionApprove { id, .. }
303 | Self::SessionUiResponse { id, .. }
304 | Self::SessionPartAction { id, .. }
305 | Self::SessionCommands { id, .. }
306 | Self::SessionDestroy { id, .. }
307 | Self::SessionSetMode { id, .. }
308 | Self::SessionPin { id, .. }
309 | Self::SessionUnpin { id, .. }
310 | Self::SessionRotate { id, .. }
311 | Self::SessionSearch { id, .. }
312 | Self::SessionSetProvider { id, .. }
313 | Self::SessionFork { id, .. }
314 | Self::ScrollbackPage { id, .. }
315 | Self::SessionSetModel { id, .. }
316 | Self::SessionRename { id, .. }
317 | Self::ClaudeConfig { id, .. }
318 | Self::SessionExport { id, .. }
319 | Self::SessionImport { id, .. }
320 | Self::SettingsSchema { id }
321 | Self::SettingsGet { id }
322 | Self::SettingsSet { id, .. } => id,
323 }
324 }
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize)]
329#[serde(rename_all = "camelCase")]
330pub struct Attachment {
331 pub path: String,
332 #[serde(default, skip_serializing_if = "Option::is_none")]
333 pub content: Option<String>,
334 #[serde(default, skip_serializing_if = "Option::is_none")]
335 pub mime_type: Option<String>,
336 #[serde(default, skip_serializing_if = "Option::is_none")]
338 pub data: Option<String>,
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
347#[serde(rename_all = "snake_case")]
348pub enum SendPriority {
349 Now,
350 Next,
351 Later,
352}
353
354#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
355#[serde(rename_all = "snake_case")]
356pub enum SearchScope {
357 Workspace,
358 All,
359}