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