|
3 | 3 | //! This module defines the Agent trait and all associated types for implementing |
4 | 4 | //! an AI coding agent that follows the Agent Client Protocol (ACP). |
5 | 5 |
|
6 | | -use std::rc::Rc; |
7 | 6 | use std::{path::PathBuf, sync::Arc}; |
8 | 7 |
|
9 | | -use anyhow::Result; |
10 | 8 | use schemars::JsonSchema; |
11 | 9 | use serde::{Deserialize, Serialize}; |
12 | 10 | use serde_json::value::RawValue; |
13 | 11 |
|
14 | 12 | use crate::ext::ExtRequest; |
15 | | -use crate::{ |
16 | | - ClientCapabilities, ContentBlock, Error, ExtNotification, ExtResponse, ProtocolVersion, |
17 | | - SessionId, |
18 | | -}; |
19 | | - |
20 | | -/// Defines the interface that all ACP-compliant agents must implement. |
21 | | -/// |
22 | | -/// Agents are programs that use generative AI to autonomously modify code. They handle |
23 | | -/// requests from clients and execute tasks using language models and tools. |
24 | | -#[async_trait::async_trait(?Send)] |
25 | | -pub trait Agent { |
26 | | - /// Establishes the connection with a client and negotiates protocol capabilities. |
27 | | - /// |
28 | | - /// This method is called once at the beginning of the connection to: |
29 | | - /// - Negotiate the protocol version to use |
30 | | - /// - Exchange capability information between client and agent |
31 | | - /// - Determine available authentication methods |
32 | | - /// |
33 | | - /// The agent should respond with its supported protocol version and capabilities. |
34 | | - /// |
35 | | - /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization) |
36 | | - async fn initialize(&self, args: InitializeRequest) -> Result<InitializeResponse, Error>; |
37 | | - |
38 | | - /// Authenticates the client using the specified authentication method. |
39 | | - /// |
40 | | - /// Called when the agent requires authentication before allowing session creation. |
41 | | - /// The client provides the authentication method ID that was advertised during initialization. |
42 | | - /// |
43 | | - /// After successful authentication, the client can proceed to create sessions with |
44 | | - /// `new_session` without receiving an `auth_required` error. |
45 | | - /// |
46 | | - /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization) |
47 | | - async fn authenticate(&self, args: AuthenticateRequest) -> Result<AuthenticateResponse, Error>; |
48 | | - |
49 | | - /// Creates a new conversation session with the agent. |
50 | | - /// |
51 | | - /// Sessions represent independent conversation contexts with their own history and state. |
52 | | - /// |
53 | | - /// The agent should: |
54 | | - /// - Create a new session context |
55 | | - /// - Connect to any specified MCP servers |
56 | | - /// - Return a unique session ID for future requests |
57 | | - /// |
58 | | - /// May return an `auth_required` error if the agent requires authentication. |
59 | | - /// |
60 | | - /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup) |
61 | | - async fn new_session(&self, args: NewSessionRequest) -> Result<NewSessionResponse, Error>; |
62 | | - |
63 | | - /// Processes a user prompt within a session. |
64 | | - /// |
65 | | - /// This method handles the whole lifecycle of a prompt: |
66 | | - /// - Receives user messages with optional context (files, images, etc.) |
67 | | - /// - Processes the prompt using language models |
68 | | - /// - Reports language model content and tool calls to the Clients |
69 | | - /// - Requests permission to run tools |
70 | | - /// - Executes any requested tool calls |
71 | | - /// - Returns when the turn is complete with a stop reason |
72 | | - /// |
73 | | - /// See protocol docs: [Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn) |
74 | | - async fn prompt(&self, args: PromptRequest) -> Result<PromptResponse, Error>; |
75 | | - |
76 | | - /// Cancels ongoing operations for a session. |
77 | | - /// |
78 | | - /// This is a notification sent by the client to cancel an ongoing prompt turn. |
79 | | - /// |
80 | | - /// Upon receiving this notification, the Agent SHOULD: |
81 | | - /// - Stop all language model requests as soon as possible |
82 | | - /// - Abort all tool call invocations in progress |
83 | | - /// - Send any pending `session/update` notifications |
84 | | - /// - Respond to the original `session/prompt` request with `StopReason::Cancelled` |
85 | | - /// |
86 | | - /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation) |
87 | | - async fn cancel(&self, args: CancelNotification) -> Result<(), Error>; |
88 | | - |
89 | | - /// Loads an existing session to resume a previous conversation. |
90 | | - /// |
91 | | - /// This method is only available if the agent advertises the `loadSession` capability. |
92 | | - /// |
93 | | - /// The agent should: |
94 | | - /// - Restore the session context and conversation history |
95 | | - /// - Connect to the specified MCP servers |
96 | | - /// - Stream the entire conversation history back to the client via notifications |
97 | | - /// |
98 | | - /// See protocol docs: [Loading Sessions](https://agentclientprotocol.com/protocol/session-setup#loading-sessions) |
99 | | - async fn load_session(&self, _args: LoadSessionRequest) -> Result<LoadSessionResponse, Error> { |
100 | | - Err(Error::method_not_found()) |
101 | | - } |
102 | | - |
103 | | - /// Sets the current mode for a session. |
104 | | - /// |
105 | | - /// Allows switching between different agent modes (e.g., "ask", "architect", "code") |
106 | | - /// that affect system prompts, tool availability, and permission behaviors. |
107 | | - /// |
108 | | - /// The mode must be one of the modes advertised in `availableModes` during session |
109 | | - /// creation or loading. Agents may also change modes autonomously and notify the |
110 | | - /// client via `current_mode_update` notifications. |
111 | | - /// |
112 | | - /// This method can be called at any time during a session, whether the Agent is |
113 | | - /// idle or actively generating a response. |
114 | | - /// |
115 | | - /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes) |
116 | | - async fn set_session_mode( |
117 | | - &self, |
118 | | - _args: SetSessionModeRequest, |
119 | | - ) -> Result<SetSessionModeResponse, Error> { |
120 | | - Err(Error::method_not_found()) |
121 | | - } |
122 | | - |
123 | | - /// **UNSTABLE** |
124 | | - /// |
125 | | - /// This capability is not part of the spec yet, and may be removed or changed at any point. |
126 | | - /// |
127 | | - /// Select a model for a given session. |
128 | | - #[cfg(feature = "unstable")] |
129 | | - async fn set_session_model( |
130 | | - &self, |
131 | | - _args: SetSessionModelRequest, |
132 | | - ) -> Result<SetSessionModelResponse, Error> { |
133 | | - Err(Error::method_not_found()) |
134 | | - } |
135 | | - |
136 | | - /// Handles extension method requests from the client. |
137 | | - /// |
138 | | - /// Extension methods provide a way to add custom functionality while maintaining |
139 | | - /// protocol compatibility. |
140 | | - /// |
141 | | - /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) |
142 | | - async fn ext_method(&self, _args: ExtRequest) -> Result<ExtResponse, Error> { |
143 | | - Ok(RawValue::NULL.to_owned().into()) |
144 | | - } |
145 | | - |
146 | | - /// Handles extension notifications from the client. |
147 | | - /// |
148 | | - /// Extension notifications provide a way to send one-way messages for custom functionality |
149 | | - /// while maintaining protocol compatibility. |
150 | | - /// |
151 | | - /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) |
152 | | - async fn ext_notification(&self, _args: ExtNotification) -> Result<(), Error> { |
153 | | - Ok(()) |
154 | | - } |
155 | | -} |
156 | | - |
157 | | -#[async_trait::async_trait(?Send)] |
158 | | -impl<T: Agent> Agent for Rc<T> { |
159 | | - async fn initialize(&self, args: InitializeRequest) -> Result<InitializeResponse, Error> { |
160 | | - self.as_ref().initialize(args).await |
161 | | - } |
162 | | - async fn authenticate(&self, args: AuthenticateRequest) -> Result<AuthenticateResponse, Error> { |
163 | | - self.as_ref().authenticate(args).await |
164 | | - } |
165 | | - async fn new_session(&self, args: NewSessionRequest) -> Result<NewSessionResponse, Error> { |
166 | | - self.as_ref().new_session(args).await |
167 | | - } |
168 | | - async fn load_session(&self, args: LoadSessionRequest) -> Result<LoadSessionResponse, Error> { |
169 | | - self.as_ref().load_session(args).await |
170 | | - } |
171 | | - async fn set_session_mode( |
172 | | - &self, |
173 | | - args: SetSessionModeRequest, |
174 | | - ) -> Result<SetSessionModeResponse, Error> { |
175 | | - self.as_ref().set_session_mode(args).await |
176 | | - } |
177 | | - async fn prompt(&self, args: PromptRequest) -> Result<PromptResponse, Error> { |
178 | | - self.as_ref().prompt(args).await |
179 | | - } |
180 | | - async fn cancel(&self, args: CancelNotification) -> Result<(), Error> { |
181 | | - self.as_ref().cancel(args).await |
182 | | - } |
183 | | - #[cfg(feature = "unstable")] |
184 | | - async fn set_session_model( |
185 | | - &self, |
186 | | - args: SetSessionModelRequest, |
187 | | - ) -> Result<SetSessionModelResponse, Error> { |
188 | | - self.as_ref().set_session_model(args).await |
189 | | - } |
190 | | - async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error> { |
191 | | - self.as_ref().ext_method(args).await |
192 | | - } |
193 | | - async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error> { |
194 | | - self.as_ref().ext_notification(args).await |
195 | | - } |
196 | | -} |
197 | | - |
198 | | -#[async_trait::async_trait(?Send)] |
199 | | -impl<T: Agent> Agent for Arc<T> { |
200 | | - async fn initialize(&self, args: InitializeRequest) -> Result<InitializeResponse, Error> { |
201 | | - self.as_ref().initialize(args).await |
202 | | - } |
203 | | - async fn authenticate(&self, args: AuthenticateRequest) -> Result<AuthenticateResponse, Error> { |
204 | | - self.as_ref().authenticate(args).await |
205 | | - } |
206 | | - async fn new_session(&self, args: NewSessionRequest) -> Result<NewSessionResponse, Error> { |
207 | | - self.as_ref().new_session(args).await |
208 | | - } |
209 | | - async fn load_session(&self, args: LoadSessionRequest) -> Result<LoadSessionResponse, Error> { |
210 | | - self.as_ref().load_session(args).await |
211 | | - } |
212 | | - async fn set_session_mode( |
213 | | - &self, |
214 | | - args: SetSessionModeRequest, |
215 | | - ) -> Result<SetSessionModeResponse, Error> { |
216 | | - self.as_ref().set_session_mode(args).await |
217 | | - } |
218 | | - async fn prompt(&self, args: PromptRequest) -> Result<PromptResponse, Error> { |
219 | | - self.as_ref().prompt(args).await |
220 | | - } |
221 | | - async fn cancel(&self, args: CancelNotification) -> Result<(), Error> { |
222 | | - self.as_ref().cancel(args).await |
223 | | - } |
224 | | - #[cfg(feature = "unstable")] |
225 | | - async fn set_session_model( |
226 | | - &self, |
227 | | - args: SetSessionModelRequest, |
228 | | - ) -> Result<SetSessionModelResponse, Error> { |
229 | | - self.as_ref().set_session_model(args).await |
230 | | - } |
231 | | - async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error> { |
232 | | - self.as_ref().ext_method(args).await |
233 | | - } |
234 | | - async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error> { |
235 | | - self.as_ref().ext_notification(args).await |
236 | | - } |
237 | | -} |
| 13 | +use crate::{ClientCapabilities, ContentBlock, ExtNotification, ProtocolVersion, SessionId}; |
238 | 14 |
|
239 | 15 | // Initialize |
240 | 16 |
|
|
0 commit comments