agent_client_protocol_schema/
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::{path::PathBuf, sync::Arc};
7
8use derive_more::{Display, From};
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12use crate::{
13    ContentBlock, ExtNotification, ExtResponse, Plan, SessionId, SessionModeId, ToolCall,
14    ToolCallUpdate, ext::ExtRequest,
15};
16
17// Session updates
18
19/// Notification containing a session update from the agent.
20///
21/// Used to stream real-time progress and results during prompt processing.
22///
23/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
24#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
25#[schemars(extend("x-side" = "client", "x-method" = SESSION_UPDATE_NOTIFICATION))]
26#[serde(rename_all = "camelCase")]
27#[non_exhaustive]
28pub struct SessionNotification {
29    /// The ID of the session this update pertains to.
30    pub session_id: SessionId,
31    /// The actual update content.
32    pub update: SessionUpdate,
33    /// Extension point for implementations
34    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
35    pub meta: Option<serde_json::Value>,
36}
37
38impl SessionNotification {
39    #[must_use]
40    pub fn new(session_id: impl Into<SessionId>, update: SessionUpdate) -> Self {
41        Self {
42            session_id: session_id.into(),
43            update,
44            meta: None,
45        }
46    }
47
48    /// Extension point for implementations
49    #[must_use]
50    pub fn meta(mut self, meta: serde_json::Value) -> Self {
51        self.meta = Some(meta);
52        self
53    }
54}
55
56/// Different types of updates that can be sent during session processing.
57///
58/// These updates provide real-time feedback about the agent's progress.
59///
60/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
61#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
62#[serde(tag = "sessionUpdate", rename_all = "snake_case")]
63#[schemars(extend("discriminator" = {"propertyName": "sessionUpdate"}))]
64#[non_exhaustive]
65pub enum SessionUpdate {
66    /// A chunk of the user's message being streamed.
67    UserMessageChunk(ContentChunk),
68    /// A chunk of the agent's response being streamed.
69    AgentMessageChunk(ContentChunk),
70    /// A chunk of the agent's internal reasoning being streamed.
71    AgentThoughtChunk(ContentChunk),
72    /// Notification that a new tool call has been initiated.
73    ToolCall(ToolCall),
74    /// Update on the status or results of a tool call.
75    ToolCallUpdate(ToolCallUpdate),
76    /// The agent's execution plan for complex tasks.
77    /// See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)
78    Plan(Plan),
79    /// Available commands are ready or have changed
80    AvailableCommandsUpdate(AvailableCommandsUpdate),
81    /// The current mode of the session has changed
82    ///
83    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
84    CurrentModeUpdate(CurrentModeUpdate),
85}
86
87/// The current mode of the session has changed
88///
89/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
90#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
91#[serde(rename_all = "camelCase")]
92#[non_exhaustive]
93pub struct CurrentModeUpdate {
94    /// The ID of the current mode
95    pub current_mode_id: SessionModeId,
96    /// Extension point for implementations
97    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
98    pub meta: Option<serde_json::Value>,
99}
100
101impl CurrentModeUpdate {
102    #[must_use]
103    pub fn new(current_mode_id: impl Into<SessionModeId>) -> Self {
104        Self {
105            current_mode_id: current_mode_id.into(),
106            meta: None,
107        }
108    }
109
110    /// Extension point for implementations
111    #[must_use]
112    pub fn meta(mut self, meta: serde_json::Value) -> Self {
113        self.meta = Some(meta);
114        self
115    }
116}
117
118/// A streamed item of content
119#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
120#[serde(rename_all = "camelCase")]
121#[non_exhaustive]
122pub struct ContentChunk {
123    /// A single item of content
124    pub content: ContentBlock,
125    /// Extension point for implementations
126    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
127    pub meta: Option<serde_json::Value>,
128}
129
130impl ContentChunk {
131    #[must_use]
132    pub fn new(content: ContentBlock) -> Self {
133        Self {
134            content,
135            meta: None,
136        }
137    }
138
139    /// Extension point for implementations
140    #[must_use]
141    pub fn meta(mut self, meta: serde_json::Value) -> Self {
142        self.meta = Some(meta);
143        self
144    }
145}
146
147/// Available commands are ready or have changed
148#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
149#[serde(rename_all = "camelCase")]
150#[non_exhaustive]
151pub struct AvailableCommandsUpdate {
152    /// Commands the agent can execute
153    pub available_commands: Vec<AvailableCommand>,
154    /// Extension point for implementations
155    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
156    pub meta: Option<serde_json::Value>,
157}
158
159impl AvailableCommandsUpdate {
160    #[must_use]
161    pub fn new(available_commands: Vec<AvailableCommand>) -> Self {
162        Self {
163            available_commands,
164            meta: None,
165        }
166    }
167
168    /// Extension point for implementations
169    #[must_use]
170    pub fn meta(mut self, meta: serde_json::Value) -> Self {
171        self.meta = Some(meta);
172        self
173    }
174}
175
176/// Information about a command.
177#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
178#[serde(rename_all = "camelCase")]
179#[non_exhaustive]
180pub struct AvailableCommand {
181    /// Command name (e.g., `create_plan`, `research_codebase`).
182    pub name: String,
183    /// Human-readable description of what the command does.
184    pub description: String,
185    /// Input for the command if required
186    pub input: Option<AvailableCommandInput>,
187    /// Extension point for implementations
188    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
189    pub meta: Option<serde_json::Value>,
190}
191
192impl AvailableCommand {
193    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
194        Self {
195            name: name.into(),
196            description: description.into(),
197            input: None,
198            meta: None,
199        }
200    }
201
202    /// Input for the command if required
203    #[must_use]
204    pub fn input(mut self, input: AvailableCommandInput) -> Self {
205        self.input = Some(input);
206        self
207    }
208
209    /// Extension point for implementations
210    #[must_use]
211    pub fn meta(mut self, meta: serde_json::Value) -> Self {
212        self.meta = Some(meta);
213        self
214    }
215}
216
217/// The input specification for a command.
218#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
219#[serde(untagged, rename_all = "camelCase")]
220#[non_exhaustive]
221pub enum AvailableCommandInput {
222    /// All text that was typed after the command name is provided as input.
223    Unstructured(UnstructuredCommandInput),
224}
225
226/// All text that was typed after the command name is provided as input.
227#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
228#[serde(rename_all = "camelCase")]
229#[non_exhaustive]
230pub struct UnstructuredCommandInput {
231    /// A hint to display when the input hasn't been provided yet
232    pub hint: String,
233    /// Extension point for implementations
234    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
235    pub meta: Option<serde_json::Value>,
236}
237
238impl UnstructuredCommandInput {
239    pub fn new(hint: impl Into<String>) -> Self {
240        Self {
241            hint: hint.into(),
242            meta: None,
243        }
244    }
245
246    /// Extension point for implementations
247    #[must_use]
248    pub fn meta(mut self, meta: serde_json::Value) -> Self {
249        self.meta = Some(meta);
250        self
251    }
252}
253
254// Permission
255
256/// Request for user permission to execute a tool call.
257///
258/// Sent when the agent needs authorization before performing a sensitive operation.
259///
260/// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
261#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
262#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
263#[serde(rename_all = "camelCase")]
264#[non_exhaustive]
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
277impl RequestPermissionRequest {
278    #[must_use]
279    pub fn new(
280        session_id: SessionId,
281        tool_call: ToolCallUpdate,
282        options: Vec<PermissionOption>,
283    ) -> Self {
284        Self {
285            session_id,
286            tool_call,
287            options,
288            meta: None,
289        }
290    }
291
292    /// Extension point for implementations
293    #[must_use]
294    pub fn meta(mut self, meta: serde_json::Value) -> Self {
295        self.meta = Some(meta);
296        self
297    }
298}
299
300/// An option presented to the user when requesting permission.
301#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
302#[serde(rename_all = "camelCase")]
303#[non_exhaustive]
304pub struct PermissionOption {
305    /// Unique identifier for this permission option.
306    pub option_id: PermissionOptionId,
307    /// Human-readable label to display to the user.
308    pub name: String,
309    /// Hint about the nature of this permission option.
310    pub kind: PermissionOptionKind,
311    /// Extension point for implementations
312    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
313    pub meta: Option<serde_json::Value>,
314}
315
316impl PermissionOption {
317    pub fn new(
318        option_id: PermissionOptionId,
319        name: impl Into<String>,
320        kind: PermissionOptionKind,
321    ) -> Self {
322        Self {
323            option_id,
324            name: name.into(),
325            kind,
326            meta: None,
327        }
328    }
329
330    /// Extension point for implementations
331    #[must_use]
332    pub fn meta(mut self, meta: serde_json::Value) -> Self {
333        self.meta = Some(meta);
334        self
335    }
336}
337
338/// Unique identifier for a permission option.
339#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
340#[serde(transparent)]
341#[from(Arc<str>, String, &'static str)]
342#[non_exhaustive]
343pub struct PermissionOptionId(pub Arc<str>);
344
345impl PermissionOptionId {
346    pub fn new(id: impl Into<Arc<str>>) -> Self {
347        Self(id.into())
348    }
349}
350
351/// The type of permission option being presented to the user.
352///
353/// Helps clients choose appropriate icons and UI treatment.
354#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
355#[serde(rename_all = "snake_case")]
356#[non_exhaustive]
357pub enum PermissionOptionKind {
358    /// Allow this operation only this time.
359    AllowOnce,
360    /// Allow this operation and remember the choice.
361    AllowAlways,
362    /// Reject this operation only this time.
363    RejectOnce,
364    /// Reject this operation and remember the choice.
365    RejectAlways,
366}
367
368/// Response to a permission request.
369#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
370#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
371#[serde(rename_all = "camelCase")]
372#[non_exhaustive]
373pub struct RequestPermissionResponse {
374    /// The user's decision on the permission request.
375    // This extra-level is unfortunately needed because the output must be an object
376    pub outcome: RequestPermissionOutcome,
377    /// Extension point for implementations
378    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
379    pub meta: Option<serde_json::Value>,
380}
381
382impl RequestPermissionResponse {
383    #[must_use]
384    pub fn new(outcome: RequestPermissionOutcome) -> Self {
385        Self {
386            outcome,
387            meta: None,
388        }
389    }
390
391    /// Extension point for implementations
392    #[must_use]
393    pub fn meta(mut self, meta: serde_json::Value) -> Self {
394        self.meta = Some(meta);
395        self
396    }
397}
398
399/// The outcome of a permission request.
400#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
401#[serde(tag = "outcome", rename_all = "snake_case")]
402#[schemars(extend("discriminator" = {"propertyName": "outcome"}))]
403#[non_exhaustive]
404pub enum RequestPermissionOutcome {
405    /// The prompt turn was cancelled before the user responded.
406    ///
407    /// When a client sends a `session/cancel` notification to cancel an ongoing
408    /// prompt turn, it MUST respond to all pending `session/request_permission`
409    /// requests with this `Cancelled` outcome.
410    ///
411    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
412    Cancelled,
413    /// The user selected one of the provided options.
414    #[serde(rename_all = "camelCase")]
415    Selected(SelectedPermissionOutcome),
416}
417
418/// The user selected one of the provided options.
419#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
420#[serde(rename_all = "camelCase")]
421#[non_exhaustive]
422pub struct SelectedPermissionOutcome {
423    /// The ID of the option the user selected.
424    pub option_id: PermissionOptionId,
425    /// Extension point for implementations
426    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
427    pub meta: Option<serde_json::Value>,
428}
429
430impl SelectedPermissionOutcome {
431    #[must_use]
432    pub fn new(option_id: impl Into<PermissionOptionId>) -> Self {
433        Self {
434            option_id: option_id.into(),
435            meta: None,
436        }
437    }
438
439    /// Extension point for implementations
440    #[must_use]
441    pub fn meta(mut self, meta: serde_json::Value) -> Self {
442        self.meta = Some(meta);
443        self
444    }
445}
446
447// Write text file
448
449/// Request to write content to a text file.
450///
451/// Only available if the client supports the `fs.writeTextFile` capability.
452#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
453#[schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME))]
454#[serde(rename_all = "camelCase")]
455#[non_exhaustive]
456pub struct WriteTextFileRequest {
457    /// The session ID for this request.
458    pub session_id: SessionId,
459    /// Absolute path to the file to write.
460    pub path: PathBuf,
461    /// The text content to write to the file.
462    pub content: String,
463    /// Extension point for implementations
464    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
465    pub meta: Option<serde_json::Value>,
466}
467
468impl WriteTextFileRequest {
469    pub fn new(
470        session_id: SessionId,
471        path: impl Into<PathBuf>,
472        content: impl Into<String>,
473    ) -> Self {
474        Self {
475            session_id,
476            path: path.into(),
477            content: content.into(),
478            meta: None,
479        }
480    }
481
482    /// Extension point for implementations
483    #[must_use]
484    pub fn meta(mut self, meta: serde_json::Value) -> Self {
485        self.meta = Some(meta);
486        self
487    }
488}
489
490/// Response to `fs/write_text_file`
491#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
492#[serde(default, rename_all = "camelCase")]
493#[schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME))]
494#[non_exhaustive]
495pub struct WriteTextFileResponse {
496    /// Extension point for implementations
497    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
498    pub meta: Option<serde_json::Value>,
499}
500
501impl WriteTextFileResponse {
502    #[must_use]
503    pub fn new() -> Self {
504        Self::default()
505    }
506
507    /// Extension point for implementations
508    #[must_use]
509    pub fn meta(mut self, meta: serde_json::Value) -> Self {
510        self.meta = Some(meta);
511        self
512    }
513}
514
515// Read text file
516
517/// Request to read content from a text file.
518///
519/// Only available if the client supports the `fs.readTextFile` capability.
520#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
521#[schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME))]
522#[serde(rename_all = "camelCase")]
523#[non_exhaustive]
524pub struct ReadTextFileRequest {
525    /// The session ID for this request.
526    pub session_id: SessionId,
527    /// Absolute path to the file to read.
528    pub path: PathBuf,
529    /// Line number to start reading from (1-based).
530    #[serde(skip_serializing_if = "Option::is_none")]
531    pub line: Option<u32>,
532    /// Maximum number of lines to read.
533    #[serde(skip_serializing_if = "Option::is_none")]
534    pub limit: Option<u32>,
535    /// Extension point for implementations
536    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
537    pub meta: Option<serde_json::Value>,
538}
539
540impl ReadTextFileRequest {
541    pub fn new(session_id: impl Into<SessionId>, path: impl Into<PathBuf>) -> Self {
542        Self {
543            session_id: session_id.into(),
544            path: path.into(),
545            line: None,
546            limit: None,
547            meta: None,
548        }
549    }
550
551    /// Line number to start reading from (1-based).
552    #[must_use]
553    pub fn line(mut self, line: u32) -> Self {
554        self.line = Some(line);
555        self
556    }
557
558    /// Maximum number of lines to read.
559    #[must_use]
560    pub fn limit(mut self, limit: u32) -> Self {
561        self.limit = Some(limit);
562        self
563    }
564
565    /// Extension point for implementations
566    #[must_use]
567    pub fn meta(mut self, meta: serde_json::Value) -> Self {
568        self.meta = Some(meta);
569        self
570    }
571}
572
573/// Response containing the contents of a text file.
574#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
575#[schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME))]
576#[serde(rename_all = "camelCase")]
577#[non_exhaustive]
578pub struct ReadTextFileResponse {
579    pub content: String,
580    /// Extension point for implementations
581    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
582    pub meta: Option<serde_json::Value>,
583}
584
585impl ReadTextFileResponse {
586    pub fn new(content: impl Into<String>) -> Self {
587        Self {
588            content: content.into(),
589            meta: None,
590        }
591    }
592
593    /// Extension point for implementations
594    #[must_use]
595    pub fn meta(mut self, meta: serde_json::Value) -> Self {
596        self.meta = Some(meta);
597        self
598    }
599}
600
601// Terminals
602
603#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
604#[serde(transparent)]
605#[from(Arc<str>, String, &'static str)]
606#[non_exhaustive]
607pub struct TerminalId(pub Arc<str>);
608
609impl TerminalId {
610    pub fn new(id: impl Into<Arc<str>>) -> Self {
611        Self(id.into())
612    }
613}
614
615/// Request to create a new terminal and execute a command.
616#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
617#[serde(rename_all = "camelCase")]
618#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME))]
619#[non_exhaustive]
620pub struct CreateTerminalRequest {
621    /// The session ID for this request.
622    pub session_id: SessionId,
623    /// The command to execute.
624    pub command: String,
625    /// Array of command arguments.
626    #[serde(default, skip_serializing_if = "Vec::is_empty")]
627    pub args: Vec<String>,
628    /// Environment variables for the command.
629    #[serde(default, skip_serializing_if = "Vec::is_empty")]
630    pub env: Vec<crate::EnvVariable>,
631    /// Working directory for the command (absolute path).
632    #[serde(skip_serializing_if = "Option::is_none")]
633    pub cwd: Option<PathBuf>,
634    /// Maximum number of output bytes to retain.
635    ///
636    /// When the limit is exceeded, the Client truncates from the beginning of the output
637    /// to stay within the limit.
638    ///
639    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
640    /// string output, even if this means the retained output is slightly less than the
641    /// specified limit.
642    #[serde(skip_serializing_if = "Option::is_none")]
643    pub output_byte_limit: Option<u64>,
644    /// Extension point for implementations
645    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
646    pub meta: Option<serde_json::Value>,
647}
648
649impl CreateTerminalRequest {
650    pub fn new(session_id: impl Into<SessionId>, command: impl Into<String>) -> Self {
651        Self {
652            session_id: session_id.into(),
653            command: command.into(),
654            args: Vec::new(),
655            env: Vec::new(),
656            cwd: None,
657            output_byte_limit: None,
658            meta: None,
659        }
660    }
661
662    /// Array of command arguments.
663    #[must_use]
664    pub fn args(mut self, args: Vec<String>) -> Self {
665        self.args = args;
666        self
667    }
668
669    /// Environment variables for the command.
670    #[must_use]
671    pub fn env(mut self, env: Vec<crate::EnvVariable>) -> Self {
672        self.env = env;
673        self
674    }
675
676    /// Working directory for the command (absolute path).
677    #[must_use]
678    pub fn cwd(mut self, cwd: impl Into<PathBuf>) -> Self {
679        self.cwd = Some(cwd.into());
680        self
681    }
682
683    /// Maximum number of output bytes to retain.
684    ///
685    /// When the limit is exceeded, the Client truncates from the beginning of the output
686    /// to stay within the limit.
687    ///
688    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
689    /// string output, even if this means the retained output is slightly less than the
690    /// specified limit.
691    #[must_use]
692    pub fn output_byte_limit(mut self, output_byte_limit: u64) -> Self {
693        self.output_byte_limit = Some(output_byte_limit);
694        self
695    }
696
697    /// Extension point for implementations
698    #[must_use]
699    pub fn meta(mut self, meta: serde_json::Value) -> Self {
700        self.meta = Some(meta);
701        self
702    }
703}
704
705/// Response containing the ID of the created terminal.
706#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
707#[serde(rename_all = "camelCase")]
708#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME))]
709#[non_exhaustive]
710pub struct CreateTerminalResponse {
711    /// The unique identifier for the created terminal.
712    pub terminal_id: TerminalId,
713    /// Extension point for implementations
714    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
715    pub meta: Option<serde_json::Value>,
716}
717
718impl CreateTerminalResponse {
719    #[must_use]
720    pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
721        Self {
722            terminal_id: terminal_id.into(),
723            meta: None,
724        }
725    }
726
727    /// Extension point for implementations
728    #[must_use]
729    pub fn meta(mut self, meta: serde_json::Value) -> Self {
730        self.meta = Some(meta);
731        self
732    }
733}
734
735/// Request to get the current output and status of a terminal.
736#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
737#[serde(rename_all = "camelCase")]
738#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME))]
739#[non_exhaustive]
740pub struct TerminalOutputRequest {
741    /// The session ID for this request.
742    pub session_id: SessionId,
743    /// The ID of the terminal to get output from.
744    pub terminal_id: TerminalId,
745    /// Extension point for implementations
746    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
747    pub meta: Option<serde_json::Value>,
748}
749
750impl TerminalOutputRequest {
751    #[must_use]
752    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
753        Self {
754            session_id: session_id.into(),
755            terminal_id: terminal_id.into(),
756            meta: None,
757        }
758    }
759
760    /// Extension point for implementations
761    #[must_use]
762    pub fn meta(mut self, meta: serde_json::Value) -> Self {
763        self.meta = Some(meta);
764        self
765    }
766}
767
768/// Response containing the terminal output and exit status.
769#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
770#[serde(rename_all = "camelCase")]
771#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME))]
772#[non_exhaustive]
773pub struct TerminalOutputResponse {
774    /// The terminal output captured so far.
775    pub output: String,
776    /// Whether the output was truncated due to byte limits.
777    pub truncated: bool,
778    /// Exit status if the command has completed.
779    pub exit_status: Option<TerminalExitStatus>,
780    /// Extension point for implementations
781    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
782    pub meta: Option<serde_json::Value>,
783}
784
785impl TerminalOutputResponse {
786    pub fn new(output: impl Into<String>, truncated: bool) -> Self {
787        Self {
788            output: output.into(),
789            truncated,
790            exit_status: None,
791            meta: None,
792        }
793    }
794
795    /// Exit status if the command has completed.
796    #[must_use]
797    pub fn exit_status(mut self, exit_status: TerminalExitStatus) -> Self {
798        self.exit_status = Some(exit_status);
799        self
800    }
801
802    /// Extension point for implementations
803    #[must_use]
804    pub fn meta(mut self, meta: serde_json::Value) -> Self {
805        self.meta = Some(meta);
806        self
807    }
808}
809
810/// Request to release a terminal and free its resources.
811#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
812#[serde(rename_all = "camelCase")]
813#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME))]
814#[non_exhaustive]
815pub struct ReleaseTerminalRequest {
816    /// The session ID for this request.
817    pub session_id: SessionId,
818    /// The ID of the terminal to release.
819    pub terminal_id: TerminalId,
820    /// Extension point for implementations
821    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
822    pub meta: Option<serde_json::Value>,
823}
824
825impl ReleaseTerminalRequest {
826    #[must_use]
827    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
828        Self {
829            session_id: session_id.into(),
830            terminal_id: terminal_id.into(),
831            meta: None,
832        }
833    }
834
835    /// Extension point for implementations
836    #[must_use]
837    pub fn meta(mut self, meta: serde_json::Value) -> Self {
838        self.meta = Some(meta);
839        self
840    }
841}
842
843/// Response to terminal/release method
844#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
845#[serde(rename_all = "camelCase")]
846#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME))]
847#[non_exhaustive]
848pub struct ReleaseTerminalResponse {
849    /// Extension point for implementations
850    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
851    pub meta: Option<serde_json::Value>,
852}
853
854impl ReleaseTerminalResponse {
855    #[must_use]
856    pub fn new() -> Self {
857        Self::default()
858    }
859
860    /// Extension point for implementations
861    #[must_use]
862    pub fn meta(mut self, meta: serde_json::Value) -> Self {
863        self.meta = Some(meta);
864        self
865    }
866}
867
868/// Request to kill a terminal command without releasing the terminal.
869#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
870#[serde(rename_all = "camelCase")]
871#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME))]
872#[non_exhaustive]
873pub struct KillTerminalCommandRequest {
874    /// The session ID for this request.
875    pub session_id: SessionId,
876    /// The ID of the terminal to kill.
877    pub terminal_id: TerminalId,
878    /// Extension point for implementations
879    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
880    pub meta: Option<serde_json::Value>,
881}
882
883impl KillTerminalCommandRequest {
884    #[must_use]
885    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
886        Self {
887            session_id: session_id.into(),
888            terminal_id: terminal_id.into(),
889            meta: None,
890        }
891    }
892
893    /// Extension point for implementations
894    #[must_use]
895    pub fn meta(mut self, meta: serde_json::Value) -> Self {
896        self.meta = Some(meta);
897        self
898    }
899}
900
901/// Response to terminal/kill command method
902#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
903#[serde(rename_all = "camelCase")]
904#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME))]
905#[non_exhaustive]
906pub struct KillTerminalCommandResponse {
907    /// Extension point for implementations
908    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
909    pub meta: Option<serde_json::Value>,
910}
911
912impl KillTerminalCommandResponse {
913    #[must_use]
914    pub fn new() -> Self {
915        Self::default()
916    }
917
918    /// Extension point for implementations
919    #[must_use]
920    pub fn meta(mut self, meta: serde_json::Value) -> Self {
921        self.meta = Some(meta);
922        self
923    }
924}
925
926/// Request to wait for a terminal command to exit.
927#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
928#[serde(rename_all = "camelCase")]
929#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME))]
930#[non_exhaustive]
931pub struct WaitForTerminalExitRequest {
932    /// The session ID for this request.
933    pub session_id: SessionId,
934    /// The ID of the terminal to wait for.
935    pub terminal_id: TerminalId,
936    /// Extension point for implementations
937    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
938    pub meta: Option<serde_json::Value>,
939}
940
941impl WaitForTerminalExitRequest {
942    #[must_use]
943    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
944        Self {
945            session_id: session_id.into(),
946            terminal_id: terminal_id.into(),
947            meta: None,
948        }
949    }
950
951    /// Extension point for implementations
952    #[must_use]
953    pub fn meta(mut self, meta: serde_json::Value) -> Self {
954        self.meta = Some(meta);
955        self
956    }
957}
958
959/// Response containing the exit status of a terminal command.
960#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
961#[serde(rename_all = "camelCase")]
962#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME))]
963#[non_exhaustive]
964pub struct WaitForTerminalExitResponse {
965    /// The exit status of the terminal command.
966    #[serde(flatten)]
967    pub exit_status: TerminalExitStatus,
968    /// Extension point for implementations
969    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
970    pub meta: Option<serde_json::Value>,
971}
972
973impl WaitForTerminalExitResponse {
974    #[must_use]
975    pub fn new(exit_status: TerminalExitStatus) -> Self {
976        Self {
977            exit_status,
978            meta: None,
979        }
980    }
981
982    /// Extension point for implementations
983    #[must_use]
984    pub fn meta(mut self, meta: serde_json::Value) -> Self {
985        self.meta = Some(meta);
986        self
987    }
988}
989
990/// Exit status of a terminal command.
991#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
992#[serde(rename_all = "camelCase")]
993#[non_exhaustive]
994pub struct TerminalExitStatus {
995    /// The process exit code (may be null if terminated by signal).
996    pub exit_code: Option<u32>,
997    /// The signal that terminated the process (may be null if exited normally).
998    pub signal: Option<String>,
999    /// Extension point for implementations
1000    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1001    pub meta: Option<serde_json::Value>,
1002}
1003
1004impl TerminalExitStatus {
1005    #[must_use]
1006    pub fn new() -> Self {
1007        Self::default()
1008    }
1009
1010    /// The process exit code (may be null if terminated by signal).
1011    #[must_use]
1012    pub fn exit_code(mut self, exit_code: u32) -> Self {
1013        self.exit_code = Some(exit_code);
1014        self
1015    }
1016
1017    /// The signal that terminated the process (may be null if exited normally).
1018    #[must_use]
1019    pub fn signal(mut self, signal: impl Into<String>) -> Self {
1020        self.signal = Some(signal.into());
1021        self
1022    }
1023
1024    /// Extension point for implementations
1025    #[must_use]
1026    pub fn meta(mut self, meta: serde_json::Value) -> Self {
1027        self.meta = Some(meta);
1028        self
1029    }
1030}
1031
1032// Capabilities
1033
1034/// Capabilities supported by the client.
1035///
1036/// Advertised during initialization to inform the agent about
1037/// available features and methods.
1038///
1039/// See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/initialization#client-capabilities)
1040#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1041#[serde(rename_all = "camelCase")]
1042#[non_exhaustive]
1043pub struct ClientCapabilities {
1044    /// File system capabilities supported by the client.
1045    /// Determines which file operations the agent can request.
1046    #[serde(default)]
1047    pub fs: FileSystemCapability,
1048    /// Whether the Client support all `terminal/*` methods.
1049    #[serde(default)]
1050    pub terminal: bool,
1051    /// Extension point for implementations
1052    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1053    pub meta: Option<serde_json::Value>,
1054}
1055
1056impl ClientCapabilities {
1057    #[must_use]
1058    pub fn new() -> Self {
1059        Self::default()
1060    }
1061
1062    /// File system capabilities supported by the client.
1063    /// Determines which file operations the agent can request.
1064    #[must_use]
1065    pub fn fs(mut self, fs: FileSystemCapability) -> Self {
1066        self.fs = fs;
1067        self
1068    }
1069
1070    /// Whether the Client support all `terminal/*` methods.
1071    #[must_use]
1072    pub fn terminal(mut self, terminal: bool) -> Self {
1073        self.terminal = terminal;
1074        self
1075    }
1076
1077    /// Extension point for implementations
1078    #[must_use]
1079    pub fn meta(mut self, meta: serde_json::Value) -> Self {
1080        self.meta = Some(meta);
1081        self
1082    }
1083}
1084
1085/// Filesystem capabilities supported by the client.
1086/// File system capabilities that a client may support.
1087///
1088/// See protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initialization#filesystem)
1089#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1090#[serde(rename_all = "camelCase")]
1091#[non_exhaustive]
1092pub struct FileSystemCapability {
1093    /// Whether the Client supports `fs/read_text_file` requests.
1094    #[serde(default)]
1095    pub read_text_file: bool,
1096    /// Whether the Client supports `fs/write_text_file` requests.
1097    #[serde(default)]
1098    pub write_text_file: bool,
1099    /// Extension point for implementations
1100    #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")]
1101    pub meta: Option<serde_json::Value>,
1102}
1103
1104impl FileSystemCapability {
1105    #[must_use]
1106    pub fn new() -> Self {
1107        Self::default()
1108    }
1109
1110    /// Whether the Client supports `fs/read_text_file` requests.
1111    #[must_use]
1112    pub fn read_text_file(mut self, read_text_file: bool) -> Self {
1113        self.read_text_file = read_text_file;
1114        self
1115    }
1116
1117    /// Whether the Client supports `fs/write_text_file` requests.
1118    #[must_use]
1119    pub fn write_text_file(mut self, write_text_file: bool) -> Self {
1120        self.write_text_file = write_text_file;
1121        self
1122    }
1123
1124    /// Extension point for implementations
1125    #[must_use]
1126    pub fn meta(mut self, meta: serde_json::Value) -> Self {
1127        self.meta = Some(meta);
1128        self
1129    }
1130}
1131
1132// Method schema
1133
1134/// Names of all methods that clients handle.
1135///
1136/// Provides a centralized definition of method names used in the protocol.
1137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1138#[non_exhaustive]
1139pub struct ClientMethodNames {
1140    /// Method for requesting permission from the user.
1141    pub session_request_permission: &'static str,
1142    /// Notification for session updates.
1143    pub session_update: &'static str,
1144    /// Method for writing text files.
1145    pub fs_write_text_file: &'static str,
1146    /// Method for reading text files.
1147    pub fs_read_text_file: &'static str,
1148    /// Method for creating new terminals.
1149    pub terminal_create: &'static str,
1150    /// Method for getting terminals output.
1151    pub terminal_output: &'static str,
1152    /// Method for releasing a terminal.
1153    pub terminal_release: &'static str,
1154    /// Method for waiting for a terminal to finish.
1155    pub terminal_wait_for_exit: &'static str,
1156    /// Method for killing a terminal.
1157    pub terminal_kill: &'static str,
1158}
1159
1160/// Constant containing all client method names.
1161pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
1162    session_update: SESSION_UPDATE_NOTIFICATION,
1163    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
1164    fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
1165    fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
1166    terminal_create: TERMINAL_CREATE_METHOD_NAME,
1167    terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
1168    terminal_release: TERMINAL_RELEASE_METHOD_NAME,
1169    terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
1170    terminal_kill: TERMINAL_KILL_METHOD_NAME,
1171};
1172
1173/// Notification name for session updates.
1174pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
1175/// Method name for requesting user permission.
1176pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
1177/// Method name for writing text files.
1178pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
1179/// Method name for reading text files.
1180pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
1181/// Method name for creating a new terminal.
1182pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
1183/// Method for getting terminals output.
1184pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
1185/// Method for releasing a terminal.
1186pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
1187/// Method for waiting for a terminal to finish.
1188pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
1189/// Method for killing a terminal.
1190pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
1191
1192/// All possible requests that an agent can send to a client.
1193///
1194/// This enum is used internally for routing RPC requests. You typically won't need
1195/// to use this directly - instead, use the methods on the [`Client`] trait.
1196///
1197/// This enum encompasses all method calls from agent to client.
1198#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1199#[serde(untagged)]
1200#[schemars(inline)]
1201#[non_exhaustive]
1202pub enum AgentRequest {
1203    /// Writes content to a text file in the client's file system.
1204    ///
1205    /// Only available if the client advertises the `fs.writeTextFile` capability.
1206    /// Allows the agent to create or modify files within the client's environment.
1207    ///
1208    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
1209    WriteTextFileRequest(WriteTextFileRequest),
1210    /// Reads content from a text file in the client's file system.
1211    ///
1212    /// Only available if the client advertises the `fs.readTextFile` capability.
1213    /// Allows the agent to access file contents within the client's environment.
1214    ///
1215    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
1216    ReadTextFileRequest(ReadTextFileRequest),
1217    /// Requests permission from the user for a tool call operation.
1218    ///
1219    /// Called by the agent when it needs user authorization before executing
1220    /// a potentially sensitive operation. The client should present the options
1221    /// to the user and return their decision.
1222    ///
1223    /// If the client cancels the prompt turn via `session/cancel`, it MUST
1224    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
1225    ///
1226    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
1227    RequestPermissionRequest(RequestPermissionRequest),
1228    /// Executes a command in a new terminal
1229    ///
1230    /// Only available if the `terminal` Client capability is set to `true`.
1231    ///
1232    /// Returns a `TerminalId` that can be used with other terminal methods
1233    /// to get the current output, wait for exit, and kill the command.
1234    ///
1235    /// The `TerminalId` can also be used to embed the terminal in a tool call
1236    /// by using the `ToolCallContent::Terminal` variant.
1237    ///
1238    /// The Agent is responsible for releasing the terminal by using the `terminal/release`
1239    /// method.
1240    ///
1241    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1242    CreateTerminalRequest(CreateTerminalRequest),
1243    /// Gets the terminal output and exit status
1244    ///
1245    /// Returns the current content in the terminal without waiting for the command to exit.
1246    /// If the command has already exited, the exit status is included.
1247    ///
1248    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1249    TerminalOutputRequest(TerminalOutputRequest),
1250    /// Releases a terminal
1251    ///
1252    /// The command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`
1253    /// to wait for the command to exit before releasing the terminal.
1254    ///
1255    /// After release, the `TerminalId` can no longer be used with other `terminal/*` methods,
1256    /// but tool calls that already contain it, continue to display its output.
1257    ///
1258    /// The `terminal/kill` method can be used to terminate the command without releasing
1259    /// the terminal, allowing the Agent to call `terminal/output` and other methods.
1260    ///
1261    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1262    ReleaseTerminalRequest(ReleaseTerminalRequest),
1263    /// Waits for the terminal command to exit and return its exit status
1264    ///
1265    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1266    WaitForTerminalExitRequest(WaitForTerminalExitRequest),
1267    /// Kills the terminal command without releasing the terminal
1268    ///
1269    /// While `terminal/release` will also kill the command, this method will keep
1270    /// the `TerminalId` valid so it can be used with other methods.
1271    ///
1272    /// This method can be helpful when implementing command timeouts which terminate
1273    /// the command as soon as elapsed, and then get the final output so it can be sent
1274    /// to the model.
1275    ///
1276    /// Note: `terminal/release` when `TerminalId` is no longer needed.
1277    ///
1278    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
1279    KillTerminalCommandRequest(KillTerminalCommandRequest),
1280    /// Handles extension method requests from the agent.
1281    ///
1282    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
1283    /// Extension methods provide a way to add custom functionality while maintaining
1284    /// protocol compatibility.
1285    ///
1286    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1287    ExtMethodRequest(ExtRequest),
1288}
1289
1290impl AgentRequest {
1291    /// Returns the corresponding method name of the request.
1292    #[must_use]
1293    pub fn method(&self) -> &str {
1294        match self {
1295            Self::WriteTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_write_text_file,
1296            Self::ReadTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_read_text_file,
1297            Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
1298            Self::CreateTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_create,
1299            Self::TerminalOutputRequest(_) => CLIENT_METHOD_NAMES.terminal_output,
1300            Self::ReleaseTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_release,
1301            Self::WaitForTerminalExitRequest(_) => CLIENT_METHOD_NAMES.terminal_wait_for_exit,
1302            Self::KillTerminalCommandRequest(_) => CLIENT_METHOD_NAMES.terminal_kill,
1303            Self::ExtMethodRequest(ext_request) => &ext_request.method,
1304        }
1305    }
1306}
1307
1308/// All possible responses that a client can send to an agent.
1309///
1310/// This enum is used internally for routing RPC responses. You typically won't need
1311/// to use this directly - the responses are handled automatically by the connection.
1312///
1313/// These are responses to the corresponding `AgentRequest` variants.
1314#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1315#[serde(untagged)]
1316#[schemars(inline)]
1317#[non_exhaustive]
1318pub enum ClientResponse {
1319    WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
1320    ReadTextFileResponse(ReadTextFileResponse),
1321    RequestPermissionResponse(RequestPermissionResponse),
1322    CreateTerminalResponse(CreateTerminalResponse),
1323    TerminalOutputResponse(TerminalOutputResponse),
1324    ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
1325    WaitForTerminalExitResponse(WaitForTerminalExitResponse),
1326    KillTerminalResponse(#[serde(default)] KillTerminalCommandResponse),
1327    ExtMethodResponse(ExtResponse),
1328}
1329
1330/// All possible notifications that an agent can send to a client.
1331///
1332/// This enum is used internally for routing RPC notifications. You typically won't need
1333/// to use this directly - use the notification methods on the [`Client`] trait instead.
1334///
1335/// Notifications do not expect a response.
1336#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1337#[serde(untagged)]
1338#[expect(clippy::large_enum_variant)]
1339#[schemars(inline)]
1340#[non_exhaustive]
1341pub enum AgentNotification {
1342    /// Handles session update notifications from the agent.
1343    ///
1344    /// This is a notification endpoint (no response expected) that receives
1345    /// real-time updates about session progress, including message chunks,
1346    /// tool calls, and execution plans.
1347    ///
1348    /// Note: Clients SHOULD continue accepting tool call updates even after
1349    /// sending a `session/cancel` notification, as the agent may send final
1350    /// updates before responding with the cancelled stop reason.
1351    ///
1352    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
1353    SessionNotification(SessionNotification),
1354    /// Handles extension notifications from the agent.
1355    ///
1356    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
1357    /// Extension notifications provide a way to send one-way messages for custom functionality
1358    /// while maintaining protocol compatibility.
1359    ///
1360    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1361    ExtNotification(ExtNotification),
1362}
1363
1364impl AgentNotification {
1365    /// Returns the corresponding method name of the notification.
1366    #[must_use]
1367    pub fn method(&self) -> &str {
1368        match self {
1369            Self::SessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
1370            Self::ExtNotification(ext_notification) => &ext_notification.method,
1371        }
1372    }
1373}