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