1use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::collections::BTreeMap;
7
8use crate::tool::ToolInfo;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(rename_all = "snake_case")]
13pub enum IdentityType {
14 Human,
15 Agent,
16 Subagent,
17 System,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(rename_all = "camelCase")]
23pub struct MessageIdentity {
24 pub sub: String,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub name: Option<String>,
28 #[serde(rename = "type")]
29 pub kind: IdentityType,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub enum MessageRole {
37 User,
38 Assistant,
39 Thinking,
40 ToolCall,
41 ToolResult,
42 System,
43 Info,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
49#[serde(tag = "kind", rename_all = "snake_case")]
50pub enum ContentPart {
51 Text {
52 text: String,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
54 markdown: Option<bool>,
55 },
56 Code {
57 code: String,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 language: Option<String>,
60 #[serde(default, skip_serializing_if = "Option::is_none", rename = "filePath")]
61 file_path: Option<String>,
62 },
63 FileRef {
64 path: String,
65 #[serde(default, skip_serializing_if = "Option::is_none")]
66 lines: Option<[u32; 2]>,
67 #[serde(default, skip_serializing_if = "Option::is_none")]
68 change: Option<FileChange>,
69 },
70 Diff {
71 path: String,
72 added: u32,
73 removed: u32,
74 #[serde(
75 default,
76 skip_serializing_if = "Option::is_none",
77 rename = "originalPath"
78 )]
79 original_path: Option<String>,
80 },
81 Tree {
82 label: String,
83 children: Vec<TreeNode>,
84 },
85 Button {
86 label: String,
87 action: String,
88 #[serde(default, skip_serializing_if = "Option::is_none")]
89 data: Option<BTreeMap<String, Value>>,
90 #[serde(default, skip_serializing_if = "Option::is_none")]
91 style: Option<ButtonStyle>,
92 },
93 Progress {
94 message: String,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 percent: Option<u8>,
97 #[serde(default, skip_serializing_if = "Option::is_none", rename = "elapsedMs")]
98 elapsed_ms: Option<u64>,
99 },
100 Image {
101 url: String,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 alt: Option<String>,
104 },
105 Anchor {
106 uri: String,
107 title: String,
108 },
109 Table {
110 headers: Vec<String>,
111 rows: Vec<Vec<String>>,
112 },
113 #[serde(other, deserialize_with = "deserialize_unknown")]
117 Unknown,
118}
119
120fn deserialize_unknown<'de, D>(deserializer: D) -> Result<(), D::Error>
121where
122 D: serde::Deserializer<'de>,
123{
124 serde::de::IgnoredAny::deserialize(deserializer).map(|_| ())
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct FileChange {
130 pub added: u32,
131 pub removed: u32,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135#[serde(rename_all = "camelCase")]
136pub struct TreeNode {
137 pub label: String,
138 #[serde(rename = "type")]
139 pub kind: TreeNodeType,
140 #[serde(default, skip_serializing_if = "Option::is_none")]
141 pub path: Option<String>,
142 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub children: Option<Vec<TreeNode>>,
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147#[serde(rename_all = "snake_case")]
148pub enum TreeNodeType {
149 File,
150 Directory,
151}
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
154#[serde(rename_all = "snake_case")]
155pub enum ButtonStyle {
156 Primary,
157 Secondary,
158 Danger,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
166#[serde(rename_all = "camelCase")]
167pub struct SessionMessage {
168 pub session_id: String,
169 pub message_id: String,
170 pub role: MessageRole,
171 pub content: String,
173 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub parts: Option<Vec<ContentPart>>,
176 pub identity: MessageIdentity,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub tool: Option<ToolInfo>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub metadata: Option<BTreeMap<String, Value>>,
181 pub timestamp: String,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct SessionMessageDelta {
192 pub session_id: String,
193 pub message_id: String,
194
195 #[serde(default, skip_serializing_if = "Option::is_none")]
196 pub content_append: Option<String>,
197
198 #[serde(default, skip_serializing_if = "Option::is_none")]
199 pub parts_append: Option<Vec<ContentPart>>,
200
201 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub parts_update: Option<Vec<PartsUpdate>>,
203
204 #[serde(default, skip_serializing_if = "Option::is_none")]
205 pub tool_state_update: Option<crate::tool::ToolState>,
206
207 pub timestamp: String,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct PartsUpdate {
212 pub index: u32,
213 pub part: ContentPart,
214}