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 },
30
31 #[serde(rename = "session.list", rename_all = "camelCase")]
32 SessionList { id: String },
33
34 #[serde(rename = "models.list", rename_all = "camelCase")]
37 ModelsList { id: String },
38
39 #[serde(rename = "session.attach", rename_all = "camelCase")]
40 SessionAttach { id: String, session_id: String },
41
42 #[serde(rename = "session.detach", rename_all = "camelCase")]
43 SessionDetach { id: String, session_id: String },
44
45 #[serde(rename = "session.send", rename_all = "camelCase")]
46 SessionSend {
47 id: String,
48 session_id: String,
49 text: String,
50 #[serde(default, skip_serializing_if = "Option::is_none")]
51 attachments: Option<Vec<Attachment>>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 priority: Option<SendPriority>,
54 },
55
56 #[serde(rename = "session.interrupt", rename_all = "camelCase")]
57 SessionInterrupt { id: String, session_id: String },
58
59 #[serde(rename = "session.approve", rename_all = "camelCase")]
60 SessionApprove {
61 id: String,
62 session_id: String,
63 approval_id: String,
64 approved: bool,
65 #[serde(default, skip_serializing_if = "Option::is_none")]
70 updated_input: Option<serde_json::Value>,
71 },
72
73 #[serde(rename = "session.destroy", rename_all = "camelCase")]
74 SessionDestroy { id: String, session_id: String },
75
76 #[serde(rename = "session.set_mode", rename_all = "camelCase")]
77 SessionSetMode {
78 id: String,
79 session_id: String,
80 mode: SessionMode,
81 #[serde(default, skip_serializing_if = "Option::is_none")]
82 max_turns: Option<u32>,
83 },
84
85 #[serde(rename = "session.pin", rename_all = "camelCase")]
86 SessionPin {
87 id: String,
88 session_id: String,
89 path: String,
90 },
91
92 #[serde(rename = "session.unpin", rename_all = "camelCase")]
93 SessionUnpin {
94 id: String,
95 session_id: String,
96 path: String,
97 },
98
99 #[serde(rename = "session.rotate", rename_all = "camelCase")]
100 SessionRotate { id: String, session_id: String },
101
102 #[serde(rename = "session.search", rename_all = "camelCase")]
103 SessionSearch {
104 id: String,
105 query: String,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
107 scope: Option<SearchScope>,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
109 workdir: Option<String>,
110 #[serde(default, skip_serializing_if = "Option::is_none")]
111 limit: Option<u32>,
112 },
113
114 #[serde(rename = "session.set_model", rename_all = "camelCase")]
115 SessionSetModel {
116 id: String,
117 session_id: String,
118 model: String,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 fallback_model: Option<Option<String>>,
121 },
122
123 #[serde(rename = "session.rename", rename_all = "camelCase")]
124 SessionRename {
125 id: String,
126 session_id: String,
127 name: String,
128 },
129
130 #[serde(rename = "claude.config", rename_all = "camelCase")]
134 ClaudeConfig { id: String, session_id: String },
135
136 #[serde(rename = "session.export", rename_all = "camelCase")]
138 SessionExport {
139 id: String,
140 session_id: String,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
142 include_memory: Option<bool>,
143 #[serde(default, skip_serializing_if = "Option::is_none")]
144 include_pinned_files: Option<bool>,
145 #[serde(default, skip_serializing_if = "Option::is_none")]
146 alias_override: Option<String>,
147 #[serde(default, skip_serializing_if = "Option::is_none")]
148 to_file: Option<bool>,
149 },
150
151 #[serde(rename = "session.import", rename_all = "camelCase")]
154 SessionImport {
155 id: String,
156 source: SessionImportSource,
157 target_workdir: String,
158 #[serde(default, skip_serializing_if = "Option::is_none")]
159 name_override: Option<String>,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
161 write_pinned_files: Option<bool>,
162 },
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167#[serde(tag = "kind", rename_all = "camelCase")]
168pub enum SessionImportSource {
169 Inline { bundle: serde_json::Value },
170 File { path: String },
171}
172
173impl ClientMessage {
174 #[must_use]
177 pub fn request_id(&self) -> &str {
178 match self {
179 Self::SessionCreate { id, .. }
180 | Self::SessionList { id }
181 | Self::ModelsList { id }
182 | Self::SessionAttach { id, .. }
183 | Self::SessionDetach { id, .. }
184 | Self::SessionSend { id, .. }
185 | Self::SessionInterrupt { id, .. }
186 | Self::SessionApprove { id, .. }
187 | Self::SessionDestroy { id, .. }
188 | Self::SessionSetMode { id, .. }
189 | Self::SessionPin { id, .. }
190 | Self::SessionUnpin { id, .. }
191 | Self::SessionRotate { id, .. }
192 | Self::SessionSearch { id, .. }
193 | Self::SessionSetModel { id, .. }
194 | Self::SessionRename { id, .. }
195 | Self::ClaudeConfig { id, .. }
196 | Self::SessionExport { id, .. }
197 | Self::SessionImport { id, .. } => id,
198 }
199 }
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
204#[serde(rename_all = "camelCase")]
205pub struct Attachment {
206 pub path: String,
207 #[serde(default, skip_serializing_if = "Option::is_none")]
208 pub content: Option<String>,
209 #[serde(default, skip_serializing_if = "Option::is_none")]
210 pub mime_type: Option<String>,
211 #[serde(default, skip_serializing_if = "Option::is_none")]
213 pub data: Option<String>,
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
222#[serde(rename_all = "snake_case")]
223pub enum SendPriority {
224 Now,
225 Next,
226 Later,
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
230#[serde(rename_all = "snake_case")]
231pub enum SearchScope {
232 Workspace,
233 All,
234}