a2a_protocol_types/
responses.rs1use serde::{Deserialize, Serialize};
18
19use crate::agent_card::AgentCard;
20use crate::message::Message;
21use crate::task::Task;
22
23#[non_exhaustive]
34#[derive(Debug, Clone)]
35pub enum SendMessageResponse {
36 Task(Task),
38
39 Message(Message),
41}
42
43impl Serialize for SendMessageResponse {
44 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
45 match self {
48 Self::Task(task) => task.serialize(serializer),
49 Self::Message(msg) => msg.serialize(serializer),
50 }
51 }
52}
53
54impl<'de> Deserialize<'de> for SendMessageResponse {
55 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
56 let value = serde_json::Value::deserialize(deserializer)?;
57
58 if value.get("role").is_some() {
60 serde_json::from_value::<Message>(value.clone())
62 .map(SendMessageResponse::Message)
63 .or_else(|_| {
64 serde_json::from_value::<Task>(value)
65 .map(SendMessageResponse::Task)
66 .map_err(serde::de::Error::custom)
67 })
68 } else {
69 serde_json::from_value::<Task>(value)
71 .map(SendMessageResponse::Task)
72 .map_err(serde::de::Error::custom)
73 }
74 }
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
81#[serde(rename_all = "camelCase")]
82pub struct TaskListResponse {
83 pub tasks: Vec<Task>,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub next_page_token: Option<String>,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub page_size: Option<u32>,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
96 pub total_size: Option<u32>,
97}
98
99impl TaskListResponse {
100 #[must_use]
102 pub const fn new(tasks: Vec<Task>) -> Self {
103 Self {
104 tasks,
105 next_page_token: None,
106 page_size: None,
107 total_size: None,
108 }
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
116#[serde(rename_all = "camelCase")]
117pub struct ListPushConfigsResponse {
118 pub configs: Vec<crate::push::TaskPushNotificationConfig>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub next_page_token: Option<String>,
124}
125
126pub type AuthenticatedExtendedCardResponse = AgentCard;
133
134#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::message::{MessageId, MessageRole, Part};
140 use crate::task::{ContextId, TaskId, TaskState, TaskStatus};
141
142 fn make_task() -> Task {
143 Task {
144 id: TaskId::new("t1"),
145 context_id: ContextId::new("c1"),
146 status: TaskStatus::new(TaskState::Completed),
147 history: None,
148 artifacts: None,
149 metadata: None,
150 }
151 }
152
153 fn make_message() -> Message {
154 Message {
155 id: MessageId::new("m1"),
156 role: MessageRole::Agent,
157 parts: vec![Part::text("hi")],
158 task_id: None,
159 context_id: None,
160 reference_task_ids: None,
161 extensions: None,
162 metadata: None,
163 }
164 }
165
166 #[test]
167 fn send_message_response_task_variant() {
168 let resp = SendMessageResponse::Task(make_task());
169 let json = serde_json::to_string(&resp).expect("serialize");
170 assert!(
171 !json.contains("\"kind\""),
172 "v1.0 should not have kind: {json}"
173 );
174
175 let back: SendMessageResponse = serde_json::from_str(&json).expect("deserialize");
176 match &back {
177 SendMessageResponse::Task(t) => {
178 assert_eq!(t.id, TaskId::new("t1"));
179 assert_eq!(t.status.state, TaskState::Completed);
180 }
181 _ => panic!("expected Task variant"),
182 }
183 }
184
185 #[test]
186 fn send_message_response_message_variant() {
187 let resp = SendMessageResponse::Message(make_message());
188 let json = serde_json::to_string(&resp).expect("serialize");
189 assert!(
190 !json.contains("\"kind\""),
191 "v1.0 should not have kind: {json}"
192 );
193
194 let back: SendMessageResponse = serde_json::from_str(&json).expect("deserialize");
195 match &back {
196 SendMessageResponse::Message(m) => {
197 assert_eq!(m.id, MessageId::new("m1"));
198 assert_eq!(m.role, MessageRole::Agent);
199 }
200 _ => panic!("expected Message variant"),
201 }
202 }
203
204 #[test]
207 fn send_message_response_fallback_role_field_to_task() {
208 let json = serde_json::json!({
213 "id": "t1",
214 "contextId": "c1",
215 "status": {"state": "completed"},
216 "role": "unexpected_extra_field"
217 });
218 let back: SendMessageResponse =
219 serde_json::from_value(json).expect("should fall back to Task");
220 match back {
221 SendMessageResponse::Task(task) => {
222 assert_eq!(task.id.as_ref(), "t1");
223 assert_eq!(task.context_id.as_ref(), "c1");
224 }
225 other => panic!("expected Task variant, got {other:?}"),
226 }
227 }
228
229 #[test]
230 fn task_list_response_roundtrip() {
231 let resp = TaskListResponse {
232 tasks: vec![make_task()],
233 next_page_token: Some("cursor-abc".into()),
234 page_size: Some(10),
235 total_size: Some(1),
236 };
237 let json = serde_json::to_string(&resp).expect("serialize");
238 assert!(json.contains("\"nextPageToken\":\"cursor-abc\""));
239
240 let back: TaskListResponse = serde_json::from_str(&json).expect("deserialize");
241 assert_eq!(back.tasks.len(), 1);
242 assert_eq!(back.next_page_token.as_deref(), Some("cursor-abc"));
243 }
244
245 #[test]
246 fn task_list_response_no_token_omitted() {
247 let resp = TaskListResponse::new(vec![]);
248 let json = serde_json::to_string(&resp).expect("serialize");
249 assert!(
250 !json.contains("\"nextPageToken\""),
251 "token should be absent: {json}"
252 );
253 }
254
255 #[test]
257 fn send_message_response_disambiguates_task() {
258 let json = serde_json::json!({
259 "id": "t1",
260 "contextId": "c1",
261 "status": { "state": "completed" }
262 });
263 let resp: SendMessageResponse =
264 serde_json::from_value(json).expect("should deserialize as Task");
265 assert!(
266 matches!(resp, SendMessageResponse::Task(_)),
267 "expected Task variant"
268 );
269 }
270
271 #[test]
273 fn send_message_response_disambiguates_message() {
274 let json = serde_json::json!({
275 "messageId": "m1",
276 "role": "agent",
277 "parts": [{ "type": "text", "text": "hi" }]
278 });
279 let resp: SendMessageResponse =
280 serde_json::from_value(json).expect("should deserialize as Message");
281 assert!(
282 matches!(resp, SendMessageResponse::Message(_)),
283 "expected Message variant"
284 );
285 }
286
287 #[test]
290 fn send_message_response_message_with_task_like_fields() {
291 let json = serde_json::json!({
292 "messageId": "m1",
293 "role": "agent",
294 "parts": [{ "type": "text", "text": "hi" }],
295 "contextId": "c1",
296 "taskId": "t1"
297 });
298 let resp: SendMessageResponse =
299 serde_json::from_value(json).expect("should deserialize as Message");
300 assert!(
301 matches!(resp, SendMessageResponse::Message(_)),
302 "expected Message variant even with task-like fields"
303 );
304 }
305}