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
456fn default_task_kind() -> String {
457 "task".to_string()
458}
459
460fn default_status_update_kind() -> String {
461 "status-update".to_string()
462}
463
464fn default_artifact_update_kind() -> String {
465 "artifact-update".to_string()
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
470#[serde(rename_all = "camelCase")]
471pub struct Artifact {
472 pub artifact_id: String,
474 #[serde(default, skip_serializing_if = "Option::is_none")]
476 pub name: Option<String>,
477 #[serde(default, skip_serializing_if = "Option::is_none")]
479 pub description: Option<String>,
480 pub parts: Vec<Part>,
482 #[serde(default, skip_serializing_if = "Option::is_none")]
484 pub metadata: Option<serde_json::Value>,
485 #[serde(default, skip_serializing_if = "Vec::is_empty")]
487 pub extensions: Vec<String>,
488}
489
490#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
492#[non_exhaustive]
493pub enum TaskState {
494 #[serde(rename = "unspecified")]
495 Unspecified,
496 #[serde(rename = "submitted")]
497 Submitted,
498 #[serde(rename = "working")]
499 Working,
500 #[serde(rename = "completed")]
501 Completed,
502 #[serde(rename = "failed")]
503 Failed,
504 #[serde(rename = "canceled")]
505 Canceled,
506 #[serde(rename = "input-required")]
507 InputRequired,
508 #[serde(rename = "rejected")]
509 Rejected,
510 #[serde(rename = "auth-required")]
511 AuthRequired,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
516#[serde(rename_all = "camelCase")]
517pub struct TaskStatus {
518 pub state: TaskState,
520 #[serde(default, skip_serializing_if = "Option::is_none")]
522 pub message: Option<Message>,
523 #[serde(default, skip_serializing_if = "Option::is_none")]
525 pub timestamp: Option<String>,
526}
527
528#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
530#[serde(rename_all = "camelCase")]
531pub struct Task {
532 #[serde(default = "default_task_kind")]
534 pub kind: String,
535 pub id: String,
537 pub context_id: String,
539 pub status: TaskStatus,
541 #[serde(default, skip_serializing_if = "Option::is_none")]
543 pub artifacts: Option<Vec<Artifact>>,
544 #[serde(default, skip_serializing_if = "Option::is_none")]
546 pub history: Option<Vec<Message>>,
547 #[serde(default, skip_serializing_if = "Option::is_none")]
549 pub metadata: Option<serde_json::Value>,
550}
551
552impl TaskState {
553 pub fn is_terminal(&self) -> bool {
555 matches!(
556 self,
557 TaskState::Completed | TaskState::Failed | TaskState::Canceled | TaskState::Rejected
558 )
559 }
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
569#[serde(rename_all = "camelCase")]
570pub enum SendMessageResponse {
571 Task(Task),
572 Message(Message),
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
580#[serde(untagged)]
581pub enum SendMessageResult {
582 Task(Task),
583 Message(Message),
584}
585
586#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
589pub struct JsonRpcRequest {
590 pub jsonrpc: String,
591 pub method: String,
592 #[serde(default)]
593 pub params: Option<serde_json::Value>,
594 pub id: serde_json::Value,
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
598pub struct JsonRpcResponse {
599 pub jsonrpc: String,
600 pub id: serde_json::Value,
601 #[serde(skip_serializing_if = "Option::is_none")]
602 pub result: Option<serde_json::Value>,
603 #[serde(skip_serializing_if = "Option::is_none")]
604 pub error: Option<JsonRpcError>,
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
608pub struct JsonRpcError {
609 pub code: i32,
610 pub message: String,
611 #[serde(skip_serializing_if = "Option::is_none")]
612 pub data: Option<serde_json::Value>,
613}
614
615pub mod errors {
617 pub const PARSE_ERROR: i32 = -32700;
619 pub const INVALID_REQUEST: i32 = -32600;
620 pub const METHOD_NOT_FOUND: i32 = -32601;
621 pub const INVALID_PARAMS: i32 = -32602;
622 pub const INTERNAL_ERROR: i32 = -32603;
623
624 pub const TASK_NOT_FOUND: i32 = -32001;
626 pub const TASK_NOT_CANCELABLE: i32 = -32002;
627 pub const PUSH_NOTIFICATION_NOT_SUPPORTED: i32 = -32003;
628 pub const UNSUPPORTED_OPERATION: i32 = -32004;
629 pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
630 pub const EXTENDED_AGENT_CARD_NOT_CONFIGURED: i32 = -32006;
631 pub const VERSION_NOT_SUPPORTED: i32 = -32007;
632 pub const INVALID_AGENT_RESPONSE: i32 = -32008;
633 pub const EXTENSION_SUPPORT_REQUIRED: i32 = -32009;
634
635 pub fn message_for_code(code: i32) -> &'static str {
636 match code {
637 PARSE_ERROR => "Parse error",
638 INVALID_REQUEST => "Invalid request",
639 METHOD_NOT_FOUND => "Method not found",
640 INVALID_PARAMS => "Invalid params",
641 INTERNAL_ERROR => "Internal error",
642 TASK_NOT_FOUND => "Task not found",
643 TASK_NOT_CANCELABLE => "Task not cancelable",
644 PUSH_NOTIFICATION_NOT_SUPPORTED => "Push notifications not supported",
645 UNSUPPORTED_OPERATION => "Unsupported operation",
646 CONTENT_TYPE_NOT_SUPPORTED => "Content type not supported",
647 EXTENDED_AGENT_CARD_NOT_CONFIGURED => "Extended agent card not configured",
648 VERSION_NOT_SUPPORTED => "Protocol version not supported",
649 INVALID_AGENT_RESPONSE => "Invalid agent response",
650 EXTENSION_SUPPORT_REQUIRED => "Extension support required",
651 _ => "Unknown error",
652 }
653 }
654}
655
656pub fn success(id: serde_json::Value, result: serde_json::Value) -> JsonRpcResponse {
657 JsonRpcResponse {
658 jsonrpc: "2.0".to_string(),
659 id,
660 result: Some(result),
661 error: None,
662 }
663}
664
665pub fn error(
666 id: serde_json::Value,
667 code: i32,
668 message: &str,
669 data: Option<serde_json::Value>,
670) -> JsonRpcResponse {
671 JsonRpcResponse {
672 jsonrpc: "2.0".to_string(),
673 id,
674 result: None,
675 error: Some(JsonRpcError {
676 code,
677 message: message.to_string(),
678 data,
679 }),
680 }
681}
682
683#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
687#[serde(rename_all = "camelCase")]
688pub struct SendMessageRequest {
689 #[serde(default, skip_serializing_if = "Option::is_none")]
691 pub tenant: Option<String>,
692 pub message: Message,
694 #[serde(default, skip_serializing_if = "Option::is_none")]
696 pub configuration: Option<SendMessageConfiguration>,
697 #[serde(default, skip_serializing_if = "Option::is_none")]
699 pub metadata: Option<serde_json::Value>,
700}
701
702#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
704#[serde(rename_all = "camelCase")]
705pub struct SendMessageConfiguration {
706 #[serde(default, skip_serializing_if = "Option::is_none")]
708 pub accepted_output_modes: Option<Vec<String>>,
709 #[serde(default, skip_serializing_if = "Option::is_none")]
711 pub push_notification_config: Option<PushNotificationConfig>,
712 #[serde(default, skip_serializing_if = "Option::is_none")]
714 pub history_length: Option<u32>,
715 #[serde(default, skip_serializing_if = "Option::is_none")]
717 pub blocking: Option<bool>,
718}
719
720#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
722#[serde(rename_all = "camelCase")]
723pub struct GetTaskRequest {
724 pub id: String,
726 #[serde(default, skip_serializing_if = "Option::is_none")]
728 pub history_length: Option<u32>,
729 #[serde(default, skip_serializing_if = "Option::is_none")]
731 pub tenant: Option<String>,
732}
733
734#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
736#[serde(rename_all = "camelCase")]
737pub struct CancelTaskRequest {
738 pub id: String,
740 #[serde(default, skip_serializing_if = "Option::is_none")]
742 pub tenant: Option<String>,
743}
744
745#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
747#[serde(rename_all = "camelCase")]
748pub struct ListTasksRequest {
749 #[serde(default, skip_serializing_if = "Option::is_none")]
751 pub context_id: Option<String>,
752 #[serde(default, skip_serializing_if = "Option::is_none")]
754 pub status: Option<TaskState>,
755 #[serde(default, skip_serializing_if = "Option::is_none")]
757 pub page_size: Option<u32>,
758 #[serde(default, skip_serializing_if = "Option::is_none")]
760 pub page_token: Option<String>,
761 #[serde(default, skip_serializing_if = "Option::is_none")]
763 pub history_length: Option<u32>,
764 #[serde(default, skip_serializing_if = "Option::is_none")]
766 pub status_timestamp_after: Option<i64>,
767 #[serde(default, skip_serializing_if = "Option::is_none")]
769 pub include_artifacts: Option<bool>,
770 #[serde(default, skip_serializing_if = "Option::is_none")]
772 pub tenant: Option<String>,
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
777#[serde(rename_all = "camelCase")]
778pub struct TaskListResponse {
779 pub tasks: Vec<Task>,
781 pub next_page_token: String,
783 pub page_size: u32,
785 pub total_size: u32,
787}
788
789#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
791#[serde(rename_all = "camelCase")]
792pub struct SubscribeToTaskRequest {
793 pub id: String,
795 #[serde(default, skip_serializing_if = "Option::is_none")]
797 pub tenant: Option<String>,
798}
799
800#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
802#[serde(rename_all = "camelCase")]
803pub struct PushNotificationConfig {
804 #[serde(default, skip_serializing_if = "Option::is_none")]
806 pub id: Option<String>,
807 pub url: String,
809 #[serde(default, skip_serializing_if = "Option::is_none")]
811 pub token: Option<String>,
812 #[serde(default, skip_serializing_if = "Option::is_none")]
814 pub authentication: Option<AuthenticationInfo>,
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
819#[serde(rename_all = "camelCase")]
820pub struct AuthenticationInfo {
821 pub scheme: String,
823 #[serde(default, skip_serializing_if = "Option::is_none")]
825 pub credentials: Option<String>,
826}
827
828#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
830#[serde(rename_all = "camelCase")]
831pub struct TaskPushNotificationConfig {
832 pub id: String,
834 pub task_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 CreateTaskPushNotificationConfigRequest {
847 pub task_id: String,
849 pub config_id: String,
851 pub push_notification_config: PushNotificationConfig,
853 #[serde(default, skip_serializing_if = "Option::is_none")]
855 pub tenant: Option<String>,
856}
857
858#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
860#[serde(rename_all = "camelCase")]
861pub struct GetTaskPushNotificationConfigRequest {
862 pub id: String,
864 pub task_id: String,
866 #[serde(default, skip_serializing_if = "Option::is_none")]
868 pub tenant: Option<String>,
869}
870
871#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
873#[serde(rename_all = "camelCase")]
874pub struct ListTaskPushNotificationConfigRequest {
875 pub task_id: String,
877 #[serde(default, skip_serializing_if = "Option::is_none")]
879 pub page_size: Option<u32>,
880 #[serde(default, skip_serializing_if = "Option::is_none")]
882 pub page_token: Option<String>,
883 #[serde(default, skip_serializing_if = "Option::is_none")]
885 pub tenant: Option<String>,
886}
887
888#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
890#[serde(rename_all = "camelCase")]
891pub struct ListTaskPushNotificationConfigResponse {
892 pub configs: Vec<TaskPushNotificationConfig>,
894 #[serde(default)]
896 pub next_page_token: String,
897}
898
899#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
901#[serde(rename_all = "camelCase")]
902pub struct DeleteTaskPushNotificationConfigRequest {
903 pub id: String,
905 pub task_id: String,
907 #[serde(default, skip_serializing_if = "Option::is_none")]
909 pub tenant: Option<String>,
910}
911
912#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
914#[serde(rename_all = "camelCase")]
915pub struct GetExtendedAgentCardRequest {
916 #[serde(default, skip_serializing_if = "Option::is_none")]
918 pub tenant: Option<String>,
919}
920
921#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
925#[serde(rename_all = "camelCase")]
926pub enum StreamResponse {
927 Task(Task),
929 Message(Message),
931 StatusUpdate(TaskStatusUpdateEvent),
933 ArtifactUpdate(TaskArtifactUpdateEvent),
935}
936
937#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
939#[serde(rename_all = "camelCase")]
940pub struct TaskStatusUpdateEvent {
941 #[serde(default = "default_status_update_kind")]
943 pub kind: String,
944 pub task_id: String,
946 pub context_id: String,
948 pub status: TaskStatus,
950 #[serde(rename = "final", default)]
952 pub is_final: bool,
953 #[serde(default, skip_serializing_if = "Option::is_none")]
955 pub metadata: Option<serde_json::Value>,
956}
957
958#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
960#[serde(rename_all = "camelCase")]
961pub struct TaskArtifactUpdateEvent {
962 #[serde(default = "default_artifact_update_kind")]
964 pub kind: String,
965 pub task_id: String,
967 pub context_id: String,
969 pub artifact: Artifact,
971 #[serde(default, skip_serializing_if = "Option::is_none")]
973 pub append: Option<bool>,
974 #[serde(default, skip_serializing_if = "Option::is_none")]
976 pub last_chunk: Option<bool>,
977 #[serde(default, skip_serializing_if = "Option::is_none")]
979 pub metadata: Option<serde_json::Value>,
980}
981
982#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
987#[serde(untagged)]
988pub enum StreamingMessageResult {
989 StatusUpdate(TaskStatusUpdateEvent),
990 ArtifactUpdate(TaskArtifactUpdateEvent),
991 Task(Task),
992 Message(Message),
993}
994
995pub fn new_message(role: Role, text: &str, context_id: Option<String>) -> Message {
999 Message {
1000 kind: "message".to_string(),
1001 message_id: Uuid::new_v4().to_string(),
1002 context_id,
1003 task_id: None,
1004 role,
1005 parts: vec![Part::text(text)],
1006 metadata: None,
1007 extensions: vec![],
1008 reference_task_ids: None,
1009 }
1010}
1011
1012pub fn completed_task_with_text(user_message: Message, reply_text: &str) -> Task {
1014 let context_id = user_message
1015 .context_id
1016 .clone()
1017 .unwrap_or_else(|| Uuid::new_v4().to_string());
1018 let task_id = Uuid::new_v4().to_string();
1019 let agent_msg = new_message(Role::Agent, reply_text, Some(context_id.clone()));
1020
1021 Task {
1022 kind: "task".to_string(),
1023 id: task_id,
1024 context_id,
1025 status: TaskStatus {
1026 state: TaskState::Completed,
1027 message: Some(agent_msg.clone()),
1028 timestamp: Some(chrono::Utc::now().to_rfc3339()),
1029 },
1030 history: Some(vec![user_message, agent_msg]),
1031 artifacts: None,
1032 metadata: None,
1033 }
1034}
1035
1036pub fn now_iso8601() -> String {
1038 chrono::Utc::now().to_rfc3339()
1039}
1040
1041pub fn validate_task_id(id: &str) -> bool {
1043 Uuid::parse_str(id).is_ok()
1044}
1045
1046#[cfg(test)]
1047mod tests {
1048 use super::*;
1049
1050 #[test]
1051 fn jsonrpc_helpers_round_trip() {
1052 let resp = success(serde_json::json!(1), serde_json::json!({"ok": true}));
1053 assert_eq!(resp.jsonrpc, "2.0");
1054 assert!(resp.error.is_none());
1055 assert!(resp.result.is_some());
1056 }
1057
1058 #[test]
1059 fn task_state_is_terminal() {
1060 assert!(TaskState::Completed.is_terminal());
1061 assert!(TaskState::Failed.is_terminal());
1062 assert!(TaskState::Canceled.is_terminal());
1063 assert!(TaskState::Rejected.is_terminal());
1064 assert!(!TaskState::Working.is_terminal());
1065 assert!(!TaskState::Submitted.is_terminal());
1066 assert!(!TaskState::InputRequired.is_terminal());
1067 }
1068
1069 #[test]
1070 fn task_state_serialization() {
1071 let state = TaskState::Working;
1072 let json = serde_json::to_string(&state).unwrap();
1073 assert_eq!(json, r#""working""#);
1074
1075 let parsed: TaskState = serde_json::from_str(&json).unwrap();
1076 assert_eq!(parsed, TaskState::Working);
1077 }
1078
1079 #[test]
1080 fn role_serialization() {
1081 let role = Role::User;
1082 let json = serde_json::to_string(&role).unwrap();
1083 assert_eq!(json, r#""user""#);
1084 }
1085
1086 #[test]
1087 fn message_serialization() {
1088 let msg = new_message(Role::User, "hello", Some("ctx-123".to_string()));
1089 let json = serde_json::to_string(&msg).unwrap();
1090 let parsed: Message = serde_json::from_str(&json).unwrap();
1091 assert_eq!(parsed.role, Role::User);
1092 assert_eq!(parsed.parts.len(), 1);
1093 assert_eq!(parsed.parts[0].as_text(), Some("hello"));
1094
1095 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1097 assert!(value.get("messageId").is_some());
1098 assert!(value.get("contextId").is_some());
1099 }
1100
1101 #[test]
1102 fn task_serialization() {
1103 let user_msg = new_message(Role::User, "test", None);
1104 let task = completed_task_with_text(user_msg, "response");
1105 let json = serde_json::to_string(&task).unwrap();
1106 let parsed: Task = serde_json::from_str(&json).unwrap();
1107 assert_eq!(parsed.status.state, TaskState::Completed);
1108 assert!(parsed.history.is_some());
1109 assert_eq!(parsed.history.unwrap().len(), 2);
1110
1111 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1113 assert!(value.get("contextId").is_some());
1114 }
1115
1116 #[test]
1117 fn part_text_serialization() {
1118 let part = Part::text("hello");
1119 let json = serde_json::to_string(&part).unwrap();
1120 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1121 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "text");
1122 assert_eq!(value.get("text").unwrap().as_str().unwrap(), "hello");
1123 }
1124
1125 #[test]
1126 fn part_text_round_trip() {
1127 let part = Part::text("hello");
1128 let json = serde_json::to_string(&part).unwrap();
1129 let parsed: Part = serde_json::from_str(&json).unwrap();
1130 assert_eq!(parsed, part);
1131 assert_eq!(parsed.as_text(), Some("hello"));
1132 }
1133
1134 #[test]
1135 fn part_file_uri_serialization() {
1136 let part = Part::file_uri("https://example.com/file.pdf", "application/pdf");
1137 let json = serde_json::to_string(&part).unwrap();
1138 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1139 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "file");
1140 let file = value.get("file").unwrap();
1141 assert_eq!(
1142 file.get("uri").unwrap().as_str().unwrap(),
1143 "https://example.com/file.pdf"
1144 );
1145 assert_eq!(
1146 file.get("mimeType").unwrap().as_str().unwrap(),
1147 "application/pdf"
1148 );
1149 }
1150
1151 #[test]
1152 fn part_data_serialization() {
1153 let part = Part::data(serde_json::json!({"key": "value"}));
1154 let json = serde_json::to_string(&part).unwrap();
1155 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1156 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "data");
1157 assert_eq!(
1158 value.get("data").unwrap(),
1159 &serde_json::json!({"key": "value"})
1160 );
1161 }
1162
1163 #[test]
1164 fn part_deserialization_from_wire_format() {
1165 let text: Part = serde_json::from_str(r#"{"kind":"text","text":"hello"}"#).unwrap();
1167 assert_eq!(text.as_text(), Some("hello"));
1168
1169 let file: Part = serde_json::from_str(
1170 r#"{"kind":"file","file":{"uri":"https://example.com/f.pdf","mimeType":"application/pdf"}}"#,
1171 )
1172 .unwrap();
1173 match &file {
1174 Part::File { file, .. } => {
1175 assert_eq!(file.uri.as_deref(), Some("https://example.com/f.pdf"));
1176 assert_eq!(file.mime_type.as_deref(), Some("application/pdf"));
1177 }
1178 _ => panic!("expected File part"),
1179 }
1180
1181 let data: Part =
1182 serde_json::from_str(r#"{"kind":"data","data":{"k":"v"}}"#).unwrap();
1183 match &data {
1184 Part::Data { data, .. } => assert_eq!(data, &serde_json::json!({"k": "v"})),
1185 _ => panic!("expected Data part"),
1186 }
1187 }
1188
1189 #[test]
1190 fn agent_card_with_security() {
1191 let card = AgentCard {
1192 name: "Test Agent".to_string(),
1193 description: "Test description".to_string(),
1194 supported_interfaces: vec![AgentInterface {
1195 url: "https://example.com/v1/rpc".to_string(),
1196 protocol_binding: "JSONRPC".to_string(),
1197 protocol_version: PROTOCOL_VERSION.to_string(),
1198 tenant: None,
1199 }],
1200 provider: Some(AgentProvider {
1201 organization: "Test Org".to_string(),
1202 url: "https://example.com".to_string(),
1203 }),
1204 version: PROTOCOL_VERSION.to_string(),
1205 documentation_url: None,
1206 capabilities: AgentCapabilities::default(),
1207 security_schemes: {
1208 let mut m = HashMap::new();
1209 m.insert(
1210 "apiKey".to_string(),
1211 SecurityScheme::ApiKeySecurityScheme(ApiKeySecurityScheme {
1212 name: "X-API-Key".to_string(),
1213 location: "header".to_string(),
1214 description: None,
1215 }),
1216 );
1217 m
1218 },
1219 security_requirements: vec![],
1220 default_input_modes: vec![],
1221 default_output_modes: vec![],
1222 skills: vec![],
1223 signatures: vec![],
1224 icon_url: None,
1225 };
1226
1227 let json = serde_json::to_string(&card).unwrap();
1228 let parsed: AgentCard = serde_json::from_str(&json).unwrap();
1229 assert_eq!(parsed.name, "Test Agent");
1230 assert_eq!(parsed.security_schemes.len(), 1);
1231 assert_eq!(parsed.endpoint(), Some("https://example.com/v1/rpc"));
1232
1233 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1235 assert!(value.get("supportedInterfaces").is_some());
1236 assert!(value.get("securitySchemes").is_some());
1237 assert!(value.get("securityRequirements").is_some());
1238 }
1239
1240 #[test]
1241 fn validate_task_id_helper() {
1242 let valid_uuid = Uuid::new_v4().to_string();
1243 assert!(validate_task_id(&valid_uuid));
1244 assert!(!validate_task_id("not-a-uuid"));
1245 }
1246
1247 #[test]
1248 fn error_codes() {
1249 use errors::*;
1250 assert_eq!(message_for_code(TASK_NOT_FOUND), "Task not found");
1251 assert_eq!(
1252 message_for_code(VERSION_NOT_SUPPORTED),
1253 "Protocol version not supported"
1254 );
1255 assert_eq!(
1256 message_for_code(INVALID_AGENT_RESPONSE),
1257 "Invalid agent response"
1258 );
1259 assert_eq!(
1260 message_for_code(EXTENSION_SUPPORT_REQUIRED),
1261 "Extension support required"
1262 );
1263 assert_eq!(message_for_code(999), "Unknown error");
1264 }
1265
1266 #[test]
1267 fn send_message_result_serialization() {
1268 let task = Task {
1269 kind: "task".to_string(),
1270 id: "t-1".to_string(),
1271 context_id: "ctx-1".to_string(),
1272 status: TaskStatus {
1273 state: TaskState::Completed,
1274 message: None,
1275 timestamp: None,
1276 },
1277 artifacts: None,
1278 history: None,
1279 metadata: None,
1280 };
1281
1282 let result = SendMessageResult::Task(task.clone());
1284 let json = serde_json::to_string(&result).unwrap();
1285 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1286 assert!(value.get("task").is_none(), "should not have wrapper key");
1287 assert_eq!(value.get("id").unwrap().as_str().unwrap(), "t-1");
1288
1289 let parsed: SendMessageResult = serde_json::from_str(&json).unwrap();
1291 assert_eq!(parsed, SendMessageResult::Task(task));
1292 }
1293
1294 #[test]
1295 fn stream_response_serialization() {
1296 let event = StreamResponse::StatusUpdate(TaskStatusUpdateEvent {
1297 kind: "status-update".to_string(),
1298 task_id: "t-1".to_string(),
1299 context_id: "ctx-1".to_string(),
1300 status: TaskStatus {
1301 state: TaskState::Working,
1302 message: None,
1303 timestamp: None,
1304 },
1305 is_final: false,
1306 metadata: None,
1307 });
1308 let json = serde_json::to_string(&event).unwrap();
1309 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1310 assert!(value.get("statusUpdate").is_some());
1311 }
1312
1313 #[test]
1314 fn push_notification_config_serialization() {
1315 let config = PushNotificationConfig {
1316 id: Some("cfg-1".to_string()),
1317 url: "https://example.com/webhook".to_string(),
1318 token: Some("secret".to_string()),
1319 authentication: Some(AuthenticationInfo {
1320 scheme: "bearer".to_string(),
1321 credentials: Some("token123".to_string()),
1322 }),
1323 };
1324 let json = serde_json::to_string(&config).unwrap();
1325 let parsed: PushNotificationConfig = serde_json::from_str(&json).unwrap();
1326 assert_eq!(parsed.url, "https://example.com/webhook");
1327 assert_eq!(parsed.authentication.unwrap().scheme, "bearer");
1328 }
1329}