1use std::future::Future;
2use std::pin::Pin;
3
4use anyclaw_sdk_types::{
5 InitializeParams, InitializeResult, PermissionRequest, SessionNewParams, SessionNewResult,
6 SessionPromptParams, SessionUpdateEvent,
7};
8
9use crate::error::AgentSdkError;
10
11pub trait AgentAdapter: Send + Sync + 'static {
16 fn on_initialize_params(
18 &self,
19 params: InitializeParams,
20 ) -> impl Future<Output = Result<InitializeParams, AgentSdkError>> + Send {
21 async move { Ok(params) }
22 }
23
24 fn on_initialize_result(
26 &self,
27 result: InitializeResult,
28 ) -> impl Future<Output = Result<InitializeResult, AgentSdkError>> + Send {
29 async move { Ok(result) }
30 }
31
32 fn on_session_new_params(
34 &self,
35 params: SessionNewParams,
36 ) -> impl Future<Output = Result<SessionNewParams, AgentSdkError>> + Send {
37 async move { Ok(params) }
38 }
39
40 fn on_session_new_result(
42 &self,
43 result: SessionNewResult,
44 ) -> impl Future<Output = Result<SessionNewResult, AgentSdkError>> + Send {
45 async move { Ok(result) }
46 }
47
48 fn on_session_prompt_params(
50 &self,
51 params: SessionPromptParams,
52 ) -> impl Future<Output = Result<SessionPromptParams, AgentSdkError>> + Send {
53 async move { Ok(params) }
54 }
55
56 fn on_session_update(
58 &self,
59 event: SessionUpdateEvent,
60 ) -> impl Future<Output = Result<SessionUpdateEvent, AgentSdkError>> + Send {
61 async move { Ok(event) }
62 }
63
64 fn on_permission_request(
66 &self,
67 request: PermissionRequest,
68 ) -> impl Future<Output = Result<PermissionRequest, AgentSdkError>> + Send {
69 async move { Ok(request) }
70 }
71}
72
73pub trait DynAgentAdapter: Send + Sync + 'static {
76 fn on_initialize_params<'a>(
78 &'a self,
79 params: InitializeParams,
80 ) -> Pin<Box<dyn Future<Output = Result<InitializeParams, AgentSdkError>> + Send + 'a>>;
81
82 fn on_initialize_result<'a>(
84 &'a self,
85 result: InitializeResult,
86 ) -> Pin<Box<dyn Future<Output = Result<InitializeResult, AgentSdkError>> + Send + 'a>>;
87
88 fn on_session_new_params<'a>(
90 &'a self,
91 params: SessionNewParams,
92 ) -> Pin<Box<dyn Future<Output = Result<SessionNewParams, AgentSdkError>> + Send + 'a>>;
93
94 fn on_session_new_result<'a>(
96 &'a self,
97 result: SessionNewResult,
98 ) -> Pin<Box<dyn Future<Output = Result<SessionNewResult, AgentSdkError>> + Send + 'a>>;
99
100 fn on_session_prompt_params<'a>(
102 &'a self,
103 params: SessionPromptParams,
104 ) -> Pin<Box<dyn Future<Output = Result<SessionPromptParams, AgentSdkError>> + Send + 'a>>;
105
106 fn on_session_update<'a>(
108 &'a self,
109 event: SessionUpdateEvent,
110 ) -> Pin<Box<dyn Future<Output = Result<SessionUpdateEvent, AgentSdkError>> + Send + 'a>>;
111
112 fn on_permission_request<'a>(
114 &'a self,
115 request: PermissionRequest,
116 ) -> Pin<Box<dyn Future<Output = Result<PermissionRequest, AgentSdkError>> + Send + 'a>>;
117}
118
119impl<T: AgentAdapter> DynAgentAdapter for T {
120 fn on_initialize_params<'a>(
121 &'a self,
122 params: InitializeParams,
123 ) -> Pin<Box<dyn Future<Output = Result<InitializeParams, AgentSdkError>> + Send + 'a>> {
124 Box::pin(AgentAdapter::on_initialize_params(self, params))
125 }
126
127 fn on_initialize_result<'a>(
128 &'a self,
129 result: InitializeResult,
130 ) -> Pin<Box<dyn Future<Output = Result<InitializeResult, AgentSdkError>> + Send + 'a>> {
131 Box::pin(AgentAdapter::on_initialize_result(self, result))
132 }
133
134 fn on_session_new_params<'a>(
135 &'a self,
136 params: SessionNewParams,
137 ) -> Pin<Box<dyn Future<Output = Result<SessionNewParams, AgentSdkError>> + Send + 'a>> {
138 Box::pin(AgentAdapter::on_session_new_params(self, params))
139 }
140
141 fn on_session_new_result<'a>(
142 &'a self,
143 result: SessionNewResult,
144 ) -> Pin<Box<dyn Future<Output = Result<SessionNewResult, AgentSdkError>> + Send + 'a>> {
145 Box::pin(AgentAdapter::on_session_new_result(self, result))
146 }
147
148 fn on_session_prompt_params<'a>(
149 &'a self,
150 params: SessionPromptParams,
151 ) -> Pin<Box<dyn Future<Output = Result<SessionPromptParams, AgentSdkError>> + Send + 'a>> {
152 Box::pin(AgentAdapter::on_session_prompt_params(self, params))
153 }
154
155 fn on_session_update<'a>(
156 &'a self,
157 event: SessionUpdateEvent,
158 ) -> Pin<Box<dyn Future<Output = Result<SessionUpdateEvent, AgentSdkError>> + Send + 'a>> {
159 Box::pin(AgentAdapter::on_session_update(self, event))
160 }
161
162 fn on_permission_request<'a>(
163 &'a self,
164 request: PermissionRequest,
165 ) -> Pin<Box<dyn Future<Output = Result<PermissionRequest, AgentSdkError>> + Send + 'a>> {
166 Box::pin(AgentAdapter::on_permission_request(self, request))
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use anyclaw_sdk_types::PermissionOption;
174 use anyclaw_sdk_types::{
175 ClientCapabilities, ContentBlock, InitializeParams, InitializeResult, PermissionRequest,
176 SessionNewParams, SessionNewResult, SessionPromptParams, SessionUpdateEvent,
177 SessionUpdateType, TextContent,
178 };
179 use rstest::rstest;
180
181 struct DefaultAdapter;
182
183 impl AgentAdapter for DefaultAdapter {}
184
185 #[rstest]
186 #[tokio::test]
187 async fn when_default_adapter_on_initialize_params_then_passthrough() {
188 let adapter = DefaultAdapter;
189 let params = InitializeParams {
190 protocol_version: 1,
191 capabilities: ClientCapabilities { experimental: None },
192 options: None,
193 meta: None,
194 };
195 let output = AgentAdapter::on_initialize_params(&adapter, params.clone())
196 .await
197 .unwrap();
198 assert_eq!(output, params);
199 }
200
201 #[rstest]
202 #[tokio::test]
203 async fn when_default_adapter_on_initialize_result_then_passthrough() {
204 let adapter = DefaultAdapter;
205 let result = InitializeResult {
206 protocol_version: 1,
207 agent_capabilities: None,
208 defaults: None,
209 meta: None,
210 };
211 let output = AgentAdapter::on_initialize_result(&adapter, result.clone())
212 .await
213 .unwrap();
214 assert_eq!(output, result);
215 }
216
217 #[rstest]
218 #[tokio::test]
219 async fn when_default_adapter_on_session_new_params_then_passthrough() {
220 let adapter = DefaultAdapter;
221 let params = SessionNewParams {
222 session_id: None,
223 cwd: "/tmp".into(),
224 mcp_servers: vec![],
225 meta: None,
226 };
227 let output = AgentAdapter::on_session_new_params(&adapter, params.clone())
228 .await
229 .unwrap();
230 assert_eq!(output, params);
231 }
232
233 #[rstest]
234 #[tokio::test]
235 async fn when_default_adapter_on_session_new_result_then_passthrough() {
236 let adapter = DefaultAdapter;
237 let result = SessionNewResult {
238 session_id: "sess-1".into(),
239 meta: None,
240 };
241 let output = AgentAdapter::on_session_new_result(&adapter, result.clone())
242 .await
243 .unwrap();
244 assert_eq!(output, result);
245 }
246
247 #[rstest]
248 #[tokio::test]
249 async fn when_default_adapter_on_session_prompt_params_then_passthrough() {
250 let adapter = DefaultAdapter;
251 let params = SessionPromptParams {
252 session_id: "sess-1".into(),
253 prompt: vec![ContentBlock::Text(TextContent::new("hello"))],
254 meta: None,
255 };
256 let output = AgentAdapter::on_session_prompt_params(&adapter, params.clone())
257 .await
258 .unwrap();
259 assert_eq!(output, params);
260 }
261
262 #[rstest]
263 #[tokio::test]
264 async fn when_default_adapter_on_session_update_then_passthrough() {
265 let adapter = DefaultAdapter;
266 let event = SessionUpdateEvent {
267 session_id: "sess-1".into(),
268 update: SessionUpdateType::Result {
269 content: Some("done".into()),
270 is_error: false,
271 },
272 };
273 let output = AgentAdapter::on_session_update(&adapter, event.clone())
274 .await
275 .unwrap();
276 assert_eq!(output, event);
277 }
278
279 #[rstest]
280 #[tokio::test]
281 async fn when_default_adapter_on_permission_request_then_passthrough() {
282 let adapter = DefaultAdapter;
283 let request = PermissionRequest {
284 request_id: "perm-1".into(),
285 description: "Allow?".into(),
286 options: vec![PermissionOption {
287 option_id: "allow".into(),
288 label: "Allow".into(),
289 }],
290 };
291 let output = AgentAdapter::on_permission_request(&adapter, request.clone())
292 .await
293 .unwrap();
294 assert_eq!(output, request);
295 }
296
297 struct PermissionRewritingAdapter;
298
299 impl AgentAdapter for PermissionRewritingAdapter {
300 async fn on_permission_request(
301 &self,
302 mut request: PermissionRequest,
303 ) -> Result<PermissionRequest, AgentSdkError> {
304 request.description = format!("REWRITTEN: {}", request.description);
305 Ok(request)
306 }
307 }
308
309 #[rstest]
310 #[tokio::test]
311 async fn when_custom_adapter_overrides_permission_request_then_transformed() {
312 let adapter = PermissionRewritingAdapter;
313 let request = PermissionRequest {
314 request_id: "perm-1".into(),
315 description: "Allow?".into(),
316 options: vec![],
317 };
318 let output = AgentAdapter::on_permission_request(&adapter, request)
319 .await
320 .unwrap();
321 assert_eq!(output.description, "REWRITTEN: Allow?");
322 assert_eq!(output.request_id, "perm-1");
323 }
324
325 #[rstest]
326 #[tokio::test]
327 async fn when_custom_adapter_on_non_overridden_hook_then_passthrough() {
328 let adapter = PermissionRewritingAdapter;
329 let result = SessionNewResult {
330 session_id: "sess-2".into(),
331 meta: None,
332 };
333 let output = AgentAdapter::on_session_new_result(&adapter, result.clone())
334 .await
335 .unwrap();
336 assert_eq!(output, result);
337 }
338
339 #[rstest]
340 #[tokio::test]
341 async fn when_dyn_adapter_dispatches_typed_params_then_compiles() {
342 let adapter: Box<dyn DynAgentAdapter> = Box::new(DefaultAdapter);
343 let params = InitializeParams {
344 protocol_version: 1,
345 capabilities: ClientCapabilities { experimental: None },
346 options: None,
347 meta: None,
348 };
349 let output = adapter.on_initialize_params(params.clone()).await.unwrap();
350 assert_eq!(output, params);
351 }
352}