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, Serialize, Deserialize, PartialEq)]
307#[serde(tag = "kind")]
308pub enum Part {
309 #[serde(rename = "text")]
311 Text {
312 text: String,
314 #[serde(default, skip_serializing_if = "Option::is_none")]
316 metadata: Option<serde_json::Value>,
317 },
318 #[serde(rename = "file")]
320 File {
321 file: FileContent,
323 #[serde(default, skip_serializing_if = "Option::is_none")]
325 metadata: Option<serde_json::Value>,
326 },
327 #[serde(rename = "data")]
329 Data {
330 data: serde_json::Value,
332 #[serde(default, skip_serializing_if = "Option::is_none")]
334 metadata: Option<serde_json::Value>,
335 },
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
340#[serde(rename_all = "camelCase")]
341pub struct FileContent {
342 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub bytes: Option<String>,
345 #[serde(default, skip_serializing_if = "Option::is_none")]
347 pub uri: Option<String>,
348 #[serde(default, skip_serializing_if = "Option::is_none")]
350 pub name: Option<String>,
351 #[serde(default, skip_serializing_if = "Option::is_none")]
353 pub mime_type: Option<String>,
354}
355
356impl Part {
357 pub fn text(text: impl Into<String>) -> Self {
359 Part::Text {
360 text: text.into(),
361 metadata: None,
362 }
363 }
364
365 pub fn file_uri(uri: impl Into<String>, mime_type: impl Into<String>) -> Self {
367 Part::File {
368 file: FileContent {
369 bytes: None,
370 uri: Some(uri.into()),
371 name: None,
372 mime_type: Some(mime_type.into()),
373 },
374 metadata: None,
375 }
376 }
377
378 pub fn file_bytes(bytes: impl Into<String>, mime_type: impl Into<String>) -> Self {
380 Part::File {
381 file: FileContent {
382 bytes: Some(bytes.into()),
383 uri: None,
384 name: None,
385 mime_type: Some(mime_type.into()),
386 },
387 metadata: None,
388 }
389 }
390
391 pub fn data(data: serde_json::Value) -> Self {
393 Part::Data {
394 data,
395 metadata: None,
396 }
397 }
398
399 pub fn as_text(&self) -> Option<&str> {
401 match self {
402 Part::Text { text, .. } => Some(text),
403 _ => None,
404 }
405 }
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
412#[non_exhaustive]
413pub enum Role {
414 #[serde(rename = "unspecified")]
415 Unspecified,
416 #[serde(rename = "user")]
417 User,
418 #[serde(rename = "agent")]
419 Agent,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
424#[serde(rename_all = "camelCase")]
425pub struct Message {
426 #[serde(default = "default_message_kind")]
428 pub kind: String,
429 pub message_id: String,
431 #[serde(default, skip_serializing_if = "Option::is_none")]
433 pub context_id: Option<String>,
434 #[serde(default, skip_serializing_if = "Option::is_none")]
436 pub task_id: Option<String>,
437 pub role: Role,
439 pub parts: Vec<Part>,
441 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pub metadata: Option<serde_json::Value>,
444 #[serde(default, skip_serializing_if = "Vec::is_empty")]
446 pub extensions: Vec<String>,
447 #[serde(default, skip_serializing_if = "Option::is_none")]
449 pub reference_task_ids: Option<Vec<String>>,
450}
451
452fn default_message_kind() -> String {
453 "message".to_string()
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
458#[serde(rename_all = "camelCase")]
459pub struct Artifact {
460 pub artifact_id: String,
462 #[serde(default, skip_serializing_if = "Option::is_none")]
464 pub name: Option<String>,
465 #[serde(default, skip_serializing_if = "Option::is_none")]
467 pub description: Option<String>,
468 pub parts: Vec<Part>,
470 #[serde(default, skip_serializing_if = "Option::is_none")]
472 pub metadata: Option<serde_json::Value>,
473 #[serde(default, skip_serializing_if = "Vec::is_empty")]
475 pub extensions: Vec<String>,
476}
477
478#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
480#[non_exhaustive]
481pub enum TaskState {
482 #[serde(rename = "unspecified")]
483 Unspecified,
484 #[serde(rename = "submitted")]
485 Submitted,
486 #[serde(rename = "working")]
487 Working,
488 #[serde(rename = "completed")]
489 Completed,
490 #[serde(rename = "failed")]
491 Failed,
492 #[serde(rename = "canceled")]
493 Canceled,
494 #[serde(rename = "input-required")]
495 InputRequired,
496 #[serde(rename = "rejected")]
497 Rejected,
498 #[serde(rename = "auth-required")]
499 AuthRequired,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
504#[serde(rename_all = "camelCase")]
505pub struct TaskStatus {
506 pub state: TaskState,
508 #[serde(default, skip_serializing_if = "Option::is_none")]
510 pub message: Option<Message>,
511 #[serde(default, skip_serializing_if = "Option::is_none")]
513 pub timestamp: Option<String>,
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
518#[serde(rename_all = "camelCase")]
519pub struct Task {
520 pub id: String,
522 pub context_id: String,
524 pub status: TaskStatus,
526 #[serde(default, skip_serializing_if = "Option::is_none")]
528 pub artifacts: Option<Vec<Artifact>>,
529 #[serde(default, skip_serializing_if = "Option::is_none")]
531 pub history: Option<Vec<Message>>,
532 #[serde(default, skip_serializing_if = "Option::is_none")]
534 pub metadata: Option<serde_json::Value>,
535}
536
537impl TaskState {
538 pub fn is_terminal(&self) -> bool {
540 matches!(
541 self,
542 TaskState::Completed | TaskState::Failed | TaskState::Canceled | TaskState::Rejected
543 )
544 }
545}
546
547#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
554#[serde(rename_all = "camelCase")]
555pub enum SendMessageResponse {
556 Task(Task),
557 Message(Message),
558}
559
560#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
565#[serde(untagged)]
566pub enum SendMessageResult {
567 Task(Task),
568 Message(Message),
569}
570
571#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
574pub struct JsonRpcRequest {
575 pub jsonrpc: String,
576 pub method: String,
577 #[serde(default)]
578 pub params: Option<serde_json::Value>,
579 pub id: serde_json::Value,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
583pub struct JsonRpcResponse {
584 pub jsonrpc: String,
585 pub id: serde_json::Value,
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub result: Option<serde_json::Value>,
588 #[serde(skip_serializing_if = "Option::is_none")]
589 pub error: Option<JsonRpcError>,
590}
591
592#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
593pub struct JsonRpcError {
594 pub code: i32,
595 pub message: String,
596 #[serde(skip_serializing_if = "Option::is_none")]
597 pub data: Option<serde_json::Value>,
598}
599
600pub mod errors {
602 pub const PARSE_ERROR: i32 = -32700;
604 pub const INVALID_REQUEST: i32 = -32600;
605 pub const METHOD_NOT_FOUND: i32 = -32601;
606 pub const INVALID_PARAMS: i32 = -32602;
607 pub const INTERNAL_ERROR: i32 = -32603;
608
609 pub const TASK_NOT_FOUND: i32 = -32001;
611 pub const TASK_NOT_CANCELABLE: i32 = -32002;
612 pub const PUSH_NOTIFICATION_NOT_SUPPORTED: i32 = -32003;
613 pub const UNSUPPORTED_OPERATION: i32 = -32004;
614 pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
615 pub const EXTENDED_AGENT_CARD_NOT_CONFIGURED: i32 = -32006;
616 pub const VERSION_NOT_SUPPORTED: i32 = -32007;
617 pub const INVALID_AGENT_RESPONSE: i32 = -32008;
618 pub const EXTENSION_SUPPORT_REQUIRED: i32 = -32009;
619
620 pub fn message_for_code(code: i32) -> &'static str {
621 match code {
622 PARSE_ERROR => "Parse error",
623 INVALID_REQUEST => "Invalid request",
624 METHOD_NOT_FOUND => "Method not found",
625 INVALID_PARAMS => "Invalid params",
626 INTERNAL_ERROR => "Internal error",
627 TASK_NOT_FOUND => "Task not found",
628 TASK_NOT_CANCELABLE => "Task not cancelable",
629 PUSH_NOTIFICATION_NOT_SUPPORTED => "Push notifications not supported",
630 UNSUPPORTED_OPERATION => "Unsupported operation",
631 CONTENT_TYPE_NOT_SUPPORTED => "Content type not supported",
632 EXTENDED_AGENT_CARD_NOT_CONFIGURED => "Extended agent card not configured",
633 VERSION_NOT_SUPPORTED => "Protocol version not supported",
634 INVALID_AGENT_RESPONSE => "Invalid agent response",
635 EXTENSION_SUPPORT_REQUIRED => "Extension support required",
636 _ => "Unknown error",
637 }
638 }
639}
640
641pub fn success(id: serde_json::Value, result: serde_json::Value) -> JsonRpcResponse {
642 JsonRpcResponse {
643 jsonrpc: "2.0".to_string(),
644 id,
645 result: Some(result),
646 error: None,
647 }
648}
649
650pub fn error(
651 id: serde_json::Value,
652 code: i32,
653 message: &str,
654 data: Option<serde_json::Value>,
655) -> JsonRpcResponse {
656 JsonRpcResponse {
657 jsonrpc: "2.0".to_string(),
658 id,
659 result: None,
660 error: Some(JsonRpcError {
661 code,
662 message: message.to_string(),
663 data,
664 }),
665 }
666}
667
668#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
672#[serde(rename_all = "camelCase")]
673pub struct SendMessageRequest {
674 #[serde(default, skip_serializing_if = "Option::is_none")]
676 pub tenant: Option<String>,
677 pub message: Message,
679 #[serde(default, skip_serializing_if = "Option::is_none")]
681 pub configuration: Option<SendMessageConfiguration>,
682 #[serde(default, skip_serializing_if = "Option::is_none")]
684 pub metadata: Option<serde_json::Value>,
685}
686
687#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
689#[serde(rename_all = "camelCase")]
690pub struct SendMessageConfiguration {
691 #[serde(default, skip_serializing_if = "Option::is_none")]
693 pub accepted_output_modes: Option<Vec<String>>,
694 #[serde(default, skip_serializing_if = "Option::is_none")]
696 pub push_notification_config: Option<PushNotificationConfig>,
697 #[serde(default, skip_serializing_if = "Option::is_none")]
699 pub history_length: Option<u32>,
700 #[serde(default, skip_serializing_if = "Option::is_none")]
702 pub blocking: Option<bool>,
703}
704
705#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
707#[serde(rename_all = "camelCase")]
708pub struct GetTaskRequest {
709 pub id: String,
711 #[serde(default, skip_serializing_if = "Option::is_none")]
713 pub history_length: Option<u32>,
714 #[serde(default, skip_serializing_if = "Option::is_none")]
716 pub tenant: Option<String>,
717}
718
719#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
721#[serde(rename_all = "camelCase")]
722pub struct CancelTaskRequest {
723 pub id: String,
725 #[serde(default, skip_serializing_if = "Option::is_none")]
727 pub tenant: Option<String>,
728}
729
730#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
732#[serde(rename_all = "camelCase")]
733pub struct ListTasksRequest {
734 #[serde(default, skip_serializing_if = "Option::is_none")]
736 pub context_id: Option<String>,
737 #[serde(default, skip_serializing_if = "Option::is_none")]
739 pub status: Option<TaskState>,
740 #[serde(default, skip_serializing_if = "Option::is_none")]
742 pub page_size: Option<u32>,
743 #[serde(default, skip_serializing_if = "Option::is_none")]
745 pub page_token: Option<String>,
746 #[serde(default, skip_serializing_if = "Option::is_none")]
748 pub history_length: Option<u32>,
749 #[serde(default, skip_serializing_if = "Option::is_none")]
751 pub status_timestamp_after: Option<i64>,
752 #[serde(default, skip_serializing_if = "Option::is_none")]
754 pub include_artifacts: Option<bool>,
755 #[serde(default, skip_serializing_if = "Option::is_none")]
757 pub tenant: Option<String>,
758}
759
760#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
762#[serde(rename_all = "camelCase")]
763pub struct TaskListResponse {
764 pub tasks: Vec<Task>,
766 pub next_page_token: String,
768 pub page_size: u32,
770 pub total_size: u32,
772}
773
774#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
776#[serde(rename_all = "camelCase")]
777pub struct SubscribeToTaskRequest {
778 pub id: String,
780 #[serde(default, skip_serializing_if = "Option::is_none")]
782 pub tenant: Option<String>,
783}
784
785#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
787#[serde(rename_all = "camelCase")]
788pub struct PushNotificationConfig {
789 #[serde(default, skip_serializing_if = "Option::is_none")]
791 pub id: Option<String>,
792 pub url: String,
794 #[serde(default, skip_serializing_if = "Option::is_none")]
796 pub token: Option<String>,
797 #[serde(default, skip_serializing_if = "Option::is_none")]
799 pub authentication: Option<AuthenticationInfo>,
800}
801
802#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
804#[serde(rename_all = "camelCase")]
805pub struct AuthenticationInfo {
806 pub scheme: String,
808 #[serde(default, skip_serializing_if = "Option::is_none")]
810 pub credentials: Option<String>,
811}
812
813#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
815#[serde(rename_all = "camelCase")]
816pub struct TaskPushNotificationConfig {
817 pub id: String,
819 pub task_id: String,
821 pub push_notification_config: PushNotificationConfig,
823 #[serde(default, skip_serializing_if = "Option::is_none")]
825 pub tenant: Option<String>,
826}
827
828#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
830#[serde(rename_all = "camelCase")]
831pub struct CreateTaskPushNotificationConfigRequest {
832 pub task_id: String,
834 pub config_id: String,
836 pub push_notification_config: PushNotificationConfig,
838 #[serde(default, skip_serializing_if = "Option::is_none")]
840 pub tenant: Option<String>,
841}
842
843#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
845#[serde(rename_all = "camelCase")]
846pub struct GetTaskPushNotificationConfigRequest {
847 pub id: String,
849 pub task_id: String,
851 #[serde(default, skip_serializing_if = "Option::is_none")]
853 pub tenant: Option<String>,
854}
855
856#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
858#[serde(rename_all = "camelCase")]
859pub struct ListTaskPushNotificationConfigRequest {
860 pub task_id: String,
862 #[serde(default, skip_serializing_if = "Option::is_none")]
864 pub page_size: Option<u32>,
865 #[serde(default, skip_serializing_if = "Option::is_none")]
867 pub page_token: Option<String>,
868 #[serde(default, skip_serializing_if = "Option::is_none")]
870 pub tenant: Option<String>,
871}
872
873#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
875#[serde(rename_all = "camelCase")]
876pub struct ListTaskPushNotificationConfigResponse {
877 pub configs: Vec<TaskPushNotificationConfig>,
879 #[serde(default)]
881 pub next_page_token: String,
882}
883
884#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
886#[serde(rename_all = "camelCase")]
887pub struct DeleteTaskPushNotificationConfigRequest {
888 pub id: String,
890 pub task_id: String,
892 #[serde(default, skip_serializing_if = "Option::is_none")]
894 pub tenant: Option<String>,
895}
896
897#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
899#[serde(rename_all = "camelCase")]
900pub struct GetExtendedAgentCardRequest {
901 #[serde(default, skip_serializing_if = "Option::is_none")]
903 pub tenant: Option<String>,
904}
905
906#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
910#[serde(rename_all = "camelCase")]
911pub enum StreamResponse {
912 Task(Task),
914 Message(Message),
916 StatusUpdate(TaskStatusUpdateEvent),
918 ArtifactUpdate(TaskArtifactUpdateEvent),
920}
921
922#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
924#[serde(rename_all = "camelCase")]
925pub struct TaskStatusUpdateEvent {
926 pub task_id: String,
928 pub context_id: String,
930 pub status: TaskStatus,
932 #[serde(default, skip_serializing_if = "Option::is_none")]
934 pub metadata: Option<serde_json::Value>,
935}
936
937#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
939#[serde(rename_all = "camelCase")]
940pub struct TaskArtifactUpdateEvent {
941 pub task_id: String,
943 pub context_id: String,
945 pub artifact: Artifact,
947 #[serde(default, skip_serializing_if = "Option::is_none")]
949 pub append: Option<bool>,
950 #[serde(default, skip_serializing_if = "Option::is_none")]
952 pub last_chunk: Option<bool>,
953 #[serde(default, skip_serializing_if = "Option::is_none")]
955 pub metadata: Option<serde_json::Value>,
956}
957
958pub fn new_message(role: Role, text: &str, context_id: Option<String>) -> Message {
962 Message {
963 kind: "message".to_string(),
964 message_id: Uuid::new_v4().to_string(),
965 context_id,
966 task_id: None,
967 role,
968 parts: vec![Part::text(text)],
969 metadata: None,
970 extensions: vec![],
971 reference_task_ids: None,
972 }
973}
974
975pub fn completed_task_with_text(user_message: Message, reply_text: &str) -> Task {
977 let context_id = user_message
978 .context_id
979 .clone()
980 .unwrap_or_else(|| Uuid::new_v4().to_string());
981 let task_id = Uuid::new_v4().to_string();
982 let agent_msg = new_message(Role::Agent, reply_text, Some(context_id.clone()));
983
984 Task {
985 id: task_id,
986 context_id,
987 status: TaskStatus {
988 state: TaskState::Completed,
989 message: Some(agent_msg.clone()),
990 timestamp: Some(chrono::Utc::now().to_rfc3339()),
991 },
992 history: Some(vec![user_message, agent_msg]),
993 artifacts: None,
994 metadata: None,
995 }
996}
997
998pub fn now_iso8601() -> String {
1000 chrono::Utc::now().to_rfc3339()
1001}
1002
1003pub fn validate_task_id(id: &str) -> bool {
1005 Uuid::parse_str(id).is_ok()
1006}
1007
1008#[cfg(test)]
1009mod tests {
1010 use super::*;
1011
1012 #[test]
1013 fn jsonrpc_helpers_round_trip() {
1014 let resp = success(serde_json::json!(1), serde_json::json!({"ok": true}));
1015 assert_eq!(resp.jsonrpc, "2.0");
1016 assert!(resp.error.is_none());
1017 assert!(resp.result.is_some());
1018 }
1019
1020 #[test]
1021 fn task_state_is_terminal() {
1022 assert!(TaskState::Completed.is_terminal());
1023 assert!(TaskState::Failed.is_terminal());
1024 assert!(TaskState::Canceled.is_terminal());
1025 assert!(TaskState::Rejected.is_terminal());
1026 assert!(!TaskState::Working.is_terminal());
1027 assert!(!TaskState::Submitted.is_terminal());
1028 assert!(!TaskState::InputRequired.is_terminal());
1029 }
1030
1031 #[test]
1032 fn task_state_serialization() {
1033 let state = TaskState::Working;
1034 let json = serde_json::to_string(&state).unwrap();
1035 assert_eq!(json, r#""working""#);
1036
1037 let parsed: TaskState = serde_json::from_str(&json).unwrap();
1038 assert_eq!(parsed, TaskState::Working);
1039 }
1040
1041 #[test]
1042 fn role_serialization() {
1043 let role = Role::User;
1044 let json = serde_json::to_string(&role).unwrap();
1045 assert_eq!(json, r#""user""#);
1046 }
1047
1048 #[test]
1049 fn message_serialization() {
1050 let msg = new_message(Role::User, "hello", Some("ctx-123".to_string()));
1051 let json = serde_json::to_string(&msg).unwrap();
1052 let parsed: Message = serde_json::from_str(&json).unwrap();
1053 assert_eq!(parsed.role, Role::User);
1054 assert_eq!(parsed.parts.len(), 1);
1055 assert_eq!(parsed.parts[0].as_text(), Some("hello"));
1056
1057 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1059 assert!(value.get("messageId").is_some());
1060 assert!(value.get("contextId").is_some());
1061 }
1062
1063 #[test]
1064 fn task_serialization() {
1065 let user_msg = new_message(Role::User, "test", None);
1066 let task = completed_task_with_text(user_msg, "response");
1067 let json = serde_json::to_string(&task).unwrap();
1068 let parsed: Task = serde_json::from_str(&json).unwrap();
1069 assert_eq!(parsed.status.state, TaskState::Completed);
1070 assert!(parsed.history.is_some());
1071 assert_eq!(parsed.history.unwrap().len(), 2);
1072
1073 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1075 assert!(value.get("contextId").is_some());
1076 }
1077
1078 #[test]
1079 fn part_text_serialization() {
1080 let part = Part::text("hello");
1081 let json = serde_json::to_string(&part).unwrap();
1082 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1083 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "text");
1084 assert_eq!(value.get("text").unwrap().as_str().unwrap(), "hello");
1085 }
1086
1087 #[test]
1088 fn part_text_round_trip() {
1089 let part = Part::text("hello");
1090 let json = serde_json::to_string(&part).unwrap();
1091 let parsed: Part = serde_json::from_str(&json).unwrap();
1092 assert_eq!(parsed, part);
1093 assert_eq!(parsed.as_text(), Some("hello"));
1094 }
1095
1096 #[test]
1097 fn part_file_uri_serialization() {
1098 let part = Part::file_uri("https://example.com/file.pdf", "application/pdf");
1099 let json = serde_json::to_string(&part).unwrap();
1100 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1101 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "file");
1102 let file = value.get("file").unwrap();
1103 assert_eq!(
1104 file.get("uri").unwrap().as_str().unwrap(),
1105 "https://example.com/file.pdf"
1106 );
1107 assert_eq!(
1108 file.get("mimeType").unwrap().as_str().unwrap(),
1109 "application/pdf"
1110 );
1111 }
1112
1113 #[test]
1114 fn part_data_serialization() {
1115 let part = Part::data(serde_json::json!({"key": "value"}));
1116 let json = serde_json::to_string(&part).unwrap();
1117 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1118 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "data");
1119 assert_eq!(
1120 value.get("data").unwrap(),
1121 &serde_json::json!({"key": "value"})
1122 );
1123 }
1124
1125 #[test]
1126 fn part_deserialization_from_wire_format() {
1127 let text: Part = serde_json::from_str(r#"{"kind":"text","text":"hello"}"#).unwrap();
1129 assert_eq!(text.as_text(), Some("hello"));
1130
1131 let file: Part = serde_json::from_str(
1132 r#"{"kind":"file","file":{"uri":"https://example.com/f.pdf","mimeType":"application/pdf"}}"#,
1133 )
1134 .unwrap();
1135 match &file {
1136 Part::File { file, .. } => {
1137 assert_eq!(file.uri.as_deref(), Some("https://example.com/f.pdf"));
1138 assert_eq!(file.mime_type.as_deref(), Some("application/pdf"));
1139 }
1140 _ => panic!("expected File part"),
1141 }
1142
1143 let data: Part =
1144 serde_json::from_str(r#"{"kind":"data","data":{"k":"v"}}"#).unwrap();
1145 match &data {
1146 Part::Data { data, .. } => assert_eq!(data, &serde_json::json!({"k": "v"})),
1147 _ => panic!("expected Data part"),
1148 }
1149 }
1150
1151 #[test]
1152 fn agent_card_with_security() {
1153 let card = AgentCard {
1154 name: "Test Agent".to_string(),
1155 description: "Test description".to_string(),
1156 supported_interfaces: vec![AgentInterface {
1157 url: "https://example.com/v1/rpc".to_string(),
1158 protocol_binding: "JSONRPC".to_string(),
1159 protocol_version: PROTOCOL_VERSION.to_string(),
1160 tenant: None,
1161 }],
1162 provider: Some(AgentProvider {
1163 organization: "Test Org".to_string(),
1164 url: "https://example.com".to_string(),
1165 }),
1166 version: PROTOCOL_VERSION.to_string(),
1167 documentation_url: None,
1168 capabilities: AgentCapabilities::default(),
1169 security_schemes: {
1170 let mut m = HashMap::new();
1171 m.insert(
1172 "apiKey".to_string(),
1173 SecurityScheme::ApiKeySecurityScheme(ApiKeySecurityScheme {
1174 name: "X-API-Key".to_string(),
1175 location: "header".to_string(),
1176 description: None,
1177 }),
1178 );
1179 m
1180 },
1181 security_requirements: vec![],
1182 default_input_modes: vec![],
1183 default_output_modes: vec![],
1184 skills: vec![],
1185 signatures: vec![],
1186 icon_url: None,
1187 };
1188
1189 let json = serde_json::to_string(&card).unwrap();
1190 let parsed: AgentCard = serde_json::from_str(&json).unwrap();
1191 assert_eq!(parsed.name, "Test Agent");
1192 assert_eq!(parsed.security_schemes.len(), 1);
1193 assert_eq!(parsed.endpoint(), Some("https://example.com/v1/rpc"));
1194
1195 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1197 assert!(value.get("supportedInterfaces").is_some());
1198 assert!(value.get("securitySchemes").is_some());
1199 assert!(value.get("securityRequirements").is_some());
1200 }
1201
1202 #[test]
1203 fn validate_task_id_helper() {
1204 let valid_uuid = Uuid::new_v4().to_string();
1205 assert!(validate_task_id(&valid_uuid));
1206 assert!(!validate_task_id("not-a-uuid"));
1207 }
1208
1209 #[test]
1210 fn error_codes() {
1211 use errors::*;
1212 assert_eq!(message_for_code(TASK_NOT_FOUND), "Task not found");
1213 assert_eq!(
1214 message_for_code(VERSION_NOT_SUPPORTED),
1215 "Protocol version not supported"
1216 );
1217 assert_eq!(
1218 message_for_code(INVALID_AGENT_RESPONSE),
1219 "Invalid agent response"
1220 );
1221 assert_eq!(
1222 message_for_code(EXTENSION_SUPPORT_REQUIRED),
1223 "Extension support required"
1224 );
1225 assert_eq!(message_for_code(999), "Unknown error");
1226 }
1227
1228 #[test]
1229 fn send_message_result_serialization() {
1230 let task = Task {
1231 id: "t-1".to_string(),
1232 context_id: "ctx-1".to_string(),
1233 status: TaskStatus {
1234 state: TaskState::Completed,
1235 message: None,
1236 timestamp: None,
1237 },
1238 artifacts: None,
1239 history: None,
1240 metadata: None,
1241 };
1242
1243 let result = SendMessageResult::Task(task.clone());
1245 let json = serde_json::to_string(&result).unwrap();
1246 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1247 assert!(value.get("task").is_none(), "should not have wrapper key");
1248 assert_eq!(value.get("id").unwrap().as_str().unwrap(), "t-1");
1249
1250 let parsed: SendMessageResult = serde_json::from_str(&json).unwrap();
1252 assert_eq!(parsed, SendMessageResult::Task(task));
1253 }
1254
1255 #[test]
1256 fn stream_response_serialization() {
1257 let event = StreamResponse::StatusUpdate(TaskStatusUpdateEvent {
1258 task_id: "t-1".to_string(),
1259 context_id: "ctx-1".to_string(),
1260 status: TaskStatus {
1261 state: TaskState::Working,
1262 message: None,
1263 timestamp: None,
1264 },
1265 metadata: None,
1266 });
1267 let json = serde_json::to_string(&event).unwrap();
1268 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1269 assert!(value.get("statusUpdate").is_some());
1270 }
1271
1272 #[test]
1273 fn push_notification_config_serialization() {
1274 let config = PushNotificationConfig {
1275 id: Some("cfg-1".to_string()),
1276 url: "https://example.com/webhook".to_string(),
1277 token: Some("secret".to_string()),
1278 authentication: Some(AuthenticationInfo {
1279 scheme: "bearer".to_string(),
1280 credentials: Some("token123".to_string()),
1281 }),
1282 };
1283 let json = serde_json::to_string(&config).unwrap();
1284 let parsed: PushNotificationConfig = serde_json::from_str(&json).unwrap();
1285 assert_eq!(parsed.url, "https://example.com/webhook");
1286 assert_eq!(parsed.authentication.unwrap().scheme, "bearer");
1287 }
1288}