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