From 2a507886d53bd683370e260e9df4d26330895f9c Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Tue, 25 Nov 2025 12:16:04 +0100 Subject: [PATCH 1/5] main repo --- Cargo.toml | 9 +++++++++ rust-toolchain.toml | 4 ---- src/agent-client-protocol/Cargo.toml | 3 +++ src/agent-client-protocol/src/lib.rs | 6 ++++-- src/agent-client-protocol/src/rpc.rs | 7 +++++-- 5 files changed, 21 insertions(+), 8 deletions(-) delete mode 100644 rust-toolchain.toml diff --git a/Cargo.toml b/Cargo.toml index 2b8fbc6..443a21c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,3 +62,12 @@ jsonrpcmsg = "0.1.2" # Testing expect-test = "1.5" tokio-test = "0.4" + +[workspace.lints.rust] +future_incompatible = { level = "warn", priority = -1 } +let-underscore = "warn" +missing_debug_implementations = "warn" +# missing_docs = "warn" +nonstandard_style = { level = "warn", priority = -1 } +rust_2018_idioms = { level = "warn", priority = -1 } +unused = { level = "warn", priority = -1 } diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 3c4b643..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "1.90" -profile = "minimal" -components = ["rustfmt", "clippy"] diff --git a/src/agent-client-protocol/Cargo.toml b/src/agent-client-protocol/Cargo.toml index 0bb7e0e..f72c461 100644 --- a/src/agent-client-protocol/Cargo.toml +++ b/src/agent-client-protocol/Cargo.toml @@ -45,3 +45,6 @@ tokio = { version = "1", features = [ "sync", ] } tokio-util = { version = "0.7", features = ["compat"] } + +[lints] +workspace = true diff --git a/src/agent-client-protocol/src/lib.rs b/src/agent-client-protocol/src/lib.rs index 7b7b10f..e47a0e1 100644 --- a/src/agent-client-protocol/src/lib.rs +++ b/src/agent-client-protocol/src/lib.rs @@ -26,6 +26,7 @@ pub use stream_broadcast::{ /// prompts, and managing the agent lifecycle. /// /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client) +#[derive(Debug)] pub struct ClientSideConnection { conn: RpcConnection, } @@ -177,7 +178,7 @@ impl Agent for ClientSideConnection { /// are incoming vs outgoing from the client's perspective. /// /// See protocol docs: [Communication Model](https://agentclientprotocol.com/protocol/overview#communication-model) -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ClientSide; impl Side for ClientSide { @@ -316,6 +317,7 @@ impl MessageHandler for T { /// and sending session updates. /// /// See protocol docs: [Agent](https://agentclientprotocol.com/protocol/overview#agent) +#[derive(Debug)] pub struct AgentSideConnection { conn: RpcConnection, } @@ -482,7 +484,7 @@ impl Client for AgentSideConnection { /// are incoming vs outgoing from the agent's perspective. /// /// See protocol docs: [Communication Model](https://agentclientprotocol.com/protocol/overview#communication-model) -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct AgentSide; impl Side for AgentSide { diff --git a/src/agent-client-protocol/src/rpc.rs b/src/agent-client-protocol/src/rpc.rs index 44374eb..ee85815 100644 --- a/src/agent-client-protocol/src/rpc.rs +++ b/src/agent-client-protocol/src/rpc.rs @@ -28,6 +28,7 @@ use serde_json::value::RawValue; use super::stream_broadcast::{StreamBroadcast, StreamReceiver, StreamSender}; +#[derive(Debug)] pub(crate) struct RpcConnection { outgoing_tx: UnboundedSender>, pending_responses: Arc>>, @@ -35,6 +36,7 @@ pub(crate) struct RpcConnection { broadcast: StreamBroadcast, } +#[derive(Debug)] struct PendingResponse { deserialize: fn(&serde_json::value::RawValue) -> Result>, respond: oneshot::Sender>>, @@ -182,7 +184,7 @@ where } log::trace!("recv: {}", &incoming_line); - match serde_json::from_str::(&incoming_line) { + match serde_json::from_str::>(&incoming_line) { Ok(message) => { if let Some(id) = message.id { if let Some(method) = message.method { @@ -299,7 +301,7 @@ where } } -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] pub struct RawIncomingMessage<'a> { id: Option, method: Option<&'a str>, @@ -308,6 +310,7 @@ pub struct RawIncomingMessage<'a> { error: Option, } +#[derive(Debug)] pub enum IncomingMessage { Request { id: RequestId, From f42d123a415e308187a15a9dafb0c3fe3fbb98d4 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Tue, 25 Nov 2025 13:09:27 +0100 Subject: [PATCH 2/5] rustc lints for all packages --- src/elizacp/Cargo.toml | 3 +++ src/elizacp/src/eliza.rs | 1 + src/sacp-conductor/Cargo.toml | 3 +++ src/sacp-conductor/src/conductor.rs | 10 ++++++++++ src/sacp-conductor/src/mcp_bridge.rs | 8 ++++---- src/sacp-conductor/tests/test_util.rs | 16 +++++++++------- src/sacp-proxy/Cargo.toml | 3 +++ src/sacp-proxy/src/to_from_successor.rs | 4 ++++ src/sacp-rmcp/Cargo.toml | 3 +++ src/sacp-rmcp/examples/with_mcp_server.rs | 2 +- src/sacp-rmcp/src/lib.rs | 2 +- src/sacp-tee/Cargo.toml | 3 +++ src/sacp-tee/src/lib.rs | 7 +++++-- src/sacp-test/Cargo.toml | 3 +++ src/sacp-test/src/lib.rs | 1 + src/sacp-tokio/Cargo.toml | 3 +++ src/sacp-tokio/src/acp_agent.rs | 2 +- src/sacp-tokio/src/lib.rs | 1 + src/sacp/Cargo.toml | 3 +++ src/sacp/examples/yolo_one_shot_client.rs | 2 +- src/sacp/src/capabilities.rs | 2 ++ src/sacp/src/component.rs | 6 ++++++ src/sacp/src/jsonrpc.rs | 15 +++++++++++++++ src/sacp/src/jsonrpc/handlers.rs | 7 ++++++- src/sacp/src/jsonrpc/task_actor.rs | 6 ++++++ src/sacp/src/util/typed.rs | 3 +++ src/sacp/tests/jsonrpc_edge_cases.rs | 2 +- src/sacp/tests/jsonrpc_error_handling.rs | 2 +- src/yopo/Cargo.toml | 3 +++ 29 files changed, 106 insertions(+), 20 deletions(-) diff --git a/src/elizacp/Cargo.toml b/src/elizacp/Cargo.toml index bae8268..3d5f2fd 100644 --- a/src/elizacp/Cargo.toml +++ b/src/elizacp/Cargo.toml @@ -36,3 +36,6 @@ sacp-tokio = { version = "1.0.0", path = "../sacp-tokio" } [dev-dependencies] expect-test.workspace = true + +[lints] +workspace = true diff --git a/src/elizacp/src/eliza.rs b/src/elizacp/src/eliza.rs index 8a8073c..e19649d 100644 --- a/src/elizacp/src/eliza.rs +++ b/src/elizacp/src/eliza.rs @@ -20,6 +20,7 @@ struct Pattern { } /// The Eliza chatbot engine. +#[derive(Debug)] pub struct Eliza { patterns: Vec, reflections: HashMap, diff --git a/src/sacp-conductor/Cargo.toml b/src/sacp-conductor/Cargo.toml index c007b7c..094ac41 100644 --- a/src/sacp-conductor/Cargo.toml +++ b/src/sacp-conductor/Cargo.toml @@ -41,3 +41,6 @@ schemars.workspace = true sacp-rmcp = { path = "../sacp-rmcp" } sacp-test = { path = "../sacp-test" } sacp-tokio = { path = "../sacp-tokio" } + +[lints] +workspace = true diff --git a/src/sacp-conductor/src/conductor.rs b/src/sacp-conductor/src/conductor.rs index 05b7379..b7db0d3 100644 --- a/src/sacp-conductor/src/conductor.rs +++ b/src/sacp-conductor/src/conductor.rs @@ -153,6 +153,15 @@ pub struct Conductor { conductor_command: Option>, } +impl std::fmt::Debug for Conductor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Conductor") + .field("name", &self.name) + .field("conductor_command", &self.conductor_command) + .finish() + } +} + impl Conductor { pub fn new( name: String, @@ -228,6 +237,7 @@ impl sacp::Component for Conductor { } } +#[derive(Debug)] pub struct ConductorMessageHandler { conductor_tx: mpsc::Sender, } diff --git a/src/sacp-conductor/src/mcp_bridge.rs b/src/sacp-conductor/src/mcp_bridge.rs index 2589f67..63e15d2 100644 --- a/src/sacp-conductor/src/mcp_bridge.rs +++ b/src/sacp-conductor/src/mcp_bridge.rs @@ -47,8 +47,8 @@ pub async fn run_mcp_bridge(port: u16) -> Result<(), sacp::Error> { } // Parse to validate JSON - let _: Value = serde_json::from_str(stdin_line.trim()) - .context("Invalid JSON from stdin")?; + drop(serde_json::from_str::(stdin_line.trim()) + .context("Invalid JSON from stdin")?); tracing::debug!("Bridge: stdin → TCP: {}", stdin_line.trim()); @@ -71,8 +71,8 @@ pub async fn run_mcp_bridge(port: u16) -> Result<(), sacp::Error> { } // Parse to validate JSON - let _: Value = serde_json::from_str(tcp_line.trim()) - .context("Invalid JSON from TCP")?; + drop(serde_json::from_str::(tcp_line.trim()) + .context("Invalid JSON from TCP")?); tracing::debug!("Bridge: TCP → stdout: {}", tcp_line.trim()); diff --git a/src/sacp-conductor/tests/test_util.rs b/src/sacp-conductor/tests/test_util.rs index f355b63..8216168 100644 --- a/src/sacp-conductor/tests/test_util.rs +++ b/src/sacp-conductor/tests/test_util.rs @@ -6,11 +6,13 @@ pub fn init_test_tracing() { use tracing_subscriber::{EnvFilter, fmt}; - let _ = fmt() - .with_env_filter( - EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new("trace,sacp::jsonrpc=trace")), - ) - .with_test_writer() - .try_init(); + drop( + fmt() + .with_env_filter( + EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new("trace,sacp::jsonrpc=trace")), + ) + .with_test_writer() + .try_init(), + ); } diff --git a/src/sacp-proxy/Cargo.toml b/src/sacp-proxy/Cargo.toml index 464dbf1..2f82653 100644 --- a/src/sacp-proxy/Cargo.toml +++ b/src/sacp-proxy/Cargo.toml @@ -25,3 +25,6 @@ uuid.workspace = true chrono.workspace = true schemars.workspace = true tracing-subscriber.workspace = true + +[lints] +workspace = true diff --git a/src/sacp-proxy/src/to_from_successor.rs b/src/sacp-proxy/src/to_from_successor.rs index c1df9a2..986a794 100644 --- a/src/sacp-proxy/src/to_from_successor.rs +++ b/src/sacp-proxy/src/to_from_successor.rs @@ -261,6 +261,7 @@ impl AcpProxyExt for JrHandlerChain { } /// Handler to process a message of type `R` coming from the successor component. +#[derive(Debug)] pub struct MessageFromSuccessorHandler where R: JrRequest, @@ -371,6 +372,7 @@ where } /// Handler to process a request of type `R` coming from the successor component. +#[derive(Debug)] pub struct RequestFromSuccessorHandler where R: JrRequest, @@ -435,6 +437,7 @@ where } /// Handler to process a notification of type `N` coming from the successor component. +#[derive(Debug)] pub struct NotificationFromSuccessorHandler where N: JrNotification, @@ -500,6 +503,7 @@ where } /// Handler for the "default proxy" behavior. +#[derive(Debug)] pub struct ProxyHandler {} impl JrMessageHandler for ProxyHandler { diff --git a/src/sacp-rmcp/Cargo.toml b/src/sacp-rmcp/Cargo.toml index b69e196..e756fff 100644 --- a/src/sacp-rmcp/Cargo.toml +++ b/src/sacp-rmcp/Cargo.toml @@ -21,3 +21,6 @@ serde.workspace = true schemars.workspace = true tracing-subscriber.workspace = true tracing.workspace = true + +[lints] +workspace = true diff --git a/src/sacp-rmcp/examples/with_mcp_server.rs b/src/sacp-rmcp/examples/with_mcp_server.rs index aecb3a5..ce656c5 100644 --- a/src/sacp-rmcp/examples/with_mcp_server.rs +++ b/src/sacp-rmcp/examples/with_mcp_server.rs @@ -28,7 +28,7 @@ struct EchoParams { } /// Simple MCP server with an echo tool -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ExampleMcpServer { tool_router: ToolRouter, } diff --git a/src/sacp-rmcp/src/lib.rs b/src/sacp-rmcp/src/lib.rs index bb5e0be..38b01f4 100644 --- a/src/sacp-rmcp/src/lib.rs +++ b/src/sacp-rmcp/src/lib.rs @@ -103,7 +103,7 @@ where // Spawn task to connect byte_streams to the provided client tokio::spawn(async move { - let _ = byte_streams.serve(client).await; + drop(byte_streams.serve(client).await); }); // Run the rmcp server with the server side of the duplex stream diff --git a/src/sacp-tee/Cargo.toml b/src/sacp-tee/Cargo.toml index ae7fa3b..f6ae2b0 100644 --- a/src/sacp-tee/Cargo.toml +++ b/src/sacp-tee/Cargo.toml @@ -15,3 +15,6 @@ serde_json.workspace = true tokio = { workspace = true, features = ["full"] } tracing.workspace = true tracing-subscriber = { workspace = true, features = ["env-filter"] } + +[lints] +workspace = true diff --git a/src/sacp-tee/src/lib.rs b/src/sacp-tee/src/lib.rs index f72ae52..35c1d26 100644 --- a/src/sacp-tee/src/lib.rs +++ b/src/sacp-tee/src/lib.rs @@ -49,6 +49,7 @@ impl LogEntry { } /// Log writer actor that receives log entries and writes them to disk +#[derive(Debug)] pub struct LogWriter { log_file: PathBuf, receiver: mpsc::UnboundedReceiver, @@ -89,6 +90,7 @@ impl LogWriter { } /// Handler that logs messages passing through +#[derive(Debug)] pub struct TeeHandler { log_tx: mpsc::UnboundedSender, next_id: u64, @@ -101,7 +103,7 @@ impl TeeHandler { fn log_entry(&self, entry: LogEntry) { // Fire and forget - if the channel is closed, we just drop the log - let _ = self.log_tx.send(entry); + drop(self.log_tx.send(entry)); } fn allocate_id(&mut self) -> u64 { @@ -149,7 +151,7 @@ impl JrMessageHandler for TeeHandler { }; let entry = LogEntry::new("upstream", json_msg); - let _ = log_tx.send(entry); + drop(log_tx.send(entry)); result }); @@ -174,6 +176,7 @@ impl JrMessageHandler for TeeHandler { /// The Tee component - can be used as a component in a larger proxy chain /// or run standalone as a binary +#[derive(Debug)] pub struct Tee { log_file: PathBuf, } diff --git a/src/sacp-test/Cargo.toml b/src/sacp-test/Cargo.toml index 78485d6..a6065d6 100644 --- a/src/sacp-test/Cargo.toml +++ b/src/sacp-test/Cargo.toml @@ -14,3 +14,6 @@ tokio.workspace = true tokio-util.workspace = true tracing.workspace = true tracing-subscriber.workspace = true + +[lints] +workspace = true diff --git a/src/sacp-test/src/lib.rs b/src/sacp-test/src/lib.rs index 5dcae20..da70851 100644 --- a/src/sacp-test/src/lib.rs +++ b/src/sacp-test/src/lib.rs @@ -7,6 +7,7 @@ pub mod test_client; /// A mock transport for doctests that panics if actually used. /// This is only for documentation examples that don't actually run. +#[derive(Debug)] pub struct MockTransport; impl Component for MockTransport { diff --git a/src/sacp-tokio/Cargo.toml b/src/sacp-tokio/Cargo.toml index 353fa89..95cfd4e 100644 --- a/src/sacp-tokio/Cargo.toml +++ b/src/sacp-tokio/Cargo.toml @@ -21,3 +21,6 @@ tokio-util.workspace = true [dev-dependencies] expect-test.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } + +[lints] +workspace = true diff --git a/src/sacp-tokio/src/acp_agent.rs b/src/sacp-tokio/src/acp_agent.rs index c0c7b5d..a658a77 100644 --- a/src/sacp-tokio/src/acp_agent.rs +++ b/src/sacp-tokio/src/acp_agent.rs @@ -157,7 +157,7 @@ impl Future for ChildHolder { impl Drop for ChildHolder { fn drop(&mut self) { - let _: Result<_, _> = self._child.start_kill(); + drop(self._child.start_kill()); } } diff --git a/src/sacp-tokio/src/lib.rs b/src/sacp-tokio/src/lib.rs index adfc0d0..720dec1 100644 --- a/src/sacp-tokio/src/lib.rs +++ b/src/sacp-tokio/src/lib.rs @@ -10,6 +10,7 @@ pub use acp_agent::AcpAgent; use sacp::{ByteStreams, Component}; use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; +#[derive(Debug)] pub struct Stdio; impl Component for Stdio { diff --git a/src/sacp/Cargo.toml b/src/sacp/Cargo.toml index e000cc2..3fca638 100644 --- a/src/sacp/Cargo.toml +++ b/src/sacp/Cargo.toml @@ -30,3 +30,6 @@ shell-words = "1.1" tokio = { workspace = true, features = ["macros", "rt-multi-thread", "io-util", "time", "process"] } tokio-util.workspace = true sacp-test = { path = "../sacp-test" } + +[lints] +workspace = true diff --git a/src/sacp/examples/yolo_one_shot_client.rs b/src/sacp/examples/yolo_one_shot_client.rs index 870bf9f..1cf0fcc 100644 --- a/src/sacp/examples/yolo_one_shot_client.rs +++ b/src/sacp/examples/yolo_one_shot_client.rs @@ -162,7 +162,7 @@ async fn main() -> Result<(), Box> { .await?; // Kill the child process when done - let _ = child.kill().await; + drop(child.kill().await); Ok(()) } diff --git a/src/sacp/src/capabilities.rs b/src/sacp/src/capabilities.rs index 66c1e84..d60b113 100644 --- a/src/sacp/src/capabilities.rs +++ b/src/sacp/src/capabilities.rs @@ -37,6 +37,7 @@ pub trait MetaCapability { /// /// When present in `_meta.symposium.proxy`, signals that the component should use /// the `_proxy/successor/*` protocol to communicate with its successor. +#[derive(Debug)] pub struct Proxy; impl MetaCapability for Proxy { @@ -49,6 +50,7 @@ impl MetaCapability for Proxy { /// /// When present in `_meta.symposium.mcp_acp_transport`, signals that the agent /// supports having MCP servers with `acp:UUID` transport proxied through the conductor. +#[derive(Debug)] pub struct McpAcpTransport; impl MetaCapability for McpAcpTransport { diff --git a/src/sacp/src/component.rs b/src/sacp/src/component.rs index ff43658..e435134 100644 --- a/src/sacp/src/component.rs +++ b/src/sacp/src/component.rs @@ -188,6 +188,12 @@ pub struct DynComponent { inner: Box, } +impl std::fmt::Debug for DynComponent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DynComponent").finish() + } +} + impl DynComponent { /// Create a new `DynComponent` from any type implementing [`Component`]. pub fn new(component: C) -> Self { diff --git a/src/sacp/src/jsonrpc.rs b/src/sacp/src/jsonrpc.rs index 2159739..7f95749 100644 --- a/src/sacp/src/jsonrpc.rs +++ b/src/sacp/src/jsonrpc.rs @@ -349,6 +349,7 @@ pub trait JrMessageHandler { /// # Ok(()) /// # } /// ``` +#[derive(Debug)] #[must_use] pub struct JrHandlerChain { name: Option, @@ -752,6 +753,7 @@ impl JrHandlerChain { /// /// Most users won't construct this directly - instead use `JrHandlerChain::connect_to()` or /// `JrHandlerChain::serve()` for convenience. +#[derive(Debug)] pub struct JrConnection { cx: JrConnectionCx, name: Option, @@ -968,6 +970,7 @@ enum OutgoingMessage { } /// Return type from JrHandler; indicates whether the request was handled or not. +#[derive(Debug)] #[must_use] pub enum Handled { /// The message was handled @@ -1755,6 +1758,16 @@ pub struct JrResponse { to_result: Box Result + Send>, } +impl std::fmt::Debug for JrResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("JrResponse") + .field("method", &self.method) + .field("connection_cx", &self.connection_cx) + .field("response_rx", &self.response_rx) + .finish() + } +} + impl JrResponse { fn new( method: String, @@ -2112,6 +2125,7 @@ fn communication_failure(err: impl ToString) -> crate::Error { /// ``` /// /// [`Component`]: crate::Component +#[derive(Debug)] pub struct ByteStreams { /// Outgoing byte stream (where we write serialized messages) pub outgoing: OB, @@ -2190,6 +2204,7 @@ where /// # Ok(()) /// # } /// ``` +#[derive(Debug)] pub struct Channel { /// Receives messages (or errors) from the counterpart. pub rx: mpsc::UnboundedReceiver>, diff --git a/src/sacp/src/jsonrpc/handlers.rs b/src/sacp/src/jsonrpc/handlers.rs index 138e2b4..c39d634 100644 --- a/src/sacp/src/jsonrpc/handlers.rs +++ b/src/sacp/src/jsonrpc/handlers.rs @@ -6,7 +6,7 @@ use std::marker::PhantomData; use std::ops::AsyncFnMut; /// Null handler that accepts no messages. -#[derive(Default)] +#[derive(Debug, Default)] pub struct NullHandler { _private: (), } @@ -25,6 +25,7 @@ impl JrMessageHandler for NullHandler { } /// Handler for typed request messages +#[derive(Debug)] pub struct RequestHandler where R: JrRequest, @@ -101,6 +102,7 @@ where } /// Handler for typed notification messages +#[derive(Debug)] pub struct NotificationHandler where N: JrNotification, @@ -179,6 +181,7 @@ where } /// Handler that tries H1 and then H2. +#[derive(Debug)] pub struct MessageHandler where R: JrRequest, @@ -278,6 +281,7 @@ where } /// Chains two handlers together, trying the first handler and falling back to the second +#[derive(Debug)] pub struct NamedHandler { name: Option, handler: H, @@ -319,6 +323,7 @@ where } /// Chains two handlers together, trying the first handler and falling back to the second +#[derive(Debug)] pub struct ChainedHandler { handler1: H1, handler2: H2, diff --git a/src/sacp/src/jsonrpc/task_actor.rs b/src/sacp/src/jsonrpc/task_actor.rs index 8907c94..5347e44 100644 --- a/src/sacp/src/jsonrpc/task_actor.rs +++ b/src/sacp/src/jsonrpc/task_actor.rs @@ -89,6 +89,12 @@ pub(crate) struct PendingTask { task_fn: Box, } +impl std::fmt::Debug for PendingTask { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PendingTask").finish() + } +} + impl PendingTask { pub fn new( location: &'static Location<'static>, diff --git a/src/sacp/src/util/typed.rs b/src/sacp/src/util/typed.rs index 9d8b6a7..ab7fbe8 100644 --- a/src/sacp/src/util/typed.rs +++ b/src/sacp/src/util/typed.rs @@ -63,6 +63,7 @@ use crate::{ /// Each `handle_if` tries to parse the message as the specified type. If parsing succeeds, /// that handler runs and subsequent handlers are skipped. If parsing fails for all types, /// the `otherwise` handler receives the original untyped message. +#[derive(Debug)] #[must_use] pub struct MatchMessage { state: Result, crate::Error>, @@ -247,12 +248,14 @@ impl MatchMessage { /// /// Since notifications don't expect responses, handlers only receive the parsed /// notification (not a request context). +#[derive(Debug)] #[must_use] pub struct TypeNotification { cx: JrConnectionCx, state: Option, } +#[derive(Debug)] enum TypeNotificationState { Unhandled(String, Option), Handled(Result<(), crate::Error>), diff --git a/src/sacp/tests/jsonrpc_edge_cases.rs b/src/sacp/tests/jsonrpc_edge_cases.rs index 7662730..d6aa4e2 100644 --- a/src/sacp/tests/jsonrpc_edge_cases.rs +++ b/src/sacp/tests/jsonrpc_edge_cases.rs @@ -322,7 +322,7 @@ async fn test_client_disconnect() { ); tokio::task::spawn_local(async move { - let _ = server.serve(server_transport).await; + drop(server.serve(server_transport).await); }); // Send partial request and then disconnect diff --git a/src/sacp/tests/jsonrpc_error_handling.rs b/src/sacp/tests/jsonrpc_error_handling.rs index 683ac91..89880a4 100644 --- a/src/sacp/tests/jsonrpc_error_handling.rs +++ b/src/sacp/tests/jsonrpc_error_handling.rs @@ -123,7 +123,7 @@ async fn test_invalid_json() { // Spawn server tokio::task::spawn_local(async move { - let _ = server.serve(server_transport).await; + drop(server.serve(server_transport).await); }); // Send invalid JSON diff --git a/src/yopo/Cargo.toml b/src/yopo/Cargo.toml index de4696c..514323f 100644 --- a/src/yopo/Cargo.toml +++ b/src/yopo/Cargo.toml @@ -10,3 +10,6 @@ repository = "https://github.com/symposium-dev/symposium-acp" sacp = { version = "1.0.0", path = "../sacp" } sacp-tokio = { version = "1.0.0", path = "../sacp-tokio" } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } + +[lints] +workspace = true From 40a81b2f46d533a80e2635adad920ee27847ef51 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Tue, 25 Nov 2025 13:28:44 +0100 Subject: [PATCH 3/5] More lints --- Cargo.toml | 12 ++++ src/elizacp/src/eliza.rs | 13 ++-- src/elizacp/src/lib.rs | 30 ++++------ src/sacp-conductor/src/conductor.rs | 51 ++++++++-------- .../src/conductor/mcp_bridge.rs | 2 +- src/sacp-conductor/src/main.rs | 8 +-- src/sacp-conductor/src/mcp_bridge.rs | 2 +- src/sacp-conductor/tests/arrow_proxy_eliza.rs | 3 +- .../tests/initialization_sequence.rs | 29 ++++----- src/sacp-conductor/tests/mcp-integration.rs | 21 +++---- .../tests/mcp_integration/agent.rs | 2 +- .../tests/nested_arrow_proxy.rs | 6 +- src/sacp-conductor/tests/nested_conductor.rs | 10 ++-- src/sacp-proxy/src/mcp_server.rs | 18 +++--- src/sacp-proxy/src/to_from_successor.rs | 9 +-- src/sacp-rmcp/examples/with_mcp_server.rs | 1 + src/sacp-tee/src/lib.rs | 3 + src/sacp-test/src/arrow_proxy.rs | 15 ++--- src/sacp-test/src/lib.rs | 9 ++- src/sacp-test/src/test_client.rs | 11 ++-- src/sacp-tokio/src/acp_agent.rs | 13 ++-- src/sacp/examples/simple_agent.rs | 13 ++-- src/sacp/examples/yolo_one_shot_client.rs | 28 ++++----- src/sacp/src/capabilities.rs | 6 +- src/sacp/src/jsonrpc.rs | 38 +++++++----- src/sacp/src/jsonrpc/actors.rs | 24 ++++---- src/sacp/src/jsonrpc/handlers.rs | 10 ++-- .../schema/agent_to_client/notifications.rs | 2 +- .../src/schema/agent_to_client/requests.rs | 16 ++--- .../schema/client_to_agent/notifications.rs | 2 +- .../src/schema/client_to_agent/requests.rs | 12 ++-- src/sacp/src/schema/enum_impls.rs | 6 +- src/sacp/tests/jsonrpc_advanced.rs | 10 ++-- src/sacp/tests/jsonrpc_edge_cases.rs | 10 ++-- src/sacp/tests/jsonrpc_error_handling.rs | 12 ++-- src/sacp/tests/jsonrpc_handler_chain.rs | 39 ++++++------ src/sacp/tests/jsonrpc_hello.rs | 42 +++++++------ src/sacp/tests/match_message.rs | 2 +- src/yopo/src/main.rs | 59 +++++++++---------- 39 files changed, 300 insertions(+), 299 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 443a21c..d1cd98b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,3 +71,15 @@ missing_debug_implementations = "warn" nonstandard_style = { level = "warn", priority = -1 } rust_2018_idioms = { level = "warn", priority = -1 } unused = { level = "warn", priority = -1 } + +[workspace.lints.clippy] +# cargo = "warn" +pedantic ={ level = "warn", priority = -1 } +doc_markdown = "allow" +missing_errors_doc = "allow" +missing_panics_doc = "allow" +needless_pass_by_value = "allow" +similar_names = "allow" +struct_field_names = "allow" +too_many_lines = "allow" +wildcard_imports = "allow" diff --git a/src/elizacp/src/eliza.rs b/src/elizacp/src/eliza.rs index e19649d..855b97f 100644 --- a/src/elizacp/src/eliza.rs +++ b/src/elizacp/src/eliza.rs @@ -30,11 +30,13 @@ pub struct Eliza { impl Eliza { /// Create a new Eliza instance with classic patterns. /// Uses a fixed seed for deterministic testing. + #[must_use] pub fn new() -> Self { Self::with_seed(42) } /// Create a new Eliza instance with a specific seed. + #[must_use] pub fn with_seed(seed: u64) -> Self { let mut eliza = Self { patterns: Vec::new(), @@ -211,7 +213,7 @@ impl Eliza { let regex = Regex::new(pattern).expect("Invalid regex pattern"); self.patterns.push(Pattern { pattern: regex, - responses: responses.into_iter().map(|s| s.to_string()).collect(), + responses: responses.into_iter().map(std::string::ToString::to_string).collect(), priority, }); } @@ -226,7 +228,7 @@ impl Eliza { self.reflections .get(&lower) .cloned() - .unwrap_or_else(|| word.to_string()) + .unwrap_or_else(|| (*word).to_string()) }) .collect(); reflected.join(" ") @@ -292,10 +294,10 @@ mod tests { let eliza = Eliza::new(); let reflected = eliza.reflect("I am happy"); - expect![[r#"you are happy"#]].assert_eq(&reflected); + expect![[r"you are happy"]].assert_eq(&reflected); let reflected = eliza.reflect("my mother"); - expect![[r#"your mother"#]].assert_eq(&reflected); + expect![[r"your mother"]].assert_eq(&reflected); } #[test] @@ -335,8 +337,7 @@ mod tests { let response2 = eliza2.respond(input); assert_eq!( response1, response2, - "Responses should be identical for input: {}", - input + "Responses should be identical for input: {input}" ); } diff --git a/src/elizacp/src/lib.rs b/src/elizacp/src/lib.rs index aa50ab5..1721a08 100644 --- a/src/elizacp/src/lib.rs +++ b/src/elizacp/src/lib.rs @@ -43,7 +43,7 @@ impl ElizaAgent { tracing::info!("Ended session: {}", session_id); } - async fn handle_new_session( + fn handle_new_session( &self, request: NewSessionRequest, request_cx: sacp::JrRequestCx, @@ -65,7 +65,7 @@ impl ElizaAgent { request_cx.respond(response) } - async fn handle_load_session( + fn handle_load_session( &self, request: LoadSessionRequest, request_cx: sacp::JrRequestCx, @@ -85,7 +85,7 @@ impl ElizaAgent { request_cx.respond(response) } - async fn handle_prompt_request( + fn handle_prompt_request( &self, request: PromptRequest, request_cx: sacp::JrRequestCx, @@ -105,10 +105,7 @@ impl ElizaAgent { let response_text = self .get_response(session_id, &input_text) .unwrap_or_else(|| { - format!( - "Error: Session {} not found. Please start a new session.", - session_id - ) + format!("Error: Session {session_id} not found. Please start a new session.") }); tracing::debug!("Eliza response: {}", response_text); @@ -159,34 +156,29 @@ pub async fn run_elizacp(transport: impl Component + 'static) -> Result<(), sacp request_cx.respond(InitializeResponse { protocol_version: initialize.protocol_version, - agent_capabilities: AgentCapabilities { - load_session: Default::default(), - prompt_capabilities: Default::default(), - mcp_capabilities: Default::default(), - meta: Default::default(), - }, - auth_methods: Default::default(), - agent_info: Default::default(), - meta: Default::default(), + agent_capabilities: AgentCapabilities::default(), + auth_methods: Vec::default(), + agent_info: Option::default(), + meta: None, }) } }) .on_receive_request({ let agent = agent.clone(); async move |request: NewSessionRequest, request_cx| { - agent.handle_new_session(request, request_cx).await + agent.handle_new_session(request, request_cx) } }) .on_receive_request({ let agent = agent.clone(); async move |request: LoadSessionRequest, request_cx| { - agent.handle_load_session(request, request_cx).await + agent.handle_load_session(request, request_cx) } }) .on_receive_request({ let agent = agent.clone(); async move |request: PromptRequest, request_cx| { - agent.handle_prompt_request(request, request_cx).await + agent.handle_prompt_request(request, request_cx) } }) .connect_to(transport)? diff --git a/src/sacp-conductor/src/conductor.rs b/src/sacp-conductor/src/conductor.rs index b7db0d3..067381b 100644 --- a/src/sacp-conductor/src/conductor.rs +++ b/src/sacp-conductor/src/conductor.rs @@ -158,7 +158,7 @@ impl std::fmt::Debug for Conductor { f.debug_struct("Conductor") .field("name", &self.name) .field("conductor_command", &self.conductor_command) - .finish() + .finish_non_exhaustive() } } @@ -187,10 +187,10 @@ impl Conductor { }); let mut state = ConductorHandlerState { - components: Default::default(), + components: Vec::default(), component_list: Some(self.component_list), - bridge_listeners: Default::default(), - bridge_connections: Default::default(), + bridge_listeners: McpBridgeListeners::default(), + bridge_connections: HashMap::default(), conductor_command, proxy_mode: AtomicBool::new(false), }; @@ -269,7 +269,7 @@ struct ConductorHandlerState { /// Whether the conductor is operating in proxy mode. /// In proxy mode, the conductor itself acts as a proxy component in a larger chain, /// and ALL components (including the last) receive the proxy capability. - /// Uses AtomicBool for thread-safe interior mutability since we detect this during initialization. + /// Uses `AtomicBool` for thread-safe interior mutability since we detect this during initialization. proxy_mode: AtomicBool, } @@ -631,8 +631,7 @@ impl ConductorHandlerState { .get_mut(&connection_id) .ok_or_else(|| { sacp::util::internal_error(format!( - "unknown connection id: {}", - connection_id + "unknown connection id: {connection_id}" )) })? .send(MessageAndCx::Request(mcp_request, request_cx)) @@ -650,8 +649,7 @@ impl ConductorHandlerState { .get_mut(&connection_id) .ok_or_else(|| { sacp::util::internal_error(format!( - "unknown connection id: {}", - connection_id + "unknown connection id: {connection_id}" )) })? .send(MessageAndCx::Notification( @@ -707,7 +705,7 @@ impl ConductorHandlerState { info!(component_index, "Spawning component"); let connection = JrHandlerChain::new() - .name(format!("conductor-to-component({})", component_index)) + .name(format!("conductor-to-component({component_index})")) // Intercept messages sent by a proxy component to its successor. .on_receive_message({ let mut conductor_tx = conductor_tx.clone(); @@ -779,7 +777,19 @@ impl ConductorHandlerState { tracing::debug!(?is_agent, "forward_initialize_request"); let conductor_tx = conductor_tx.clone(); - if !is_agent { + if is_agent { + // Agent component - no proxy capability + self.components[target_component_index] + .send_request(initialize_req) + .await_when_result_received(async move |response| { + tracing::debug!(?response, "got initialize response"); + + match response { + Ok(response) => request_cx.respond(response), + Err(error) => request_cx.respond_with_error(error), + } + }) + } else { // Add proxy capability and verify response initialize_req = initialize_req.add_meta_capability(Proxy); self.components[target_component_index] @@ -791,8 +801,7 @@ impl ConductorHandlerState { // abort the conductor. if !response.has_meta_capability(Proxy) { return Err(sacp::util::internal_error(format!( - "component {} is not a proxy", - target_component_index + "component {target_component_index} is not a proxy" ))); } @@ -806,18 +815,6 @@ impl ConductorHandlerState { } Err(error) => request_cx.respond_with_error(error), }) - } else { - // Agent component - no proxy capability - self.components[target_component_index] - .send_request(initialize_req) - .await_when_result_received(async move |response| { - tracing::debug!(?response, "got initialize response"); - - match response { - Ok(response) => request_cx.respond(response), - Err(error) => request_cx.respond_with_error(error), - } - }) } } @@ -1094,7 +1091,7 @@ impl JrRequestCxExt for JrRequestCx { result, }) .await - .map_err(|e| sacp::util::internal_error(format!("Failed to send response: {}", e))) + .map_err(|e| sacp::util::internal_error(format!("Failed to send response: {e}"))) } async fn respond_with_result_via( @@ -1109,7 +1106,7 @@ impl JrRequestCxExt for JrRequestCx { result, }) .await - .map_err(|e| sacp::util::internal_error(format!("Failed to send response: {}", e))) + .map_err(|e| sacp::util::internal_error(format!("Failed to send response: {e}"))) } } diff --git a/src/sacp-conductor/src/conductor/mcp_bridge.rs b/src/sacp-conductor/src/conductor/mcp_bridge.rs index eafe021..d5a3a2a 100644 --- a/src/sacp-conductor/src/conductor/mcp_bridge.rs +++ b/src/sacp-conductor/src/conductor/mcp_bridge.rs @@ -43,7 +43,7 @@ impl McpBridgeListeners { /// 2. Stores the mapping for message routing /// 3. Transforms the server to use stdio transport pointing to `conductor mcp $PORT` /// - /// Returns the modified NewSessionRequest with transformed MCP servers. + /// Returns the modified `NewSessionRequest` with transformed MCP servers. pub async fn transform_mcp_servers( &mut self, cx: &JrConnectionCx, diff --git a/src/sacp-conductor/src/main.rs b/src/sacp-conductor/src/main.rs index b1624a5..e95774a 100644 --- a/src/sacp-conductor/src/main.rs +++ b/src/sacp-conductor/src/main.rs @@ -8,16 +8,12 @@ use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitEx #[tokio::main] async fn main() -> anyhow::Result<()> { let pid = std::process::id(); - let cwd = std::env::current_dir() - .map(|p| p.display().to_string()) - .unwrap_or_else(|_| "".to_string()); + let cwd = std::env::current_dir().map_or_else(|_| "".to_string(), |p| p.display().to_string()); // Check for SYMPOSIUM_LOG environment variable if let Ok(log_level) = std::env::var("SYMPOSIUM_LOG") { // Set up file logging to ~/.symposium/logs.$DATE - let home = std::env::var("HOME") - .map(PathBuf::from) - .unwrap_or_else(|_| PathBuf::from(".")); + let home = std::env::var("HOME").map_or_else(|_| PathBuf::from("."), PathBuf::from); let log_dir = home.join(".symposium"); std::fs::create_dir_all(&log_dir)?; diff --git a/src/sacp-conductor/src/mcp_bridge.rs b/src/sacp-conductor/src/mcp_bridge.rs index 63e15d2..c3ebe02 100644 --- a/src/sacp-conductor/src/mcp_bridge.rs +++ b/src/sacp-conductor/src/mcp_bridge.rs @@ -97,7 +97,7 @@ async fn connect_with_retry(port: u16) -> Result { let mut retry_delay_ms = 50; for attempt in 1..=max_retries { - match TcpStream::connect(format!("127.0.0.1:{}", port)).await { + match TcpStream::connect(format!("127.0.0.1:{port}")).await { Ok(stream) => { tracing::info!("Connected to localhost:{} on attempt {}", port, attempt); return Ok(stream); diff --git a/src/sacp-conductor/tests/arrow_proxy_eliza.rs b/src/sacp-conductor/tests/arrow_proxy_eliza.rs index bc58fe6..8213ced 100644 --- a/src/sacp-conductor/tests/arrow_proxy_eliza.rs +++ b/src/sacp-conductor/tests/arrow_proxy_eliza.rs @@ -45,8 +45,7 @@ async fn test_conductor_with_arrow_proxy_and_eliza() -> Result<(), sacp::Error> assert!( result.starts_with('>'), - "Expected response to start with '>' from arrow proxy, got: {}", - result + "Expected response to start with '>' from arrow proxy, got: {result}" ); Ok::(result) diff --git a/src/sacp-conductor/tests/initialization_sequence.rs b/src/sacp-conductor/tests/initialization_sequence.rs index 73d95a0..eb214d3 100644 --- a/src/sacp-conductor/tests/initialization_sequence.rs +++ b/src/sacp-conductor/tests/initialization_sequence.rs @@ -6,6 +6,7 @@ //! 3. Proxy components must accept the capability or initialization fails //! 4. Last component (agent) never receives proxy capability offer +use agent_client_protocol_schema::{ClientCapabilities, ProtocolVersion}; use sacp::schema::{AgentCapabilities, InitializeRequest, InitializeResponse}; use sacp::{Component, JrHandlerChain, MetaCapabilityExt, Proxy}; use sacp_conductor::conductor::Conductor; @@ -144,8 +145,8 @@ async fn test_single_component_no_proxy_offer() -> Result<(), sacp::Error> { run_test_with_components(vec![InitComponent::new(&component1)], async |editor_cx| { let init_response = recv(editor_cx.send_request(InitializeRequest { - protocol_version: Default::default(), - client_capabilities: Default::default(), + protocol_version: ProtocolVersion::default(), + client_capabilities: ClientCapabilities::default(), meta: None, client_info: None, })) @@ -153,8 +154,7 @@ async fn test_single_component_no_proxy_offer() -> Result<(), sacp::Error> { assert!( init_response.is_ok(), - "Initialize should succeed: {:?}", - init_response + "Initialize should succeed: {init_response:?}" ); Ok::<(), sacp::Error>(()) @@ -179,8 +179,8 @@ async fn test_two_components() -> Result<(), sacp::Error> { ], async |editor_cx| { let init_response = recv(editor_cx.send_request(InitializeRequest { - protocol_version: Default::default(), - client_capabilities: Default::default(), + protocol_version: ProtocolVersion::default(), + client_capabilities: ClientCapabilities::default(), meta: None, client_info: None, })) @@ -188,8 +188,7 @@ async fn test_two_components() -> Result<(), sacp::Error> { assert!( init_response.is_ok(), - "Initialize should succeed: {:?}", - init_response + "Initialize should succeed: {init_response:?}" ); Ok::<(), sacp::Error>(()) @@ -216,8 +215,8 @@ async fn test_proxy_component_must_respond_with_proxy() -> Result<(), sacp::Erro ], async |editor_cx| { let init_response = recv(editor_cx.send_request(InitializeRequest { - protocol_version: Default::default(), - client_capabilities: Default::default(), + protocol_version: ProtocolVersion::default(), + client_capabilities: ClientCapabilities::default(), meta: None, client_info: None, })) @@ -239,8 +238,7 @@ async fn test_proxy_component_must_respond_with_proxy() -> Result<(), sacp::Erro let error = result.unwrap_err(); assert!( error.to_string().contains("component 0 is not a proxy"), - "Expected 'component 0 is not a proxy' error, got: {:?}", - error + "Expected 'component 0 is not a proxy' error, got: {error:?}" ); // Verify component1 was offered proxy @@ -262,8 +260,8 @@ async fn test_proxy_component_must_strip_proxy_when_forwarding() -> Result<(), s ], async |editor_cx| { let init_response = recv(editor_cx.send_request(InitializeRequest { - protocol_version: Default::default(), - client_capabilities: Default::default(), + protocol_version: ProtocolVersion::default(), + client_capabilities: ClientCapabilities::default(), meta: None, client_info: None, })) @@ -287,8 +285,7 @@ async fn test_proxy_component_must_strip_proxy_when_forwarding() -> Result<(), s error .to_string() .contains("conductor received unexpected initialization request with proxy capability"), - "Expected 'conductor received unexpected initialization request with proxy capability' error, got: {:?}", - error + "Expected 'conductor received unexpected initialization request with proxy capability' error, got: {error:?}" ); // Verify component1 was offered proxy diff --git a/src/sacp-conductor/tests/mcp-integration.rs b/src/sacp-conductor/tests/mcp-integration.rs index 14f5920..f5bb9e4 100644 --- a/src/sacp-conductor/tests/mcp-integration.rs +++ b/src/sacp-conductor/tests/mcp-integration.rs @@ -7,6 +7,9 @@ mod mcp_integration; +use std::path::PathBuf; + +use agent_client_protocol_schema::{ClientCapabilities, ProtocolVersion}; use expect_test::expect; use futures::{SinkExt, StreamExt, channel::mpsc}; use sacp::JrHandlerChain; @@ -78,8 +81,8 @@ async fn test_proxy_provides_mcp_tools() -> Result<(), sacp::Error> { async |editor_cx| { // Send initialization request let init_response = recv(editor_cx.send_request(InitializeRequest { - protocol_version: Default::default(), - client_capabilities: Default::default(), + protocol_version: ProtocolVersion::default(), + client_capabilities: ClientCapabilities::default(), meta: None, client_info: None, })) @@ -87,13 +90,12 @@ async fn test_proxy_provides_mcp_tools() -> Result<(), sacp::Error> { assert!( init_response.is_ok(), - "Initialize should succeed: {:?}", - init_response + "Initialize should succeed: {init_response:?}" ); // Send session/new request let session_response = recv(editor_cx.send_request(NewSessionRequest { - cwd: Default::default(), + cwd: PathBuf::default(), mcp_servers: vec![], meta: None, })) @@ -101,8 +103,7 @@ async fn test_proxy_provides_mcp_tools() -> Result<(), sacp::Error> { assert!( session_response.is_ok(), - "Session/new should succeed: {:?}", - session_response + "Session/new should succeed: {session_response:?}" ); let session = session_response.unwrap(); @@ -158,8 +159,8 @@ async fn test_agent_handles_prompt() -> Result<(), sacp::Error> { .with_client(transport, async |editor_cx| { // Initialize recv(editor_cx.send_request(InitializeRequest { - protocol_version: Default::default(), - client_capabilities: Default::default(), + protocol_version: ProtocolVersion::default(), + client_capabilities: ClientCapabilities::default(), meta: None, client_info: None, })) @@ -167,7 +168,7 @@ async fn test_agent_handles_prompt() -> Result<(), sacp::Error> { // Create session let session = recv(editor_cx.send_request(NewSessionRequest { - cwd: Default::default(), + cwd: PathBuf::default(), mcp_servers: vec![], meta: None, })) diff --git a/src/sacp-conductor/tests/mcp_integration/agent.rs b/src/sacp-conductor/tests/mcp_integration/agent.rs index da151b9..19b3b2d 100644 --- a/src/sacp-conductor/tests/mcp_integration/agent.rs +++ b/src/sacp-conductor/tests/mcp_integration/agent.rs @@ -180,7 +180,7 @@ impl AgentComponent { update: SessionUpdate::AgentMessageChunk(ContentChunk { content: ContentBlock::Text(TextContent { annotations: None, - text: format!("MCP tool result: {:?}", tool_result), + text: format!("MCP tool result: {tool_result:?}"), meta: None, }), meta: None, diff --git a/src/sacp-conductor/tests/nested_arrow_proxy.rs b/src/sacp-conductor/tests/nested_arrow_proxy.rs index 5590c7e..5def588 100644 --- a/src/sacp-conductor/tests/nested_arrow_proxy.rs +++ b/src/sacp-conductor/tests/nested_arrow_proxy.rs @@ -6,11 +6,11 @@ //! 3. The full proxy chain works end-to-end //! //! Chain structure: -//! test-editor -> conductor -> arrow_proxy1 -> arrow_proxy2 -> eliza +//! test-editor -> conductor -> `arrow_proxy1` -> `arrow_proxy2` -> eliza //! //! Expected behavior: -//! - arrow_proxy2 adds first '>' to eliza's response: ">Hello..." -//! - arrow_proxy1 adds second '>' to that: ">>Hello..." +//! - `arrow_proxy2` adds first '>' to eliza's response: ">Hello..." +//! - `arrow_proxy1` adds second '>' to that: ">>Hello..." use sacp_conductor::conductor::Conductor; use sacp_test::test_client::yolo_prompt; diff --git a/src/sacp-conductor/tests/nested_conductor.rs b/src/sacp-conductor/tests/nested_conductor.rs index 45b724d..a3f13cc 100644 --- a/src/sacp-conductor/tests/nested_conductor.rs +++ b/src/sacp-conductor/tests/nested_conductor.rs @@ -7,13 +7,13 @@ //! 4. The '>' prefix is applied multiple times (once per proxy) //! //! Chain structure: -//! test-editor -> outer_conductor -> inner_conductor -> eliza -//! ├─ arrow_proxy1 -//! └─ arrow_proxy2 +//! test-editor -> `outer_conductor` -> `inner_conductor` -> eliza +//! ├─ `arrow_proxy1` +//! └─ `arrow_proxy2` //! //! Expected behavior: -//! - arrow_proxy1 adds first '>' to eliza's response: ">Hello..." -//! - arrow_proxy2 adds second '>' to that: ">>Hello..." +//! - `arrow_proxy1` adds first '>' to eliza's response: ">Hello..." +//! - `arrow_proxy2` adds second '>' to that: ">>Hello..." //! - Inner conductor operates in proxy mode, forwarding to eliza //! - Outer conductor receives the ">>" prefixed response diff --git a/src/sacp-proxy/src/mcp_server.rs b/src/sacp-proxy/src/mcp_server.rs index 4f558da..8b83dd8 100644 --- a/src/sacp-proxy/src/mcp_server.rs +++ b/src/sacp-proxy/src/mcp_server.rs @@ -41,6 +41,7 @@ struct McpServiceRegistryData { impl McpServiceRegistry { /// Creates a new empty MCP service registry + #[must_use] pub fn new() -> Self { Self::default() } @@ -80,8 +81,7 @@ impl McpServiceRegistry { let name = name.to_string(); if self.get_registered_server_by_name(&name).is_some() { return Err(sacp::util::internal_error(format!( - "Server with name '{}' already exists", - name + "Server with name '{name}' already exists" ))); } @@ -147,7 +147,7 @@ impl McpServiceRegistry { .is_some() } - async fn handle_connect_request( + fn handle_connect_request( &self, result: Result, sacp::Error>, request_cx: JrRequestCx, @@ -298,7 +298,7 @@ impl McpServiceRegistry { Ok(Handled::Yes) } - async fn handle_mcp_disconnect_notification( + fn handle_mcp_disconnect_notification( &self, result: Result, sacp::Error>, notification_cx: JrConnectionCx, @@ -320,7 +320,7 @@ impl McpServiceRegistry { } } - async fn handle_new_session_request( + fn handle_new_session_request( &self, result: Result, request_cx: JrRequestCx, @@ -370,7 +370,7 @@ impl JrMessageHandler for McpServiceRegistry { if let Some(result) = >::parse_request(cx.method(), params) { - cx = match self.handle_connect_request(result, cx).await? { + cx = match self.handle_connect_request(result, cx)? { Handled::Yes => return Ok(Handled::Yes), Handled::No(cx) => cx, }; @@ -389,7 +389,7 @@ impl JrMessageHandler for McpServiceRegistry { } if let Some(result) = ::parse_request(cx.method(), params) { - cx = match self.handle_new_session_request(result, cx).await? { + cx = match self.handle_new_session_request(result, cx)? { Handled::Yes => return Ok(Handled::Yes), Handled::No(cx) => cx, }; @@ -418,7 +418,7 @@ impl JrMessageHandler for McpServiceRegistry { params, ) { - cx = match self.handle_mcp_disconnect_notification(result, cx).await? { + cx = match self.handle_mcp_disconnect_notification(result, cx)? { Handled::Yes => return Ok(Handled::Yes), Handled::No(cx) => cx, }; @@ -452,7 +452,7 @@ impl std::fmt::Debug for RegisteredMcpServer { f.debug_struct("RegisteredMcpServer") .field("name", &self.name) .field("url", &self.url) - .finish() + .finish_non_exhaustive() } } diff --git a/src/sacp-proxy/src/to_from_successor.rs b/src/sacp-proxy/src/to_from_successor.rs index 986a794..d1acc77 100644 --- a/src/sacp-proxy/src/to_from_successor.rs +++ b/src/sacp-proxy/src/to_from_successor.rs @@ -123,7 +123,7 @@ impl JrNotification for SuccessorNotification {} // Proxy methods // ============================================================ -/// Extension trait for JrConnection that adds proxy-specific functionality +/// Extension trait for `JrConnection` that adds proxy-specific functionality pub trait AcpProxyExt { /// Adds a handler for requests received from the successor component. /// @@ -540,9 +540,7 @@ impl JrMessageHandler for ProxyHandler { InitializeRequest::parse_request(&request.method, &request.params) { let request = result?; - return self - .forward_initialize(request, request_cx.cast()) - .await + return Self::forward_initialize(request, request_cx.cast()) .map(|()| Handled::Yes); } @@ -581,8 +579,7 @@ impl ProxyHandler { /// Proxy initialization requires (1) a `Proxy` capability to be /// provided by the conductor and (2) provides a `Proxy` capability /// in our response. - async fn forward_initialize( - &mut self, + fn forward_initialize( mut request: InitializeRequest, request_cx: JrRequestCx, ) -> Result<(), sacp::Error> { diff --git a/src/sacp-rmcp/examples/with_mcp_server.rs b/src/sacp-rmcp/examples/with_mcp_server.rs index ce656c5..678eb96 100644 --- a/src/sacp-rmcp/examples/with_mcp_server.rs +++ b/src/sacp-rmcp/examples/with_mcp_server.rs @@ -34,6 +34,7 @@ pub struct ExampleMcpServer { } impl ExampleMcpServer { + #[must_use] pub fn new() -> Self { Self { tool_router: Self::tool_router(), diff --git a/src/sacp-tee/src/lib.rs b/src/sacp-tee/src/lib.rs index 35c1d26..30f37f4 100644 --- a/src/sacp-tee/src/lib.rs +++ b/src/sacp-tee/src/lib.rs @@ -56,6 +56,7 @@ pub struct LogWriter { } impl LogWriter { + #[must_use] pub fn new(log_file: PathBuf) -> (Self, mpsc::UnboundedSender) { let (tx, rx) = mpsc::unbounded_channel(); ( @@ -97,6 +98,7 @@ pub struct TeeHandler { } impl TeeHandler { + #[must_use] pub fn new(log_tx: mpsc::UnboundedSender) -> Self { Self { log_tx, next_id: 1 } } @@ -182,6 +184,7 @@ pub struct Tee { } impl Tee { + #[must_use] pub fn new(log_file: PathBuf) -> Self { Self { log_file } } diff --git a/src/sacp-test/src/arrow_proxy.rs b/src/sacp-test/src/arrow_proxy.rs index 61b9c2e..b236e16 100644 --- a/src/sacp-test/src/arrow_proxy.rs +++ b/src/sacp-test/src/arrow_proxy.rs @@ -19,17 +19,14 @@ pub async fn run_arrow_proxy(transport: impl Component + 'static) -> Result<(), .on_receive_notification_from_successor( async |mut notification: SessionNotification, cx| { // Modify the content by adding > prefix - match &mut notification.update { - SessionUpdate::AgentMessageChunk(ContentChunk { + if let SessionUpdate::AgentMessageChunk(ContentChunk { content: ContentBlock::Text(text_content), .. - }) => { - // Add > prefix to text content - text_content.text = format!(">{}", text_content.text); - } - _ => { - // Don't modify other update types - } + }) = &mut notification.update { + // Add > prefix to text content + text_content.text = format!(">{}", text_content.text); + } else { + // Don't modify other update types } // Forward modified notification to predecessor diff --git a/src/sacp-test/src/lib.rs b/src/sacp-test/src/lib.rs index da70851..4bbc0de 100644 --- a/src/sacp-test/src/lib.rs +++ b/src/sacp-test/src/lib.rs @@ -1,5 +1,8 @@ -use sacp::handler::*; -use sacp::*; +use sacp::handler::NullHandler; +use sacp::{ + Component, Error, JrHandlerChain, JrMessage, JrNotification, JrRequest, JrResponsePayload, + UntypedMessage, +}; use serde::{Deserialize, Serialize}; pub mod arrow_proxy; @@ -237,10 +240,12 @@ impl JrResponsePayload for OtherResponse { } // Mock async functions +#[expect(clippy::unused_async)] pub async fn expensive_analysis(_data: &str) -> Result { Ok("analysis result".into()) } +#[expect(clippy::unused_async)] pub async fn expensive_operation(_data: &str) -> Result { Ok("operation result".into()) } diff --git a/src/sacp-test/src/test_client.rs b/src/sacp-test/src/test_client.rs index 7c97da0..1af3404 100644 --- a/src/sacp-test/src/test_client.rs +++ b/src/sacp-test/src/test_client.rs @@ -6,8 +6,9 @@ use sacp::{ JrHandlerChain, schema::{ - ContentBlock, InitializeRequest, InitializeResponse, NewSessionRequest, NewSessionResponse, - PromptRequest, PromptResponse, SessionNotification, SessionUpdate, TextContent, + ClientCapabilities, ContentBlock, InitializeRequest, InitializeResponse, NewSessionRequest, + NewSessionResponse, PromptRequest, PromptResponse, ProtocolVersion, SessionNotification, + SessionUpdate, TextContent, }, }; @@ -18,7 +19,7 @@ use sacp::{ /// - Initializes the connection /// - Creates a new session /// - Sends the prompt -/// - Collects all text from AgentMessageChunk session updates +/// - Collects all text from `AgentMessageChunk` session updates /// - Returns the concatenated result /// /// # Example @@ -55,8 +56,8 @@ where // Initialize let InitializeResponse { .. } = cx .send_request(InitializeRequest { - protocol_version: Default::default(), - client_capabilities: Default::default(), + protocol_version: ProtocolVersion::default(), + client_capabilities: ClientCapabilities::default(), meta: None, client_info: None, }) diff --git a/src/sacp-tokio/src/acp_agent.rs b/src/sacp-tokio/src/acp_agent.rs index a658a77..85f9d18 100644 --- a/src/sacp-tokio/src/acp_agent.rs +++ b/src/sacp-tokio/src/acp_agent.rs @@ -76,16 +76,19 @@ pub struct AcpAgent { impl AcpAgent { /// Create a new `AcpAgent` from an [`sacp::schema::McpServer`] configuration. + #[must_use] pub fn new(server: sacp::schema::McpServer) -> Self { Self { server } } /// Get the underlying [`sacp::schema::McpServer`] configuration. + #[must_use] pub fn server(&self) -> &sacp::schema::McpServer { &self.server } /// Convert into the underlying [`sacp::schema::McpServer`] configuration. + #[must_use] pub fn into_server(self) -> sacp::schema::McpServer { self.server } @@ -143,7 +146,7 @@ impl AcpAgent { /// A future that holds a `Child` process and never resolves. /// When dropped, the child process is killed. struct ChildHolder { - _child: Child, + child: Child, } impl Future for ChildHolder { @@ -157,7 +160,7 @@ impl Future for ChildHolder { impl Drop for ChildHolder { fn drop(&mut self) { - drop(self._child.start_kill()); + drop(self.child.start_kill()); } } @@ -166,7 +169,7 @@ impl sacp::Component for AcpAgent { let (child_stdin, child_stdout, child) = self.spawn_process()?; // Hold the child process - it will be killed when this future completes - let _child_holder = ChildHolder { _child: child }; + let _child_holder = ChildHolder { child }; // Create the ByteStreams component and serve it sacp::ByteStreams::new(child_stdin.compat_write(), child_stdout.compat()) @@ -184,7 +187,7 @@ impl FromStr for AcpAgent { // If it starts with '{', try to parse as JSON if trimmed.starts_with('{') { let server: sacp::schema::McpServer = serde_json::from_str(trimmed) - .map_err(|e| sacp::util::internal_error(format!("Failed to parse JSON: {}", e)))?; + .map_err(|e| sacp::util::internal_error(format!("Failed to parse JSON: {e}")))?; return Ok(Self { server }); } @@ -196,7 +199,7 @@ impl FromStr for AcpAgent { fn parse_command_string(s: &str) -> Result { // Split the command string into words, respecting quotes let parts = shell_words::split(s) - .map_err(|e| sacp::util::internal_error(format!("Failed to parse command: {}", e)))?; + .map_err(|e| sacp::util::internal_error(format!("Failed to parse command: {e}")))?; if parts.is_empty() { return Err(sacp::util::internal_error("Command string cannot be empty")); diff --git a/src/sacp/examples/simple_agent.rs b/src/sacp/examples/simple_agent.rs index b0ec4bb..dcfc106 100644 --- a/src/sacp/examples/simple_agent.rs +++ b/src/sacp/examples/simple_agent.rs @@ -10,15 +10,10 @@ async fn main() -> Result<(), sacp::Error> { // Respond to initialize successfully request_cx.respond(InitializeResponse { protocol_version: initialize.protocol_version, - agent_capabilities: AgentCapabilities { - load_session: Default::default(), - prompt_capabilities: Default::default(), - mcp_capabilities: Default::default(), - meta: Default::default(), - }, - auth_methods: Default::default(), - agent_info: Default::default(), - meta: Default::default(), + agent_capabilities: AgentCapabilities::default(), + auth_methods: vec![], + agent_info: None, + meta: None, }) }) .on_receive_message( diff --git a/src/sacp/examples/yolo_one_shot_client.rs b/src/sacp/examples/yolo_one_shot_client.rs index 1cf0fcc..5a385b7 100644 --- a/src/sacp/examples/yolo_one_shot_client.rs +++ b/src/sacp/examples/yolo_one_shot_client.rs @@ -11,6 +11,7 @@ //! cargo run --example yolo_one_shot_client -- --command "python my_agent.py" "What is 2+2?" //! ``` +use agent_client_protocol_schema::ClientCapabilities; use clap::Parser; use sacp::JrHandlerChain; use sacp::schema::{ @@ -94,20 +95,19 @@ async fn main() -> Result<(), Box> { }) .on_receive_request(async move |request: RequestPermissionRequest, request_cx| { // YOLO: Auto-approve all permission requests by selecting the first option - eprintln!("✅ Auto-approving permission request: {:?}", request); + eprintln!("✅ Auto-approving permission request: {request:?}"); let option_id = request.options.first().map(|opt| opt.id.clone()); - match option_id { - Some(id) => request_cx.respond(RequestPermissionResponse { + if let Some(id) = option_id { + request_cx.respond(RequestPermissionResponse { outcome: RequestPermissionOutcome::Selected { option_id: id }, meta: None, - }), - None => { - eprintln!("⚠️ No options provided in permission request, cancelling"); - request_cx.respond(RequestPermissionResponse { - outcome: RequestPermissionOutcome::Cancelled, - meta: None, - }) - } + }) + } else { + eprintln!("⚠️ No options provided in permission request, cancelling"); + request_cx.respond(RequestPermissionResponse { + outcome: RequestPermissionOutcome::Cancelled, + meta: None, + }) } }) .with_client(transport, |cx: sacp::JrConnectionCx| async move { @@ -116,8 +116,8 @@ async fn main() -> Result<(), Box> { let init_response = cx .send_request(InitializeRequest { protocol_version: PROTOCOL_VERSION, - client_capabilities: Default::default(), - client_info: Default::default(), + client_capabilities: ClientCapabilities::default(), + client_info: None, meta: None, }) .block_task() @@ -137,7 +137,7 @@ async fn main() -> Result<(), Box> { .await?; let session_id = new_session_response.session_id; - eprintln!("✓ Session created: {}", session_id); + eprintln!("✓ Session created: {session_id}"); // Send the prompt eprintln!("💬 Sending prompt: \"{}\"", cli.prompt); diff --git a/src/sacp/src/capabilities.rs b/src/sacp/src/capabilities.rs index d60b113..d703206 100644 --- a/src/sacp/src/capabilities.rs +++ b/src/sacp/src/capabilities.rs @@ -24,7 +24,7 @@ use serde_json::json; /// Capabilities are key-value pairs that signal features or context to components /// in the proxy chain. Implement this trait to define new capabilities. pub trait MetaCapability { - /// The key name in the `_meta.symposium` object (e.g., "proxy", "mcp_acp_transport") + /// The key name in the `_meta.symposium` object (e.g., "proxy", "`mcp_acp_transport`") fn key(&self) -> &'static str; /// The value to set when adding this capability (defaults to `true`) @@ -46,7 +46,7 @@ impl MetaCapability for Proxy { } } -/// The mcp_acp_transport capability - indicates support for MCP-over-ACP bridging. +/// The `mcp_acp_transport` capability - indicates support for MCP-over-ACP bridging. /// /// When present in `_meta.symposium.mcp_acp_transport`, signals that the agent /// supports having MCP servers with `acp:UUID` transport proxied through the conductor. @@ -65,9 +65,11 @@ pub trait MetaCapabilityExt { fn has_meta_capability(&self, capability: impl MetaCapability) -> bool; /// Add a capability to `_meta.symposium`, creating the structure if needed + #[must_use] fn add_meta_capability(self, capability: impl MetaCapability) -> Self; /// Remove a capability from `_meta.symposium` if present + #[must_use] fn remove_meta_capability(self, capability: impl MetaCapability) -> Self; } diff --git a/src/sacp/src/jsonrpc.rs b/src/sacp/src/jsonrpc.rs index 7f95749..33ca3ad 100644 --- a/src/sacp/src/jsonrpc.rs +++ b/src/sacp/src/jsonrpc.rs @@ -362,7 +362,7 @@ pub struct JrHandlerChain { } impl JrHandlerChain { - /// Create a new JrConnection. + /// Create a new `JrConnection`. /// This type follows a builder pattern; use other methods to configure and then invoke /// [`Self::serve`] (to use as a server) or [`Self::with_client`] to use as a client. pub fn new() -> Self { @@ -380,9 +380,9 @@ impl JrHandlerChain { /// Create a new handler chain with the given handler. pub fn new_with(handler: H) -> Self { Self { - name: Default::default(), + name: Option::default(), handler, - pending_tasks: Default::default(), + pending_tasks: Vec::default(), } } @@ -969,7 +969,7 @@ enum OutgoingMessage { Error { error: crate::Error }, } -/// Return type from JrHandler; indicates whether the request was handled or not. +/// Return type from `JrHandler`; indicates whether the request was handled or not. #[derive(Debug)] #[must_use] pub enum Handled { @@ -1367,7 +1367,7 @@ impl std::fmt::Debug for JrRequestCx { .field("method", &self.method) .field("id", &self.id) .field("response_type", &std::any::type_name::()) - .finish() + .finish_non_exhaustive() } } @@ -1393,6 +1393,7 @@ impl JrRequestCx { impl JrRequestCx { /// Method of the incoming request + #[must_use] pub fn method(&self) -> &str { &self.method } @@ -1404,7 +1405,7 @@ impl JrRequestCx { self.wrap_params(|method, value| T::from_value(method, value?)) } - /// Return a new JrResponse that expects a response of type U and serializes it. + /// Return a new `JrResponse` that expects a response of type U and serializes it. pub fn wrap_method(self, method: String) -> JrRequestCx { JrRequestCx { cx: self.cx, @@ -1414,7 +1415,7 @@ impl JrRequestCx { } } - /// Return a new JrResponse that expects a response of type U and serializes it. + /// Return a new `JrResponse` that expects a response of type U and serializes it. /// /// `wrap_fn` will be invoked with the method name and the result of the wrapped function. pub fn wrap_params( @@ -1433,6 +1434,7 @@ impl JrRequestCx { } /// Get the underlying JSON RPC context. + #[must_use] pub fn connection_cx(&self) -> JrConnectionCx { self.cx.clone() } @@ -1592,19 +1594,19 @@ impl MessageAndCx { } impl MessageAndCx { - /// Returns the method of the message (only available for UntypedMessage). + /// Returns the method of the message (only available for `UntypedMessage`). + #[must_use] pub fn method(&self) -> &str { match self { - MessageAndCx::Request(msg, _) => &msg.method, - MessageAndCx::Notification(msg, _) => &msg.method, + MessageAndCx::Request(msg, _) | MessageAndCx::Notification(msg, _) => &msg.method, } } - /// Returns the message of the message (only available for UntypedMessage). + /// Returns the message of the message (only available for `UntypedMessage`). + #[must_use] pub fn message(&self) -> &UntypedMessage { match self { - MessageAndCx::Request(msg, _) => msg, - MessageAndCx::Notification(msg, _) => msg, + MessageAndCx::Request(msg, _) | MessageAndCx::Notification(msg, _) => msg, } } } @@ -1629,16 +1631,19 @@ impl UntypedMessage { } /// Returns the method name + #[must_use] pub fn method(&self) -> &str { &self.method } /// Returns the parameters as a JSON value + #[must_use] pub fn params(&self) -> &serde_json::Value { &self.params } /// Consumes this message and returns the method and params + #[must_use] pub fn into_parts(self) -> (String, serde_json::Value) { (self.method, self.params) } @@ -1764,7 +1769,7 @@ impl std::fmt::Debug for JrResponse { .field("method", &self.method) .field("connection_cx", &self.connection_cx) .field("response_rx", &self.response_rx) - .finish() + .finish_non_exhaustive() } } @@ -1785,6 +1790,7 @@ impl JrResponse { impl JrResponse { /// The method of the request this is in response to. + #[must_use] pub fn method(&self) -> &str { &self.method } @@ -2152,8 +2158,7 @@ where async fn serve(self, client: impl Component) -> Result<(), crate::Error> { let (channel, serve_self) = self.into_server(); match futures::future::select(Box::pin(client.serve(channel)), serve_self).await { - Either::Left((result, _)) => result, - Either::Right((result, _)) => result, + Either::Left((result, _)) | Either::Right((result, _)) => result, } } @@ -2222,6 +2227,7 @@ impl Channel { /// # Returns /// /// A tuple `(channel_a, channel_b)` of connected channel endpoints. + #[must_use] pub fn duplex() -> (Self, Self) { // Create channels: A sends Result which B receives as Message let (a_tx, b_rx) = mpsc::unbounded(); diff --git a/src/sacp/src/jsonrpc/actors.rs b/src/sacp/src/jsonrpc/actors.rs index 934e46a..84df9c3 100644 --- a/src/sacp/src/jsonrpc/actors.rs +++ b/src/sacp/src/jsonrpc/actors.rs @@ -72,12 +72,12 @@ pub(super) async fn reply_actor( // Split Actors for Pluggable Transport // ============================================================================ -/// Outgoing protocol actor: Converts application-level OutgoingMessage to protocol-level jsonrpcmsg::Message. +/// Outgoing protocol actor: Converts application-level `OutgoingMessage` to protocol-level `jsonrpcmsg::Message`. /// /// This actor handles JSON-RPC protocol semantics: /// - Assigns unique IDs to outgoing requests -/// - Subscribes to reply_actor for response correlation -/// - Converts OutgoingMessage variants to jsonrpcmsg::Message +/// - Subscribes to `reply_actor` for response correlation +/// - Converts `OutgoingMessage` variants to `jsonrpcmsg::Message` /// /// This is the protocol layer - it has no knowledge of how messages are transported. pub(super) async fn outgoing_protocol_actor( @@ -153,11 +153,11 @@ pub(super) async fn outgoing_protocol_actor( Ok(()) } -/// Transport outgoing actor: Serializes jsonrpcmsg::Message and writes to byte stream. +/// Transport outgoing actor: Serializes `jsonrpcmsg::Message` and writes to byte stream. /// /// This actor handles transport mechanics: /// - Unwraps Result from the channel -/// - Serializes jsonrpcmsg::Message to JSON +/// - Serializes `jsonrpcmsg::Message` to JSON /// - Writes newline-delimited JSON to the stream /// - Handles serialization errors /// @@ -219,17 +219,17 @@ pub(super) async fn transport_outgoing_actor( } } } - }; + } } Ok(()) } -/// Incoming protocol actor: Routes jsonrpcmsg::Message to reply_actor or handler. +/// Incoming protocol actor: Routes `jsonrpcmsg::Message` to `reply_actor` or handler. /// /// This actor handles JSON-RPC protocol semantics: -/// - Routes responses to reply_actor (for request/response correlation) +/// - Routes responses to `reply_actor` (for request/response correlation) /// - Routes requests/notifications to handler chain -/// - Converts jsonrpcmsg::Request to UntypedMessage for handlers +/// - Converts `jsonrpcmsg::Request` to `UntypedMessage` for handlers /// /// This is the protocol layer - it has no knowledge of how messages arrived. pub(super) async fn incoming_protocol_actor( @@ -243,7 +243,7 @@ pub(super) async fn incoming_protocol_actor( Ok(message) => match message { jsonrpcmsg::Message::Request(request) => { tracing::trace!(method = %request.method, id = ?request.id, "Handling request"); - dispatch_request(json_rpc_cx, request, &mut handler).await? + dispatch_request(json_rpc_cx, request, &mut handler).await?; } jsonrpcmsg::Message::Response(response) => { tracing::trace!(id = ?response.id, has_result = response.result.is_some(), has_error = response.error.is_some(), "Handling response"); @@ -276,11 +276,11 @@ pub(super) async fn incoming_protocol_actor( Ok(()) } -/// Transport incoming actor: Parses bytes into jsonrpcmsg::Message. +/// Transport incoming actor: Parses bytes into `jsonrpcmsg::Message`. /// /// This actor handles transport mechanics: /// - Reads newline-delimited JSON from the stream -/// - Parses to jsonrpcmsg::Message +/// - Parses to `jsonrpcmsg::Message` /// - Handles parse errors /// /// This is the transport layer - it has no knowledge of protocol semantics. diff --git a/src/sacp/src/jsonrpc/handlers.rs b/src/sacp/src/jsonrpc/handlers.rs index c39d634..26dad50 100644 --- a/src/sacp/src/jsonrpc/handlers.rs +++ b/src/sacp/src/jsonrpc/handlers.rs @@ -342,11 +342,6 @@ where H2: JrMessageHandler, { fn describe_chain(&self) -> impl std::fmt::Debug { - return DebugImpl { - handler1: &self.handler1, - handler2: &self.handler2, - }; - struct DebugImpl<'h, H1, H2> { handler1: &'h H1, handler2: &'h H2, @@ -362,6 +357,11 @@ where ) } } + + DebugImpl { + handler1: &self.handler1, + handler2: &self.handler2, + } } async fn handle_message( diff --git a/src/sacp/src/schema/agent_to_client/notifications.rs b/src/sacp/src/schema/agent_to_client/notifications.rs index f5729a4..d5eefdb 100644 --- a/src/sacp/src/schema/agent_to_client/notifications.rs +++ b/src/sacp/src/schema/agent_to_client/notifications.rs @@ -12,7 +12,7 @@ impl JrMessage for SessionNotification { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "session/update" } diff --git a/src/sacp/src/schema/agent_to_client/requests.rs b/src/sacp/src/schema/agent_to_client/requests.rs index 523e518..d2c6195 100644 --- a/src/sacp/src/schema/agent_to_client/requests.rs +++ b/src/sacp/src/schema/agent_to_client/requests.rs @@ -23,7 +23,7 @@ impl JrMessage for RequestPermissionRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "session/request_permission" } @@ -67,7 +67,7 @@ impl JrMessage for WriteTextFileRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "fs/write_text_file" } @@ -111,7 +111,7 @@ impl JrMessage for ReadTextFileRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "fs/read_text_file" } @@ -155,7 +155,7 @@ impl JrMessage for CreateTerminalRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "terminal/create" } @@ -199,7 +199,7 @@ impl JrMessage for TerminalOutputRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "terminal/output" } @@ -243,7 +243,7 @@ impl JrMessage for ReleaseTerminalRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "terminal/release" } @@ -287,7 +287,7 @@ impl JrMessage for WaitForTerminalExitRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "terminal/wait_for_exit" } @@ -331,7 +331,7 @@ impl JrMessage for KillTerminalCommandRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "terminal/kill" } diff --git a/src/sacp/src/schema/client_to_agent/notifications.rs b/src/sacp/src/schema/client_to_agent/notifications.rs index 9c0cb06..934bc9e 100644 --- a/src/sacp/src/schema/client_to_agent/notifications.rs +++ b/src/sacp/src/schema/client_to_agent/notifications.rs @@ -10,7 +10,7 @@ impl JrMessage for CancelNotification { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "session/cancel" } diff --git a/src/sacp/src/schema/client_to_agent/requests.rs b/src/sacp/src/schema/client_to_agent/requests.rs index 1024f4d..789bec1 100644 --- a/src/sacp/src/schema/client_to_agent/requests.rs +++ b/src/sacp/src/schema/client_to_agent/requests.rs @@ -18,7 +18,7 @@ impl JrMessage for InitializeRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "initialize" } @@ -63,7 +63,7 @@ impl JrMessage for AuthenticateRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "authenticate" } @@ -107,7 +107,7 @@ impl JrMessage for LoadSessionRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "session/load" } @@ -151,7 +151,7 @@ impl JrMessage for NewSessionRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "session/new" } @@ -195,7 +195,7 @@ impl JrMessage for PromptRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "session/prompt" } @@ -239,7 +239,7 @@ impl JrMessage for SetSessionModeRequest { crate::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "session/set_mode" } diff --git a/src/sacp/src/schema/enum_impls.rs b/src/sacp/src/schema/enum_impls.rs index e2f074b..7c1aa98 100644 --- a/src/sacp/src/schema/enum_impls.rs +++ b/src/sacp/src/schema/enum_impls.rs @@ -1,11 +1,11 @@ -//! JrRequest and JrNotification implementations for ACP enum types. +//! `JrRequest` and `JrNotification` implementations for ACP enum types. //! //! This module implements the JSON-RPC traits for the enum types from //! agent-client-protocol-schema that represent all possible messages: //! - ClientRequest/AgentResponse (messages agents receive/send) -//! - ClientNotification (notifications agents receive) +//! - `ClientNotification` (notifications agents receive) //! - AgentRequest/ClientResponse (messages clients receive/send) -//! - AgentNotification (notifications clients receive) +//! - `AgentNotification` (notifications clients receive) use crate::schema::{AgentNotification, AgentRequest, ClientNotification, ClientRequest}; use serde::Serialize; diff --git a/src/sacp/tests/jsonrpc_advanced.rs b/src/sacp/tests/jsonrpc_advanced.rs index a113c1d..cd5cfd4 100644 --- a/src/sacp/tests/jsonrpc_advanced.rs +++ b/src/sacp/tests/jsonrpc_advanced.rs @@ -52,7 +52,7 @@ impl JrMessage for PingRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "ping" } @@ -106,7 +106,7 @@ impl JrMessage for SlowRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "slow" } @@ -195,7 +195,7 @@ async fn test_bidirectional_communication() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -254,7 +254,7 @@ async fn test_request_ids() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -328,7 +328,7 @@ async fn test_out_of_order_responses() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } diff --git a/src/sacp/tests/jsonrpc_edge_cases.rs b/src/sacp/tests/jsonrpc_edge_cases.rs index d6aa4e2..61a47c4 100644 --- a/src/sacp/tests/jsonrpc_edge_cases.rs +++ b/src/sacp/tests/jsonrpc_edge_cases.rs @@ -51,7 +51,7 @@ impl JrMessage for EmptyRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "empty_method" } @@ -90,7 +90,7 @@ impl JrMessage for OptionalParamsRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "optional_params_method" } @@ -177,7 +177,7 @@ async fn test_empty_request() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -224,7 +224,7 @@ async fn test_null_params() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -288,7 +288,7 @@ async fn test_server_shutdown() { // Wait for client to finish let result = client_result.await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } diff --git a/src/sacp/tests/jsonrpc_error_handling.rs b/src/sacp/tests/jsonrpc_error_handling.rs index 89880a4..934cea5 100644 --- a/src/sacp/tests/jsonrpc_error_handling.rs +++ b/src/sacp/tests/jsonrpc_error_handling.rs @@ -55,7 +55,7 @@ impl JrMessage for SimpleRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "simple_method" } @@ -222,7 +222,7 @@ async fn test_unknown_method() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -242,7 +242,7 @@ impl JrMessage for ErrorRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "error_method" } @@ -315,7 +315,7 @@ async fn test_handler_returns_error() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -333,7 +333,7 @@ impl JrMessage for EmptyRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "strict_method" } @@ -407,7 +407,7 @@ async fn test_missing_required_params() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } diff --git a/src/sacp/tests/jsonrpc_handler_chain.rs b/src/sacp/tests/jsonrpc_handler_chain.rs index 6953ebc..0743d18 100644 --- a/src/sacp/tests/jsonrpc_handler_chain.rs +++ b/src/sacp/tests/jsonrpc_handler_chain.rs @@ -37,7 +37,7 @@ impl JrMessage for FooRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "foo" } @@ -90,7 +90,7 @@ impl JrMessage for BarRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "bar" } @@ -194,7 +194,7 @@ async fn test_multiple_handlers_different_methods() { })) .await .map_err(|e| -> sacp::Error { - sacp::util::internal_error(format!("Bar request failed: {:?}", e)) + sacp::util::internal_error(format!("Bar request failed: {e:?}")) })?; assert_eq!(bar_response.result, "bar: test2"); @@ -203,7 +203,7 @@ async fn test_multiple_handlers_different_methods() { ) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -223,7 +223,7 @@ impl JrMessage for TrackRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "track" } @@ -294,7 +294,7 @@ async fn test_handler_priority_ordering() { tokio::task::spawn_local(async move { if let Err(e) = server.serve(server_transport).await { - eprintln!("Server error: {:?}", e); + eprintln!("Server error: {e:?}"); } }); @@ -307,7 +307,7 @@ async fn test_handler_priority_ordering() { })) .await .map_err(|e| { - sacp::util::internal_error(format!("Track request failed: {:?}", e)) + sacp::util::internal_error(format!("Track request failed: {e:?}")) })?; // First handler should have handled it @@ -318,7 +318,7 @@ async fn test_handler_priority_ordering() { ) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); // Verify only handler1 was invoked let handled_by = handled.lock().unwrap(); @@ -343,7 +343,7 @@ impl JrMessage for Method1Request { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "method1" } @@ -381,7 +381,7 @@ impl JrMessage for Method2Request { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "method2" } @@ -452,7 +452,7 @@ async fn test_fallthrough_behavior() { tokio::task::spawn_local(async move { if let Err(e) = server.serve(server_transport).await { - eprintln!("Server error: {:?}", e); + eprintln!("Server error: {e:?}"); } }); @@ -466,7 +466,7 @@ async fn test_fallthrough_behavior() { })) .await .map_err(|e| { - sacp::util::internal_error(format!("Method2 request failed: {:?}", e)) + sacp::util::internal_error(format!("Method2 request failed: {e:?}")) })?; assert_eq!(response.result, "method2: fallthrough"); @@ -476,7 +476,7 @@ async fn test_fallthrough_behavior() { ) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); // Verify only method2 was handled (handler1 passed through) let handled_methods = handled.lock().unwrap(); @@ -520,7 +520,7 @@ async fn test_no_handler_claims() { tokio::task::spawn_local(async move { if let Err(e) = server.serve(server_transport).await { - eprintln!("Server error: {:?}", e); + eprintln!("Server error: {e:?}"); } }); @@ -542,7 +542,7 @@ async fn test_no_handler_claims() { ) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -562,7 +562,7 @@ impl JrMessage for EventNotification { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "event" } @@ -619,7 +619,7 @@ async fn test_handler_claims_notification() { tokio::task::spawn_local(async move { if let Err(e) = server.serve(server_transport).await { - eprintln!("Server error: {:?}", e); + eprintln!("Server error: {e:?}"); } }); @@ -632,8 +632,7 @@ async fn test_handler_claims_notification() { }) .map_err(|e| { sacp::util::internal_error(format!( - "Failed to send notification: {:?}", - e + "Failed to send notification: {e:?}" )) })?; @@ -645,7 +644,7 @@ async fn test_handler_claims_notification() { ) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); let received_events = events.lock().unwrap(); assert_eq!(received_events.len(), 1); diff --git a/src/sacp/tests/jsonrpc_hello.rs b/src/sacp/tests/jsonrpc_hello.rs index ed58fa8..b233b5a 100644 --- a/src/sacp/tests/jsonrpc_hello.rs +++ b/src/sacp/tests/jsonrpc_hello.rs @@ -23,7 +23,7 @@ async fn recv(response: JrResponse) -> Result ( impl AsyncRead, impl AsyncWrite, @@ -53,7 +53,7 @@ impl JrMessage for PingRequest { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "ping" } @@ -122,7 +122,7 @@ async fn test_hello_world() { // Spawn the server in the background tokio::task::spawn_local(async move { if let Err(e) = server.serve(server_transport).await { - eprintln!("Server error: {:?}", e); + eprintln!("Server error: {e:?}"); } }); @@ -136,7 +136,7 @@ async fn test_hello_world() { }; let response = recv(cx.send_request(request)).await.map_err(|e| { - sacp::util::internal_error(format!("Request failed: {:?}", e)) + sacp::util::internal_error(format!("Request failed: {e:?}")) })?; assert_eq!(response.echo, "pong: hello world"); @@ -145,7 +145,7 @@ async fn test_hello_world() { }) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -162,7 +162,7 @@ impl JrMessage for LogNotification { sacp::UntypedMessage::new(&method, self) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "log" } @@ -214,7 +214,7 @@ async fn test_notification() { tokio::task::spawn_local(async move { if let Err(e) = server.serve(server_transport).await { - eprintln!("Server error: {:?}", e); + eprintln!("Server error: {e:?}"); } }); @@ -228,8 +228,7 @@ async fn test_notification() { }) .map_err(|e| { sacp::util::internal_error(format!( - "Failed to send notification: {:?}", - e + "Failed to send notification: {e:?}" )) })?; @@ -238,8 +237,7 @@ async fn test_notification() { }) .map_err(|e| { sacp::util::internal_error(format!( - "Failed to send notification: {:?}", - e + "Failed to send notification: {e:?}" )) })?; @@ -251,7 +249,7 @@ async fn test_notification() { ) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); let received_logs = logs.lock().unwrap(); assert_eq!(received_logs.len(), 2); @@ -286,7 +284,7 @@ async fn test_multiple_sequential_requests() { tokio::task::spawn_local(async move { if let Err(e) = server.serve(server_transport).await { - eprintln!("Server error: {:?}", e); + eprintln!("Server error: {e:?}"); } }); @@ -297,14 +295,14 @@ async fn test_multiple_sequential_requests() { // Send multiple requests sequentially for i in 1..=5 { let request = PingRequest { - message: format!("message {}", i), + message: format!("message {i}"), }; let response = recv(cx.send_request(request)).await.map_err(|e| { - sacp::util::internal_error(format!("Request {} failed: {:?}", i, e)) + sacp::util::internal_error(format!("Request {i} failed: {e:?}")) })?; - assert_eq!(response.echo, format!("pong: message {}", i)); + assert_eq!(response.echo, format!("pong: message {i}")); } Ok(()) @@ -312,7 +310,7 @@ async fn test_multiple_sequential_requests() { ) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } @@ -342,7 +340,7 @@ async fn test_concurrent_requests() { tokio::task::spawn_local(async move { if let Err(e) = server.serve(server_transport).await { - eprintln!("Server error: {:?}", e); + eprintln!("Server error: {e:?}"); } }); @@ -355,7 +353,7 @@ async fn test_concurrent_requests() { for i in 1..=5 { let request = PingRequest { - message: format!("concurrent message {}", i), + message: format!("concurrent message {i}"), }; // Start all requests without awaiting @@ -365,10 +363,10 @@ async fn test_concurrent_requests() { // Now await all responses for (i, response_future) in responses { let response = recv(response_future).await.map_err(|e| { - sacp::util::internal_error(format!("Request {} failed: {:?}", i, e)) + sacp::util::internal_error(format!("Request {i} failed: {e:?}")) })?; - assert_eq!(response.echo, format!("pong: concurrent message {}", i)); + assert_eq!(response.echo, format!("pong: concurrent message {i}")); } Ok(()) @@ -376,7 +374,7 @@ async fn test_concurrent_requests() { ) .await; - assert!(result.is_ok(), "Test failed: {:?}", result); + assert!(result.is_ok(), "Test failed: {result:?}"); }) .await; } diff --git a/src/sacp/tests/match_message.rs b/src/sacp/tests/match_message.rs index 7dd0d60..6683e40 100644 --- a/src/sacp/tests/match_message.rs +++ b/src/sacp/tests/match_message.rs @@ -17,7 +17,7 @@ impl JrMessage for EchoRequestResponse { }) } - fn method(&self) -> &str { + fn method(&self) -> &'static str { "echo" } diff --git a/src/yopo/src/main.rs b/src/yopo/src/main.rs index 929f860..640611f 100644 --- a/src/yopo/src/main.rs +++ b/src/yopo/src/main.rs @@ -22,9 +22,9 @@ use sacp::JrHandlerChain; use sacp::schema::{ - ContentBlock, InitializeRequest, NewSessionRequest, PromptRequest, RequestPermissionOutcome, - RequestPermissionRequest, RequestPermissionResponse, SessionNotification, TextContent, - VERSION as PROTOCOL_VERSION, + ClientCapabilities, ContentBlock, InitializeRequest, NewSessionRequest, PromptRequest, + RequestPermissionOutcome, RequestPermissionRequest, RequestPermissionResponse, + SessionNotification, TextContent, VERSION as PROTOCOL_VERSION, }; use sacp_tokio::AcpAgent; use std::path::PathBuf; @@ -69,38 +69,37 @@ async fn main() -> Result<(), Box> { text, meta: _, }) => print!("{text}"), - ContentBlock::Image(_) => {} - ContentBlock::Audio(_) => {} - ContentBlock::ResourceLink(_) => {} - ContentBlock::Resource(_) => {} + ContentBlock::Image(_) + | ContentBlock::Audio(_) + | ContentBlock::ResourceLink(_) + | ContentBlock::Resource(_) => {} } } - sacp::schema::SessionUpdate::UserMessageChunk(_) => {} - sacp::schema::SessionUpdate::AgentThoughtChunk(_) => {} - sacp::schema::SessionUpdate::ToolCall(_) => {} - sacp::schema::SessionUpdate::ToolCallUpdate(_) => {} - sacp::schema::SessionUpdate::Plan(_) => {} - sacp::schema::SessionUpdate::AvailableCommandsUpdate(_) => {} - sacp::schema::SessionUpdate::CurrentModeUpdate(_) => {} + sacp::schema::SessionUpdate::UserMessageChunk(_) + | sacp::schema::SessionUpdate::AgentThoughtChunk(_) + | sacp::schema::SessionUpdate::ToolCall(_) + | sacp::schema::SessionUpdate::ToolCallUpdate(_) + | sacp::schema::SessionUpdate::Plan(_) + | sacp::schema::SessionUpdate::AvailableCommandsUpdate(_) + | sacp::schema::SessionUpdate::CurrentModeUpdate(_) => {} } Ok(()) }) .on_receive_request(async move |request: RequestPermissionRequest, request_cx| { // YOPO: Auto-approve all permission requests by selecting the first option - eprintln!("✅ Auto-approving permission request: {:?}", request); + eprintln!("✅ Auto-approving permission request: {request:?}"); let option_id = request.options.first().map(|opt| opt.id.clone()); - match option_id { - Some(id) => request_cx.respond(RequestPermissionResponse { + if let Some(id) = option_id { + request_cx.respond(RequestPermissionResponse { outcome: RequestPermissionOutcome::Selected { option_id: id }, meta: None, - }), - None => { - eprintln!("⚠️ No options provided in permission request, cancelling"); - request_cx.respond(RequestPermissionResponse { - outcome: RequestPermissionOutcome::Cancelled, - meta: None, - }) - } + }) + } else { + eprintln!("⚠️ No options provided in permission request, cancelling"); + request_cx.respond(RequestPermissionResponse { + outcome: RequestPermissionOutcome::Cancelled, + meta: None, + }) } }) .connect_to(agent)? @@ -110,8 +109,8 @@ async fn main() -> Result<(), Box> { let init_response = cx .send_request(InitializeRequest { protocol_version: PROTOCOL_VERSION, - client_capabilities: Default::default(), - client_info: Default::default(), + client_capabilities: ClientCapabilities::default(), + client_info: None, meta: None, }) .block_task() @@ -131,15 +130,15 @@ async fn main() -> Result<(), Box> { .await?; let session_id = new_session_response.session_id; - eprintln!("✓ Session created: {}", session_id); + eprintln!("✓ Session created: {session_id}"); // Send the prompt - eprintln!("💬 Sending prompt: \"{}\"", prompt); + eprintln!("💬 Sending prompt: \"{prompt}\""); let prompt_response = cx .send_request(PromptRequest { session_id: session_id.clone(), prompt: vec![ContentBlock::Text(TextContent { - text: prompt.to_string(), + text: prompt.clone(), annotations: None, meta: None, })], From 30ff9b4b29817008a17660b0a1162b3f9d087d99 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Tue, 25 Nov 2025 13:30:30 +0100 Subject: [PATCH 4/5] adjust config --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1cd98b..011eec5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,8 +73,8 @@ rust_2018_idioms = { level = "warn", priority = -1 } unused = { level = "warn", priority = -1 } [workspace.lints.clippy] -# cargo = "warn" -pedantic ={ level = "warn", priority = -1 } +# cargo = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } doc_markdown = "allow" missing_errors_doc = "allow" missing_panics_doc = "allow" From 08fc8693f6154147301cb1cd9fecd3f799858913 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Tue, 25 Nov 2025 13:32:57 +0100 Subject: [PATCH 5/5] fmt --- src/elizacp/src/eliza.rs | 9 ++++++--- src/sacp-conductor/src/main.rs | 3 ++- src/sacp-tee/src/lib.rs | 6 +++--- src/sacp-test/src/arrow_proxy.rs | 7 ++++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/elizacp/src/eliza.rs b/src/elizacp/src/eliza.rs index 855b97f..9fad9a2 100644 --- a/src/elizacp/src/eliza.rs +++ b/src/elizacp/src/eliza.rs @@ -30,13 +30,13 @@ pub struct Eliza { impl Eliza { /// Create a new Eliza instance with classic patterns. /// Uses a fixed seed for deterministic testing. - #[must_use] + #[must_use] pub fn new() -> Self { Self::with_seed(42) } /// Create a new Eliza instance with a specific seed. - #[must_use] + #[must_use] pub fn with_seed(seed: u64) -> Self { let mut eliza = Self { patterns: Vec::new(), @@ -213,7 +213,10 @@ impl Eliza { let regex = Regex::new(pattern).expect("Invalid regex pattern"); self.patterns.push(Pattern { pattern: regex, - responses: responses.into_iter().map(std::string::ToString::to_string).collect(), + responses: responses + .into_iter() + .map(std::string::ToString::to_string) + .collect(), priority, }); } diff --git a/src/sacp-conductor/src/main.rs b/src/sacp-conductor/src/main.rs index e95774a..9baa3f8 100644 --- a/src/sacp-conductor/src/main.rs +++ b/src/sacp-conductor/src/main.rs @@ -8,7 +8,8 @@ use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitEx #[tokio::main] async fn main() -> anyhow::Result<()> { let pid = std::process::id(); - let cwd = std::env::current_dir().map_or_else(|_| "".to_string(), |p| p.display().to_string()); + let cwd = std::env::current_dir() + .map_or_else(|_| "".to_string(), |p| p.display().to_string()); // Check for SYMPOSIUM_LOG environment variable if let Ok(log_level) = std::env::var("SYMPOSIUM_LOG") { diff --git a/src/sacp-tee/src/lib.rs b/src/sacp-tee/src/lib.rs index 30f37f4..4b67a15 100644 --- a/src/sacp-tee/src/lib.rs +++ b/src/sacp-tee/src/lib.rs @@ -56,7 +56,7 @@ pub struct LogWriter { } impl LogWriter { - #[must_use] + #[must_use] pub fn new(log_file: PathBuf) -> (Self, mpsc::UnboundedSender) { let (tx, rx) = mpsc::unbounded_channel(); ( @@ -98,7 +98,7 @@ pub struct TeeHandler { } impl TeeHandler { - #[must_use] + #[must_use] pub fn new(log_tx: mpsc::UnboundedSender) -> Self { Self { log_tx, next_id: 1 } } @@ -184,7 +184,7 @@ pub struct Tee { } impl Tee { - #[must_use] + #[must_use] pub fn new(log_file: PathBuf) -> Self { Self { log_file } } diff --git a/src/sacp-test/src/arrow_proxy.rs b/src/sacp-test/src/arrow_proxy.rs index b236e16..7329ecd 100644 --- a/src/sacp-test/src/arrow_proxy.rs +++ b/src/sacp-test/src/arrow_proxy.rs @@ -20,9 +20,10 @@ pub async fn run_arrow_proxy(transport: impl Component + 'static) -> Result<(), async |mut notification: SessionNotification, cx| { // Modify the content by adding > prefix if let SessionUpdate::AgentMessageChunk(ContentChunk { - content: ContentBlock::Text(text_content), - .. - }) = &mut notification.update { + content: ContentBlock::Text(text_content), + .. + }) = &mut notification.update + { // Add > prefix to text content text_content.text = format!(">{}", text_content.text); } else {