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(
606 id: serde_json::Value,
607 code: i32,
608 message: &str,
609 data: Option<serde_json::Value>,
610) -> JsonRpcResponse {
611 JsonRpcResponse {
612 jsonrpc: "2.0".to_string(),
613 id,
614 result: None,
615 error: Some(JsonRpcError {
616 code,
617 message: message.to_string(),
618 data,
619 }),
620 }
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
627#[serde(rename_all = "camelCase")]
628pub struct SendMessageRequest {
629 #[serde(default, skip_serializing_if = "Option::is_none")]
631 pub tenant: Option<String>,
632 pub message: Message,
634 #[serde(default, skip_serializing_if = "Option::is_none")]
636 pub configuration: Option<SendMessageConfiguration>,
637 #[serde(default, skip_serializing_if = "Option::is_none")]
639 pub metadata: Option<serde_json::Value>,
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
644#[serde(rename_all = "camelCase")]
645pub struct SendMessageConfiguration {
646 #[serde(default, skip_serializing_if = "Option::is_none")]
648 pub accepted_output_modes: Option<Vec<String>>,
649 #[serde(default, skip_serializing_if = "Option::is_none")]
651 pub push_notification_config: Option<PushNotificationConfig>,
652 #[serde(default, skip_serializing_if = "Option::is_none")]
654 pub history_length: Option<u32>,
655 #[serde(default, skip_serializing_if = "Option::is_none")]
657 pub blocking: Option<bool>,
658}
659
660#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
662#[serde(rename_all = "camelCase")]
663pub struct GetTaskRequest {
664 pub id: String,
666 #[serde(default, skip_serializing_if = "Option::is_none")]
668 pub history_length: Option<u32>,
669 #[serde(default, skip_serializing_if = "Option::is_none")]
671 pub tenant: Option<String>,
672}
673
674#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
676#[serde(rename_all = "camelCase")]
677pub struct CancelTaskRequest {
678 pub id: String,
680 #[serde(default, skip_serializing_if = "Option::is_none")]
682 pub tenant: Option<String>,
683}
684
685#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
687#[serde(rename_all = "camelCase")]
688pub struct ListTasksRequest {
689 #[serde(default, skip_serializing_if = "Option::is_none")]
691 pub context_id: Option<String>,
692 #[serde(default, skip_serializing_if = "Option::is_none")]
694 pub status: Option<TaskState>,
695 #[serde(default, skip_serializing_if = "Option::is_none")]
697 pub page_size: Option<u32>,
698 #[serde(default, skip_serializing_if = "Option::is_none")]
700 pub page_token: Option<String>,
701 #[serde(default, skip_serializing_if = "Option::is_none")]
703 pub history_length: Option<u32>,
704 #[serde(default, skip_serializing_if = "Option::is_none")]
706 pub status_timestamp_after: Option<i64>,
707 #[serde(default, skip_serializing_if = "Option::is_none")]
709 pub include_artifacts: Option<bool>,
710 #[serde(default, skip_serializing_if = "Option::is_none")]
712 pub tenant: Option<String>,
713}
714
715#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
717#[serde(rename_all = "camelCase")]
718pub struct TaskListResponse {
719 pub tasks: Vec<Task>,
721 pub next_page_token: String,
723 pub page_size: u32,
725 pub total_size: u32,
727}
728
729#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
731#[serde(rename_all = "camelCase")]
732pub struct SubscribeToTaskRequest {
733 pub id: String,
735 #[serde(default, skip_serializing_if = "Option::is_none")]
737 pub tenant: Option<String>,
738}
739
740#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
742#[serde(rename_all = "camelCase")]
743pub struct PushNotificationConfig {
744 #[serde(default, skip_serializing_if = "Option::is_none")]
746 pub id: Option<String>,
747 pub url: String,
749 #[serde(default, skip_serializing_if = "Option::is_none")]
751 pub token: Option<String>,
752 #[serde(default, skip_serializing_if = "Option::is_none")]
754 pub authentication: Option<AuthenticationInfo>,
755}
756
757#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
759#[serde(rename_all = "camelCase")]
760pub struct AuthenticationInfo {
761 pub scheme: String,
763 #[serde(default, skip_serializing_if = "Option::is_none")]
765 pub credentials: Option<String>,
766}
767
768#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
770#[serde(rename_all = "camelCase")]
771pub struct TaskPushNotificationConfig {
772 pub id: String,
774 pub task_id: String,
776 pub push_notification_config: PushNotificationConfig,
778 #[serde(default, skip_serializing_if = "Option::is_none")]
780 pub tenant: Option<String>,
781}
782
783#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
785#[serde(rename_all = "camelCase")]
786pub struct CreateTaskPushNotificationConfigRequest {
787 pub task_id: String,
789 pub config_id: String,
791 pub push_notification_config: PushNotificationConfig,
793 #[serde(default, skip_serializing_if = "Option::is_none")]
795 pub tenant: Option<String>,
796}
797
798#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
800#[serde(rename_all = "camelCase")]
801pub struct GetTaskPushNotificationConfigRequest {
802 pub id: String,
804 pub task_id: String,
806 #[serde(default, skip_serializing_if = "Option::is_none")]
808 pub tenant: Option<String>,
809}
810
811#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
813#[serde(rename_all = "camelCase")]
814pub struct ListTaskPushNotificationConfigRequest {
815 pub task_id: String,
817 #[serde(default, skip_serializing_if = "Option::is_none")]
819 pub page_size: Option<u32>,
820 #[serde(default, skip_serializing_if = "Option::is_none")]
822 pub page_token: Option<String>,
823 #[serde(default, skip_serializing_if = "Option::is_none")]
825 pub tenant: Option<String>,
826}
827
828#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
830#[serde(rename_all = "camelCase")]
831pub struct ListTaskPushNotificationConfigResponse {
832 pub configs: Vec<TaskPushNotificationConfig>,
834 #[serde(default)]
836 pub next_page_token: String,
837}
838
839#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
841#[serde(rename_all = "camelCase")]
842pub struct DeleteTaskPushNotificationConfigRequest {
843 pub id: String,
845 pub task_id: String,
847 #[serde(default, skip_serializing_if = "Option::is_none")]
849 pub tenant: Option<String>,
850}
851
852#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
854#[serde(rename_all = "camelCase")]
855pub struct GetExtendedAgentCardRequest {
856 #[serde(default, skip_serializing_if = "Option::is_none")]
858 pub tenant: Option<String>,
859}
860
861#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
865#[serde(rename_all = "camelCase")]
866pub enum StreamResponse {
867 Task(Task),
869 Message(Message),
871 StatusUpdate(TaskStatusUpdateEvent),
873 ArtifactUpdate(TaskArtifactUpdateEvent),
875}
876
877#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
879#[serde(rename_all = "camelCase")]
880pub struct TaskStatusUpdateEvent {
881 pub task_id: String,
883 pub context_id: String,
885 pub status: TaskStatus,
887 #[serde(default, skip_serializing_if = "Option::is_none")]
889 pub metadata: Option<serde_json::Value>,
890}
891
892#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
894#[serde(rename_all = "camelCase")]
895pub struct TaskArtifactUpdateEvent {
896 pub task_id: String,
898 pub context_id: String,
900 pub artifact: Artifact,
902 #[serde(default, skip_serializing_if = "Option::is_none")]
904 pub append: Option<bool>,
905 #[serde(default, skip_serializing_if = "Option::is_none")]
907 pub last_chunk: Option<bool>,
908 #[serde(default, skip_serializing_if = "Option::is_none")]
910 pub metadata: Option<serde_json::Value>,
911}
912
913pub fn new_message(role: Role, text: &str, context_id: Option<String>) -> Message {
917 Message {
918 message_id: Uuid::new_v4().to_string(),
919 context_id,
920 task_id: None,
921 role,
922 parts: vec![Part::text(text)],
923 metadata: None,
924 extensions: vec![],
925 reference_task_ids: None,
926 }
927}
928
929pub fn completed_task_with_text(user_message: Message, reply_text: &str) -> Task {
931 let context_id = user_message
932 .context_id
933 .clone()
934 .unwrap_or_else(|| Uuid::new_v4().to_string());
935 let task_id = Uuid::new_v4().to_string();
936 let agent_msg = new_message(Role::Agent, reply_text, Some(context_id.clone()));
937
938 Task {
939 id: task_id,
940 context_id,
941 status: TaskStatus {
942 state: TaskState::Completed,
943 message: Some(agent_msg.clone()),
944 timestamp: Some(chrono::Utc::now().to_rfc3339()),
945 },
946 history: Some(vec![user_message, agent_msg]),
947 artifacts: None,
948 metadata: None,
949 }
950}
951
952pub fn now_iso8601() -> String {
954 chrono::Utc::now().to_rfc3339()
955}
956
957pub fn validate_task_id(id: &str) -> bool {
959 Uuid::parse_str(id).is_ok()
960}
961
962#[cfg(test)]
963mod tests {
964 use super::*;
965
966 #[test]
967 fn jsonrpc_helpers_round_trip() {
968 let resp = success(serde_json::json!(1), serde_json::json!({"ok": true}));
969 assert_eq!(resp.jsonrpc, "2.0");
970 assert!(resp.error.is_none());
971 assert!(resp.result.is_some());
972 }
973
974 #[test]
975 fn task_state_is_terminal() {
976 assert!(TaskState::Completed.is_terminal());
977 assert!(TaskState::Failed.is_terminal());
978 assert!(TaskState::Canceled.is_terminal());
979 assert!(TaskState::Rejected.is_terminal());
980 assert!(!TaskState::Working.is_terminal());
981 assert!(!TaskState::Submitted.is_terminal());
982 assert!(!TaskState::InputRequired.is_terminal());
983 }
984
985 #[test]
986 fn task_state_serialization() {
987 let state = TaskState::Working;
988 let json = serde_json::to_string(&state).unwrap();
989 assert_eq!(json, r#""TASK_STATE_WORKING""#);
990
991 let parsed: TaskState = serde_json::from_str(&json).unwrap();
992 assert_eq!(parsed, TaskState::Working);
993 }
994
995 #[test]
996 fn role_serialization() {
997 let role = Role::User;
998 let json = serde_json::to_string(&role).unwrap();
999 assert_eq!(json, r#""ROLE_USER""#);
1000 }
1001
1002 #[test]
1003 fn message_serialization() {
1004 let msg = new_message(Role::User, "hello", Some("ctx-123".to_string()));
1005 let json = serde_json::to_string(&msg).unwrap();
1006 let parsed: Message = serde_json::from_str(&json).unwrap();
1007 assert_eq!(parsed.role, Role::User);
1008 assert_eq!(parsed.parts.len(), 1);
1009 assert_eq!(parsed.parts[0].text.as_deref(), Some("hello"));
1010
1011 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1013 assert!(value.get("messageId").is_some());
1014 assert!(value.get("contextId").is_some());
1015 }
1016
1017 #[test]
1018 fn task_serialization() {
1019 let user_msg = new_message(Role::User, "test", None);
1020 let task = completed_task_with_text(user_msg, "response");
1021 let json = serde_json::to_string(&task).unwrap();
1022 let parsed: Task = serde_json::from_str(&json).unwrap();
1023 assert_eq!(parsed.status.state, TaskState::Completed);
1024 assert!(parsed.history.is_some());
1025 assert_eq!(parsed.history.unwrap().len(), 2);
1026
1027 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1029 assert!(value.get("contextId").is_some());
1030 }
1031
1032 #[test]
1033 fn part_text_serialization() {
1034 let part = Part::text("hello");
1035 let json = serde_json::to_string(&part).unwrap();
1036 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1037 assert_eq!(value.get("text").unwrap().as_str().unwrap(), "hello");
1038 assert!(value.get("type").is_none());
1040 }
1041
1042 #[test]
1043 fn part_url_serialization() {
1044 let part = Part::url("https://example.com/file.pdf", "application/pdf");
1045 let json = serde_json::to_string(&part).unwrap();
1046 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1047 assert_eq!(
1048 value.get("url").unwrap().as_str().unwrap(),
1049 "https://example.com/file.pdf"
1050 );
1051 assert_eq!(
1052 value.get("mediaType").unwrap().as_str().unwrap(),
1053 "application/pdf"
1054 );
1055 }
1056
1057 #[test]
1058 fn agent_card_with_security() {
1059 let card = AgentCard {
1060 name: "Test Agent".to_string(),
1061 description: "Test description".to_string(),
1062 supported_interfaces: vec![AgentInterface {
1063 url: "https://example.com/v1/rpc".to_string(),
1064 protocol_binding: "JSONRPC".to_string(),
1065 protocol_version: PROTOCOL_VERSION.to_string(),
1066 tenant: None,
1067 }],
1068 provider: Some(AgentProvider {
1069 organization: "Test Org".to_string(),
1070 url: "https://example.com".to_string(),
1071 }),
1072 version: PROTOCOL_VERSION.to_string(),
1073 documentation_url: None,
1074 capabilities: AgentCapabilities::default(),
1075 security_schemes: {
1076 let mut m = HashMap::new();
1077 m.insert(
1078 "apiKey".to_string(),
1079 SecurityScheme::ApiKeySecurityScheme(ApiKeySecurityScheme {
1080 name: "X-API-Key".to_string(),
1081 location: "header".to_string(),
1082 description: None,
1083 }),
1084 );
1085 m
1086 },
1087 security_requirements: vec![],
1088 default_input_modes: vec![],
1089 default_output_modes: vec![],
1090 skills: vec![],
1091 signatures: vec![],
1092 icon_url: None,
1093 };
1094
1095 let json = serde_json::to_string(&card).unwrap();
1096 let parsed: AgentCard = serde_json::from_str(&json).unwrap();
1097 assert_eq!(parsed.name, "Test Agent");
1098 assert_eq!(parsed.security_schemes.len(), 1);
1099 assert_eq!(parsed.endpoint(), Some("https://example.com/v1/rpc"));
1100
1101 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1103 assert!(value.get("supportedInterfaces").is_some());
1104 assert!(value.get("securitySchemes").is_some());
1105 assert!(value.get("securityRequirements").is_some());
1106 }
1107
1108 #[test]
1109 fn validate_task_id_helper() {
1110 let valid_uuid = Uuid::new_v4().to_string();
1111 assert!(validate_task_id(&valid_uuid));
1112 assert!(!validate_task_id("not-a-uuid"));
1113 }
1114
1115 #[test]
1116 fn error_codes() {
1117 use errors::*;
1118 assert_eq!(message_for_code(TASK_NOT_FOUND), "Task not found");
1119 assert_eq!(
1120 message_for_code(VERSION_NOT_SUPPORTED),
1121 "Protocol version not supported"
1122 );
1123 assert_eq!(
1124 message_for_code(INVALID_AGENT_RESPONSE),
1125 "Invalid agent response"
1126 );
1127 assert_eq!(
1128 message_for_code(EXTENSION_SUPPORT_REQUIRED),
1129 "Extension support required"
1130 );
1131 assert_eq!(message_for_code(999), "Unknown error");
1132 }
1133
1134 #[test]
1135 fn send_message_response_serialization() {
1136 let task = Task {
1137 id: "t-1".to_string(),
1138 context_id: "ctx-1".to_string(),
1139 status: TaskStatus {
1140 state: TaskState::Completed,
1141 message: None,
1142 timestamp: None,
1143 },
1144 artifacts: None,
1145 history: None,
1146 metadata: None,
1147 };
1148 let resp = SendMessageResponse::Task(task);
1149 let json = serde_json::to_string(&resp).unwrap();
1150 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1151 assert!(value.get("task").is_some());
1152 }
1153
1154 #[test]
1155 fn stream_response_serialization() {
1156 let event = StreamResponse::StatusUpdate(TaskStatusUpdateEvent {
1157 task_id: "t-1".to_string(),
1158 context_id: "ctx-1".to_string(),
1159 status: TaskStatus {
1160 state: TaskState::Working,
1161 message: None,
1162 timestamp: None,
1163 },
1164 metadata: None,
1165 });
1166 let json = serde_json::to_string(&event).unwrap();
1167 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1168 assert!(value.get("statusUpdate").is_some());
1169 }
1170
1171 #[test]
1172 fn push_notification_config_serialization() {
1173 let config = PushNotificationConfig {
1174 id: Some("cfg-1".to_string()),
1175 url: "https://example.com/webhook".to_string(),
1176 token: Some("secret".to_string()),
1177 authentication: Some(AuthenticationInfo {
1178 scheme: "bearer".to_string(),
1179 credentials: Some("token123".to_string()),
1180 }),
1181 };
1182 let json = serde_json::to_string(&config).unwrap();
1183 let parsed: PushNotificationConfig = serde_json::from_str(&json).unwrap();
1184 assert_eq!(parsed.url, "https://example.com/webhook");
1185 assert_eq!(parsed.authentication.unwrap().scheme, "bearer");
1186 }
1187}