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