1#![warn(missing_docs)]
11
12pub mod adapter;
14pub mod error;
16pub mod generic;
18
19pub use adapter::{AgentAdapter, DynAgentAdapter};
20pub use error::AgentSdkError;
21pub use generic::GenericAcpAdapter;
22
23#[cfg(test)]
24mod tests {
25 use super::*;
26 use adapter::AgentAdapter;
27 use anyclaw_sdk_types::{
28 ClientCapabilities, ContentBlock, InitializeParams, InitializeResult, PermissionOption,
29 PermissionRequest, SessionNewParams, SessionNewResult, SessionPromptParams,
30 SessionUpdateEvent, SessionUpdateType, TextContent,
31 };
32
33 #[tokio::test]
34 async fn when_generic_adapter_on_initialize_result_called_then_passthrough() {
35 let adapter = GenericAcpAdapter;
36 let input = InitializeResult {
37 protocol_version: 1,
38 agent_capabilities: None,
39 defaults: None,
40 meta: None,
41 };
42 let output = AgentAdapter::on_initialize_result(&adapter, input.clone())
43 .await
44 .unwrap();
45 assert_eq!(input, output);
46 }
47
48 #[tokio::test]
49 async fn when_generic_adapter_on_session_new_result_called_then_passthrough() {
50 let adapter = GenericAcpAdapter;
51 let input = SessionNewResult {
52 session_id: "sess-42".into(),
53 meta: None,
54 };
55 let output = AgentAdapter::on_session_new_result(&adapter, input.clone())
56 .await
57 .unwrap();
58 assert_eq!(input, output);
59 }
60
61 #[test]
62 fn when_generic_adapter_cast_to_dyn_trait_object_then_compiles() {
63 let _adapter: Box<dyn DynAgentAdapter> = Box::new(GenericAcpAdapter);
64 }
65
66 #[tokio::test]
67 async fn when_generic_adapter_on_initialize_params_called_then_passthrough() {
68 let adapter = GenericAcpAdapter;
69 let input = InitializeParams {
70 protocol_version: 1,
71 capabilities: ClientCapabilities { experimental: None },
72 options: None,
73 meta: None,
74 };
75 let output = AgentAdapter::on_initialize_params(&adapter, input.clone())
76 .await
77 .unwrap();
78 assert_eq!(input, output);
79 }
80
81 #[tokio::test]
82 async fn when_generic_adapter_on_session_new_params_called_then_passthrough() {
83 let adapter = GenericAcpAdapter;
84 let input = SessionNewParams {
85 session_id: None,
86 cwd: "/tmp".into(),
87 mcp_servers: vec![],
88 meta: None,
89 };
90 let output = AgentAdapter::on_session_new_params(&adapter, input.clone())
91 .await
92 .unwrap();
93 assert_eq!(input, output);
94 }
95
96 #[tokio::test]
97 async fn when_generic_adapter_on_session_prompt_params_called_then_passthrough() {
98 let adapter = GenericAcpAdapter;
99 let input = SessionPromptParams {
100 session_id: "s1".into(),
101 prompt: vec![ContentBlock::Text(TextContent::new("hi"))],
102 meta: None,
103 };
104 let output = AgentAdapter::on_session_prompt_params(&adapter, input.clone())
105 .await
106 .unwrap();
107 assert_eq!(input, output);
108 }
109
110 #[tokio::test]
111 async fn when_generic_adapter_on_session_update_called_then_passthrough() {
112 let adapter = GenericAcpAdapter;
113 let input = SessionUpdateEvent {
114 session_id: "s1".into(),
115 update: SessionUpdateType::Result {
116 content: Some("hello".into()),
117 is_error: false,
118 },
119 };
120 let output = AgentAdapter::on_session_update(&adapter, input.clone())
121 .await
122 .unwrap();
123 assert_eq!(input, output);
124 }
125
126 #[tokio::test]
127 async fn when_generic_adapter_on_permission_request_called_then_passthrough() {
128 let adapter = GenericAcpAdapter;
129 let input = PermissionRequest {
130 request_id: "r1".into(),
131 description: "Allow?".into(),
132 options: vec![PermissionOption {
133 option_id: "allow".into(),
134 label: "Allow".into(),
135 }],
136 };
137 let output = AgentAdapter::on_permission_request(&adapter, input.clone())
138 .await
139 .unwrap();
140 assert_eq!(input, output);
141 }
142
143 #[test]
144 fn when_agent_sdk_error_checked_then_implements_std_error() {
145 let err = AgentSdkError::Protocol("test".into());
146 let _: &dyn std::error::Error = &err;
147 assert!(err.to_string().contains("test"));
148 }
149
150 #[test]
151 fn when_protocol_error_created_then_wraps_string_message() {
152 let err = AgentSdkError::Protocol("bad handshake".into());
153 assert!(matches!(err, AgentSdkError::Protocol(_)));
154 assert!(err.to_string().contains("bad handshake"));
155 }
156
157 struct InjectingAdapter;
158
159 impl AgentAdapter for InjectingAdapter {
160 async fn on_session_prompt_params(
161 &self,
162 mut params: SessionPromptParams,
163 ) -> Result<SessionPromptParams, AgentSdkError> {
164 params
165 .prompt
166 .push(ContentBlock::Text(TextContent::new("injected")));
167 Ok(params)
168 }
169 }
170
171 #[tokio::test]
172 async fn when_custom_adapter_overrides_hook_then_transformed_value_returned() {
173 let adapter = InjectingAdapter;
174 let input = SessionPromptParams {
175 session_id: "s1".into(),
176 prompt: vec![ContentBlock::Text(TextContent::new("hi"))],
177 meta: None,
178 };
179 let output = AgentAdapter::on_session_prompt_params(&adapter, input)
180 .await
181 .unwrap();
182 assert_eq!(output.prompt.len(), 2);
183 assert_eq!(output.session_id, "s1");
184 }
185
186 #[tokio::test]
187 async fn when_custom_adapter_non_overridden_hook_called_then_passthrough() {
188 let adapter = InjectingAdapter;
189 let input = 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, input.clone())
196 .await
197 .unwrap();
198 assert_eq!(input, output);
199 }
200}