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