#![warn(missing_docs)]
pub mod adapter;
pub mod error;
pub mod generic;
pub use adapter::{AgentAdapter, DynAgentAdapter};
pub use error::AgentSdkError;
pub use generic::GenericAcpAdapter;
#[cfg(test)]
mod tests {
use super::*;
use adapter::AgentAdapter;
use anyclaw_sdk_types::{
ClientCapabilities, ContentBlock, InitializeParams, InitializeResult, PermissionOption,
PermissionRequest, SessionNewParams, SessionNewResult, SessionPromptParams,
SessionUpdateEvent, SessionUpdateType, TextContent,
};
#[tokio::test]
async fn when_generic_adapter_on_initialize_result_called_then_passthrough() {
let adapter = GenericAcpAdapter;
let input = InitializeResult {
protocol_version: 1,
agent_capabilities: None,
defaults: None,
meta: None,
};
let output = AgentAdapter::on_initialize_result(&adapter, input.clone())
.await
.unwrap();
assert_eq!(input, output);
}
#[tokio::test]
async fn when_generic_adapter_on_session_new_result_called_then_passthrough() {
let adapter = GenericAcpAdapter;
let input = SessionNewResult {
session_id: "sess-42".into(),
meta: None,
};
let output = AgentAdapter::on_session_new_result(&adapter, input.clone())
.await
.unwrap();
assert_eq!(input, output);
}
#[test]
fn when_generic_adapter_cast_to_dyn_trait_object_then_compiles() {
let _adapter: Box<dyn DynAgentAdapter> = Box::new(GenericAcpAdapter);
}
#[tokio::test]
async fn when_generic_adapter_on_initialize_params_called_then_passthrough() {
let adapter = GenericAcpAdapter;
let input = InitializeParams {
protocol_version: 1,
capabilities: ClientCapabilities { experimental: None },
options: None,
meta: None,
};
let output = AgentAdapter::on_initialize_params(&adapter, input.clone())
.await
.unwrap();
assert_eq!(input, output);
}
#[tokio::test]
async fn when_generic_adapter_on_session_new_params_called_then_passthrough() {
let adapter = GenericAcpAdapter;
let input = SessionNewParams {
session_id: None,
cwd: "/tmp".into(),
mcp_servers: vec![],
meta: None,
};
let output = AgentAdapter::on_session_new_params(&adapter, input.clone())
.await
.unwrap();
assert_eq!(input, output);
}
#[tokio::test]
async fn when_generic_adapter_on_session_prompt_params_called_then_passthrough() {
let adapter = GenericAcpAdapter;
let input = SessionPromptParams {
session_id: "s1".into(),
prompt: vec![ContentBlock::Text(TextContent::new("hi"))],
meta: None,
};
let output = AgentAdapter::on_session_prompt_params(&adapter, input.clone())
.await
.unwrap();
assert_eq!(input, output);
}
#[tokio::test]
async fn when_generic_adapter_on_session_update_called_then_passthrough() {
let adapter = GenericAcpAdapter;
let input = SessionUpdateEvent {
session_id: "s1".into(),
update: SessionUpdateType::Result {
content: Some("hello".into()),
is_error: false,
},
};
let output = AgentAdapter::on_session_update(&adapter, input.clone())
.await
.unwrap();
assert_eq!(input, output);
}
#[tokio::test]
async fn when_generic_adapter_on_permission_request_called_then_passthrough() {
let adapter = GenericAcpAdapter;
let input = PermissionRequest {
request_id: "r1".into(),
description: "Allow?".into(),
options: vec![PermissionOption {
option_id: "allow".into(),
label: "Allow".into(),
}],
};
let output = AgentAdapter::on_permission_request(&adapter, input.clone())
.await
.unwrap();
assert_eq!(input, output);
}
#[test]
fn when_agent_sdk_error_checked_then_implements_std_error() {
let err = AgentSdkError::Protocol("test".into());
let _: &dyn std::error::Error = &err;
assert!(err.to_string().contains("test"));
}
#[test]
fn when_protocol_error_created_then_wraps_string_message() {
let err = AgentSdkError::Protocol("bad handshake".into());
assert!(matches!(err, AgentSdkError::Protocol(_)));
assert!(err.to_string().contains("bad handshake"));
}
struct InjectingAdapter;
impl AgentAdapter for InjectingAdapter {
async fn on_session_prompt_params(
&self,
mut params: SessionPromptParams,
) -> Result<SessionPromptParams, AgentSdkError> {
params
.prompt
.push(ContentBlock::Text(TextContent::new("injected")));
Ok(params)
}
}
#[tokio::test]
async fn when_custom_adapter_overrides_hook_then_transformed_value_returned() {
let adapter = InjectingAdapter;
let input = SessionPromptParams {
session_id: "s1".into(),
prompt: vec![ContentBlock::Text(TextContent::new("hi"))],
meta: None,
};
let output = AgentAdapter::on_session_prompt_params(&adapter, input)
.await
.unwrap();
assert_eq!(output.prompt.len(), 2);
assert_eq!(output.session_id, "s1");
}
#[tokio::test]
async fn when_custom_adapter_non_overridden_hook_called_then_passthrough() {
let adapter = InjectingAdapter;
let input = InitializeParams {
protocol_version: 1,
capabilities: ClientCapabilities { experimental: None },
options: None,
meta: None,
};
let output = AgentAdapter::on_initialize_params(&adapter, input.clone())
.await
.unwrap();
assert_eq!(input, output);
}
}