agent_client_protocol/
client.rs

1//! Methods and notifications the client handles/receives.
2//!
3//! This module defines the Client trait and all associated types for implementing
4//! a client that interacts with AI coding agents via the Agent Client Protocol (ACP).
5
6use std::{fmt, path::PathBuf, sync::Arc};
7
8use anyhow::Result;
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11use serde_json::value::RawValue;
12
13use crate::ext::ExtRequest;
14use crate::{ContentBlock, Error, ExtNotification, Plan, SessionId, ToolCall, ToolCallUpdate};
15use crate::{ExtResponse, SessionModeId};
16
17/// Defines the interface that ACP-compliant clients must implement.
18///
19/// Clients are typically code editors (IDEs, text editors) that provide the interface
20/// between users and AI agents. They manage the environment, handle user interactions,
21/// and control access to resources.
22#[async_trait::async_trait(?Send)]
23pub trait Client {
24    /// Requests permission from the user for a tool call operation.
25    ///
26    /// Called by the agent when it needs user authorization before executing
27    /// a potentially sensitive operation. The client should present the options
28    /// to the user and return their decision.
29    ///
30    /// If the client cancels the prompt turn via `session/cancel`, it MUST
31    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
32    ///
33    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
34    async fn request_permission(
35        &self,
36        args: RequestPermissionRequest,
37    ) -> Result<RequestPermissionResponse, Error>;
38
39    /// Writes content to a text file in the client's file system.
40    ///
41    /// Only available if the client advertises the `fs.writeTextFile` capability.
42    /// Allows the agent to create or modify files within the client's environment.
43    ///
44    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
45    async fn write_text_file(
46        &self,
47        args: WriteTextFileRequest,
48    ) -> Result<WriteTextFileResponse, Error>;
49
50    /// Reads content from a text file in the client's file system.
51    ///
52    /// Only available if the client advertises the `fs.readTextFile` capability.
53    /// Allows the agent to access file contents within the client's environment.
54    ///
55    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
56    async fn read_text_file(
57        &self,
58        args: ReadTextFileRequest,
59    ) -> Result<ReadTextFileResponse, Error>;
60
61    /// Handles session update notifications from the agent.
62    ///
63    /// This is a notification endpoint (no response expected) that receives
64    /// real-time updates about session progress, including message chunks,
65    /// tool calls, and execution plans.
66    ///
67    /// Note: Clients SHOULD continue accepting tool call updates even after
68    /// sending a `session/cancel` notification, as the agent may send final
69    /// updates before responding with the cancelled stop reason.
70    ///
71    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
72    async fn session_notification(&self, args: SessionNotification) -> Result<(), Error>;
73
74    /// Executes a command in a new terminal
75    ///
76    /// Only available if the `terminal` Client capability is set to `true`.
77    ///
78    /// Returns a `TerminalId` that can be used with other terminal methods
79    /// to get the current output, wait for exit, and kill the command.
80    ///
81    /// The `TerminalId` can also be used to embed the terminal in a tool call
82    /// by using the `ToolCallContent::Terminal` variant.
83    ///
84    /// The Agent is responsible for releasing the terminal by using the `terminal/release`
85    /// method.
86    ///
87    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
88    async fn create_terminal(
89        &self,
90        args: CreateTerminalRequest,
91    ) -> Result<CreateTerminalResponse, Error>;
92
93    /// Gets the terminal output and exit status
94    ///
95    /// Returns the current content in the terminal without waiting for the command to exit.
96    /// If the command has already exited, the exit status is included.
97    ///
98    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
99    async fn terminal_output(
100        &self,
101        args: TerminalOutputRequest,
102    ) -> Result<TerminalOutputResponse, Error>;
103
104    /// Releases a terminal
105    ///
106    /// The command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`
107    /// to wait for the command to exit before releasing the terminal.
108    ///
109    /// After release, the `TerminalId` can no longer be used with other `terminal/*` methods,
110    /// but tool calls that already contain it, continue to display its output.
111    ///
112    /// The `terminal/kill` method can be used to terminate the command without releasing
113    /// the terminal, allowing the Agent to call `terminal/output` and other methods.
114    ///
115    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
116    async fn release_terminal(
117        &self,
118        args: ReleaseTerminalRequest,
119    ) -> Result<ReleaseTerminalResponse, Error>;
120
121    /// Waits for the terminal command to exit and return its exit status
122    ///
123    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
124    async fn wait_for_terminal_exit(
125        &self,
126        args: WaitForTerminalExitRequest,
127    ) -> Result<WaitForTerminalExitResponse, Error>;
128
129    /// Kills the terminal command without releasing the terminal
130    ///
131    /// While `terminal/release` will also kill the command, this method will keep
132    /// the `TerminalId` valid so it can be used with other methods.
133    ///
134    /// This method can be helpful when implementing command timeouts which terminate
135    /// the command as soon as elapsed, and then get the final output so it can be sent
136    /// to the model.
137    ///
138    /// Note: `terminal/release` when `TerminalId` is no longer needed.
139    ///
140    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
141    async fn kill_terminal_command(
142        &self,
143        args: KillTerminalCommandRequest,
144    ) -> Result<KillTerminalCommandResponse, Error>;
145
146    /// Handles extension method requests from the agent.
147    ///
148    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
149    /// Extension methods provide a way to add custom functionality while maintaining
150    /// protocol compatibility.
151    ///
152    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
153    async fn ext_method(&self, args: ExtRequest) -> Result<ExtResponse, Error>;
154
155    /// Handles extension notifications from the agent.
156    ///
157    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
158    /// Extension notifications provide a way to send one-way messages for custom functionality
159    /// while maintaining protocol compatibility.
160    ///
161    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
162    async fn ext_notification(&self, args: ExtNotification) -> Result<(), Error>;
163}
164
165// Session updates
166
167/// Notification containing a session update from the agent.
168///
169/// Used to stream real-time progress and results during prompt processing.
170///
171/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
172#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
173#[schemars(extend("x-side" = "client", "x-method" = SESSION_UPDATE_NOTIFICATION))]
174#[serde(rename_all = "camelCase")]
175pub struct SessionNotification {
176    /// The ID of the session this update pertains to.
177    pub session_id: SessionId,
178    /// The actual update content.
179    pub update: SessionUpdate,
180    /// Extension point for implementations
181    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
182    pub meta: Option<serde_json::Value>,
183}
184
185/// Different types of updates that can be sent during session processing.
186///
187/// These updates provide real-time feedback about the agent's progress.
188///
189/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
190#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
191#[serde(rename_all = "snake_case", tag = "sessionUpdate")]
192pub enum SessionUpdate {
193    /// A chunk of the user's message being streamed.
194    UserMessageChunk { content: ContentBlock },
195    /// A chunk of the agent's response being streamed.
196    AgentMessageChunk { content: ContentBlock },
197    /// A chunk of the agent's internal reasoning being streamed.
198    AgentThoughtChunk { content: ContentBlock },
199    /// Notification that a new tool call has been initiated.
200    ToolCall(ToolCall),
201    /// Update on the status or results of a tool call.
202    ToolCallUpdate(ToolCallUpdate),
203    /// The agent's execution plan for complex tasks.
204    /// See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)
205    Plan(Plan),
206    /// Available commands are ready or have changed
207    #[serde(rename_all = "camelCase")]
208    AvailableCommandsUpdate {
209        available_commands: Vec<AvailableCommand>,
210    },
211    /// The current mode of the session has changed
212    ///
213    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
214    #[serde(rename_all = "camelCase")]
215    CurrentModeUpdate { current_mode_id: SessionModeId },
216}
217
218/// Information about a command.
219#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
220#[serde(rename_all = "camelCase")]
221pub struct AvailableCommand {
222    /// Command name (e.g., `create_plan`, `research_codebase`).
223    pub name: String,
224    /// Human-readable description of what the command does.
225    pub description: String,
226    /// Input for the command if required
227    pub input: Option<AvailableCommandInput>,
228    /// Extension point for implementations
229    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
230    pub meta: Option<serde_json::Value>,
231}
232
233/// The input specification for a command.
234#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
235#[serde(untagged, rename_all = "camelCase")]
236pub enum AvailableCommandInput {
237    /// All text that was typed after the command name is provided as input.
238    #[schemars(rename = "UnstructuredCommandInput")]
239    Unstructured {
240        /// A hint to display when the input hasn't been provided yet
241        hint: String,
242    },
243}
244
245// Permission
246
247/// Request for user permission to execute a tool call.
248///
249/// Sent when the agent needs authorization before performing a sensitive operation.
250///
251/// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
252#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
253#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
254#[serde(rename_all = "camelCase")]
255pub struct RequestPermissionRequest {
256    /// The session ID for this request.
257    pub session_id: SessionId,
258    /// Details about the tool call requiring permission.
259    pub tool_call: ToolCallUpdate,
260    /// Available permission options for the user to choose from.
261    pub options: Vec<PermissionOption>,
262    /// Extension point for implementations
263    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
264    pub meta: Option<serde_json::Value>,
265}
266
267/// An option presented to the user when requesting permission.
268#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
269pub struct PermissionOption {
270    /// Unique identifier for this permission option.
271    #[serde(rename = "optionId")]
272    pub id: PermissionOptionId,
273    /// Human-readable label to display to the user.
274    pub name: String,
275    /// Hint about the nature of this permission option.
276    pub kind: PermissionOptionKind,
277    /// Extension point for implementations
278    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
279    pub meta: Option<serde_json::Value>,
280}
281
282/// Unique identifier for a permission option.
283#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
284#[serde(transparent)]
285pub struct PermissionOptionId(pub Arc<str>);
286
287impl fmt::Display for PermissionOptionId {
288    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289        self.0.fmt(f)
290    }
291}
292
293/// The type of permission option being presented to the user.
294///
295/// Helps clients choose appropriate icons and UI treatment.
296#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
297#[serde(rename_all = "snake_case")]
298pub enum PermissionOptionKind {
299    /// Allow this operation only this time.
300    AllowOnce,
301    /// Allow this operation and remember the choice.
302    AllowAlways,
303    /// Reject this operation only this time.
304    RejectOnce,
305    /// Reject this operation and remember the choice.
306    RejectAlways,
307}
308
309/// Response to a permission request.
310#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
311#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
312#[serde(rename_all = "camelCase")]
313pub struct RequestPermissionResponse {
314    /// The user's decision on the permission request.
315    // This extra-level is unfortunately needed because the output must be an object
316    pub outcome: RequestPermissionOutcome,
317    /// Extension point for implementations
318    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
319    pub meta: Option<serde_json::Value>,
320}
321
322/// The outcome of a permission request.
323#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
324#[serde(tag = "outcome", rename_all = "snake_case")]
325pub enum RequestPermissionOutcome {
326    /// The prompt turn was cancelled before the user responded.
327    ///
328    /// When a client sends a `session/cancel` notification to cancel an ongoing
329    /// prompt turn, it MUST respond to all pending `session/request_permission`
330    /// requests with this `Cancelled` outcome.
331    ///
332    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
333    Cancelled,
334    /// The user selected one of the provided options.
335    #[serde(rename_all = "camelCase")]
336    Selected {
337        /// The ID of the option the user selected.
338        option_id: PermissionOptionId,
339    },
340}
341
342// Write text file
343
344/// Request to write content to a text file.
345///
346/// Only available if the client supports the `fs.writeTextFile` capability.
347#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
348#[schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME))]
349#[serde(rename_all = "camelCase")]
350pub struct WriteTextFileRequest {
351    /// The session ID for this request.
352    pub session_id: SessionId,
353    /// Absolute path to the file to write.
354    pub path: PathBuf,
355    /// The text content to write to the file.
356    pub content: String,
357    /// Extension point for implementations
358    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
359    pub meta: Option<serde_json::Value>,
360}
361
362/// Response to `fs/write_text_file`
363#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
364#[serde(rename_all = "camelCase")]
365#[schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME))]
366#[serde(default)]
367pub struct WriteTextFileResponse {
368    /// Extension point for implementations
369    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
370    pub meta: Option<serde_json::Value>,
371}
372
373// Read text file
374
375/// Request to read content from a text file.
376///
377/// Only available if the client supports the `fs.readTextFile` capability.
378#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
379#[schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME))]
380#[serde(rename_all = "camelCase")]
381pub struct ReadTextFileRequest {
382    /// The session ID for this request.
383    pub session_id: SessionId,
384    /// Absolute path to the file to read.
385    pub path: PathBuf,
386    /// Line number to start reading from (1-based).
387    #[serde(default, skip_serializing_if = "Option::is_none")]
388    pub line: Option<u32>,
389    /// Maximum number of lines to read.
390    #[serde(default, skip_serializing_if = "Option::is_none")]
391    pub limit: Option<u32>,
392    /// Extension point for implementations
393    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
394    pub meta: Option<serde_json::Value>,
395}
396
397/// Response containing the contents of a text file.
398#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
399#[schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME))]
400#[serde(rename_all = "camelCase")]
401pub struct ReadTextFileResponse {
402    pub content: String,
403    /// Extension point for implementations
404    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
405    pub meta: Option<serde_json::Value>,
406}
407
408// Terminals
409
410#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash)]
411#[serde(transparent)]
412pub struct TerminalId(pub Arc<str>);
413
414impl std::fmt::Display for TerminalId {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        write!(f, "{}", self.0)
417    }
418}
419
420/// Request to create a new terminal and execute a command.
421#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
422#[serde(rename_all = "camelCase")]
423#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME))]
424pub struct CreateTerminalRequest {
425    /// The session ID for this request.
426    pub session_id: SessionId,
427    /// The command to execute.
428    pub command: String,
429    /// Array of command arguments.
430    #[serde(default, skip_serializing_if = "Vec::is_empty")]
431    pub args: Vec<String>,
432    /// Environment variables for the command.
433    #[serde(default, skip_serializing_if = "Vec::is_empty")]
434    pub env: Vec<crate::EnvVariable>,
435    /// Working directory for the command (absolute path).
436    #[serde(default, skip_serializing_if = "Option::is_none")]
437    pub cwd: Option<PathBuf>,
438    /// Maximum number of output bytes to retain.
439    ///
440    /// When the limit is exceeded, the Client truncates from the beginning of the output
441    /// to stay within the limit.
442    ///
443    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
444    /// string output, even if this means the retained output is slightly less than the
445    /// specified limit.
446    #[serde(default, skip_serializing_if = "Option::is_none")]
447    pub output_byte_limit: Option<u64>,
448    /// Extension point for implementations
449    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
450    pub meta: Option<serde_json::Value>,
451}
452
453/// Response containing the ID of the created terminal.
454#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
455#[serde(rename_all = "camelCase")]
456#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME))]
457pub struct CreateTerminalResponse {
458    /// The unique identifier for the created terminal.
459    pub terminal_id: TerminalId,
460    /// Extension point for implementations
461    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
462    pub meta: Option<serde_json::Value>,
463}
464
465/// Request to get the current output and status of a terminal.
466#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
467#[serde(rename_all = "camelCase")]
468#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME))]
469pub struct TerminalOutputRequest {
470    /// The session ID for this request.
471    pub session_id: SessionId,
472    /// The ID of the terminal to get output from.
473    pub terminal_id: TerminalId,
474    /// Extension point for implementations
475    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
476    pub meta: Option<serde_json::Value>,
477}
478
479/// Response containing the terminal output and exit status.
480#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
481#[serde(rename_all = "camelCase")]
482#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME))]
483pub struct TerminalOutputResponse {
484    /// The terminal output captured so far.
485    pub output: String,
486    /// Whether the output was truncated due to byte limits.
487    pub truncated: bool,
488    /// Exit status if the command has completed.
489    pub exit_status: Option<TerminalExitStatus>,
490    /// Extension point for implementations
491    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
492    pub meta: Option<serde_json::Value>,
493}
494
495/// Request to release a terminal and free its resources.
496#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
497#[serde(rename_all = "camelCase")]
498#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME))]
499pub struct ReleaseTerminalRequest {
500    /// The session ID for this request.
501    pub session_id: SessionId,
502    /// The ID of the terminal to release.
503    pub terminal_id: TerminalId,
504    /// Extension point for implementations
505    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
506    pub meta: Option<serde_json::Value>,
507}
508
509/// Response to terminal/release method
510#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
511#[serde(rename_all = "camelCase")]
512#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME))]
513pub struct ReleaseTerminalResponse {
514    /// Extension point for implementations
515    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
516    pub meta: Option<serde_json::Value>,
517}
518
519/// Request to kill a terminal command without releasing the terminal.
520#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
521#[serde(rename_all = "camelCase")]
522#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME))]
523pub struct KillTerminalCommandRequest {
524    /// The session ID for this request.
525    pub session_id: SessionId,
526    /// The ID of the terminal to kill.
527    pub terminal_id: TerminalId,
528    /// Extension point for implementations
529    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
530    pub meta: Option<serde_json::Value>,
531}
532
533/// Response to terminal/kill command method
534#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
535#[serde(rename_all = "camelCase")]
536#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME))]
537pub struct KillTerminalCommandResponse {
538    /// Extension point for implementations
539    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
540    pub meta: Option<serde_json::Value>,
541}
542
543/// Request to wait for a terminal command to exit.
544#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
545#[serde(rename_all = "camelCase")]
546#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME))]
547pub struct WaitForTerminalExitRequest {
548    /// The session ID for this request.
549    pub session_id: SessionId,
550    /// The ID of the terminal to wait for.
551    pub terminal_id: TerminalId,
552    /// Extension point for implementations
553    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
554    pub meta: Option<serde_json::Value>,
555}
556
557/// Response containing the exit status of a terminal command.
558#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
559#[serde(rename_all = "camelCase")]
560#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME))]
561pub struct WaitForTerminalExitResponse {
562    /// The exit status of the terminal command.
563    #[serde(flatten)]
564    pub exit_status: TerminalExitStatus,
565    /// Extension point for implementations
566    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
567    pub meta: Option<serde_json::Value>,
568}
569
570/// Exit status of a terminal command.
571#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
572#[serde(rename_all = "camelCase")]
573pub struct TerminalExitStatus {
574    /// The process exit code (may be null if terminated by signal).
575    pub exit_code: Option<u32>,
576    /// The signal that terminated the process (may be null if exited normally).
577    pub signal: Option<String>,
578    /// Extension point for implementations
579    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
580    pub meta: Option<serde_json::Value>,
581}
582
583// Capabilities
584
585/// Capabilities supported by the client.
586///
587/// Advertised during initialization to inform the agent about
588/// available features and methods.
589///
590/// See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/initialization#client-capabilities)
591#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
592#[serde(rename_all = "camelCase")]
593pub struct ClientCapabilities {
594    /// File system capabilities supported by the client.
595    /// Determines which file operations the agent can request.
596    #[serde(default)]
597    pub fs: FileSystemCapability,
598    /// Whether the Client support all `terminal/*` methods.
599    #[serde(default)]
600    pub terminal: bool,
601    /// Extension point for implementations
602    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
603    pub meta: Option<serde_json::Value>,
604}
605
606/// File system capabilities that a client may support.
607///
608/// See protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initialization#filesystem)
609#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
610#[serde(rename_all = "camelCase")]
611pub struct FileSystemCapability {
612    /// Whether the Client supports `fs/read_text_file` requests.
613    #[serde(default)]
614    pub read_text_file: bool,
615    /// Whether the Client supports `fs/write_text_file` requests.
616    #[serde(default)]
617    pub write_text_file: bool,
618    /// Extension point for implementations
619    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
620    pub meta: Option<serde_json::Value>,
621}
622
623// Method schema
624
625/// Names of all methods that clients handle.
626///
627/// Provides a centralized definition of method names used in the protocol.
628#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct ClientMethodNames {
630    /// Method for requesting permission from the user.
631    pub session_request_permission: &'static str,
632    /// Notification for session updates.
633    pub session_update: &'static str,
634    /// Method for writing text files.
635    pub fs_write_text_file: &'static str,
636    /// Method for reading text files.
637    pub fs_read_text_file: &'static str,
638    /// Method for creating new terminals.
639    pub terminal_create: &'static str,
640    /// Method for getting terminals output.
641    pub terminal_output: &'static str,
642    /// Method for releasing a terminal.
643    pub terminal_release: &'static str,
644    /// Method for waiting for a terminal to finish.
645    pub terminal_wait_for_exit: &'static str,
646    /// Method for killing a terminal.
647    pub terminal_kill: &'static str,
648}
649
650/// Constant containing all client method names.
651pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
652    session_update: SESSION_UPDATE_NOTIFICATION,
653    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
654    fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
655    fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
656    terminal_create: TERMINAL_CREATE_METHOD_NAME,
657    terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
658    terminal_release: TERMINAL_RELEASE_METHOD_NAME,
659    terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
660    terminal_kill: TERMINAL_KILL_METHOD_NAME,
661};
662
663/// Notification name for session updates.
664pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
665/// Method name for requesting user permission.
666pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
667/// Method name for writing text files.
668pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
669/// Method name for reading text files.
670pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
671/// Method name for creating a new terminal.
672pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
673/// Method for getting terminals output.
674pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
675/// Method for releasing a terminal.
676pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
677/// Method for waiting for a terminal to finish.
678pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
679/// Method for killing a terminal.
680pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
681
682/// All possible requests that an agent can send to a client.
683///
684/// This enum is used internally for routing RPC requests. You typically won't need
685/// to use this directly - instead, use the methods on the [`Client`] trait.
686///
687/// This enum encompasses all method calls from agent to client.
688#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
689#[serde(untagged)]
690#[schemars(extend("x-docs-ignore" = true))]
691pub enum AgentRequest {
692    WriteTextFileRequest(WriteTextFileRequest),
693    ReadTextFileRequest(ReadTextFileRequest),
694    RequestPermissionRequest(RequestPermissionRequest),
695    CreateTerminalRequest(CreateTerminalRequest),
696    TerminalOutputRequest(TerminalOutputRequest),
697    ReleaseTerminalRequest(ReleaseTerminalRequest),
698    WaitForTerminalExitRequest(WaitForTerminalExitRequest),
699    KillTerminalCommandRequest(KillTerminalCommandRequest),
700    ExtMethodRequest(ExtRequest),
701}
702
703/// All possible responses that a client can send to an agent.
704///
705/// This enum is used internally for routing RPC responses. You typically won't need
706/// to use this directly - the responses are handled automatically by the connection.
707///
708/// These are responses to the corresponding `AgentRequest` variants.
709#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
710#[serde(untagged)]
711#[schemars(extend("x-docs-ignore" = true))]
712pub enum ClientResponse {
713    WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
714    ReadTextFileResponse(ReadTextFileResponse),
715    RequestPermissionResponse(RequestPermissionResponse),
716    CreateTerminalResponse(CreateTerminalResponse),
717    TerminalOutputResponse(TerminalOutputResponse),
718    ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
719    WaitForTerminalExitResponse(WaitForTerminalExitResponse),
720    KillTerminalResponse(#[serde(default)] KillTerminalCommandResponse),
721    ExtMethodResponse(#[schemars(with = "serde_json::Value")] Arc<RawValue>),
722}
723
724/// All possible notifications that an agent can send to a client.
725///
726/// This enum is used internally for routing RPC notifications. You typically won't need
727/// to use this directly - use the notification methods on the [`Client`] trait instead.
728///
729/// Notifications do not expect a response.
730#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
731#[serde(untagged)]
732#[allow(clippy::large_enum_variant)]
733#[schemars(extend("x-docs-ignore" = true))]
734pub enum AgentNotification {
735    SessionNotification(SessionNotification),
736    ExtNotification(ExtNotification),
737}