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