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