Skip to content

Commit acc85c1

Browse files
benbrandtnerzhulart
authored andcommitted
Make Agent and Client dyn compatible (agentclientprotocol#97)
For now, we still won't require Send/Sync Signed-off-by: Ben Brandt <benjamin.j.brandt@gmail.com>
1 parent 2145b86 commit acc85c1

8 files changed

Lines changed: 59 additions & 53 deletions

File tree

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,23 @@ name = "client"
3333
path = "rust/example_client.rs"
3434

3535
[dependencies]
36-
anyhow = "1.0"
37-
async-broadcast = "0.7.2"
36+
anyhow = "1"
37+
async-broadcast = "0.7"
38+
async-trait = "0.1"
3839
futures = { version = "0.3" }
3940
log = "0.4"
4041
parking_lot = "0.12"
41-
schemars = { version = "1.0" }
42-
serde = { version = "1.0", features = ["derive", "rc"] }
43-
serde_json = { version = "1.0", features = ["raw_value"] }
42+
schemars = { version = "1" }
43+
serde = { version = "1", features = ["derive", "rc"] }
44+
serde_json = { version = "1", features = ["raw_value"] }
4445

4546
[dev-dependencies]
4647
env_logger = "0.11"
4748
futures-util = { version = "0.3", features = ["io"] }
4849
piper = "0.2"
49-
pretty_assertions = "1.4.1"
50-
rustyline = "17.0.1"
51-
tokio-util = { version = "0.7.16", features = ["compat"] }
52-
tokio = { version = "1.0", features = [
50+
pretty_assertions = "1"
51+
rustyline = "17"
52+
tokio = { version = "1", features = [
5353
"macros",
5454
"rt",
5555
"time",
@@ -59,3 +59,4 @@ tokio = { version = "1.0", features = [
5959
"process",
6060
"sync",
6161
] }
62+
tokio-util = { version = "0.7", features = ["compat"] }

rust/acp.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ impl ClientSideConnection {
166166
}
167167
}
168168

169+
#[async_trait::async_trait(?Send)]
169170
impl Agent for ClientSideConnection {
170171
async fn initialize(&self, args: InitializeRequest) -> Result<InitializeResponse, Error> {
171172
self.conn
@@ -441,6 +442,7 @@ impl AgentSideConnection {
441442
}
442443
}
443444

445+
#[async_trait::async_trait(?Send)]
444446
impl Client for AgentSideConnection {
445447
async fn request_permission(
446448
&self,

rust/agent.rs

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::{
2020
///
2121
/// Agents are programs that use generative AI to autonomously modify code. They handle
2222
/// requests from clients and execute tasks using language models and tools.
23+
#[async_trait::async_trait(?Send)]
2324
pub trait Agent {
2425
/// Establishes the connection with a client and negotiates protocol capabilities.
2526
///
@@ -31,10 +32,7 @@ pub trait Agent {
3132
/// The agent should respond with its supported protocol version and capabilities.
3233
///
3334
/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
34-
fn initialize(
35-
&self,
36-
args: InitializeRequest,
37-
) -> impl Future<Output = Result<InitializeResponse, Error>>;
35+
async fn initialize(&self, args: InitializeRequest) -> Result<InitializeResponse, Error>;
3836

3937
/// Authenticates the client using the specified authentication method.
4038
///
@@ -45,10 +43,7 @@ pub trait Agent {
4543
/// `new_session` without receiving an `auth_required` error.
4644
///
4745
/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
48-
fn authenticate(
49-
&self,
50-
args: AuthenticateRequest,
51-
) -> impl Future<Output = Result<AuthenticateResponse, Error>>;
46+
async fn authenticate(&self, args: AuthenticateRequest) -> Result<AuthenticateResponse, Error>;
5247

5348
/// Creates a new conversation session with the agent.
5449
///
@@ -62,10 +57,7 @@ pub trait Agent {
6257
/// May return an `auth_required` error if the agent requires authentication.
6358
///
6459
/// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup)
65-
fn new_session(
66-
&self,
67-
args: NewSessionRequest,
68-
) -> impl Future<Output = Result<NewSessionResponse, Error>>;
60+
async fn new_session(&self, args: NewSessionRequest) -> Result<NewSessionResponse, Error>;
6961

7062
/// Loads an existing session to resume a previous conversation.
7163
///
@@ -77,10 +69,7 @@ pub trait Agent {
7769
/// - Stream the entire conversation history back to the client via notifications
7870
///
7971
/// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions)
80-
fn load_session(
81-
&self,
82-
args: LoadSessionRequest,
83-
) -> impl Future<Output = Result<LoadSessionResponse, Error>>;
72+
async fn load_session(&self, args: LoadSessionRequest) -> Result<LoadSessionResponse, Error>;
8473

8574
/// Sets the current mode for a session.
8675
///
@@ -95,10 +84,10 @@ pub trait Agent {
9584
/// idle or actively generating a response.
9685
///
9786
/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
98-
fn set_session_mode(
87+
async fn set_session_mode(
9988
&self,
10089
args: SetSessionModeRequest,
101-
) -> impl Future<Output = Result<SetSessionModeResponse, Error>>;
90+
) -> Result<SetSessionModeResponse, Error>;
10291

10392
/// Processes a user prompt within a session.
10493
///
@@ -111,7 +100,7 @@ pub trait Agent {
111100
/// - Returns when the turn is complete with a stop reason
112101
///
113102
/// See protocol docs: [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn)
114-
fn prompt(&self, args: PromptRequest) -> impl Future<Output = Result<PromptResponse, Error>>;
103+
async fn prompt(&self, args: PromptRequest) -> Result<PromptResponse, Error>;
115104

116105
/// Cancels ongoing operations for a session.
117106
///
@@ -124,23 +113,23 @@ pub trait Agent {
124113
/// - Respond to the original `session/prompt` request with `StopReason::Cancelled`
125114
///
126115
/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
127-
fn cancel(&self, args: CancelNotification) -> impl Future<Output = Result<(), Error>>;
116+
async fn cancel(&self, args: CancelNotification) -> Result<(), Error>;
128117

129118
/// Handles extension method requests from the client.
130119
///
131120
/// Extension methods provide a way to add custom functionality while maintaining
132121
/// protocol compatibility.
133122
///
134123
/// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
135-
fn ext_method(&self, args: ExtRequest) -> impl Future<Output = Result<ExtResponse, Error>>;
124+
async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error>;
136125

137126
/// Handles extension notifications from the client.
138127
///
139128
/// Extension notifications provide a way to send one-way messages for custom functionality
140129
/// while maintaining protocol compatibility.
141130
///
142131
/// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
143-
fn ext_notification(&self, args: ExtNotification) -> impl Future<Output = Result<(), Error>>;
132+
async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error>;
144133
}
145134

146135
// Initialize

rust/client.rs

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::{ExtResponse, SessionModeId};
1919
/// Clients are typically code editors (IDEs, text editors) that provide the interface
2020
/// between users and AI agents. They manage the environment, handle user interactions,
2121
/// and control access to resources.
22+
#[async_trait::async_trait(?Send)]
2223
pub trait Client {
2324
/// Requests permission from the user for a tool call operation.
2425
///
@@ -30,32 +31,32 @@ pub trait Client {
3031
/// respond to this request with `RequestPermissionOutcome::Cancelled`.
3132
///
3233
/// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
33-
fn request_permission(
34+
async fn request_permission(
3435
&self,
3536
args: RequestPermissionRequest,
36-
) -> impl Future<Output = Result<RequestPermissionResponse, Error>>;
37+
) -> Result<RequestPermissionResponse, Error>;
3738

3839
/// Writes content to a text file in the client's file system.
3940
///
4041
/// Only available if the client advertises the `fs.writeTextFile` capability.
4142
/// Allows the agent to create or modify files within the client's environment.
4243
///
4344
/// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
44-
fn write_text_file(
45+
async fn write_text_file(
4546
&self,
4647
args: WriteTextFileRequest,
47-
) -> impl Future<Output = Result<WriteTextFileResponse, Error>>;
48+
) -> Result<WriteTextFileResponse, Error>;
4849

4950
/// Reads content from a text file in the client's file system.
5051
///
5152
/// Only available if the client advertises the `fs.readTextFile` capability.
5253
/// Allows the agent to access file contents within the client's environment.
5354
///
5455
/// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
55-
fn read_text_file(
56+
async fn read_text_file(
5657
&self,
5758
args: ReadTextFileRequest,
58-
) -> impl Future<Output = Result<ReadTextFileResponse, Error>>;
59+
) -> Result<ReadTextFileResponse, Error>;
5960

6061
/// Handles session update notifications from the agent.
6162
///
@@ -68,10 +69,7 @@ pub trait Client {
6869
/// updates before responding with the cancelled stop reason.
6970
///
7071
/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
71-
fn session_notification(
72-
&self,
73-
args: SessionNotification,
74-
) -> impl Future<Output = Result<(), Error>>;
72+
async fn session_notification(&self, args: SessionNotification) -> Result<(), Error>;
7573

7674
/// Executes a command in a new terminal
7775
///
@@ -87,21 +85,21 @@ pub trait Client {
8785
/// method.
8886
///
8987
/// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
90-
fn create_terminal(
88+
async fn create_terminal(
9189
&self,
9290
args: CreateTerminalRequest,
93-
) -> impl Future<Output = Result<CreateTerminalResponse, Error>>;
91+
) -> Result<CreateTerminalResponse, Error>;
9492

9593
/// Gets the terminal output and exit status
9694
///
9795
/// Returns the current content in the terminal without waiting for the command to exit.
9896
/// If the command has already exited, the exit status is included.
9997
///
10098
/// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
101-
fn terminal_output(
99+
async fn terminal_output(
102100
&self,
103101
args: TerminalOutputRequest,
104-
) -> impl Future<Output = Result<TerminalOutputResponse, Error>>;
102+
) -> Result<TerminalOutputResponse, Error>;
105103

106104
/// Releases a terminal
107105
///
@@ -115,18 +113,18 @@ pub trait Client {
115113
/// the terminal, allowing the Agent to call `terminal/output` and other methods.
116114
///
117115
/// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
118-
fn release_terminal(
116+
async fn release_terminal(
119117
&self,
120118
args: ReleaseTerminalRequest,
121-
) -> impl Future<Output = Result<ReleaseTerminalResponse, Error>>;
119+
) -> Result<ReleaseTerminalResponse, Error>;
122120

123121
/// Waits for the terminal command to exit and return its exit status
124122
///
125123
/// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
126-
fn wait_for_terminal_exit(
124+
async fn wait_for_terminal_exit(
127125
&self,
128126
args: WaitForTerminalExitRequest,
129-
) -> impl Future<Output = Result<WaitForTerminalExitResponse, Error>>;
127+
) -> Result<WaitForTerminalExitResponse, Error>;
130128

131129
/// Kills the terminal command without releasing the terminal
132130
///
@@ -140,10 +138,10 @@ pub trait Client {
140138
/// Note: `terminal/release` when `TerminalId` is no longer needed.
141139
///
142140
/// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
143-
fn kill_terminal_command(
141+
async fn kill_terminal_command(
144142
&self,
145143
args: KillTerminalCommandRequest,
146-
) -> impl Future<Output = Result<KillTerminalCommandResponse, Error>>;
144+
) -> Result<KillTerminalCommandResponse, Error>;
147145

148146
/// Handles extension method requests from the agent.
149147
///
@@ -152,7 +150,7 @@ pub trait Client {
152150
/// protocol compatibility.
153151
///
154152
/// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
155-
fn ext_method(&self, args: ExtRequest) -> impl Future<Output = Result<ExtResponse, Error>>;
153+
async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error>;
156154

157155
/// Handles extension notifications from the agent.
158156
///
@@ -161,7 +159,7 @@ pub trait Client {
161159
/// while maintaining protocol compatibility.
162160
///
163161
/// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
164-
fn ext_notification(&self, args: ExtNotification) -> impl Future<Output = Result<(), Error>>;
162+
async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error>;
165163
}
166164

167165
// Session updates

rust/example_agent.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ impl ExampleAgent {
3838
}
3939
}
4040

41+
#[async_trait::async_trait(?Send)]
4142
impl acp::Agent for ExampleAgent {
4243
async fn initialize(
4344
&self,

rust/example_client.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
2020

2121
struct ExampleClient {}
2222

23+
#[async_trait::async_trait(?Send)]
2324
impl acp::Client for ExampleClient {
2425
async fn request_permission(
2526
&self,

rust/rpc_tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ macro_rules! raw_json {
4040
}};
4141
}
4242

43+
#[async_trait::async_trait(?Send)]
4344
impl Client for TestClient {
4445
async fn request_permission(
4546
&self,
@@ -162,6 +163,7 @@ impl TestAgent {
162163
}
163164
}
164165

166+
#[async_trait::async_trait(?Send)]
165167
impl Agent for TestAgent {
166168
async fn initialize(&self, arguments: InitializeRequest) -> Result<InitializeResponse, Error> {
167169
Ok(InitializeResponse {

0 commit comments

Comments
 (0)