1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use uuid::Uuid;
8
9pub const PROTOCOL_VERSION: &str = "1.0";
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
15#[serde(rename_all = "camelCase")]
16pub struct AgentCard {
17 pub name: String,
19 pub description: String,
21 #[serde(default)]
23 pub supported_interfaces: Vec<AgentInterface>,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub provider: Option<AgentProvider>,
27 pub version: String,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub documentation_url: Option<String>,
32 #[serde(default)]
34 pub capabilities: AgentCapabilities,
35 #[serde(default)]
37 pub security_schemes: HashMap<String, SecurityScheme>,
38 #[serde(default)]
40 pub security_requirements: Vec<SecurityRequirement>,
41 #[serde(default)]
43 pub default_input_modes: Vec<String>,
44 #[serde(default)]
46 pub default_output_modes: Vec<String>,
47 #[serde(default)]
49 pub skills: Vec<AgentSkill>,
50 #[serde(default, skip_serializing_if = "Vec::is_empty")]
52 pub signatures: Vec<AgentCardSignature>,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub icon_url: Option<String>,
56}
57
58impl AgentCard {
59 pub fn endpoint(&self) -> Option<&str> {
61 self.supported_interfaces
62 .iter()
63 .find(|i| i.protocol_binding.eq_ignore_ascii_case("jsonrpc"))
64 .map(|i| i.url.as_str())
65 }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
70#[serde(rename_all = "camelCase")]
71pub struct AgentInterface {
72 pub url: String,
73 pub protocol_binding: String,
74 pub protocol_version: String,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub tenant: Option<String>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
81#[serde(rename_all = "camelCase")]
82pub struct AgentProvider {
83 pub organization: String,
85 pub url: String,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
91#[serde(rename_all = "camelCase")]
92#[non_exhaustive]
93pub enum SecurityScheme {
94 ApiKeySecurityScheme(ApiKeySecurityScheme),
95 HttpAuthSecurityScheme(HttpAuthSecurityScheme),
96 Oauth2SecurityScheme(OAuth2SecurityScheme),
97 OpenIdConnectSecurityScheme(OpenIdConnectSecurityScheme),
98 MtlsSecurityScheme(MutualTlsSecurityScheme),
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
102#[serde(rename_all = "camelCase")]
103pub struct ApiKeySecurityScheme {
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub description: Option<String>,
106 pub location: String,
108 pub name: String,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
113#[serde(rename_all = "camelCase")]
114pub struct HttpAuthSecurityScheme {
115 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub description: Option<String>,
117 pub scheme: String,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub bearer_format: Option<String>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124#[serde(rename_all = "camelCase")]
125pub struct OAuth2SecurityScheme {
126 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub description: Option<String>,
128 pub flows: OAuthFlows,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub oauth2_metadata_url: Option<String>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
134#[serde(rename_all = "camelCase")]
135pub struct OpenIdConnectSecurityScheme {
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub description: Option<String>,
138 pub open_id_connect_url: String,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
142#[serde(rename_all = "camelCase")]
143pub struct MutualTlsSecurityScheme {
144 #[serde(default, skip_serializing_if = "Option::is_none")]
145 pub description: Option<String>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
150#[serde(rename_all = "camelCase")]
151#[non_exhaustive]
152pub enum OAuthFlows {
153 AuthorizationCode(AuthorizationCodeOAuthFlow),
154 ClientCredentials(ClientCredentialsOAuthFlow),
155 Implicit(ImplicitOAuthFlow),
157 Password(PasswordOAuthFlow),
159 DeviceCode(DeviceCodeOAuthFlow),
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
163#[serde(rename_all = "camelCase")]
164pub struct AuthorizationCodeOAuthFlow {
165 pub authorization_url: String,
166 pub token_url: String,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub refresh_url: Option<String>,
169 #[serde(default)]
170 pub scopes: HashMap<String, String>,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
172 pub pkce_required: Option<bool>,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
176#[serde(rename_all = "camelCase")]
177pub struct ClientCredentialsOAuthFlow {
178 pub token_url: String,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub refresh_url: Option<String>,
181 #[serde(default)]
182 pub scopes: HashMap<String, String>,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
186#[serde(rename_all = "camelCase")]
187pub struct ImplicitOAuthFlow {
188 pub authorization_url: String,
189 #[serde(default, skip_serializing_if = "Option::is_none")]
190 pub refresh_url: Option<String>,
191 #[serde(default)]
192 pub scopes: HashMap<String, String>,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
196#[serde(rename_all = "camelCase")]
197pub struct PasswordOAuthFlow {
198 pub token_url: String,
199 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub refresh_url: Option<String>,
201 #[serde(default)]
202 pub scopes: HashMap<String, String>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
206#[serde(rename_all = "camelCase")]
207pub struct DeviceCodeOAuthFlow {
208 pub device_authorization_url: String,
209 pub token_url: String,
210 #[serde(default, skip_serializing_if = "Option::is_none")]
211 pub refresh_url: Option<String>,
212 #[serde(default)]
213 pub scopes: HashMap<String, String>,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
218#[serde(rename_all = "camelCase")]
219pub struct StringList {
220 #[serde(default)]
221 pub list: Vec<String>,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
226#[serde(rename_all = "camelCase")]
227pub struct SecurityRequirement {
228 #[serde(default)]
230 pub schemes: HashMap<String, StringList>,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
235#[serde(rename_all = "camelCase")]
236pub struct AgentCardSignature {
237 pub protected: String,
239 pub signature: String,
241 #[serde(default, skip_serializing_if = "Option::is_none")]
243 pub header: Option<serde_json::Value>,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
247#[serde(rename_all = "camelCase")]
248pub struct AgentCapabilities {
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub streaming: Option<bool>,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub push_notifications: Option<bool>,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub extended_agent_card: Option<bool>,
255 #[serde(default, skip_serializing_if = "Vec::is_empty")]
256 pub extensions: Vec<AgentExtension>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
260#[serde(rename_all = "camelCase")]
261pub struct AgentExtension {
262 pub uri: String,
263 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub description: Option<String>,
265 #[serde(default, skip_serializing_if = "Option::is_none")]
266 pub required: Option<bool>,
267 #[serde(default, skip_serializing_if = "Option::is_none")]
268 pub params: Option<serde_json::Value>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
273#[serde(rename_all = "camelCase")]
274pub struct AgentSkill {
275 pub id: String,
277 pub name: String,
279 pub description: String,
281 #[serde(default)]
283 pub tags: Vec<String>,
284 #[serde(default)]
286 pub examples: Vec<String>,
287 #[serde(default)]
289 pub input_modes: Vec<String>,
290 #[serde(default)]
292 pub output_modes: Vec<String>,
293 #[serde(default)]
295 pub security_requirements: Vec<SecurityRequirement>,
296}
297
298#[derive(Debug, Clone, PartialEq)]
309pub enum Part {
310 Text {
312 text: String,
314 metadata: Option<serde_json::Value>,
316 },
317 File {
319 file: FileContent,
321 metadata: Option<serde_json::Value>,
323 },
324 Data {
326 data: serde_json::Value,
328 metadata: Option<serde_json::Value>,
330 },
331}
332
333impl Serialize for Part {
334 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
335 use serde::ser::SerializeMap;
336 match self {
337 Part::Text { text, metadata } => {
338 let mut map = serializer.serialize_map(None)?;
339 map.serialize_entry("text", text)?;
340 if let Some(m) = metadata {
341 map.serialize_entry("metadata", m)?;
342 }
343 map.end()
344 }
345 Part::File { file, metadata } => {
346 let mut map = serializer.serialize_map(None)?;
347 map.serialize_entry("file", file)?;
348 if let Some(m) = metadata {
349 map.serialize_entry("metadata", m)?;
350 }
351 map.end()
352 }
353 Part::Data { data, metadata } => {
354 let mut map = serializer.serialize_map(None)?;
355 map.serialize_entry("data", data)?;
356 if let Some(m) = metadata {
357 map.serialize_entry("metadata", m)?;
358 }
359 map.end()
360 }
361 }
362 }
363}
364
365impl<'de> Deserialize<'de> for Part {
366 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
367 let value = serde_json::Value::deserialize(deserializer)?;
368 let obj = value
369 .as_object()
370 .ok_or_else(|| serde::de::Error::custom("expected object for Part"))?;
371 let metadata = obj.get("metadata").cloned();
372
373 if let Some(text) = obj.get("text") {
374 Ok(Part::Text {
375 text: text
376 .as_str()
377 .ok_or_else(|| serde::de::Error::custom("text must be a string"))?
378 .to_string(),
379 metadata,
380 })
381 } else if let Some(file) = obj.get("file") {
382 let file: FileContent = serde_json::from_value(file.clone())
383 .map_err(serde::de::Error::custom)?;
384 Ok(Part::File { file, metadata })
385 } else if let Some(data) = obj.get("data") {
386 Ok(Part::Data {
387 data: data.clone(),
388 metadata,
389 })
390 } else if obj.contains_key("kind") {
391 let kind = obj["kind"].as_str().unwrap_or("");
393 match kind {
394 "text" => Ok(Part::Text {
395 text: obj
396 .get("text")
397 .and_then(|v| v.as_str())
398 .unwrap_or("")
399 .to_string(),
400 metadata,
401 }),
402 "file" => {
403 let file: FileContent = serde_json::from_value(
404 obj.get("file").cloned().unwrap_or_default(),
405 )
406 .map_err(serde::de::Error::custom)?;
407 Ok(Part::File { file, metadata })
408 }
409 "data" => Ok(Part::Data {
410 data: obj.get("data").cloned().unwrap_or_default(),
411 metadata,
412 }),
413 _ => Err(serde::de::Error::custom(format!(
414 "unknown part kind: {kind}"
415 ))),
416 }
417 } else {
418 Err(serde::de::Error::custom(
419 "Part must have text, file, or data field",
420 ))
421 }
422 }
423}
424
425#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
427#[serde(rename_all = "camelCase")]
428pub struct FileContent {
429 #[serde(default, skip_serializing_if = "Option::is_none")]
431 pub bytes: Option<String>,
432 #[serde(default, skip_serializing_if = "Option::is_none")]
434 pub uri: Option<String>,
435 #[serde(default, skip_serializing_if = "Option::is_none")]
437 pub name: Option<String>,
438 #[serde(default, skip_serializing_if = "Option::is_none")]
440 pub mime_type: Option<String>,
441}
442
443impl Part {
444 pub fn text(text: impl Into<String>) -> Self {
446 Part::Text {
447 text: text.into(),
448 metadata: None,
449 }
450 }
451
452 pub fn file_uri(uri: impl Into<String>, mime_type: impl Into<String>) -> Self {
454 Part::File {
455 file: FileContent {
456 bytes: None,
457 uri: Some(uri.into()),
458 name: None,
459 mime_type: Some(mime_type.into()),
460 },
461 metadata: None,
462 }
463 }
464
465 pub fn file_bytes(bytes: impl Into<String>, mime_type: impl Into<String>) -> Self {
467 Part::File {
468 file: FileContent {
469 bytes: Some(bytes.into()),
470 uri: None,
471 name: None,
472 mime_type: Some(mime_type.into()),
473 },
474 metadata: None,
475 }
476 }
477
478 pub fn data(data: serde_json::Value) -> Self {
480 Part::Data {
481 data,
482 metadata: None,
483 }
484 }
485
486 pub fn as_text(&self) -> Option<&str> {
488 match self {
489 Part::Text { text, .. } => Some(text),
490 _ => None,
491 }
492 }
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
499#[non_exhaustive]
500pub enum Role {
501 #[serde(rename = "ROLE_UNSPECIFIED")]
502 Unspecified,
503 #[serde(rename = "ROLE_USER")]
504 User,
505 #[serde(rename = "ROLE_AGENT")]
506 Agent,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
511#[serde(rename_all = "camelCase")]
512pub struct Message {
513 #[serde(default = "default_message_kind", skip_serializing)]
515 pub kind: String,
516 pub message_id: String,
518 #[serde(default, skip_serializing_if = "Option::is_none")]
520 pub context_id: Option<String>,
521 #[serde(default, skip_serializing_if = "Option::is_none")]
523 pub task_id: Option<String>,
524 pub role: Role,
526 pub parts: Vec<Part>,
528 #[serde(default, skip_serializing_if = "Option::is_none")]
530 pub metadata: Option<serde_json::Value>,
531 #[serde(default, skip_serializing_if = "Vec::is_empty")]
533 pub extensions: Vec<String>,
534 #[serde(default, skip_serializing_if = "Option::is_none")]
536 pub reference_task_ids: Option<Vec<String>>,
537}
538
539fn default_message_kind() -> String {
540 "message".to_string()
541}
542
543fn default_task_kind() -> String {
544 "task".to_string()
545}
546
547fn default_status_update_kind() -> String {
548 "status-update".to_string()
549}
550
551fn default_artifact_update_kind() -> String {
552 "artifact-update".to_string()
553}
554
555#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
557#[serde(rename_all = "camelCase")]
558pub struct Artifact {
559 pub artifact_id: String,
561 #[serde(default, skip_serializing_if = "Option::is_none")]
563 pub name: Option<String>,
564 #[serde(default, skip_serializing_if = "Option::is_none")]
566 pub description: Option<String>,
567 pub parts: Vec<Part>,
569 #[serde(default, skip_serializing_if = "Option::is_none")]
571 pub metadata: Option<serde_json::Value>,
572 #[serde(default, skip_serializing_if = "Vec::is_empty")]
574 pub extensions: Vec<String>,
575}
576
577#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
579#[non_exhaustive]
580pub enum TaskState {
581 #[serde(rename = "TASK_STATE_UNSPECIFIED")]
582 Unspecified,
583 #[serde(rename = "TASK_STATE_SUBMITTED")]
584 Submitted,
585 #[serde(rename = "TASK_STATE_WORKING")]
586 Working,
587 #[serde(rename = "TASK_STATE_COMPLETED")]
588 Completed,
589 #[serde(rename = "TASK_STATE_FAILED")]
590 Failed,
591 #[serde(rename = "TASK_STATE_CANCELED")]
592 Canceled,
593 #[serde(rename = "TASK_STATE_INPUT_REQUIRED")]
594 InputRequired,
595 #[serde(rename = "TASK_STATE_REJECTED")]
596 Rejected,
597 #[serde(rename = "TASK_STATE_AUTH_REQUIRED")]
598 AuthRequired,
599}
600
601#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
603#[serde(rename_all = "camelCase")]
604pub struct TaskStatus {
605 pub state: TaskState,
607 #[serde(default, skip_serializing_if = "Option::is_none")]
609 pub message: Option<Message>,
610 #[serde(default, skip_serializing_if = "Option::is_none")]
612 pub timestamp: Option<String>,
613}
614
615#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
617#[serde(rename_all = "camelCase")]
618pub struct Task {
619 #[serde(default = "default_task_kind", skip_serializing)]
621 pub kind: String,
622 pub id: String,
624 pub context_id: String,
626 pub status: TaskStatus,
628 #[serde(default, skip_serializing_if = "Option::is_none")]
630 pub artifacts: Option<Vec<Artifact>>,
631 #[serde(default, skip_serializing_if = "Option::is_none")]
633 pub history: Option<Vec<Message>>,
634 #[serde(default, skip_serializing_if = "Option::is_none")]
636 pub metadata: Option<serde_json::Value>,
637}
638
639impl TaskState {
640 pub fn is_terminal(&self) -> bool {
642 matches!(
643 self,
644 TaskState::Completed | TaskState::Failed | TaskState::Canceled | TaskState::Rejected
645 )
646 }
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
656#[serde(rename_all = "camelCase")]
657pub enum SendMessageResponse {
658 Task(Task),
659 Message(Message),
660}
661
662#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
667#[serde(rename_all = "camelCase")]
668pub enum SendMessageResult {
669 Task(Task),
670 Message(Message),
671}
672
673#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
676pub struct JsonRpcRequest {
677 pub jsonrpc: String,
678 pub method: String,
679 #[serde(default)]
680 pub params: Option<serde_json::Value>,
681 pub id: serde_json::Value,
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
685pub struct JsonRpcResponse {
686 pub jsonrpc: String,
687 pub id: serde_json::Value,
688 #[serde(skip_serializing_if = "Option::is_none")]
689 pub result: Option<serde_json::Value>,
690 #[serde(skip_serializing_if = "Option::is_none")]
691 pub error: Option<JsonRpcError>,
692}
693
694#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
695pub struct JsonRpcError {
696 pub code: i32,
697 pub message: String,
698 #[serde(skip_serializing_if = "Option::is_none")]
699 pub data: Option<serde_json::Value>,
700}
701
702pub mod errors {
704 pub const PARSE_ERROR: i32 = -32700;
706 pub const INVALID_REQUEST: i32 = -32600;
707 pub const METHOD_NOT_FOUND: i32 = -32601;
708 pub const INVALID_PARAMS: i32 = -32602;
709 pub const INTERNAL_ERROR: i32 = -32603;
710
711 pub const TASK_NOT_FOUND: i32 = -32001;
713 pub const TASK_NOT_CANCELABLE: i32 = -32002;
714 pub const PUSH_NOTIFICATION_NOT_SUPPORTED: i32 = -32003;
715 pub const UNSUPPORTED_OPERATION: i32 = -32004;
716 pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
717 pub const EXTENDED_AGENT_CARD_NOT_CONFIGURED: i32 = -32006;
718 pub const VERSION_NOT_SUPPORTED: i32 = -32007;
719 pub const INVALID_AGENT_RESPONSE: i32 = -32008;
720 pub const EXTENSION_SUPPORT_REQUIRED: i32 = -32009;
721
722 pub fn message_for_code(code: i32) -> &'static str {
723 match code {
724 PARSE_ERROR => "Parse error",
725 INVALID_REQUEST => "Invalid request",
726 METHOD_NOT_FOUND => "Method not found",
727 INVALID_PARAMS => "Invalid params",
728 INTERNAL_ERROR => "Internal error",
729 TASK_NOT_FOUND => "Task not found",
730 TASK_NOT_CANCELABLE => "Task not cancelable",
731 PUSH_NOTIFICATION_NOT_SUPPORTED => "Push notifications not supported",
732 UNSUPPORTED_OPERATION => "Unsupported operation",
733 CONTENT_TYPE_NOT_SUPPORTED => "Content type not supported",
734 EXTENDED_AGENT_CARD_NOT_CONFIGURED => "Extended agent card not configured",
735 VERSION_NOT_SUPPORTED => "Protocol version not supported",
736 INVALID_AGENT_RESPONSE => "Invalid agent response",
737 EXTENSION_SUPPORT_REQUIRED => "Extension support required",
738 _ => "Unknown error",
739 }
740 }
741}
742
743pub fn success(id: serde_json::Value, result: serde_json::Value) -> JsonRpcResponse {
744 JsonRpcResponse {
745 jsonrpc: "2.0".to_string(),
746 id,
747 result: Some(result),
748 error: None,
749 }
750}
751
752pub fn error(
753 id: serde_json::Value,
754 code: i32,
755 message: &str,
756 data: Option<serde_json::Value>,
757) -> JsonRpcResponse {
758 JsonRpcResponse {
759 jsonrpc: "2.0".to_string(),
760 id,
761 result: None,
762 error: Some(JsonRpcError {
763 code,
764 message: message.to_string(),
765 data,
766 }),
767 }
768}
769
770#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
774#[serde(rename_all = "camelCase")]
775pub struct SendMessageRequest {
776 #[serde(default, skip_serializing_if = "Option::is_none")]
778 pub tenant: Option<String>,
779 pub message: Message,
781 #[serde(default, skip_serializing_if = "Option::is_none")]
783 pub configuration: Option<SendMessageConfiguration>,
784 #[serde(default, skip_serializing_if = "Option::is_none")]
786 pub metadata: Option<serde_json::Value>,
787}
788
789#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
791#[serde(rename_all = "camelCase")]
792pub struct SendMessageConfiguration {
793 #[serde(default, skip_serializing_if = "Option::is_none")]
795 pub accepted_output_modes: Option<Vec<String>>,
796 #[serde(default, skip_serializing_if = "Option::is_none")]
798 pub push_notification_config: Option<PushNotificationConfig>,
799 #[serde(default, skip_serializing_if = "Option::is_none")]
801 pub history_length: Option<u32>,
802 #[serde(default, skip_serializing_if = "Option::is_none")]
804 pub blocking: Option<bool>,
805 #[serde(default, skip_serializing_if = "Option::is_none")]
808 pub return_immediately: Option<bool>,
809}
810
811#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
813#[serde(rename_all = "camelCase")]
814pub struct GetTaskRequest {
815 pub id: String,
817 #[serde(default, skip_serializing_if = "Option::is_none")]
819 pub history_length: Option<u32>,
820 #[serde(default, skip_serializing_if = "Option::is_none")]
822 pub tenant: Option<String>,
823}
824
825#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
827#[serde(rename_all = "camelCase")]
828pub struct CancelTaskRequest {
829 pub id: String,
831 #[serde(default, skip_serializing_if = "Option::is_none")]
833 pub tenant: Option<String>,
834}
835
836#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
838#[serde(rename_all = "camelCase")]
839pub struct ListTasksRequest {
840 #[serde(default, skip_serializing_if = "Option::is_none")]
842 pub context_id: Option<String>,
843 #[serde(default, skip_serializing_if = "Option::is_none")]
845 pub status: Option<TaskState>,
846 #[serde(default, skip_serializing_if = "Option::is_none")]
848 pub page_size: Option<u32>,
849 #[serde(default, skip_serializing_if = "Option::is_none")]
851 pub page_token: Option<String>,
852 #[serde(default, skip_serializing_if = "Option::is_none")]
854 pub history_length: Option<u32>,
855 #[serde(default, skip_serializing_if = "Option::is_none")]
857 pub status_timestamp_after: Option<i64>,
858 #[serde(default, skip_serializing_if = "Option::is_none")]
860 pub include_artifacts: Option<bool>,
861 #[serde(default, skip_serializing_if = "Option::is_none")]
863 pub tenant: Option<String>,
864}
865
866#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
868#[serde(rename_all = "camelCase")]
869pub struct TaskListResponse {
870 pub tasks: Vec<Task>,
872 pub next_page_token: String,
874 pub page_size: u32,
876 pub total_size: u32,
878}
879
880#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
882#[serde(rename_all = "camelCase")]
883pub struct SubscribeToTaskRequest {
884 pub id: String,
886 #[serde(default, skip_serializing_if = "Option::is_none")]
888 pub tenant: Option<String>,
889}
890
891#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
893#[serde(rename_all = "camelCase")]
894pub struct PushNotificationConfig {
895 #[serde(default, skip_serializing_if = "Option::is_none")]
897 pub id: Option<String>,
898 pub url: String,
900 #[serde(default, skip_serializing_if = "Option::is_none")]
902 pub token: Option<String>,
903 #[serde(default, skip_serializing_if = "Option::is_none")]
905 pub authentication: Option<AuthenticationInfo>,
906}
907
908#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
910#[serde(rename_all = "camelCase")]
911pub struct AuthenticationInfo {
912 pub scheme: String,
914 #[serde(default, skip_serializing_if = "Option::is_none")]
916 pub credentials: Option<String>,
917}
918
919#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
921#[serde(rename_all = "camelCase")]
922pub struct TaskPushNotificationConfig {
923 pub id: String,
925 pub task_id: String,
927 pub push_notification_config: PushNotificationConfig,
929 #[serde(default, skip_serializing_if = "Option::is_none")]
931 pub tenant: Option<String>,
932}
933
934#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
936#[serde(rename_all = "camelCase")]
937pub struct CreateTaskPushNotificationConfigRequest {
938 pub task_id: String,
940 pub config_id: String,
942 pub push_notification_config: PushNotificationConfig,
944 #[serde(default, skip_serializing_if = "Option::is_none")]
946 pub tenant: Option<String>,
947}
948
949#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
951#[serde(rename_all = "camelCase")]
952pub struct GetTaskPushNotificationConfigRequest {
953 pub id: String,
955 pub task_id: String,
957 #[serde(default, skip_serializing_if = "Option::is_none")]
959 pub tenant: Option<String>,
960}
961
962#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
964#[serde(rename_all = "camelCase")]
965pub struct ListTaskPushNotificationConfigRequest {
966 pub task_id: String,
968 #[serde(default, skip_serializing_if = "Option::is_none")]
970 pub page_size: Option<u32>,
971 #[serde(default, skip_serializing_if = "Option::is_none")]
973 pub page_token: Option<String>,
974 #[serde(default, skip_serializing_if = "Option::is_none")]
976 pub tenant: Option<String>,
977}
978
979#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
981#[serde(rename_all = "camelCase")]
982pub struct ListTaskPushNotificationConfigResponse {
983 pub configs: Vec<TaskPushNotificationConfig>,
985 #[serde(default)]
987 pub next_page_token: String,
988}
989
990#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
992#[serde(rename_all = "camelCase")]
993pub struct DeleteTaskPushNotificationConfigRequest {
994 pub id: String,
996 pub task_id: String,
998 #[serde(default, skip_serializing_if = "Option::is_none")]
1000 pub tenant: Option<String>,
1001}
1002
1003#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1005#[serde(rename_all = "camelCase")]
1006pub struct GetExtendedAgentCardRequest {
1007 #[serde(default, skip_serializing_if = "Option::is_none")]
1009 pub tenant: Option<String>,
1010}
1011
1012#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1016#[serde(rename_all = "camelCase")]
1017pub enum StreamResponse {
1018 Task(Task),
1020 Message(Message),
1022 StatusUpdate(TaskStatusUpdateEvent),
1024 ArtifactUpdate(TaskArtifactUpdateEvent),
1026}
1027
1028#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1030#[serde(rename_all = "camelCase")]
1031pub struct TaskStatusUpdateEvent {
1032 #[serde(default = "default_status_update_kind", skip_serializing)]
1034 pub kind: String,
1035 pub task_id: String,
1037 pub context_id: String,
1039 pub status: TaskStatus,
1041 #[serde(rename = "final", default)]
1043 pub is_final: bool,
1044 #[serde(default, skip_serializing_if = "Option::is_none")]
1046 pub metadata: Option<serde_json::Value>,
1047}
1048
1049#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1051#[serde(rename_all = "camelCase")]
1052pub struct TaskArtifactUpdateEvent {
1053 #[serde(default = "default_artifact_update_kind", skip_serializing)]
1055 pub kind: String,
1056 pub task_id: String,
1058 pub context_id: String,
1060 pub artifact: Artifact,
1062 #[serde(default, skip_serializing_if = "Option::is_none")]
1064 pub append: Option<bool>,
1065 #[serde(default, skip_serializing_if = "Option::is_none")]
1067 pub last_chunk: Option<bool>,
1068 #[serde(default, skip_serializing_if = "Option::is_none")]
1070 pub metadata: Option<serde_json::Value>,
1071}
1072
1073#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1078#[serde(rename_all = "camelCase")]
1079pub enum StreamingMessageResult {
1080 StatusUpdate(TaskStatusUpdateEvent),
1081 ArtifactUpdate(TaskArtifactUpdateEvent),
1082 Task(Task),
1083 Message(Message),
1084}
1085
1086pub fn new_message(role: Role, text: &str, context_id: Option<String>) -> Message {
1090 Message {
1091 kind: "message".to_string(),
1092 message_id: Uuid::new_v4().to_string(),
1093 context_id,
1094 task_id: None,
1095 role,
1096 parts: vec![Part::text(text)],
1097 metadata: None,
1098 extensions: vec![],
1099 reference_task_ids: None,
1100 }
1101}
1102
1103pub fn completed_task_with_text(user_message: Message, reply_text: &str) -> Task {
1105 let context_id = user_message
1106 .context_id
1107 .clone()
1108 .unwrap_or_else(|| Uuid::new_v4().to_string());
1109 let task_id = Uuid::new_v4().to_string();
1110 let agent_msg = new_message(Role::Agent, reply_text, Some(context_id.clone()));
1111
1112 Task {
1113 kind: "task".to_string(),
1114 id: task_id,
1115 context_id,
1116 status: TaskStatus {
1117 state: TaskState::Completed,
1118 message: Some(agent_msg.clone()),
1119 timestamp: Some(chrono::Utc::now().to_rfc3339()),
1120 },
1121 history: Some(vec![user_message, agent_msg]),
1122 artifacts: None,
1123 metadata: None,
1124 }
1125}
1126
1127pub fn now_iso8601() -> String {
1129 chrono::Utc::now().to_rfc3339()
1130}
1131
1132pub fn validate_task_id(id: &str) -> bool {
1134 Uuid::parse_str(id).is_ok()
1135}
1136
1137#[cfg(test)]
1138mod tests {
1139 use super::*;
1140
1141 #[test]
1142 fn jsonrpc_helpers_round_trip() {
1143 let resp = success(serde_json::json!(1), serde_json::json!({"ok": true}));
1144 assert_eq!(resp.jsonrpc, "2.0");
1145 assert!(resp.error.is_none());
1146 assert!(resp.result.is_some());
1147 }
1148
1149 #[test]
1150 fn task_state_is_terminal() {
1151 assert!(TaskState::Completed.is_terminal());
1152 assert!(TaskState::Failed.is_terminal());
1153 assert!(TaskState::Canceled.is_terminal());
1154 assert!(TaskState::Rejected.is_terminal());
1155 assert!(!TaskState::Working.is_terminal());
1156 assert!(!TaskState::Submitted.is_terminal());
1157 assert!(!TaskState::InputRequired.is_terminal());
1158 }
1159
1160 #[test]
1161 fn task_state_serialization() {
1162 let state = TaskState::Working;
1163 let json = serde_json::to_string(&state).unwrap();
1164 assert_eq!(json, r#""TASK_STATE_WORKING""#);
1165
1166 let parsed: TaskState = serde_json::from_str(&json).unwrap();
1167 assert_eq!(parsed, TaskState::Working);
1168 }
1169
1170 #[test]
1171 fn role_serialization() {
1172 let role = Role::User;
1173 let json = serde_json::to_string(&role).unwrap();
1174 assert_eq!(json, r#""ROLE_USER""#);
1175 }
1176
1177 #[test]
1178 fn message_serialization() {
1179 let msg = new_message(Role::User, "hello", Some("ctx-123".to_string()));
1180 let json = serde_json::to_string(&msg).unwrap();
1181 let parsed: Message = serde_json::from_str(&json).unwrap();
1182 assert_eq!(parsed.role, Role::User);
1183 assert_eq!(parsed.parts.len(), 1);
1184 assert_eq!(parsed.parts[0].as_text(), Some("hello"));
1185
1186 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1188 assert!(value.get("messageId").is_some());
1189 assert!(value.get("contextId").is_some());
1190 }
1191
1192 #[test]
1193 fn task_serialization() {
1194 let user_msg = new_message(Role::User, "test", None);
1195 let task = completed_task_with_text(user_msg, "response");
1196 let json = serde_json::to_string(&task).unwrap();
1197 let parsed: Task = serde_json::from_str(&json).unwrap();
1198 assert_eq!(parsed.status.state, TaskState::Completed);
1199 assert!(parsed.history.is_some());
1200 assert_eq!(parsed.history.unwrap().len(), 2);
1201
1202 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1204 assert!(value.get("contextId").is_some());
1205 }
1206
1207 #[test]
1208 fn part_text_serialization() {
1209 let part = Part::text("hello");
1210 let json = serde_json::to_string(&part).unwrap();
1211 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1212 assert!(value.get("kind").is_none());
1214 assert_eq!(value.get("text").unwrap().as_str().unwrap(), "hello");
1215 }
1216
1217 #[test]
1218 fn part_text_round_trip() {
1219 let part = Part::text("hello");
1220 let json = serde_json::to_string(&part).unwrap();
1221 let parsed: Part = serde_json::from_str(&json).unwrap();
1222 assert_eq!(parsed, part);
1223 assert_eq!(parsed.as_text(), Some("hello"));
1224 }
1225
1226 #[test]
1227 fn part_file_uri_serialization() {
1228 let part = Part::file_uri("https://example.com/file.pdf", "application/pdf");
1229 let json = serde_json::to_string(&part).unwrap();
1230 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1231 assert!(value.get("kind").is_none());
1232 let file = value.get("file").unwrap();
1233 assert_eq!(
1234 file.get("uri").unwrap().as_str().unwrap(),
1235 "https://example.com/file.pdf"
1236 );
1237 assert_eq!(
1238 file.get("mimeType").unwrap().as_str().unwrap(),
1239 "application/pdf"
1240 );
1241 }
1242
1243 #[test]
1244 fn part_data_serialization() {
1245 let part = Part::data(serde_json::json!({"key": "value"}));
1246 let json = serde_json::to_string(&part).unwrap();
1247 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1248 assert!(value.get("kind").is_none());
1249 assert_eq!(
1250 value.get("data").unwrap(),
1251 &serde_json::json!({"key": "value"})
1252 );
1253 }
1254
1255 #[test]
1256 fn part_deserialization_from_wire_format() {
1257 let text: Part = serde_json::from_str(r#"{"text":"hello"}"#).unwrap();
1259 assert_eq!(text.as_text(), Some("hello"));
1260
1261 let file: Part = serde_json::from_str(
1262 r#"{"file":{"uri":"https://example.com/f.pdf","mimeType":"application/pdf"}}"#,
1263 )
1264 .unwrap();
1265 match &file {
1266 Part::File { file, .. } => {
1267 assert_eq!(file.uri.as_deref(), Some("https://example.com/f.pdf"));
1268 assert_eq!(file.mime_type.as_deref(), Some("application/pdf"));
1269 }
1270 _ => panic!("expected File part"),
1271 }
1272
1273 let data: Part = serde_json::from_str(r#"{"data":{"k":"v"}}"#).unwrap();
1274 match &data {
1275 Part::Data { data, .. } => assert_eq!(data, &serde_json::json!({"k": "v"})),
1276 _ => panic!("expected Data part"),
1277 }
1278
1279 let text_v03: Part =
1281 serde_json::from_str(r#"{"kind":"text","text":"hello v03"}"#).unwrap();
1282 assert_eq!(text_v03.as_text(), Some("hello v03"));
1283 }
1284
1285 #[test]
1286 fn agent_card_with_security() {
1287 let card = AgentCard {
1288 name: "Test Agent".to_string(),
1289 description: "Test description".to_string(),
1290 supported_interfaces: vec![AgentInterface {
1291 url: "https://example.com/v1/rpc".to_string(),
1292 protocol_binding: "JSONRPC".to_string(),
1293 protocol_version: PROTOCOL_VERSION.to_string(),
1294 tenant: None,
1295 }],
1296 provider: Some(AgentProvider {
1297 organization: "Test Org".to_string(),
1298 url: "https://example.com".to_string(),
1299 }),
1300 version: PROTOCOL_VERSION.to_string(),
1301 documentation_url: None,
1302 capabilities: AgentCapabilities::default(),
1303 security_schemes: {
1304 let mut m = HashMap::new();
1305 m.insert(
1306 "apiKey".to_string(),
1307 SecurityScheme::ApiKeySecurityScheme(ApiKeySecurityScheme {
1308 name: "X-API-Key".to_string(),
1309 location: "header".to_string(),
1310 description: None,
1311 }),
1312 );
1313 m
1314 },
1315 security_requirements: vec![],
1316 default_input_modes: vec![],
1317 default_output_modes: vec![],
1318 skills: vec![],
1319 signatures: vec![],
1320 icon_url: None,
1321 };
1322
1323 let json = serde_json::to_string(&card).unwrap();
1324 let parsed: AgentCard = serde_json::from_str(&json).unwrap();
1325 assert_eq!(parsed.name, "Test Agent");
1326 assert_eq!(parsed.security_schemes.len(), 1);
1327 assert_eq!(parsed.endpoint(), Some("https://example.com/v1/rpc"));
1328
1329 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1331 assert!(value.get("supportedInterfaces").is_some());
1332 assert!(value.get("securitySchemes").is_some());
1333 assert!(value.get("securityRequirements").is_some());
1334 }
1335
1336 #[test]
1337 fn validate_task_id_helper() {
1338 let valid_uuid = Uuid::new_v4().to_string();
1339 assert!(validate_task_id(&valid_uuid));
1340 assert!(!validate_task_id("not-a-uuid"));
1341 }
1342
1343 #[test]
1344 fn error_codes() {
1345 use errors::*;
1346 assert_eq!(message_for_code(TASK_NOT_FOUND), "Task not found");
1347 assert_eq!(
1348 message_for_code(VERSION_NOT_SUPPORTED),
1349 "Protocol version not supported"
1350 );
1351 assert_eq!(
1352 message_for_code(INVALID_AGENT_RESPONSE),
1353 "Invalid agent response"
1354 );
1355 assert_eq!(
1356 message_for_code(EXTENSION_SUPPORT_REQUIRED),
1357 "Extension support required"
1358 );
1359 assert_eq!(message_for_code(999), "Unknown error");
1360 }
1361
1362 #[test]
1363 fn send_message_result_serialization() {
1364 let task = Task {
1365 kind: "task".to_string(),
1366 id: "t-1".to_string(),
1367 context_id: "ctx-1".to_string(),
1368 status: TaskStatus {
1369 state: TaskState::Completed,
1370 message: None,
1371 timestamp: None,
1372 },
1373 artifacts: None,
1374 history: None,
1375 metadata: None,
1376 };
1377
1378 let result = SendMessageResult::Task(task.clone());
1380 let json = serde_json::to_string(&result).unwrap();
1381 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1382 assert!(value.get("task").is_some(), "should have task wrapper key");
1383 let inner = value.get("task").unwrap();
1384 assert_eq!(inner.get("id").unwrap().as_str().unwrap(), "t-1");
1385
1386 let parsed: SendMessageResult = serde_json::from_str(&json).unwrap();
1388 assert_eq!(parsed, SendMessageResult::Task(task));
1389 }
1390
1391 #[test]
1392 fn stream_response_serialization() {
1393 let event = StreamResponse::StatusUpdate(TaskStatusUpdateEvent {
1394 kind: "status-update".to_string(),
1395 task_id: "t-1".to_string(),
1396 context_id: "ctx-1".to_string(),
1397 status: TaskStatus {
1398 state: TaskState::Working,
1399 message: None,
1400 timestamp: None,
1401 },
1402 is_final: false,
1403 metadata: None,
1404 });
1405 let json = serde_json::to_string(&event).unwrap();
1406 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1407 assert!(value.get("statusUpdate").is_some());
1408 }
1409
1410 #[test]
1411 fn push_notification_config_serialization() {
1412 let config = PushNotificationConfig {
1413 id: Some("cfg-1".to_string()),
1414 url: "https://example.com/webhook".to_string(),
1415 token: Some("secret".to_string()),
1416 authentication: Some(AuthenticationInfo {
1417 scheme: "bearer".to_string(),
1418 credentials: Some("token123".to_string()),
1419 }),
1420 };
1421 let json = serde_json::to_string(&config).unwrap();
1422 let parsed: PushNotificationConfig = serde_json::from_str(&json).unwrap();
1423 assert_eq!(parsed.url, "https://example.com/webhook");
1424 assert_eq!(parsed.authentication.unwrap().scheme, "bearer");
1425 }
1426}