1pub mod agent_card;
4pub mod executor;
5pub mod handler;
6pub mod jsonrpc;
7pub mod middleware;
8pub mod pagination;
9pub mod push;
10pub mod rest;
11pub mod sse;
12pub mod task_store;
13
14#[cfg(any(feature = "rustls-tls", feature = "rustls-no-provider"))]
15pub mod tls;
16
17pub use agent_card::{AgentCardProducer, StaticAgentCard, WELL_KNOWN_AGENT_CARD_PATH};
18pub use executor::{AgentExecutor, ExecutorContext};
19pub use handler::{
20 DefaultRequestHandler, ExtendedAgentCardResolver, RequestAuthorizer, RequestHandler,
21};
22pub use middleware::{CallContext, CallInterceptor, InterceptedHandler, ServiceParams, User};
23pub use push::{HttpPushSender, HttpPushSenderConfig, InMemoryPushConfigStore, PushConfigStore};
24pub use task_store::{InMemoryTaskStore, TaskStore};
25
26#[cfg(test)]
27pub(crate) mod test_util {
28 use std::collections::HashMap;
29 use std::sync::{Arc, Mutex};
30
31 use a2a::*;
32 use async_trait::async_trait;
33 use futures::stream::BoxStream;
34
35 use crate::handler::RequestHandler;
36 use crate::middleware::ServiceParams;
37
38 pub fn install_crypto_provider() {
39 #[cfg(feature = "rustls-tls")]
40 {
41 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
42 }
43 }
44
45 pub(crate) type CapturedParams = Arc<Mutex<HashMap<&'static str, ServiceParams>>>;
46
47 pub(crate) struct CapturingHandler {
54 captured: CapturedParams,
55 }
56
57 impl CapturingHandler {
58 pub fn new() -> (Arc<Self>, CapturedParams) {
59 let captured = Arc::new(Mutex::new(HashMap::new()));
60 let handler = Arc::new(CapturingHandler {
61 captured: captured.clone(),
62 });
63 (handler, captured)
64 }
65
66 fn record(&self, method: &'static str, params: &ServiceParams) {
67 self.captured.lock().unwrap().insert(method, params.clone());
68 }
69 }
70
71 #[async_trait]
72 impl RequestHandler for CapturingHandler {
73 async fn send_message(
74 &self,
75 params: &ServiceParams,
76 _req: SendMessageRequest,
77 ) -> Result<SendMessageResponse, A2AError> {
78 self.record("send_message", params);
79 Err(A2AError::internal("stub"))
80 }
81
82 async fn send_streaming_message(
83 &self,
84 params: &ServiceParams,
85 _req: SendMessageRequest,
86 ) -> Result<BoxStream<'static, Result<StreamResponse, A2AError>>, A2AError> {
87 self.record("send_streaming_message", params);
88 Ok(Box::pin(futures::stream::empty()))
89 }
90
91 async fn get_task(
92 &self,
93 params: &ServiceParams,
94 _req: GetTaskRequest,
95 ) -> Result<Task, A2AError> {
96 self.record("get_task", params);
97 Err(A2AError::task_not_found("stub"))
98 }
99
100 async fn list_tasks(
101 &self,
102 params: &ServiceParams,
103 _req: ListTasksRequest,
104 ) -> Result<ListTasksResponse, A2AError> {
105 self.record("list_tasks", params);
106 Ok(ListTasksResponse {
107 tasks: Vec::new(),
108 next_page_token: String::new(),
109 page_size: 0,
110 total_size: 0,
111 })
112 }
113
114 async fn cancel_task(
115 &self,
116 params: &ServiceParams,
117 _req: CancelTaskRequest,
118 ) -> Result<Task, A2AError> {
119 self.record("cancel_task", params);
120 Err(A2AError::task_not_found("stub"))
121 }
122
123 async fn subscribe_to_task(
124 &self,
125 params: &ServiceParams,
126 _req: SubscribeToTaskRequest,
127 ) -> Result<BoxStream<'static, Result<StreamResponse, A2AError>>, A2AError> {
128 self.record("subscribe_to_task", params);
129 Ok(Box::pin(futures::stream::empty()))
130 }
131
132 async fn create_push_config(
133 &self,
134 params: &ServiceParams,
135 req: TaskPushNotificationConfig,
136 ) -> Result<TaskPushNotificationConfig, A2AError> {
137 self.record("create_push_config", params);
138 Ok(req)
139 }
140
141 async fn get_push_config(
142 &self,
143 params: &ServiceParams,
144 _req: GetTaskPushNotificationConfigRequest,
145 ) -> Result<TaskPushNotificationConfig, A2AError> {
146 self.record("get_push_config", params);
147 Err(A2AError::internal("stub"))
148 }
149
150 async fn list_push_configs(
151 &self,
152 params: &ServiceParams,
153 _req: ListTaskPushNotificationConfigsRequest,
154 ) -> Result<ListTaskPushNotificationConfigsResponse, A2AError> {
155 self.record("list_push_configs", params);
156 Ok(ListTaskPushNotificationConfigsResponse {
157 configs: Vec::new(),
158 next_page_token: None,
159 })
160 }
161
162 async fn delete_push_config(
163 &self,
164 params: &ServiceParams,
165 _req: DeleteTaskPushNotificationConfigRequest,
166 ) -> Result<(), A2AError> {
167 self.record("delete_push_config", params);
168 Ok(())
169 }
170
171 async fn get_extended_agent_card(
172 &self,
173 params: &ServiceParams,
174 _req: GetExtendedAgentCardRequest,
175 ) -> Result<AgentCard, A2AError> {
176 self.record("get_extended_agent_card", params);
177 Err(A2AError::internal("stub"))
178 }
179 }
180
181 #[track_caller]
183 pub(crate) fn assert_header_captured(
184 captured: &CapturedParams,
185 method: &'static str,
186 header: &str,
187 expected: &str,
188 ) {
189 let captured = captured.lock().unwrap();
190 let params = captured
191 .get(method)
192 .unwrap_or_else(|| panic!("{method} should have been called; captured={captured:?}"));
193 assert_eq!(
194 params.get(header),
195 Some(&vec![expected.to_string()]),
196 "{header} should be propagated to {method}; got {params:?}",
197 );
198 }
199}