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 deny_unknown_fields,
65 rename_all = "snake_case",
66 rename_all_fields = "camelCase"
67)]
68pub enum ThreadRenderNode {
69 UserMessage {
70 id: String,
71 turn_id: Option<String>,
72 item_id: Option<String>,
73 text: String,
74 #[serde(default)]
75 image_urls: Vec<String>,
76 },
77 AssistantMarkdown {
78 id: String,
79 turn_id: Option<String>,
80 item_id: Option<String>,
81 text: String,
82 phase: Option<String>,
83 streaming: bool,
84 },
85 FinalSeparator {
86 id: String,
87 turn_id: Option<String>,
88 },
89 ReasoningSummary {
90 id: String,
91 turn_id: Option<String>,
92 item_id: Option<String>,
93 title: String,
94 text: String,
95 },
96 PlanUpdate {
97 id: String,
98 turn_id: Option<String>,
99 item_id: Option<String>,
100 title: String,
101 explanation: Option<String>,
102 #[serde(default)]
103 steps: Vec<RenderPlanStep>,
104 streaming: bool,
105 },
106 ProposedPlan {
107 id: String,
108 turn_id: Option<String>,
109 item_id: Option<String>,
110 title: String,
111 text: String,
112 },
113 ExecGroup {
114 id: String,
115 turn_id: Option<String>,
116 item_id: Option<String>,
117 title: String,
118 #[serde(default)]
119 kind: String,
120 state: String,
121 #[serde(default)]
122 commands: Vec<ExecCommandLine>,
123 output_text: Option<String>,
124 exit_code: Option<i64>,
125 },
126 FileChange {
127 id: String,
128 turn_id: Option<String>,
129 item_id: Option<String>,
130 title: String,
131 state: String,
132 #[serde(default)]
133 changes: Vec<FileChangeLine>,
134 },
135 McpToolCall {
136 id: String,
137 turn_id: Option<String>,
138 item_id: Option<String>,
139 title: String,
140 state: String,
141 subtitle: String,
142 detail: Option<String>,
143 },
144 DynamicToolCall {
145 id: String,
146 turn_id: Option<String>,
147 item_id: Option<String>,
148 title: String,
149 state: String,
150 subtitle: String,
151 detail: Option<String>,
152 },
153 WebSearch {
154 id: String,
155 turn_id: Option<String>,
156 item_id: Option<String>,
157 title: String,
158 state: String,
159 detail: Option<String>,
160 },
161 TerminalInteraction {
162 id: String,
163 turn_id: Option<String>,
164 item_id: Option<String>,
165 title: String,
166 command: Option<String>,
167 stdin: String,
168 waited: bool,
169 },
170 CollabEvent {
171 id: String,
172 turn_id: Option<String>,
173 item_id: Option<String>,
174 title: String,
175 #[serde(default)]
176 detail_lines: Vec<String>,
177 },
178 HookEvent {
179 id: String,
180 turn_id: Option<String>,
181 item_id: Option<String>,
182 title: String,
183 state: String,
184 #[serde(default)]
185 detail_lines: Vec<String>,
186 },
187 ApprovalReview {
188 id: String,
189 turn_id: Option<String>,
190 item_id: Option<String>,
191 title: String,
192 state: String,
193 #[serde(default)]
194 detail_lines: Vec<String>,
195 },
196 InfoNotice {
197 id: String,
198 turn_id: Option<String>,
199 item_id: Option<String>,
200 title: String,
201 detail: Option<String>,
202 },
203 ViewImage {
204 id: String,
205 turn_id: Option<String>,
206 item_id: Option<String>,
207 title: String,
208 path: String,
209 },
210 ImageGeneration {
211 id: String,
212 turn_id: Option<String>,
213 item_id: Option<String>,
214 title: String,
215 state: String,
216 prompt: Option<String>,
217 result: String,
218 saved_path: Option<String>,
219 },
220}
221
222impl Default for ThreadRenderNode {
223 fn default() -> Self {
224 Self::InfoNotice {
225 id: String::new(),
226 turn_id: None,
227 item_id: None,
228 title: String::new(),
229 detail: None,
230 }
231 }
232}