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 = "ROLE_UNSPECIFIED")]
415 Unspecified,
416 #[serde(rename = "ROLE_USER")]
417 User,
418 #[serde(rename = "ROLE_AGENT")]
419 Agent,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
424#[serde(rename_all = "camelCase")]
425pub struct Message {
426 pub message_id: String,
428 #[serde(default, skip_serializing_if = "Option::is_none")]
430 pub context_id: Option<String>,
431 #[serde(default, skip_serializing_if = "Option::is_none")]
433 pub task_id: Option<String>,
434 pub role: Role,
436 pub parts: Vec<Part>,
438 #[serde(default, skip_serializing_if = "Option::is_none")]
440 pub metadata: Option<serde_json::Value>,
441 #[serde(default, skip_serializing_if = "Vec::is_empty")]
443 pub extensions: Vec<String>,
444 #[serde(default, skip_serializing_if = "Option::is_none")]
446 pub reference_task_ids: Option<Vec<String>>,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
451#[serde(rename_all = "camelCase")]
452pub struct Artifact {
453 pub artifact_id: String,
455 #[serde(default, skip_serializing_if = "Option::is_none")]
457 pub name: Option<String>,
458 #[serde(default, skip_serializing_if = "Option::is_none")]
460 pub description: Option<String>,
461 pub parts: Vec<Part>,
463 #[serde(default, skip_serializing_if = "Option::is_none")]
465 pub metadata: Option<serde_json::Value>,
466 #[serde(default, skip_serializing_if = "Vec::is_empty")]
468 pub extensions: Vec<String>,
469}
470
471#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
473#[non_exhaustive]
474pub enum TaskState {
475 #[serde(rename = "TASK_STATE_UNSPECIFIED")]
476 Unspecified,
477 #[serde(rename = "TASK_STATE_SUBMITTED")]
478 Submitted,
479 #[serde(rename = "TASK_STATE_WORKING")]
480 Working,
481 #[serde(rename = "TASK_STATE_COMPLETED")]
482 Completed,
483 #[serde(rename = "TASK_STATE_FAILED")]
484 Failed,
485 #[serde(rename = "TASK_STATE_CANCELED")]
486 Canceled,
487 #[serde(rename = "TASK_STATE_INPUT_REQUIRED")]
488 InputRequired,
489 #[serde(rename = "TASK_STATE_REJECTED")]
490 Rejected,
491 #[serde(rename = "TASK_STATE_AUTH_REQUIRED")]
492 AuthRequired,
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
497#[serde(rename_all = "camelCase")]
498pub struct TaskStatus {
499 pub state: TaskState,
501 #[serde(default, skip_serializing_if = "Option::is_none")]
503 pub message: Option<Message>,
504 #[serde(default, skip_serializing_if = "Option::is_none")]
506 pub timestamp: Option<String>,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
511#[serde(rename_all = "camelCase")]
512pub struct Task {
513 pub id: String,
515 pub context_id: String,
517 pub status: TaskStatus,
519 #[serde(default, skip_serializing_if = "Option::is_none")]
521 pub artifacts: Option<Vec<Artifact>>,
522 #[serde(default, skip_serializing_if = "Option::is_none")]
524 pub history: Option<Vec<Message>>,
525 #[serde(default, skip_serializing_if = "Option::is_none")]
527 pub metadata: Option<serde_json::Value>,
528}
529
530impl TaskState {
531 pub fn is_terminal(&self) -> bool {
533 matches!(
534 self,
535 TaskState::Completed | TaskState::Failed | TaskState::Canceled | TaskState::Rejected
536 )
537 }
538}
539
540#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
547#[serde(rename_all = "camelCase")]
548pub enum SendMessageResponse {
549 Task(Task),
550 Message(Message),
551}
552
553#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
558#[serde(untagged)]
559pub enum SendMessageResult {
560 Task(Task),
561 Message(Message),
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
567pub struct JsonRpcRequest {
568 pub jsonrpc: String,
569 pub method: String,
570 #[serde(default)]
571 pub params: Option<serde_json::Value>,
572 pub id: serde_json::Value,
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
576pub struct JsonRpcResponse {
577 pub jsonrpc: String,
578 pub id: serde_json::Value,
579 #[serde(skip_serializing_if = "Option::is_none")]
580 pub result: Option<serde_json::Value>,
581 #[serde(skip_serializing_if = "Option::is_none")]
582 pub error: Option<JsonRpcError>,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
586pub struct JsonRpcError {
587 pub code: i32,
588 pub message: String,
589 #[serde(skip_serializing_if = "Option::is_none")]
590 pub data: Option<serde_json::Value>,
591}
592
593pub mod errors {
595 pub const PARSE_ERROR: i32 = -32700;
597 pub const INVALID_REQUEST: i32 = -32600;
598 pub const METHOD_NOT_FOUND: i32 = -32601;
599 pub const INVALID_PARAMS: i32 = -32602;
600 pub const INTERNAL_ERROR: i32 = -32603;
601
602 pub const TASK_NOT_FOUND: i32 = -32001;
604 pub const TASK_NOT_CANCELABLE: i32 = -32002;
605 pub const PUSH_NOTIFICATION_NOT_SUPPORTED: i32 = -32003;
606 pub const UNSUPPORTED_OPERATION: i32 = -32004;
607 pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
608 pub const EXTENDED_AGENT_CARD_NOT_CONFIGURED: i32 = -32006;
609 pub const VERSION_NOT_SUPPORTED: i32 = -32007;
610 pub const INVALID_AGENT_RESPONSE: i32 = -32008;
611 pub const EXTENSION_SUPPORT_REQUIRED: i32 = -32009;
612
613 pub fn message_for_code(code: i32) -> &'static str {
614 match code {
615 PARSE_ERROR => "Parse error",
616 INVALID_REQUEST => "Invalid request",
617 METHOD_NOT_FOUND => "Method not found",
618 INVALID_PARAMS => "Invalid params",
619 INTERNAL_ERROR => "Internal error",
620 TASK_NOT_FOUND => "Task not found",
621 TASK_NOT_CANCELABLE => "Task not cancelable",
622 PUSH_NOTIFICATION_NOT_SUPPORTED => "Push notifications not supported",
623 UNSUPPORTED_OPERATION => "Unsupported operation",
624 CONTENT_TYPE_NOT_SUPPORTED => "Content type not supported",
625 EXTENDED_AGENT_CARD_NOT_CONFIGURED => "Extended agent card not configured",
626 VERSION_NOT_SUPPORTED => "Protocol version not supported",
627 INVALID_AGENT_RESPONSE => "Invalid agent response",
628 EXTENSION_SUPPORT_REQUIRED => "Extension support required",
629 _ => "Unknown error",
630 }
631 }
632}
633
634pub fn success(id: serde_json::Value, result: serde_json::Value) -> JsonRpcResponse {
635 JsonRpcResponse {
636 jsonrpc: "2.0".to_string(),
637 id,
638 result: Some(result),
639 error: None,
640 }
641}
642
643pub fn error(
644 id: serde_json::Value,
645 code: i32,
646 message: &str,
647 data: Option<serde_json::Value>,
648) -> JsonRpcResponse {
649 JsonRpcResponse {
650 jsonrpc: "2.0".to_string(),
651 id,
652 result: None,
653 error: Some(JsonRpcError {
654 code,
655 message: message.to_string(),
656 data,
657 }),
658 }
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
665#[serde(rename_all = "camelCase")]
666pub struct SendMessageRequest {
667 #[serde(default, skip_serializing_if = "Option::is_none")]
669 pub tenant: Option<String>,
670 pub message: Message,
672 #[serde(default, skip_serializing_if = "Option::is_none")]
674 pub configuration: Option<SendMessageConfiguration>,
675 #[serde(default, skip_serializing_if = "Option::is_none")]
677 pub metadata: Option<serde_json::Value>,
678}
679
680#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
682#[serde(rename_all = "camelCase")]
683pub struct SendMessageConfiguration {
684 #[serde(default, skip_serializing_if = "Option::is_none")]
686 pub accepted_output_modes: Option<Vec<String>>,
687 #[serde(default, skip_serializing_if = "Option::is_none")]
689 pub push_notification_config: Option<PushNotificationConfig>,
690 #[serde(default, skip_serializing_if = "Option::is_none")]
692 pub history_length: Option<u32>,
693 #[serde(default, skip_serializing_if = "Option::is_none")]
695 pub blocking: Option<bool>,
696}
697
698#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
700#[serde(rename_all = "camelCase")]
701pub struct GetTaskRequest {
702 pub id: String,
704 #[serde(default, skip_serializing_if = "Option::is_none")]
706 pub history_length: Option<u32>,
707 #[serde(default, skip_serializing_if = "Option::is_none")]
709 pub tenant: Option<String>,
710}
711
712#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
714#[serde(rename_all = "camelCase")]
715pub struct CancelTaskRequest {
716 pub id: String,
718 #[serde(default, skip_serializing_if = "Option::is_none")]
720 pub tenant: Option<String>,
721}
722
723#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
725#[serde(rename_all = "camelCase")]
726pub struct ListTasksRequest {
727 #[serde(default, skip_serializing_if = "Option::is_none")]
729 pub context_id: Option<String>,
730 #[serde(default, skip_serializing_if = "Option::is_none")]
732 pub status: Option<TaskState>,
733 #[serde(default, skip_serializing_if = "Option::is_none")]
735 pub page_size: Option<u32>,
736 #[serde(default, skip_serializing_if = "Option::is_none")]
738 pub page_token: Option<String>,
739 #[serde(default, skip_serializing_if = "Option::is_none")]
741 pub history_length: Option<u32>,
742 #[serde(default, skip_serializing_if = "Option::is_none")]
744 pub status_timestamp_after: Option<i64>,
745 #[serde(default, skip_serializing_if = "Option::is_none")]
747 pub include_artifacts: Option<bool>,
748 #[serde(default, skip_serializing_if = "Option::is_none")]
750 pub tenant: Option<String>,
751}
752
753#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
755#[serde(rename_all = "camelCase")]
756pub struct TaskListResponse {
757 pub tasks: Vec<Task>,
759 pub next_page_token: String,
761 pub page_size: u32,
763 pub total_size: u32,
765}
766
767#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
769#[serde(rename_all = "camelCase")]
770pub struct SubscribeToTaskRequest {
771 pub id: String,
773 #[serde(default, skip_serializing_if = "Option::is_none")]
775 pub tenant: Option<String>,
776}
777
778#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
780#[serde(rename_all = "camelCase")]
781pub struct PushNotificationConfig {
782 #[serde(default, skip_serializing_if = "Option::is_none")]
784 pub id: Option<String>,
785 pub url: String,
787 #[serde(default, skip_serializing_if = "Option::is_none")]
789 pub token: Option<String>,
790 #[serde(default, skip_serializing_if = "Option::is_none")]
792 pub authentication: Option<AuthenticationInfo>,
793}
794
795#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
797#[serde(rename_all = "camelCase")]
798pub struct AuthenticationInfo {
799 pub scheme: String,
801 #[serde(default, skip_serializing_if = "Option::is_none")]
803 pub credentials: Option<String>,
804}
805
806#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
808#[serde(rename_all = "camelCase")]
809pub struct TaskPushNotificationConfig {
810 pub id: String,
812 pub task_id: String,
814 pub push_notification_config: PushNotificationConfig,
816 #[serde(default, skip_serializing_if = "Option::is_none")]
818 pub tenant: Option<String>,
819}
820
821#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
823#[serde(rename_all = "camelCase")]
824pub struct CreateTaskPushNotificationConfigRequest {
825 pub task_id: String,
827 pub config_id: String,
829 pub push_notification_config: PushNotificationConfig,
831 #[serde(default, skip_serializing_if = "Option::is_none")]
833 pub tenant: Option<String>,
834}
835
836#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
838#[serde(rename_all = "camelCase")]
839pub struct GetTaskPushNotificationConfigRequest {
840 pub id: String,
842 pub task_id: String,
844 #[serde(default, skip_serializing_if = "Option::is_none")]
846 pub tenant: Option<String>,
847}
848
849#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
851#[serde(rename_all = "camelCase")]
852pub struct ListTaskPushNotificationConfigRequest {
853 pub task_id: String,
855 #[serde(default, skip_serializing_if = "Option::is_none")]
857 pub page_size: Option<u32>,
858 #[serde(default, skip_serializing_if = "Option::is_none")]
860 pub page_token: Option<String>,
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 ListTaskPushNotificationConfigResponse {
870 pub configs: Vec<TaskPushNotificationConfig>,
872 #[serde(default)]
874 pub next_page_token: String,
875}
876
877#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
879#[serde(rename_all = "camelCase")]
880pub struct DeleteTaskPushNotificationConfigRequest {
881 pub id: String,
883 pub task_id: String,
885 #[serde(default, skip_serializing_if = "Option::is_none")]
887 pub tenant: Option<String>,
888}
889
890#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
892#[serde(rename_all = "camelCase")]
893pub struct GetExtendedAgentCardRequest {
894 #[serde(default, skip_serializing_if = "Option::is_none")]
896 pub tenant: Option<String>,
897}
898
899#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
903#[serde(rename_all = "camelCase")]
904pub enum StreamResponse {
905 Task(Task),
907 Message(Message),
909 StatusUpdate(TaskStatusUpdateEvent),
911 ArtifactUpdate(TaskArtifactUpdateEvent),
913}
914
915#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
917#[serde(rename_all = "camelCase")]
918pub struct TaskStatusUpdateEvent {
919 pub task_id: String,
921 pub context_id: String,
923 pub status: TaskStatus,
925 #[serde(default, skip_serializing_if = "Option::is_none")]
927 pub metadata: Option<serde_json::Value>,
928}
929
930#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
932#[serde(rename_all = "camelCase")]
933pub struct TaskArtifactUpdateEvent {
934 pub task_id: String,
936 pub context_id: String,
938 pub artifact: Artifact,
940 #[serde(default, skip_serializing_if = "Option::is_none")]
942 pub append: Option<bool>,
943 #[serde(default, skip_serializing_if = "Option::is_none")]
945 pub last_chunk: Option<bool>,
946 #[serde(default, skip_serializing_if = "Option::is_none")]
948 pub metadata: Option<serde_json::Value>,
949}
950
951pub fn new_message(role: Role, text: &str, context_id: Option<String>) -> Message {
955 Message {
956 message_id: Uuid::new_v4().to_string(),
957 context_id,
958 task_id: None,
959 role,
960 parts: vec![Part::text(text)],
961 metadata: None,
962 extensions: vec![],
963 reference_task_ids: None,
964 }
965}
966
967pub fn completed_task_with_text(user_message: Message, reply_text: &str) -> Task {
969 let context_id = user_message
970 .context_id
971 .clone()
972 .unwrap_or_else(|| Uuid::new_v4().to_string());
973 let task_id = Uuid::new_v4().to_string();
974 let agent_msg = new_message(Role::Agent, reply_text, Some(context_id.clone()));
975
976 Task {
977 id: task_id,
978 context_id,
979 status: TaskStatus {
980 state: TaskState::Completed,
981 message: Some(agent_msg.clone()),
982 timestamp: Some(chrono::Utc::now().to_rfc3339()),
983 },
984 history: Some(vec![user_message, agent_msg]),
985 artifacts: None,
986 metadata: None,
987 }
988}
989
990pub fn now_iso8601() -> String {
992 chrono::Utc::now().to_rfc3339()
993}
994
995pub fn validate_task_id(id: &str) -> bool {
997 Uuid::parse_str(id).is_ok()
998}
999
1000#[cfg(test)]
1001mod tests {
1002 use super::*;
1003
1004 #[test]
1005 fn jsonrpc_helpers_round_trip() {
1006 let resp = success(serde_json::json!(1), serde_json::json!({"ok": true}));
1007 assert_eq!(resp.jsonrpc, "2.0");
1008 assert!(resp.error.is_none());
1009 assert!(resp.result.is_some());
1010 }
1011
1012 #[test]
1013 fn task_state_is_terminal() {
1014 assert!(TaskState::Completed.is_terminal());
1015 assert!(TaskState::Failed.is_terminal());
1016 assert!(TaskState::Canceled.is_terminal());
1017 assert!(TaskState::Rejected.is_terminal());
1018 assert!(!TaskState::Working.is_terminal());
1019 assert!(!TaskState::Submitted.is_terminal());
1020 assert!(!TaskState::InputRequired.is_terminal());
1021 }
1022
1023 #[test]
1024 fn task_state_serialization() {
1025 let state = TaskState::Working;
1026 let json = serde_json::to_string(&state).unwrap();
1027 assert_eq!(json, r#""TASK_STATE_WORKING""#);
1028
1029 let parsed: TaskState = serde_json::from_str(&json).unwrap();
1030 assert_eq!(parsed, TaskState::Working);
1031 }
1032
1033 #[test]
1034 fn role_serialization() {
1035 let role = Role::User;
1036 let json = serde_json::to_string(&role).unwrap();
1037 assert_eq!(json, r#""ROLE_USER""#);
1038 }
1039
1040 #[test]
1041 fn message_serialization() {
1042 let msg = new_message(Role::User, "hello", Some("ctx-123".to_string()));
1043 let json = serde_json::to_string(&msg).unwrap();
1044 let parsed: Message = serde_json::from_str(&json).unwrap();
1045 assert_eq!(parsed.role, Role::User);
1046 assert_eq!(parsed.parts.len(), 1);
1047 assert_eq!(parsed.parts[0].as_text(), Some("hello"));
1048
1049 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1051 assert!(value.get("messageId").is_some());
1052 assert!(value.get("contextId").is_some());
1053 }
1054
1055 #[test]
1056 fn task_serialization() {
1057 let user_msg = new_message(Role::User, "test", None);
1058 let task = completed_task_with_text(user_msg, "response");
1059 let json = serde_json::to_string(&task).unwrap();
1060 let parsed: Task = serde_json::from_str(&json).unwrap();
1061 assert_eq!(parsed.status.state, TaskState::Completed);
1062 assert!(parsed.history.is_some());
1063 assert_eq!(parsed.history.unwrap().len(), 2);
1064
1065 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1067 assert!(value.get("contextId").is_some());
1068 }
1069
1070 #[test]
1071 fn part_text_serialization() {
1072 let part = Part::text("hello");
1073 let json = serde_json::to_string(&part).unwrap();
1074 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1075 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "text");
1076 assert_eq!(value.get("text").unwrap().as_str().unwrap(), "hello");
1077 }
1078
1079 #[test]
1080 fn part_text_round_trip() {
1081 let part = Part::text("hello");
1082 let json = serde_json::to_string(&part).unwrap();
1083 let parsed: Part = serde_json::from_str(&json).unwrap();
1084 assert_eq!(parsed, part);
1085 assert_eq!(parsed.as_text(), Some("hello"));
1086 }
1087
1088 #[test]
1089 fn part_file_uri_serialization() {
1090 let part = Part::file_uri("https://example.com/file.pdf", "application/pdf");
1091 let json = serde_json::to_string(&part).unwrap();
1092 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1093 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "file");
1094 let file = value.get("file").unwrap();
1095 assert_eq!(
1096 file.get("uri").unwrap().as_str().unwrap(),
1097 "https://example.com/file.pdf"
1098 );
1099 assert_eq!(
1100 file.get("mimeType").unwrap().as_str().unwrap(),
1101 "application/pdf"
1102 );
1103 }
1104
1105 #[test]
1106 fn part_data_serialization() {
1107 let part = Part::data(serde_json::json!({"key": "value"}));
1108 let json = serde_json::to_string(&part).unwrap();
1109 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1110 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "data");
1111 assert_eq!(
1112 value.get("data").unwrap(),
1113 &serde_json::json!({"key": "value"})
1114 );
1115 }
1116
1117 #[test]
1118 fn part_deserialization_from_wire_format() {
1119 let text: Part = serde_json::from_str(r#"{"kind":"text","text":"hello"}"#).unwrap();
1121 assert_eq!(text.as_text(), Some("hello"));
1122
1123 let file: Part = serde_json::from_str(
1124 r#"{"kind":"file","file":{"uri":"https://example.com/f.pdf","mimeType":"application/pdf"}}"#,
1125 )
1126 .unwrap();
1127 match &file {
1128 Part::File { file, .. } => {
1129 assert_eq!(file.uri.as_deref(), Some("https://example.com/f.pdf"));
1130 assert_eq!(file.mime_type.as_deref(), Some("application/pdf"));
1131 }
1132 _ => panic!("expected File part"),
1133 }
1134
1135 let data: Part =
1136 serde_json::from_str(r#"{"kind":"data","data":{"k":"v"}}"#).unwrap();
1137 match &data {
1138 Part::Data { data, .. } => assert_eq!(data, &serde_json::json!({"k": "v"})),
1139 _ => panic!("expected Data part"),
1140 }
1141 }
1142
1143 #[test]
1144 fn agent_card_with_security() {
1145 let card = AgentCard {
1146 name: "Test Agent".to_string(),
1147 description: "Test description".to_string(),
1148 supported_interfaces: vec![AgentInterface {
1149 url: "https://example.com/v1/rpc".to_string(),
1150 protocol_binding: "JSONRPC".to_string(),
1151 protocol_version: PROTOCOL_VERSION.to_string(),
1152 tenant: None,
1153 }],
1154 provider: Some(AgentProvider {
1155 organization: "Test Org".to_string(),
1156 url: "https://example.com".to_string(),
1157 }),
1158 version: PROTOCOL_VERSION.to_string(),
1159 documentation_url: None,
1160 capabilities: AgentCapabilities::default(),
1161 security_schemes: {
1162 let mut m = HashMap::new();
1163 m.insert(
1164 "apiKey".to_string(),
1165 SecurityScheme::ApiKeySecurityScheme(ApiKeySecurityScheme {
1166 name: "X-API-Key".to_string(),
1167 location: "header".to_string(),
1168 description: None,
1169 }),
1170 );
1171 m
1172 },
1173 security_requirements: vec![],
1174 default_input_modes: vec![],
1175 default_output_modes: vec![],
1176 skills: vec![],
1177 signatures: vec![],
1178 icon_url: None,
1179 };
1180
1181 let json = serde_json::to_string(&card).unwrap();
1182 let parsed: AgentCard = serde_json::from_str(&json).unwrap();
1183 assert_eq!(parsed.name, "Test Agent");
1184 assert_eq!(parsed.security_schemes.len(), 1);
1185 assert_eq!(parsed.endpoint(), Some("https://example.com/v1/rpc"));
1186
1187 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1189 assert!(value.get("supportedInterfaces").is_some());
1190 assert!(value.get("securitySchemes").is_some());
1191 assert!(value.get("securityRequirements").is_some());
1192 }
1193
1194 #[test]
1195 fn validate_task_id_helper() {
1196 let valid_uuid = Uuid::new_v4().to_string();
1197 assert!(validate_task_id(&valid_uuid));
1198 assert!(!validate_task_id("not-a-uuid"));
1199 }
1200
1201 #[test]
1202 fn error_codes() {
1203 use errors::*;
1204 assert_eq!(message_for_code(TASK_NOT_FOUND), "Task not found");
1205 assert_eq!(
1206 message_for_code(VERSION_NOT_SUPPORTED),
1207 "Protocol version not supported"
1208 );
1209 assert_eq!(
1210 message_for_code(INVALID_AGENT_RESPONSE),
1211 "Invalid agent response"
1212 );
1213 assert_eq!(
1214 message_for_code(EXTENSION_SUPPORT_REQUIRED),
1215 "Extension support required"
1216 );
1217 assert_eq!(message_for_code(999), "Unknown error");
1218 }
1219
1220 #[test]
1221 fn send_message_result_serialization() {
1222 let task = Task {
1223 id: "t-1".to_string(),
1224 context_id: "ctx-1".to_string(),
1225 status: TaskStatus {
1226 state: TaskState::Completed,
1227 message: None,
1228 timestamp: None,
1229 },
1230 artifacts: None,
1231 history: None,
1232 metadata: None,
1233 };
1234
1235 let result = SendMessageResult::Task(task.clone());
1237 let json = serde_json::to_string(&result).unwrap();
1238 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1239 assert!(value.get("task").is_none(), "should not have wrapper key");
1240 assert_eq!(value.get("id").unwrap().as_str().unwrap(), "t-1");
1241
1242 let parsed: SendMessageResult = serde_json::from_str(&json).unwrap();
1244 assert_eq!(parsed, SendMessageResult::Task(task));
1245 }
1246
1247 #[test]
1248 fn stream_response_serialization() {
1249 let event = StreamResponse::StatusUpdate(TaskStatusUpdateEvent {
1250 task_id: "t-1".to_string(),
1251 context_id: "ctx-1".to_string(),
1252 status: TaskStatus {
1253 state: TaskState::Working,
1254 message: None,
1255 timestamp: None,
1256 },
1257 metadata: None,
1258 });
1259 let json = serde_json::to_string(&event).unwrap();
1260 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1261 assert!(value.get("statusUpdate").is_some());
1262 }
1263
1264 #[test]
1265 fn push_notification_config_serialization() {
1266 let config = PushNotificationConfig {
1267 id: Some("cfg-1".to_string()),
1268 url: "https://example.com/webhook".to_string(),
1269 token: Some("secret".to_string()),
1270 authentication: Some(AuthenticationInfo {
1271 scheme: "bearer".to_string(),
1272 credentials: Some("token123".to_string()),
1273 }),
1274 };
1275 let json = serde_json::to_string(&config).unwrap();
1276 let parsed: PushNotificationConfig = serde_json::from_str(&json).unwrap();
1277 assert_eq!(parsed.url, "https://example.com/webhook");
1278 assert_eq!(parsed.authentication.unwrap().scheme, "bearer");
1279 }
1280}