1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
4#[serde(rename_all = "camelCase")]
5#[serde(default)]
6pub struct StatusSurfaceState {
7 pub kind: String,
8 pub header: String,
9 pub details: Option<String>,
10 pub inline_message: Option<String>,
11 pub interrupt_visible: bool,
12 pub revision: i64,
13}
14
15#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
16#[serde(rename_all = "camelCase")]
17#[serde(default)]
18pub struct RenderPlanStep {
19 pub step: String,
20 pub status: String,
21}
22
23#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
24#[serde(rename_all = "camelCase")]
25#[serde(default)]
26pub struct ExecCommandLine {
27 pub label: String,
28 pub text: String,
29}
30
31#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
32#[serde(rename_all = "camelCase")]
33#[serde(default)]
34pub struct FileChangeLine {
35 pub path: String,
36 pub summary: String,
37 pub diff: Option<String>,
38}
39
40#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
41#[serde(rename_all = "camelCase")]
42#[serde(default)]
43pub struct ThreadRenderSnapshot {
44 pub runtime_id: String,
45 pub thread_id: String,
46 pub revision: i64,
47 pub status_surface: Option<StatusSurfaceState>,
48 #[serde(default)]
49 pub nodes: Vec<ThreadRenderNode>,
50}
51
52#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
53#[serde(rename_all = "camelCase")]
54#[serde(default)]
55pub struct ThreadRenderPatch {
56 pub runtime_id: String,
57 pub thread_id: String,
58 pub snapshot: ThreadRenderSnapshot,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
62#[serde(
63 tag = "type",
64 rename_all = "snake_case",
65 rename_all_fields = "camelCase"
66)]
67pub enum ThreadRenderNode {
68 UserMessage {
69 id: String,
70 #[serde(alias = "turn_id")]
71 turn_id: Option<String>,
72 #[serde(alias = "item_id")]
73 item_id: Option<String>,
74 text: String,
75 #[serde(default)]
76 #[serde(alias = "image_urls")]
77 image_urls: Vec<String>,
78 },
79 AssistantMarkdown {
80 id: String,
81 #[serde(alias = "turn_id")]
82 turn_id: Option<String>,
83 #[serde(alias = "item_id")]
84 item_id: Option<String>,
85 text: String,
86 phase: Option<String>,
87 streaming: bool,
88 },
89 FinalSeparator {
90 id: String,
91 #[serde(alias = "turn_id")]
92 turn_id: Option<String>,
93 },
94 ReasoningSummary {
95 id: String,
96 #[serde(alias = "turn_id")]
97 turn_id: Option<String>,
98 #[serde(alias = "item_id")]
99 item_id: Option<String>,
100 title: String,
101 text: String,
102 },
103 PlanUpdate {
104 id: String,
105 #[serde(alias = "turn_id")]
106 turn_id: Option<String>,
107 #[serde(alias = "item_id")]
108 item_id: Option<String>,
109 title: String,
110 explanation: Option<String>,
111 #[serde(default)]
112 steps: Vec<RenderPlanStep>,
113 streaming: bool,
114 },
115 ProposedPlan {
116 id: String,
117 #[serde(alias = "turn_id")]
118 turn_id: Option<String>,
119 #[serde(alias = "item_id")]
120 item_id: Option<String>,
121 title: String,
122 text: String,
123 },
124 ExecGroup {
125 id: String,
126 #[serde(alias = "turn_id")]
127 turn_id: Option<String>,
128 #[serde(alias = "item_id")]
129 item_id: Option<String>,
130 title: String,
131 #[serde(default)]
132 kind: String,
133 state: String,
134 #[serde(default)]
135 commands: Vec<ExecCommandLine>,
136 #[serde(alias = "output_text")]
137 output_text: Option<String>,
138 #[serde(alias = "exit_code")]
139 exit_code: Option<i64>,
140 },
141 FileChange {
142 id: String,
143 #[serde(alias = "turn_id")]
144 turn_id: Option<String>,
145 #[serde(alias = "item_id")]
146 item_id: Option<String>,
147 title: String,
148 state: String,
149 #[serde(default)]
150 changes: Vec<FileChangeLine>,
151 },
152 McpToolCall {
153 id: String,
154 #[serde(alias = "turn_id")]
155 turn_id: Option<String>,
156 #[serde(alias = "item_id")]
157 item_id: Option<String>,
158 title: String,
159 state: String,
160 subtitle: String,
161 detail: Option<String>,
162 },
163 DynamicToolCall {
164 id: String,
165 #[serde(alias = "turn_id")]
166 turn_id: Option<String>,
167 #[serde(alias = "item_id")]
168 item_id: Option<String>,
169 title: String,
170 state: String,
171 subtitle: String,
172 detail: Option<String>,
173 },
174 WebSearch {
175 id: String,
176 #[serde(alias = "turn_id")]
177 turn_id: Option<String>,
178 #[serde(alias = "item_id")]
179 item_id: Option<String>,
180 title: String,
181 state: String,
182 detail: Option<String>,
183 },
184 TerminalInteraction {
185 id: String,
186 #[serde(alias = "turn_id")]
187 turn_id: Option<String>,
188 #[serde(alias = "item_id")]
189 item_id: Option<String>,
190 title: String,
191 command: Option<String>,
192 stdin: String,
193 waited: bool,
194 },
195 CollabEvent {
196 id: String,
197 #[serde(alias = "turn_id")]
198 turn_id: Option<String>,
199 #[serde(alias = "item_id")]
200 item_id: Option<String>,
201 title: String,
202 #[serde(default)]
203 #[serde(alias = "detail_lines")]
204 detail_lines: Vec<String>,
205 },
206 HookEvent {
207 id: String,
208 #[serde(alias = "turn_id")]
209 turn_id: Option<String>,
210 #[serde(alias = "item_id")]
211 item_id: Option<String>,
212 title: String,
213 state: String,
214 #[serde(default)]
215 #[serde(alias = "detail_lines")]
216 detail_lines: Vec<String>,
217 },
218 ApprovalReview {
219 id: String,
220 #[serde(alias = "turn_id")]
221 turn_id: Option<String>,
222 #[serde(alias = "item_id")]
223 item_id: Option<String>,
224 title: String,
225 state: String,
226 #[serde(default)]
227 #[serde(alias = "detail_lines")]
228 detail_lines: Vec<String>,
229 },
230 InfoNotice {
231 id: String,
232 #[serde(alias = "turn_id")]
233 turn_id: Option<String>,
234 #[serde(alias = "item_id")]
235 item_id: Option<String>,
236 title: String,
237 detail: Option<String>,
238 },
239 ViewImage {
240 id: String,
241 #[serde(alias = "turn_id")]
242 turn_id: Option<String>,
243 #[serde(alias = "item_id")]
244 item_id: Option<String>,
245 title: String,
246 path: String,
247 },
248 ImageGeneration {
249 id: String,
250 #[serde(alias = "turn_id")]
251 turn_id: Option<String>,
252 #[serde(alias = "item_id")]
253 item_id: Option<String>,
254 title: String,
255 state: String,
256 prompt: Option<String>,
257 result: String,
258 #[serde(alias = "saved_path")]
259 saved_path: Option<String>,
260 },
261}
262
263impl Default for ThreadRenderNode {
264 fn default() -> Self {
265 Self::InfoNotice {
266 id: String::new(),
267 turn_id: None,
268 item_id: None,
269 title: String::new(),
270 detail: None,
271 }
272 }
273}