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)]
305#[serde(rename_all = "camelCase")]
306pub struct Part {
307 #[serde(default, skip_serializing_if = "Option::is_none")]
309 pub text: Option<String>,
310 #[serde(default, skip_serializing_if = "Option::is_none")]
312 pub raw: Option<String>,
313 #[serde(default, skip_serializing_if = "Option::is_none")]
315 pub url: Option<String>,
316 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub data: Option<serde_json::Value>,
319 #[serde(default, skip_serializing_if = "Option::is_none")]
321 pub metadata: Option<serde_json::Value>,
322 #[serde(default, skip_serializing_if = "Option::is_none")]
324 pub filename: Option<String>,
325 #[serde(default, skip_serializing_if = "Option::is_none")]
327 pub media_type: Option<String>,
328}
329
330impl Part {
331 pub fn text(text: impl Into<String>) -> Self {
333 Self {
334 text: Some(text.into()),
335 raw: None,
336 url: None,
337 data: None,
338 metadata: None,
339 filename: None,
340 media_type: None,
341 }
342 }
343
344 pub fn url(url: impl Into<String>, media_type: impl Into<String>) -> Self {
346 Self {
347 text: None,
348 raw: None,
349 url: Some(url.into()),
350 data: None,
351 metadata: None,
352 filename: None,
353 media_type: Some(media_type.into()),
354 }
355 }
356
357 pub fn data(data: serde_json::Value, media_type: impl Into<String>) -> Self {
359 Self {
360 text: None,
361 raw: None,
362 url: None,
363 data: Some(data),
364 metadata: None,
365 filename: None,
366 media_type: Some(media_type.into()),
367 }
368 }
369
370 pub fn raw(raw: impl Into<String>, media_type: impl Into<String>) -> Self {
372 Self {
373 text: None,
374 raw: Some(raw.into()),
375 url: None,
376 data: None,
377 metadata: None,
378 filename: None,
379 media_type: Some(media_type.into()),
380 }
381 }
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
388#[non_exhaustive]
389pub enum Role {
390 #[serde(rename = "ROLE_UNSPECIFIED")]
391 Unspecified,
392 #[serde(rename = "ROLE_USER")]
393 User,
394 #[serde(rename = "ROLE_AGENT")]
395 Agent,
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
400#[serde(rename_all = "camelCase")]
401pub struct Message {
402 pub message_id: String,
404 #[serde(default, skip_serializing_if = "Option::is_none")]
406 pub context_id: Option<String>,
407 #[serde(default, skip_serializing_if = "Option::is_none")]
409 pub task_id: Option<String>,
410 pub role: Role,
412 pub parts: Vec<Part>,
414 #[serde(default, skip_serializing_if = "Option::is_none")]
416 pub metadata: Option<serde_json::Value>,
417 #[serde(default, skip_serializing_if = "Vec::is_empty")]
419 pub extensions: Vec<String>,
420 #[serde(default, skip_serializing_if = "Option::is_none")]
422 pub reference_task_ids: Option<Vec<String>>,
423}
424
425#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
427#[serde(rename_all = "camelCase")]
428pub struct Artifact {
429 pub artifact_id: String,
431 #[serde(default, skip_serializing_if = "Option::is_none")]
433 pub name: Option<String>,
434 #[serde(default, skip_serializing_if = "Option::is_none")]
436 pub description: Option<String>,
437 pub parts: Vec<Part>,
439 #[serde(default, skip_serializing_if = "Option::is_none")]
441 pub metadata: Option<serde_json::Value>,
442 #[serde(default, skip_serializing_if = "Vec::is_empty")]
444 pub extensions: Vec<String>,
445}
446
447#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
449#[non_exhaustive]
450pub enum TaskState {
451 #[serde(rename = "TASK_STATE_UNSPECIFIED")]
452 Unspecified,
453 #[serde(rename = "TASK_STATE_SUBMITTED")]
454 Submitted,
455 #[serde(rename = "TASK_STATE_WORKING")]
456 Working,
457 #[serde(rename = "TASK_STATE_COMPLETED")]
458 Completed,
459 #[serde(rename = "TASK_STATE_FAILED")]
460 Failed,
461 #[serde(rename = "TASK_STATE_CANCELED")]
462 Canceled,
463 #[serde(rename = "TASK_STATE_INPUT_REQUIRED")]
464 InputRequired,
465 #[serde(rename = "TASK_STATE_REJECTED")]
466 Rejected,
467 #[serde(rename = "TASK_STATE_AUTH_REQUIRED")]
468 AuthRequired,
469}
470
471#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
473#[serde(rename_all = "camelCase")]
474pub struct TaskStatus {
475 pub state: TaskState,
477 #[serde(default, skip_serializing_if = "Option::is_none")]
479 pub message: Option<Message>,
480 #[serde(default, skip_serializing_if = "Option::is_none")]
482 pub timestamp: Option<String>,
483}
484
485#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
487#[serde(rename_all = "camelCase")]
488pub struct Task {
489 pub id: String,
491 pub context_id: String,
493 pub status: TaskStatus,
495 #[serde(default, skip_serializing_if = "Option::is_none")]
497 pub artifacts: Option<Vec<Artifact>>,
498 #[serde(default, skip_serializing_if = "Option::is_none")]
500 pub history: Option<Vec<Message>>,
501 #[serde(default, skip_serializing_if = "Option::is_none")]
503 pub metadata: Option<serde_json::Value>,
504}
505
506impl TaskState {
507 pub fn is_terminal(&self) -> bool {
509 matches!(
510 self,
511 TaskState::Completed | TaskState::Failed | TaskState::Canceled | TaskState::Rejected
512 )
513 }
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
520#[serde(rename_all = "camelCase")]
521pub enum SendMessageResponse {
522 Task(Task),
523 Message(Message),
524}
525
526#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
529pub struct JsonRpcRequest {
530 pub jsonrpc: String,
531 pub method: String,
532 #[serde(default)]
533 pub params: Option<serde_json::Value>,
534 pub id: serde_json::Value,
535}
536
537#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
538pub struct JsonRpcResponse {
539 pub jsonrpc: String,
540 pub id: serde_json::Value,
541 #[serde(skip_serializing_if = "Option::is_none")]
542 pub result: Option<serde_json::Value>,
543 #[serde(skip_serializing_if = "Option::is_none")]
544 pub error: Option<JsonRpcError>,
545}
546
547#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
548pub struct JsonRpcError {
549 pub code: i32,
550 pub message: String,
551 #[serde(skip_serializing_if = "Option::is_none")]
552 pub data: Option<serde_json::Value>,
553}
554
555pub mod errors {
557 pub const PARSE_ERROR: i32 = -32700;
559 pub const INVALID_REQUEST: i32 = -32600;
560 pub const METHOD_NOT_FOUND: i32 = -32601;
561 pub const INVALID_PARAMS: i32 = -32602;
562 pub const INTERNAL_ERROR: i32 = -32603;
563
564 pub const TASK_NOT_FOUND: i32 = -32001;
566 pub const TASK_NOT_CANCELABLE: i32 = -32002;
567 pub const PUSH_NOTIFICATION_NOT_SUPPORTED: i32 = -32003;
568 pub const UNSUPPORTED_OPERATION: i32 = -32004;
569 pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
570 pub const EXTENDED_AGENT_CARD_NOT_CONFIGURED: i32 = -32006;
571 pub const VERSION_NOT_SUPPORTED: i32 = -32007;
572 pub const INVALID_AGENT_RESPONSE: i32 = -32008;
573 pub const EXTENSION_SUPPORT_REQUIRED: i32 = -32009;
574
575 pub fn message_for_code(code: i32) -> &'static str {
576 match code {
577 PARSE_ERROR => "Parse error",
578 INVALID_REQUEST => "Invalid request",
579 METHOD_NOT_FOUND => "Method not found",
580 INVALID_PARAMS => "Invalid params",
581 INTERNAL_ERROR => "Internal error",
582 TASK_NOT_FOUND => "Task not found",
583 TASK_NOT_CANCELABLE => "Task not cancelable",
584 PUSH_NOTIFICATION_NOT_SUPPORTED => "Push notifications not supported",
585 UNSUPPORTED_OPERATION => "Unsupported operation",
586 CONTENT_TYPE_NOT_SUPPORTED => "Content type not supported",
587 EXTENDED_AGENT_CARD_NOT_CONFIGURED => "Extended agent card not configured",
588 VERSION_NOT_SUPPORTED => "Protocol version not supported",
589 INVALID_AGENT_RESPONSE => "Invalid agent response",
590 EXTENSION_SUPPORT_REQUIRED => "Extension support required",
591 _ => "Unknown error",
592 }
593 }
594}
595
596pub fn success(id: serde_json::Value, result: serde_json::Value) -> JsonRpcResponse {
597 JsonRpcResponse {
598 jsonrpc: "2.0".to_string(),
599 id,
600 result: Some(result),
601 error: None,
602 }
603}
604
605pub fn error(id: serde_json::Value, code: i32, message: &str, data: Option<serde_json::Value>) -> JsonRpcResponse {
606 JsonRpcResponse {
607 jsonrpc: "2.0".to_string(),
608 id,
609 result: None,
610 error: Some(JsonRpcError {
611 code,
612 message: message.to_string(),
613 data,
614 }),
615 }
616}
617
618#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
622#[serde(rename_all = "camelCase")]
623pub struct SendMessageRequest {
624 #[serde(default, skip_serializing_if = "Option::is_none")]
626 pub tenant: Option<String>,
627 pub message: Message,
629 #[serde(default, skip_serializing_if = "Option::is_none")]
631 pub configuration: Option<SendMessageConfiguration>,
632 #[serde(default, skip_serializing_if = "Option::is_none")]
634 pub metadata: Option<serde_json::Value>,
635}
636
637#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
639#[serde(rename_all = "camelCase")]
640pub struct SendMessageConfiguration {
641 #[serde(default, skip_serializing_if = "Option::is_none")]
643 pub accepted_output_modes: Option<Vec<String>>,
644 #[serde(default, skip_serializing_if = "Option::is_none")]
646 pub push_notification_config: Option<PushNotificationConfig>,
647 #[serde(default, skip_serializing_if = "Option::is_none")]
649 pub history_length: Option<u32>,
650 #[serde(default, skip_serializing_if = "Option::is_none")]
652 pub blocking: Option<bool>,
653}
654
655#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
657#[serde(rename_all = "camelCase")]
658pub struct GetTaskRequest {
659 pub id: String,
661 #[serde(default, skip_serializing_if = "Option::is_none")]
663 pub history_length: Option<u32>,
664 #[serde(default, skip_serializing_if = "Option::is_none")]
666 pub tenant: Option<String>,
667}
668
669#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
671#[serde(rename_all = "camelCase")]
672pub struct CancelTaskRequest {
673 pub id: String,
675 #[serde(default, skip_serializing_if = "Option::is_none")]
677 pub tenant: Option<String>,
678}
679
680#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
682#[serde(rename_all = "camelCase")]
683pub struct ListTasksRequest {
684 #[serde(default, skip_serializing_if = "Option::is_none")]
686 pub context_id: Option<String>,
687 #[serde(default, skip_serializing_if = "Option::is_none")]
689 pub status: Option<TaskState>,
690 #[serde(default, skip_serializing_if = "Option::is_none")]
692 pub page_size: Option<u32>,
693 #[serde(default, skip_serializing_if = "Option::is_none")]
695 pub page_token: Option<String>,
696 #[serde(default, skip_serializing_if = "Option::is_none")]
698 pub history_length: Option<u32>,
699 #[serde(default, skip_serializing_if = "Option::is_none")]
701 pub status_timestamp_after: Option<i64>,
702 #[serde(default, skip_serializing_if = "Option::is_none")]
704 pub include_artifacts: Option<bool>,
705 #[serde(default, skip_serializing_if = "Option::is_none")]
707 pub tenant: Option<String>,
708}
709
710#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
712#[serde(rename_all = "camelCase")]
713pub struct TaskListResponse {
714 pub tasks: Vec<Task>,
716 pub next_page_token: String,
718 pub page_size: u32,
720 pub total_size: u32,
722}
723
724#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
726#[serde(rename_all = "camelCase")]
727pub struct SubscribeToTaskRequest {
728 pub id: String,
730 #[serde(default, skip_serializing_if = "Option::is_none")]
732 pub tenant: Option<String>,
733}
734
735#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
737#[serde(rename_all = "camelCase")]
738pub struct PushNotificationConfig {
739 #[serde(default, skip_serializing_if = "Option::is_none")]
741 pub id: Option<String>,
742 pub url: String,
744 #[serde(default, skip_serializing_if = "Option::is_none")]
746 pub token: Option<String>,
747 #[serde(default, skip_serializing_if = "Option::is_none")]
749 pub authentication: Option<AuthenticationInfo>,
750}
751
752#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
754#[serde(rename_all = "camelCase")]
755pub struct AuthenticationInfo {
756 pub scheme: String,
758 #[serde(default, skip_serializing_if = "Option::is_none")]
760 pub credentials: Option<String>,
761}
762
763#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
765#[serde(rename_all = "camelCase")]
766pub struct TaskPushNotificationConfig {
767 pub id: String,
769 pub task_id: String,
771 pub push_notification_config: PushNotificationConfig,
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 CreateTaskPushNotificationConfigRequest {
782 pub task_id: String,
784 pub config_id: String,
786 pub push_notification_config: PushNotificationConfig,
788 #[serde(default, skip_serializing_if = "Option::is_none")]
790 pub tenant: Option<String>,
791}
792
793#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
795#[serde(rename_all = "camelCase")]
796pub struct GetTaskPushNotificationConfigRequest {
797 pub id: String,
799 pub task_id: String,
801 #[serde(default, skip_serializing_if = "Option::is_none")]
803 pub tenant: Option<String>,
804}
805
806#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
808#[serde(rename_all = "camelCase")]
809pub struct ListTaskPushNotificationConfigRequest {
810 pub task_id: String,
812 #[serde(default, skip_serializing_if = "Option::is_none")]
814 pub page_size: Option<u32>,
815 #[serde(default, skip_serializing_if = "Option::is_none")]
817 pub page_token: Option<String>,
818 #[serde(default, skip_serializing_if = "Option::is_none")]
820 pub tenant: Option<String>,
821}
822
823#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
825#[serde(rename_all = "camelCase")]
826pub struct ListTaskPushNotificationConfigResponse {
827 pub configs: Vec<TaskPushNotificationConfig>,
829 #[serde(default)]
831 pub next_page_token: String,
832}
833
834#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
836#[serde(rename_all = "camelCase")]
837pub struct DeleteTaskPushNotificationConfigRequest {
838 pub id: String,
840 pub task_id: String,
842 #[serde(default, skip_serializing_if = "Option::is_none")]
844 pub tenant: Option<String>,
845}
846
847#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
849#[serde(rename_all = "camelCase")]
850pub struct GetExtendedAgentCardRequest {
851 #[serde(default, skip_serializing_if = "Option::is_none")]
853 pub tenant: Option<String>,
854}
855
856#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
860#[serde(rename_all = "camelCase")]
861pub enum StreamResponse {
862 Task(Task),
864 Message(Message),
866 StatusUpdate(TaskStatusUpdateEvent),
868 ArtifactUpdate(TaskArtifactUpdateEvent),
870}
871
872#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
874#[serde(rename_all = "camelCase")]
875pub struct TaskStatusUpdateEvent {
876 pub task_id: String,
878 pub context_id: String,
880 pub status: TaskStatus,
882 #[serde(default, skip_serializing_if = "Option::is_none")]
884 pub metadata: Option<serde_json::Value>,
885}
886
887#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
889#[serde(rename_all = "camelCase")]
890pub struct TaskArtifactUpdateEvent {
891 pub task_id: String,
893 pub context_id: String,
895 pub artifact: Artifact,
897 #[serde(default, skip_serializing_if = "Option::is_none")]
899 pub append: Option<bool>,
900 #[serde(default, skip_serializing_if = "Option::is_none")]
902 pub last_chunk: Option<bool>,
903 #[serde(default, skip_serializing_if = "Option::is_none")]
905 pub metadata: Option<serde_json::Value>,
906}
907
908pub fn new_message(role: Role, text: &str, context_id: Option<String>) -> Message {
912 Message {
913 message_id: Uuid::new_v4().to_string(),
914 context_id,
915 task_id: None,
916 role,
917 parts: vec![Part::text(text)],
918 metadata: None,
919 extensions: vec![],
920 reference_task_ids: None,
921 }
922}
923
924pub fn completed_task_with_text(user_message: Message, reply_text: &str) -> Task {
926 let context_id = user_message
927 .context_id
928 .clone()
929 .unwrap_or_else(|| Uuid::new_v4().to_string());
930 let task_id = Uuid::new_v4().to_string();
931 let agent_msg = new_message(Role::Agent, reply_text, Some(context_id.clone()));
932
933 Task {
934 id: task_id,
935 context_id,
936 status: TaskStatus {
937 state: TaskState::Completed,
938 message: Some(agent_msg.clone()),
939 timestamp: Some(chrono::Utc::now().to_rfc3339()),
940 },
941 history: Some(vec![user_message, agent_msg]),
942 artifacts: None,
943 metadata: None,
944 }
945}
946
947pub fn now_iso8601() -> String {
949 chrono::Utc::now().to_rfc3339()
950}
951
952pub fn validate_task_id(id: &str) -> bool {
954 Uuid::parse_str(id).is_ok()
955}
956
957#[cfg(test)]
958mod tests {
959 use super::*;
960
961 #[test]
962 fn jsonrpc_helpers_round_trip() {
963 let resp = success(serde_json::json!(1), serde_json::json!({"ok": true}));
964 assert_eq!(resp.jsonrpc, "2.0");
965 assert!(resp.error.is_none());
966 assert!(resp.result.is_some());
967 }
968
969 #[test]
970 fn task_state_is_terminal() {
971 assert!(TaskState::Completed.is_terminal());
972 assert!(TaskState::Failed.is_terminal());
973 assert!(TaskState::Canceled.is_terminal());
974 assert!(TaskState::Rejected.is_terminal());
975 assert!(!TaskState::Working.is_terminal());
976 assert!(!TaskState::Submitted.is_terminal());
977 assert!(!TaskState::InputRequired.is_terminal());
978 }
979
980 #[test]
981 fn task_state_serialization() {
982 let state = TaskState::Working;
983 let json = serde_json::to_string(&state).unwrap();
984 assert_eq!(json, r#""TASK_STATE_WORKING""#);
985
986 let parsed: TaskState = serde_json::from_str(&json).unwrap();
987 assert_eq!(parsed, TaskState::Working);
988 }
989
990 #[test]
991 fn role_serialization() {
992 let role = Role::User;
993 let json = serde_json::to_string(&role).unwrap();
994 assert_eq!(json, r#""ROLE_USER""#);
995 }
996
997 #[test]
998 fn message_serialization() {
999 let msg = new_message(Role::User, "hello", Some("ctx-123".to_string()));
1000 let json = serde_json::to_string(&msg).unwrap();
1001 let parsed: Message = serde_json::from_str(&json).unwrap();
1002 assert_eq!(parsed.role, Role::User);
1003 assert_eq!(parsed.parts.len(), 1);
1004 assert_eq!(parsed.parts[0].text.as_deref(), Some("hello"));
1005
1006 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1008 assert!(value.get("messageId").is_some());
1009 assert!(value.get("contextId").is_some());
1010 }
1011
1012 #[test]
1013 fn task_serialization() {
1014 let user_msg = new_message(Role::User, "test", None);
1015 let task = completed_task_with_text(user_msg, "response");
1016 let json = serde_json::to_string(&task).unwrap();
1017 let parsed: Task = serde_json::from_str(&json).unwrap();
1018 assert_eq!(parsed.status.state, TaskState::Completed);
1019 assert!(parsed.history.is_some());
1020 assert_eq!(parsed.history.unwrap().len(), 2);
1021
1022 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1024 assert!(value.get("contextId").is_some());
1025 }
1026
1027 #[test]
1028 fn part_text_serialization() {
1029 let part = Part::text("hello");
1030 let json = serde_json::to_string(&part).unwrap();
1031 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1032 assert_eq!(value.get("text").unwrap().as_str().unwrap(), "hello");
1033 assert!(value.get("type").is_none());
1035 }
1036
1037 #[test]
1038 fn part_url_serialization() {
1039 let part = Part::url("https://example.com/file.pdf", "application/pdf");
1040 let json = serde_json::to_string(&part).unwrap();
1041 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1042 assert_eq!(value.get("url").unwrap().as_str().unwrap(), "https://example.com/file.pdf");
1043 assert_eq!(value.get("mediaType").unwrap().as_str().unwrap(), "application/pdf");
1044 }
1045
1046 #[test]
1047 fn agent_card_with_security() {
1048 let card = AgentCard {
1049 name: "Test Agent".to_string(),
1050 description: "Test description".to_string(),
1051 supported_interfaces: vec![AgentInterface {
1052 url: "https://example.com/v1/rpc".to_string(),
1053 protocol_binding: "JSONRPC".to_string(),
1054 protocol_version: PROTOCOL_VERSION.to_string(),
1055 tenant: None,
1056 }],
1057 provider: Some(AgentProvider {
1058 organization: "Test Org".to_string(),
1059 url: "https://example.com".to_string(),
1060 }),
1061 version: PROTOCOL_VERSION.to_string(),
1062 documentation_url: None,
1063 capabilities: AgentCapabilities::default(),
1064 security_schemes: {
1065 let mut m = HashMap::new();
1066 m.insert("apiKey".to_string(), SecurityScheme::ApiKeySecurityScheme(ApiKeySecurityScheme {
1067 name: "X-API-Key".to_string(),
1068 location: "header".to_string(),
1069 description: None,
1070 }));
1071 m
1072 },
1073 security_requirements: vec![],
1074 default_input_modes: vec![],
1075 default_output_modes: vec![],
1076 skills: vec![],
1077 signatures: vec![],
1078 icon_url: None,
1079 };
1080
1081 let json = serde_json::to_string(&card).unwrap();
1082 let parsed: AgentCard = serde_json::from_str(&json).unwrap();
1083 assert_eq!(parsed.name, "Test Agent");
1084 assert_eq!(parsed.security_schemes.len(), 1);
1085 assert_eq!(parsed.endpoint(), Some("https://example.com/v1/rpc"));
1086
1087 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1089 assert!(value.get("supportedInterfaces").is_some());
1090 assert!(value.get("securitySchemes").is_some());
1091 assert!(value.get("securityRequirements").is_some());
1092 }
1093
1094 #[test]
1095 fn validate_task_id_helper() {
1096 let valid_uuid = Uuid::new_v4().to_string();
1097 assert!(validate_task_id(&valid_uuid));
1098 assert!(!validate_task_id("not-a-uuid"));
1099 }
1100
1101 #[test]
1102 fn error_codes() {
1103 use errors::*;
1104 assert_eq!(message_for_code(TASK_NOT_FOUND), "Task not found");
1105 assert_eq!(message_for_code(VERSION_NOT_SUPPORTED), "Protocol version not supported");
1106 assert_eq!(message_for_code(INVALID_AGENT_RESPONSE), "Invalid agent response");
1107 assert_eq!(message_for_code(EXTENSION_SUPPORT_REQUIRED), "Extension support required");
1108 assert_eq!(message_for_code(999), "Unknown error");
1109 }
1110
1111 #[test]
1112 fn send_message_response_serialization() {
1113 let task = Task {
1114 id: "t-1".to_string(),
1115 context_id: "ctx-1".to_string(),
1116 status: TaskStatus {
1117 state: TaskState::Completed,
1118 message: None,
1119 timestamp: None,
1120 },
1121 artifacts: None,
1122 history: None,
1123 metadata: None,
1124 };
1125 let resp = SendMessageResponse::Task(task);
1126 let json = serde_json::to_string(&resp).unwrap();
1127 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1128 assert!(value.get("task").is_some());
1129 }
1130
1131 #[test]
1132 fn stream_response_serialization() {
1133 let event = StreamResponse::StatusUpdate(TaskStatusUpdateEvent {
1134 task_id: "t-1".to_string(),
1135 context_id: "ctx-1".to_string(),
1136 status: TaskStatus {
1137 state: TaskState::Working,
1138 message: None,
1139 timestamp: None,
1140 },
1141 metadata: None,
1142 });
1143 let json = serde_json::to_string(&event).unwrap();
1144 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1145 assert!(value.get("statusUpdate").is_some());
1146 }
1147
1148 #[test]
1149 fn push_notification_config_serialization() {
1150 let config = PushNotificationConfig {
1151 id: Some("cfg-1".to_string()),
1152 url: "https://example.com/webhook".to_string(),
1153 token: Some("secret".to_string()),
1154 authentication: Some(AuthenticationInfo {
1155 scheme: "bearer".to_string(),
1156 credentials: Some("token123".to_string()),
1157 }),
1158 };
1159 let json = serde_json::to_string(&config).unwrap();
1160 let parsed: PushNotificationConfig = serde_json::from_str(&json).unwrap();
1161 assert_eq!(parsed.url, "https://example.com/webhook");
1162 assert_eq!(parsed.authentication.unwrap().scheme, "bearer");
1163 }
1164}