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}