Skip to main content

agent_client_protocol_schema/v1/
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_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
12
13#[cfg(feature = "unstable_elicitation")]
14use super::{
15    CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse,
16    ElicitationCapabilities,
17};
18use crate::{IntoMaybeUndefined, IntoOption, MaybeUndefined, SkipListener};
19
20use super::{
21    ContentBlock, EnvVariable, ExtNotification, ExtRequest, ExtResponse, Meta, Plan,
22    SessionConfigOption, SessionId, SessionModeId, ToolCall, ToolCallUpdate,
23};
24#[cfg(feature = "unstable_plan_operations")]
25use super::{PlanCapabilities, PlanRemoved, PlanUpdate};
26
27#[cfg(feature = "unstable_mcp_over_acp")]
28use super::mcp::{
29    ConnectMcpRequest, ConnectMcpResponse, DisconnectMcpRequest, DisconnectMcpResponse,
30    MCP_CONNECT_METHOD_NAME, MCP_DISCONNECT_METHOD_NAME, MCP_MESSAGE_METHOD_NAME,
31    MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
32};
33
34#[cfg(feature = "unstable_nes")]
35use super::{ClientNesCapabilities, PositionEncodingKind};
36
37// Session updates
38
39/// Notification containing a session update from the agent.
40///
41/// Used to stream real-time progress and results during prompt processing.
42///
43/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
44#[skip_serializing_none]
45#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
46#[schemars(extend("x-side" = "client", "x-method" = SESSION_UPDATE_NOTIFICATION))]
47#[serde(rename_all = "camelCase")]
48#[non_exhaustive]
49pub struct SessionNotification {
50    /// The ID of the session this update pertains to.
51    pub session_id: SessionId,
52    /// The actual update content.
53    pub update: SessionUpdate,
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    #[serde(rename = "_meta")]
60    pub meta: Option<Meta>,
61}
62
63impl SessionNotification {
64    /// Builds [`SessionNotification`] with the required notification fields set; optional fields start unset or empty.
65    #[must_use]
66    pub fn new(session_id: impl Into<SessionId>, update: SessionUpdate) -> Self {
67        Self {
68            session_id: session_id.into(),
69            update,
70            meta: None,
71        }
72    }
73
74    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
75    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
76    /// these keys.
77    ///
78    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
79    #[must_use]
80    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
81        self.meta = meta.into_option();
82        self
83    }
84}
85
86/// Different types of updates that can be sent during session processing.
87///
88/// These updates provide real-time feedback about the agent's progress.
89///
90/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
91#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
92#[serde(tag = "sessionUpdate", rename_all = "snake_case")]
93#[schemars(extend("discriminator" = {"propertyName": "sessionUpdate"}))]
94#[non_exhaustive]
95pub enum SessionUpdate {
96    /// A chunk of the user's message being streamed.
97    UserMessageChunk(ContentChunk),
98    /// A chunk of the agent's response being streamed.
99    AgentMessageChunk(ContentChunk),
100    /// A chunk of the agent's internal reasoning being streamed.
101    AgentThoughtChunk(ContentChunk),
102    /// Notification that a new tool call has been initiated.
103    ToolCall(ToolCall),
104    /// Update on the status or results of a tool call.
105    ToolCallUpdate(ToolCallUpdate),
106    /// The agent's execution plan for complex tasks.
107    /// See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)
108    Plan(Plan),
109    /// **UNSTABLE**
110    ///
111    /// This capability is not part of the spec yet, and may be removed or changed at any point.
112    ///
113    /// A content update for a plan identified by ID.
114    #[cfg(feature = "unstable_plan_operations")]
115    PlanUpdate(PlanUpdate),
116    /// **UNSTABLE**
117    ///
118    /// This capability is not part of the spec yet, and may be removed or changed at any point.
119    ///
120    /// Removal notice for a plan identified by ID.
121    #[cfg(feature = "unstable_plan_operations")]
122    PlanRemoved(PlanRemoved),
123    /// Available commands are ready or have changed
124    AvailableCommandsUpdate(AvailableCommandsUpdate),
125    /// The current mode of the session has changed
126    ///
127    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
128    CurrentModeUpdate(CurrentModeUpdate),
129    /// Session configuration options have been updated.
130    ConfigOptionUpdate(ConfigOptionUpdate),
131    /// Session metadata has been updated (title, timestamps, custom metadata)
132    SessionInfoUpdate(SessionInfoUpdate),
133    /// Context window and cost update for the session.
134    UsageUpdate(UsageUpdate),
135}
136
137/// The current mode of the session has changed
138///
139/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
140#[skip_serializing_none]
141#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
142#[serde(rename_all = "camelCase")]
143#[non_exhaustive]
144pub struct CurrentModeUpdate {
145    /// The ID of the current mode
146    pub current_mode_id: SessionModeId,
147    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
148    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
149    /// these keys.
150    ///
151    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
152    #[serde(rename = "_meta")]
153    pub meta: Option<Meta>,
154}
155
156impl CurrentModeUpdate {
157    /// Builds [`CurrentModeUpdate`] with the required fields set; optional fields start unset or empty.
158    #[must_use]
159    pub fn new(current_mode_id: impl Into<SessionModeId>) -> Self {
160        Self {
161            current_mode_id: current_mode_id.into(),
162            meta: None,
163        }
164    }
165
166    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
167    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
168    /// these keys.
169    ///
170    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
171    #[must_use]
172    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
173        self.meta = meta.into_option();
174        self
175    }
176}
177
178/// Session configuration options have been updated.
179#[serde_as]
180#[skip_serializing_none]
181#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
182#[serde(rename_all = "camelCase")]
183#[non_exhaustive]
184pub struct ConfigOptionUpdate {
185    /// The full set of configuration options and their current values.
186    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
187    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
188    pub config_options: Vec<SessionConfigOption>,
189    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
190    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
191    /// these keys.
192    ///
193    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
194    #[serde(rename = "_meta")]
195    pub meta: Option<Meta>,
196}
197
198impl ConfigOptionUpdate {
199    /// Builds [`ConfigOptionUpdate`] with the required fields set; optional fields start unset or empty.
200    #[must_use]
201    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
202        Self {
203            config_options,
204            meta: None,
205        }
206    }
207
208    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
209    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
210    /// these keys.
211    ///
212    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
213    #[must_use]
214    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
215        self.meta = meta.into_option();
216        self
217    }
218}
219
220/// Update to session metadata. All fields are optional to support partial updates.
221///
222/// Agents send this notification to update session information like title or custom metadata.
223/// This allows clients to display dynamic session names and track session state changes.
224#[skip_serializing_none]
225#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
226#[serde(rename_all = "camelCase")]
227#[non_exhaustive]
228pub struct SessionInfoUpdate {
229    /// Human-readable title for the session. Set to null to clear.
230    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
231    pub title: MaybeUndefined<String>,
232    /// ISO 8601 timestamp of last activity. Set to null to clear.
233    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
234    pub updated_at: MaybeUndefined<String>,
235    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
236    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
237    /// these keys.
238    ///
239    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
240    #[serde(rename = "_meta")]
241    pub meta: Option<Meta>,
242}
243
244impl SessionInfoUpdate {
245    /// Builds [`SessionInfoUpdate`] with the required fields set; optional fields start unset or empty.
246    #[must_use]
247    pub fn new() -> Self {
248        Self::default()
249    }
250
251    /// Human-readable title for the session. Set to null to clear.
252    #[must_use]
253    pub fn title(mut self, title: impl IntoMaybeUndefined<String>) -> Self {
254        self.title = title.into_maybe_undefined();
255        self
256    }
257
258    /// ISO 8601 timestamp of last activity. Set to null to clear.
259    #[must_use]
260    pub fn updated_at(mut self, updated_at: impl IntoMaybeUndefined<String>) -> Self {
261        self.updated_at = updated_at.into_maybe_undefined();
262        self
263    }
264
265    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
266    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
267    /// these keys.
268    ///
269    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
270    #[must_use]
271    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
272        self.meta = meta.into_option();
273        self
274    }
275}
276
277/// Context window and cost update for a session.
278#[serde_as]
279#[skip_serializing_none]
280#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
281#[serde(rename_all = "camelCase")]
282#[non_exhaustive]
283pub struct UsageUpdate {
284    /// Tokens currently in context.
285    pub used: u64,
286    /// Total context window size in tokens.
287    pub size: u64,
288    /// Cumulative session cost (optional).
289    #[serde_as(deserialize_as = "DefaultOnError")]
290    #[schemars(extend("x-deserialize-default-on-error" = true))]
291    #[serde(default)]
292    pub cost: Option<Cost>,
293    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
294    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
295    /// these keys.
296    ///
297    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
298    #[serde(rename = "_meta")]
299    pub meta: Option<Meta>,
300}
301
302impl UsageUpdate {
303    /// Builds [`UsageUpdate`] with the required fields set; optional fields start unset or empty.
304    #[must_use]
305    pub fn new(used: u64, size: u64) -> Self {
306        Self {
307            used,
308            size,
309            cost: None,
310            meta: None,
311        }
312    }
313
314    /// Cumulative session cost (optional).
315    #[must_use]
316    pub fn cost(mut self, cost: impl IntoOption<Cost>) -> Self {
317        self.cost = cost.into_option();
318        self
319    }
320
321    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
322    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
323    /// these keys.
324    ///
325    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
326    #[must_use]
327    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
328        self.meta = meta.into_option();
329        self
330    }
331}
332
333/// Cost information for a session.
334#[skip_serializing_none]
335#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
336#[serde(rename_all = "camelCase")]
337#[non_exhaustive]
338pub struct Cost {
339    /// Total cumulative cost for session.
340    pub amount: f64,
341    /// ISO 4217 currency code (e.g., "USD", "EUR").
342    pub currency: String,
343    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
344    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
345    /// these keys.
346    ///
347    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
348    #[serde(rename = "_meta")]
349    pub meta: Option<Meta>,
350}
351
352impl Cost {
353    /// Builds [`Cost`] with the required fields set; optional fields start unset or empty.
354    #[must_use]
355    pub fn new(amount: f64, currency: impl Into<String>) -> Self {
356        Self {
357            amount,
358            currency: currency.into(),
359            meta: None,
360        }
361    }
362
363    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
364    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
365    /// these keys.
366    ///
367    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
368    #[must_use]
369    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
370        self.meta = meta.into_option();
371        self
372    }
373}
374
375/// A streamed item of content
376#[skip_serializing_none]
377#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
378#[serde(rename_all = "camelCase")]
379#[non_exhaustive]
380pub struct ContentChunk {
381    /// A single item of content
382    pub content: ContentBlock,
383    /// A unique identifier for the message this chunk belongs to.
384    ///
385    /// All chunks belonging to the same message share the same `messageId`.
386    /// A change in `messageId` indicates a new message has started.
387    pub message_id: Option<MessageId>,
388    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
389    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
390    /// these keys.
391    ///
392    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
393    #[serde(rename = "_meta")]
394    pub meta: Option<Meta>,
395}
396
397impl ContentChunk {
398    /// Builds [`ContentChunk`] with the required fields set; optional fields start unset or empty.
399    #[must_use]
400    pub fn new(content: ContentBlock) -> Self {
401        Self {
402            content,
403            message_id: None,
404            meta: None,
405        }
406    }
407
408    /// A unique identifier for the message this chunk belongs to.
409    ///
410    /// All chunks belonging to the same message share the same `messageId`.
411    /// A change in `messageId` indicates a new message has started.
412    #[must_use]
413    pub fn message_id(mut self, message_id: impl IntoOption<MessageId>) -> Self {
414        self.message_id = message_id.into_option();
415        self
416    }
417
418    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
419    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
420    /// these keys.
421    ///
422    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
423    #[must_use]
424    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
425        self.meta = meta.into_option();
426        self
427    }
428}
429
430/// Unique identifier for a message within a session.
431#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
432#[serde(transparent)]
433#[from(Arc<str>, String, &'static str)]
434#[non_exhaustive]
435pub struct MessageId(pub Arc<str>);
436
437impl MessageId {
438    /// Wraps a protocol string as a typed [`MessageId`].
439    #[must_use]
440    pub fn new(id: impl Into<Arc<str>>) -> Self {
441        Self(id.into())
442    }
443}
444
445impl IntoOption<MessageId> for &str {
446    fn into_option(self) -> Option<MessageId> {
447        Some(MessageId::new(self))
448    }
449}
450
451/// Available commands are ready or have changed
452#[serde_as]
453#[skip_serializing_none]
454#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
455#[serde(rename_all = "camelCase")]
456#[non_exhaustive]
457pub struct AvailableCommandsUpdate {
458    /// Commands the agent can execute
459    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
460    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
461    pub available_commands: Vec<AvailableCommand>,
462    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
463    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
464    /// these keys.
465    ///
466    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
467    #[serde(rename = "_meta")]
468    pub meta: Option<Meta>,
469}
470
471impl AvailableCommandsUpdate {
472    /// Builds [`AvailableCommandsUpdate`] with the required fields set; optional fields start unset or empty.
473    #[must_use]
474    pub fn new(available_commands: Vec<AvailableCommand>) -> Self {
475        Self {
476            available_commands,
477            meta: None,
478        }
479    }
480
481    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
482    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
483    /// these keys.
484    ///
485    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
486    #[must_use]
487    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
488        self.meta = meta.into_option();
489        self
490    }
491}
492
493/// Information about a command.
494#[serde_as]
495#[skip_serializing_none]
496#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
497#[serde(rename_all = "camelCase")]
498#[non_exhaustive]
499pub struct AvailableCommand {
500    /// Command name (e.g., `create_plan`, `research_codebase`).
501    pub name: String,
502    /// Human-readable description of what the command does.
503    pub description: String,
504    /// Input for the command if required
505    #[serde_as(deserialize_as = "DefaultOnError")]
506    #[schemars(extend("x-deserialize-default-on-error" = true))]
507    #[serde(default)]
508    pub input: Option<AvailableCommandInput>,
509    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
510    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
511    /// these keys.
512    ///
513    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
514    #[serde(rename = "_meta")]
515    pub meta: Option<Meta>,
516}
517
518impl AvailableCommand {
519    /// Builds [`AvailableCommand`] with the required fields set; optional fields start unset or empty.
520    #[must_use]
521    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
522        Self {
523            name: name.into(),
524            description: description.into(),
525            input: None,
526            meta: None,
527        }
528    }
529
530    /// Input for the command if required
531    #[must_use]
532    pub fn input(mut self, input: impl IntoOption<AvailableCommandInput>) -> Self {
533        self.input = input.into_option();
534        self
535    }
536
537    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
538    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
539    /// these keys.
540    ///
541    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
542    #[must_use]
543    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
544        self.meta = meta.into_option();
545        self
546    }
547}
548
549/// The input specification for a command.
550#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
551#[serde(untagged, rename_all = "camelCase")]
552#[non_exhaustive]
553pub enum AvailableCommandInput {
554    /// All text that was typed after the command name is provided as input.
555    Unstructured(UnstructuredCommandInput),
556}
557
558/// All text that was typed after the command name is provided as input.
559#[skip_serializing_none]
560#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
561#[serde(rename_all = "camelCase")]
562#[non_exhaustive]
563pub struct UnstructuredCommandInput {
564    /// A hint to display when the input hasn't been provided yet
565    pub hint: String,
566    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
567    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
568    /// these keys.
569    ///
570    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
571    #[serde(rename = "_meta")]
572    pub meta: Option<Meta>,
573}
574
575impl UnstructuredCommandInput {
576    /// Builds [`UnstructuredCommandInput`] with the required fields set; optional fields start unset or empty.
577    #[must_use]
578    pub fn new(hint: impl Into<String>) -> Self {
579        Self {
580            hint: hint.into(),
581            meta: None,
582        }
583    }
584
585    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
586    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
587    /// these keys.
588    ///
589    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
590    #[must_use]
591    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
592        self.meta = meta.into_option();
593        self
594    }
595}
596
597// Permission
598
599/// Request for user permission to execute a tool call.
600///
601/// Sent when the agent needs authorization before performing a sensitive operation.
602///
603/// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
604#[skip_serializing_none]
605#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
606#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
607#[serde(rename_all = "camelCase")]
608#[non_exhaustive]
609pub struct RequestPermissionRequest {
610    /// The session ID for this request.
611    pub session_id: SessionId,
612    /// Details about the tool call requiring permission.
613    pub tool_call: ToolCallUpdate,
614    /// Available permission options for the user to choose from.
615    pub options: Vec<PermissionOption>,
616    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
617    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
618    /// these keys.
619    ///
620    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
621    #[serde(rename = "_meta")]
622    pub meta: Option<Meta>,
623}
624
625impl RequestPermissionRequest {
626    /// Builds [`RequestPermissionRequest`] with the required request fields set; optional fields start unset or empty.
627    #[must_use]
628    pub fn new(
629        session_id: impl Into<SessionId>,
630        tool_call: ToolCallUpdate,
631        options: Vec<PermissionOption>,
632    ) -> Self {
633        Self {
634            session_id: session_id.into(),
635            tool_call,
636            options,
637            meta: None,
638        }
639    }
640
641    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
642    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
643    /// these keys.
644    ///
645    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
646    #[must_use]
647    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
648        self.meta = meta.into_option();
649        self
650    }
651}
652
653/// An option presented to the user when requesting permission.
654#[skip_serializing_none]
655#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
656#[serde(rename_all = "camelCase")]
657#[non_exhaustive]
658pub struct PermissionOption {
659    /// Unique identifier for this permission option.
660    pub option_id: PermissionOptionId,
661    /// Human-readable label to display to the user.
662    pub name: String,
663    /// Hint about the nature of this permission option.
664    pub kind: PermissionOptionKind,
665    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
666    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
667    /// these keys.
668    ///
669    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
670    #[serde(rename = "_meta")]
671    pub meta: Option<Meta>,
672}
673
674impl PermissionOption {
675    /// Builds [`PermissionOption`] with the required fields set; optional fields start unset or empty.
676    #[must_use]
677    pub fn new(
678        option_id: impl Into<PermissionOptionId>,
679        name: impl Into<String>,
680        kind: PermissionOptionKind,
681    ) -> Self {
682        Self {
683            option_id: option_id.into(),
684            name: name.into(),
685            kind,
686            meta: None,
687        }
688    }
689
690    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
691    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
692    /// these keys.
693    ///
694    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
695    #[must_use]
696    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
697        self.meta = meta.into_option();
698        self
699    }
700}
701
702/// Unique identifier for a permission option.
703#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
704#[serde(transparent)]
705#[from(Arc<str>, String, &'static str)]
706#[non_exhaustive]
707pub struct PermissionOptionId(pub Arc<str>);
708
709impl PermissionOptionId {
710    /// Wraps a protocol string as a typed [`PermissionOptionId`].
711    #[must_use]
712    pub fn new(id: impl Into<Arc<str>>) -> Self {
713        Self(id.into())
714    }
715}
716
717/// The type of permission option being presented to the user.
718///
719/// Helps clients choose appropriate icons and UI treatment.
720#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
721#[serde(rename_all = "snake_case")]
722#[non_exhaustive]
723pub enum PermissionOptionKind {
724    /// Allow this operation only this time.
725    AllowOnce,
726    /// Allow this operation and remember the choice.
727    AllowAlways,
728    /// Reject this operation only this time.
729    RejectOnce,
730    /// Reject this operation and remember the choice.
731    RejectAlways,
732}
733
734/// Response to a permission request.
735#[skip_serializing_none]
736#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
737#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
738#[serde(rename_all = "camelCase")]
739#[non_exhaustive]
740pub struct RequestPermissionResponse {
741    /// The user's decision on the permission request.
742    // This extra-level is unfortunately needed because the output must be an object
743    pub outcome: RequestPermissionOutcome,
744    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
745    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
746    /// these keys.
747    ///
748    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
749    #[serde(rename = "_meta")]
750    pub meta: Option<Meta>,
751}
752
753impl RequestPermissionResponse {
754    /// Builds [`RequestPermissionResponse`] with the required response fields set; optional fields start unset or empty.
755    #[must_use]
756    pub fn new(outcome: RequestPermissionOutcome) -> Self {
757        Self {
758            outcome,
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/// The outcome of a permission request.
776#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
777#[serde(tag = "outcome", rename_all = "snake_case")]
778#[schemars(extend("discriminator" = {"propertyName": "outcome"}))]
779#[non_exhaustive]
780pub enum RequestPermissionOutcome {
781    /// The prompt turn was cancelled before the user responded.
782    ///
783    /// When a client sends a `session/cancel` notification to cancel an ongoing
784    /// prompt turn, it MUST respond to all pending `session/request_permission`
785    /// requests with this `Cancelled` outcome.
786    ///
787    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
788    Cancelled,
789    /// The user selected one of the provided options.
790    #[serde(rename_all = "camelCase")]
791    Selected(SelectedPermissionOutcome),
792}
793
794/// The user selected one of the provided options.
795#[skip_serializing_none]
796#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
797#[serde(rename_all = "camelCase")]
798#[non_exhaustive]
799pub struct SelectedPermissionOutcome {
800    /// The ID of the option the user selected.
801    pub option_id: PermissionOptionId,
802    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
803    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
804    /// these keys.
805    ///
806    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
807    #[serde(rename = "_meta")]
808    pub meta: Option<Meta>,
809}
810
811impl SelectedPermissionOutcome {
812    /// Builds [`SelectedPermissionOutcome`] with the required fields set; optional fields start unset or empty.
813    #[must_use]
814    pub fn new(option_id: impl Into<PermissionOptionId>) -> Self {
815        Self {
816            option_id: option_id.into(),
817            meta: None,
818        }
819    }
820
821    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
822    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
823    /// these keys.
824    ///
825    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
826    #[must_use]
827    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
828        self.meta = meta.into_option();
829        self
830    }
831}
832
833// Write text file
834
835/// Request to write content to a text file.
836///
837/// Only available if the client supports the `fs.writeTextFile` capability.
838#[skip_serializing_none]
839#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
840#[schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME))]
841#[serde(rename_all = "camelCase")]
842#[non_exhaustive]
843pub struct WriteTextFileRequest {
844    /// The session ID for this request.
845    pub session_id: SessionId,
846    /// Absolute path to the file to write.
847    pub path: PathBuf,
848    /// The text content to write to the file.
849    pub content: String,
850    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
851    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
852    /// these keys.
853    ///
854    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
855    #[serde(rename = "_meta")]
856    pub meta: Option<Meta>,
857}
858
859impl WriteTextFileRequest {
860    /// Builds [`WriteTextFileRequest`] with the required request fields set; optional fields start unset or empty.
861    #[must_use]
862    pub fn new(
863        session_id: impl Into<SessionId>,
864        path: impl Into<PathBuf>,
865        content: impl Into<String>,
866    ) -> Self {
867        Self {
868            session_id: session_id.into(),
869            path: path.into(),
870            content: content.into(),
871            meta: None,
872        }
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 to `fs/write_text_file`
888#[skip_serializing_none]
889#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
890#[serde(rename_all = "camelCase")]
891#[schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME))]
892#[non_exhaustive]
893pub struct WriteTextFileResponse {
894    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
895    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
896    /// these keys.
897    ///
898    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
899    #[serde(rename = "_meta")]
900    pub meta: Option<Meta>,
901}
902
903impl WriteTextFileResponse {
904    /// Builds [`WriteTextFileResponse`] with the required response fields set; optional fields start unset or empty.
905    #[must_use]
906    pub fn new() -> Self {
907        Self::default()
908    }
909
910    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
911    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
912    /// these keys.
913    ///
914    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
915    #[must_use]
916    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
917        self.meta = meta.into_option();
918        self
919    }
920}
921
922// Read text file
923
924/// Request to read content from a text file.
925///
926/// Only available if the client supports the `fs.readTextFile` capability.
927#[skip_serializing_none]
928#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
929#[schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME))]
930#[serde(rename_all = "camelCase")]
931#[non_exhaustive]
932pub struct ReadTextFileRequest {
933    /// The session ID for this request.
934    pub session_id: SessionId,
935    /// Absolute path to the file to read.
936    pub path: PathBuf,
937    /// Line number to start reading from (1-based).
938    pub line: Option<u32>,
939    /// Maximum number of lines to read.
940    pub limit: Option<u32>,
941    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
942    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
943    /// these keys.
944    ///
945    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
946    #[serde(rename = "_meta")]
947    pub meta: Option<Meta>,
948}
949
950impl ReadTextFileRequest {
951    /// Builds [`ReadTextFileRequest`] with the required request fields set; optional fields start unset or empty.
952    #[must_use]
953    pub fn new(session_id: impl Into<SessionId>, path: impl Into<PathBuf>) -> Self {
954        Self {
955            session_id: session_id.into(),
956            path: path.into(),
957            line: None,
958            limit: None,
959            meta: None,
960        }
961    }
962
963    /// Line number to start reading from (1-based).
964    #[must_use]
965    pub fn line(mut self, line: impl IntoOption<u32>) -> Self {
966        self.line = line.into_option();
967        self
968    }
969
970    /// Maximum number of lines to read.
971    #[must_use]
972    pub fn limit(mut self, limit: impl IntoOption<u32>) -> Self {
973        self.limit = limit.into_option();
974        self
975    }
976
977    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
978    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
979    /// these keys.
980    ///
981    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
982    #[must_use]
983    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
984        self.meta = meta.into_option();
985        self
986    }
987}
988
989/// Response containing the contents of a text file.
990#[skip_serializing_none]
991#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
992#[schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME))]
993#[serde(rename_all = "camelCase")]
994#[non_exhaustive]
995pub struct ReadTextFileResponse {
996    /// Content payload returned by this response.
997    pub content: String,
998    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
999    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1000    /// these keys.
1001    ///
1002    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1003    #[serde(rename = "_meta")]
1004    pub meta: Option<Meta>,
1005}
1006
1007impl ReadTextFileResponse {
1008    /// Builds [`ReadTextFileResponse`] with the required response fields set; optional fields start unset or empty.
1009    #[must_use]
1010    pub fn new(content: impl Into<String>) -> Self {
1011        Self {
1012            content: content.into(),
1013            meta: None,
1014        }
1015    }
1016
1017    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1018    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1019    /// these keys.
1020    ///
1021    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1022    #[must_use]
1023    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1024        self.meta = meta.into_option();
1025        self
1026    }
1027}
1028
1029// Terminals
1030
1031/// Typed identifier used for terminal values on the wire.
1032#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
1033#[serde(transparent)]
1034#[from(Arc<str>, String, &'static str)]
1035#[non_exhaustive]
1036pub struct TerminalId(pub Arc<str>);
1037
1038impl TerminalId {
1039    /// Wraps a protocol string as a typed [`TerminalId`].
1040    #[must_use]
1041    pub fn new(id: impl Into<Arc<str>>) -> Self {
1042        Self(id.into())
1043    }
1044}
1045
1046/// Request to create a new terminal and execute a command.
1047#[skip_serializing_none]
1048#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1049#[serde(rename_all = "camelCase")]
1050#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME))]
1051#[non_exhaustive]
1052pub struct CreateTerminalRequest {
1053    /// The session ID for this request.
1054    pub session_id: SessionId,
1055    /// The command to execute.
1056    pub command: String,
1057    /// Array of command arguments.
1058    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1059    pub args: Vec<String>,
1060    /// Environment variables for the command.
1061    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1062    pub env: Vec<EnvVariable>,
1063    /// Working directory for the command (absolute path).
1064    pub cwd: Option<PathBuf>,
1065    /// Maximum number of output bytes to retain.
1066    ///
1067    /// When the limit is exceeded, the Client truncates from the beginning of the output
1068    /// to stay within the limit.
1069    ///
1070    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
1071    /// string output, even if this means the retained output is slightly less than the
1072    /// specified limit.
1073    pub output_byte_limit: Option<u64>,
1074    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1075    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1076    /// these keys.
1077    ///
1078    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1079    #[serde(rename = "_meta")]
1080    pub meta: Option<Meta>,
1081}
1082
1083impl CreateTerminalRequest {
1084    /// Builds [`CreateTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1085    #[must_use]
1086    pub fn new(session_id: impl Into<SessionId>, command: impl Into<String>) -> Self {
1087        Self {
1088            session_id: session_id.into(),
1089            command: command.into(),
1090            args: Vec::new(),
1091            env: Vec::new(),
1092            cwd: None,
1093            output_byte_limit: None,
1094            meta: None,
1095        }
1096    }
1097
1098    /// Array of command arguments.
1099    #[must_use]
1100    pub fn args(mut self, args: Vec<String>) -> Self {
1101        self.args = args;
1102        self
1103    }
1104
1105    /// Environment variables for the command.
1106    #[must_use]
1107    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1108        self.env = env;
1109        self
1110    }
1111
1112    /// Working directory for the command (absolute path).
1113    #[must_use]
1114    pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1115        self.cwd = cwd.into_option();
1116        self
1117    }
1118
1119    /// Maximum number of output bytes to retain.
1120    ///
1121    /// When the limit is exceeded, the Client truncates from the beginning of the output
1122    /// to stay within the limit.
1123    ///
1124    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
1125    /// string output, even if this means the retained output is slightly less than the
1126    /// specified limit.
1127    #[must_use]
1128    pub fn output_byte_limit(mut self, output_byte_limit: impl IntoOption<u64>) -> Self {
1129        self.output_byte_limit = output_byte_limit.into_option();
1130        self
1131    }
1132
1133    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1134    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1135    /// these keys.
1136    ///
1137    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1138    #[must_use]
1139    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1140        self.meta = meta.into_option();
1141        self
1142    }
1143}
1144
1145/// Response containing the ID of the created terminal.
1146#[skip_serializing_none]
1147#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1148#[serde(rename_all = "camelCase")]
1149#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME))]
1150#[non_exhaustive]
1151pub struct CreateTerminalResponse {
1152    /// The unique identifier for the created terminal.
1153    pub terminal_id: TerminalId,
1154    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1155    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1156    /// these keys.
1157    ///
1158    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1159    #[serde(rename = "_meta")]
1160    pub meta: Option<Meta>,
1161}
1162
1163impl CreateTerminalResponse {
1164    /// Builds [`CreateTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1165    #[must_use]
1166    pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
1167        Self {
1168            terminal_id: terminal_id.into(),
1169            meta: None,
1170        }
1171    }
1172
1173    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1174    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1175    /// these keys.
1176    ///
1177    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1178    #[must_use]
1179    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1180        self.meta = meta.into_option();
1181        self
1182    }
1183}
1184
1185/// Request to get the current output and status of a terminal.
1186#[skip_serializing_none]
1187#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1188#[serde(rename_all = "camelCase")]
1189#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME))]
1190#[non_exhaustive]
1191pub struct TerminalOutputRequest {
1192    /// The session ID for this request.
1193    pub session_id: SessionId,
1194    /// The ID of the terminal to get output from.
1195    pub terminal_id: TerminalId,
1196    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1197    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1198    /// these keys.
1199    ///
1200    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1201    #[serde(rename = "_meta")]
1202    pub meta: Option<Meta>,
1203}
1204
1205impl TerminalOutputRequest {
1206    /// Builds [`TerminalOutputRequest`] with the required request fields set; optional fields start unset or empty.
1207    #[must_use]
1208    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1209        Self {
1210            session_id: session_id.into(),
1211            terminal_id: terminal_id.into(),
1212            meta: None,
1213        }
1214    }
1215
1216    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1217    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1218    /// these keys.
1219    ///
1220    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1221    #[must_use]
1222    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1223        self.meta = meta.into_option();
1224        self
1225    }
1226}
1227
1228/// Response containing the terminal output and exit status.
1229#[skip_serializing_none]
1230#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1231#[serde(rename_all = "camelCase")]
1232#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME))]
1233#[non_exhaustive]
1234pub struct TerminalOutputResponse {
1235    /// The terminal output captured so far.
1236    pub output: String,
1237    /// Whether the output was truncated due to byte limits.
1238    pub truncated: bool,
1239    /// Exit status if the command has completed.
1240    pub exit_status: Option<TerminalExitStatus>,
1241    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1242    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1243    /// these keys.
1244    ///
1245    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1246    #[serde(rename = "_meta")]
1247    pub meta: Option<Meta>,
1248}
1249
1250impl TerminalOutputResponse {
1251    /// Builds [`TerminalOutputResponse`] with the required response fields set; optional fields start unset or empty.
1252    #[must_use]
1253    pub fn new(output: impl Into<String>, truncated: bool) -> Self {
1254        Self {
1255            output: output.into(),
1256            truncated,
1257            exit_status: None,
1258            meta: None,
1259        }
1260    }
1261
1262    /// Exit status if the command has completed.
1263    #[must_use]
1264    pub fn exit_status(mut self, exit_status: impl IntoOption<TerminalExitStatus>) -> Self {
1265        self.exit_status = exit_status.into_option();
1266        self
1267    }
1268
1269    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1270    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1271    /// these keys.
1272    ///
1273    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1274    #[must_use]
1275    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1276        self.meta = meta.into_option();
1277        self
1278    }
1279}
1280
1281/// Request to release a terminal and free its resources.
1282#[skip_serializing_none]
1283#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1284#[serde(rename_all = "camelCase")]
1285#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME))]
1286#[non_exhaustive]
1287pub struct ReleaseTerminalRequest {
1288    /// The session ID for this request.
1289    pub session_id: SessionId,
1290    /// The ID of the terminal to release.
1291    pub terminal_id: TerminalId,
1292    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1293    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1294    /// these keys.
1295    ///
1296    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1297    #[serde(rename = "_meta")]
1298    pub meta: Option<Meta>,
1299}
1300
1301impl ReleaseTerminalRequest {
1302    /// Builds [`ReleaseTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1303    #[must_use]
1304    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1305        Self {
1306            session_id: session_id.into(),
1307            terminal_id: terminal_id.into(),
1308            meta: None,
1309        }
1310    }
1311
1312    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1313    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1314    /// these keys.
1315    ///
1316    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1317    #[must_use]
1318    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1319        self.meta = meta.into_option();
1320        self
1321    }
1322}
1323
1324/// Response to terminal/release method
1325#[skip_serializing_none]
1326#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1327#[serde(rename_all = "camelCase")]
1328#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME))]
1329#[non_exhaustive]
1330pub struct ReleaseTerminalResponse {
1331    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1332    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1333    /// these keys.
1334    ///
1335    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1336    #[serde(rename = "_meta")]
1337    pub meta: Option<Meta>,
1338}
1339
1340impl ReleaseTerminalResponse {
1341    /// Builds [`ReleaseTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1342    #[must_use]
1343    pub fn new() -> Self {
1344        Self::default()
1345    }
1346
1347    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1348    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1349    /// these keys.
1350    ///
1351    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1352    #[must_use]
1353    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1354        self.meta = meta.into_option();
1355        self
1356    }
1357}
1358
1359/// Request to kill a terminal without releasing it.
1360#[skip_serializing_none]
1361#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1362#[serde(rename_all = "camelCase")]
1363#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME))]
1364#[non_exhaustive]
1365pub struct KillTerminalRequest {
1366    /// The session ID for this request.
1367    pub session_id: SessionId,
1368    /// The ID of the terminal to kill.
1369    pub terminal_id: TerminalId,
1370    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1371    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1372    /// these keys.
1373    ///
1374    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1375    #[serde(rename = "_meta")]
1376    pub meta: Option<Meta>,
1377}
1378
1379impl KillTerminalRequest {
1380    /// Builds [`KillTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1381    #[must_use]
1382    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1383        Self {
1384            session_id: session_id.into(),
1385            terminal_id: terminal_id.into(),
1386            meta: None,
1387        }
1388    }
1389
1390    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1391    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1392    /// these keys.
1393    ///
1394    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1395    #[must_use]
1396    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1397        self.meta = meta.into_option();
1398        self
1399    }
1400}
1401
1402/// Response to `terminal/kill` method
1403#[skip_serializing_none]
1404#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1405#[serde(rename_all = "camelCase")]
1406#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME))]
1407#[non_exhaustive]
1408pub struct KillTerminalResponse {
1409    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1410    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1411    /// these keys.
1412    ///
1413    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1414    #[serde(rename = "_meta")]
1415    pub meta: Option<Meta>,
1416}
1417
1418impl KillTerminalResponse {
1419    /// Builds [`KillTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1420    #[must_use]
1421    pub fn new() -> Self {
1422        Self::default()
1423    }
1424
1425    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1426    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1427    /// these keys.
1428    ///
1429    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1430    #[must_use]
1431    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1432        self.meta = meta.into_option();
1433        self
1434    }
1435}
1436
1437/// Request to wait for a terminal command to exit.
1438#[skip_serializing_none]
1439#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1440#[serde(rename_all = "camelCase")]
1441#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME))]
1442#[non_exhaustive]
1443pub struct WaitForTerminalExitRequest {
1444    /// The session ID for this request.
1445    pub session_id: SessionId,
1446    /// The ID of the terminal to wait for.
1447    pub terminal_id: TerminalId,
1448    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1449    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1450    /// these keys.
1451    ///
1452    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1453    #[serde(rename = "_meta")]
1454    pub meta: Option<Meta>,
1455}
1456
1457impl WaitForTerminalExitRequest {
1458    /// Builds [`WaitForTerminalExitRequest`] with the required request fields set; optional fields start unset or empty.
1459    #[must_use]
1460    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1461        Self {
1462            session_id: session_id.into(),
1463            terminal_id: terminal_id.into(),
1464            meta: None,
1465        }
1466    }
1467
1468    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1469    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1470    /// these keys.
1471    ///
1472    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1473    #[must_use]
1474    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1475        self.meta = meta.into_option();
1476        self
1477    }
1478}
1479
1480/// Response containing the exit status of a terminal command.
1481#[skip_serializing_none]
1482#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1483#[serde(rename_all = "camelCase")]
1484#[schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME))]
1485#[non_exhaustive]
1486pub struct WaitForTerminalExitResponse {
1487    /// The exit status of the terminal command.
1488    #[serde(flatten)]
1489    pub exit_status: TerminalExitStatus,
1490    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1491    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1492    /// these keys.
1493    ///
1494    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1495    #[serde(rename = "_meta")]
1496    pub meta: Option<Meta>,
1497}
1498
1499impl WaitForTerminalExitResponse {
1500    /// Builds [`WaitForTerminalExitResponse`] with the required response fields set; optional fields start unset or empty.
1501    #[must_use]
1502    pub fn new(exit_status: TerminalExitStatus) -> Self {
1503        Self {
1504            exit_status,
1505            meta: None,
1506        }
1507    }
1508
1509    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1510    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1511    /// these keys.
1512    ///
1513    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1514    #[must_use]
1515    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1516        self.meta = meta.into_option();
1517        self
1518    }
1519}
1520
1521/// Exit status of a terminal command.
1522#[skip_serializing_none]
1523#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1524#[serde(rename_all = "camelCase")]
1525#[non_exhaustive]
1526pub struct TerminalExitStatus {
1527    /// The process exit code (may be null if terminated by signal).
1528    pub exit_code: Option<u32>,
1529    /// The signal that terminated the process (may be null if exited normally).
1530    pub signal: Option<String>,
1531    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1532    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1533    /// these keys.
1534    ///
1535    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1536    #[serde(rename = "_meta")]
1537    pub meta: Option<Meta>,
1538}
1539
1540impl TerminalExitStatus {
1541    /// Builds [`TerminalExitStatus`] with the required fields set; optional fields start unset or empty.
1542    #[must_use]
1543    pub fn new() -> Self {
1544        Self::default()
1545    }
1546
1547    /// The process exit code (may be null if terminated by signal).
1548    #[must_use]
1549    pub fn exit_code(mut self, exit_code: impl IntoOption<u32>) -> Self {
1550        self.exit_code = exit_code.into_option();
1551        self
1552    }
1553
1554    /// The signal that terminated the process (may be null if exited normally).
1555    #[must_use]
1556    pub fn signal(mut self, signal: impl IntoOption<String>) -> Self {
1557        self.signal = signal.into_option();
1558        self
1559    }
1560
1561    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1562    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1563    /// these keys.
1564    ///
1565    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1566    #[must_use]
1567    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1568        self.meta = meta.into_option();
1569        self
1570    }
1571}
1572
1573// Capabilities
1574
1575/// Capabilities supported by the client.
1576///
1577/// Advertised during initialization to inform the agent about
1578/// available features and methods.
1579///
1580/// See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/initialization#client-capabilities)
1581#[serde_as]
1582#[skip_serializing_none]
1583#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1584#[serde(rename_all = "camelCase")]
1585#[non_exhaustive]
1586pub struct ClientCapabilities {
1587    /// File system capabilities supported by the client.
1588    /// Determines which file operations the agent can request.
1589    #[serde(default)]
1590    pub fs: FileSystemCapabilities,
1591    /// Whether the Client support all `terminal/*` methods.
1592    #[serde(default)]
1593    pub terminal: bool,
1594    /// **UNSTABLE**
1595    ///
1596    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1597    ///
1598    /// Whether the client supports `plan_update` and `plan_removed` session updates.
1599    ///
1600    /// Optional. Omitted means the client does not advertise support.
1601    /// Supplying `{}` means the client can receive both update types.
1602    #[cfg(feature = "unstable_plan_operations")]
1603    #[serde_as(deserialize_as = "DefaultOnError")]
1604    #[schemars(extend("x-deserialize-default-on-error" = true))]
1605    #[serde(default)]
1606    pub plan: Option<PlanCapabilities>,
1607    /// **UNSTABLE**
1608    ///
1609    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1610    ///
1611    /// Authentication capabilities supported by the client.
1612    /// Determines which authentication method types the agent may include
1613    /// in its `InitializeResponse`.
1614    #[cfg(feature = "unstable_auth_methods")]
1615    #[serde(default)]
1616    pub auth: AuthCapabilities,
1617    /// **UNSTABLE**
1618    ///
1619    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1620    ///
1621    /// Elicitation capabilities supported by the client.
1622    /// Determines which elicitation modes the agent may use.
1623    #[cfg(feature = "unstable_elicitation")]
1624    #[serde_as(deserialize_as = "DefaultOnError")]
1625    #[schemars(extend("x-deserialize-default-on-error" = true))]
1626    #[serde(default)]
1627    pub elicitation: Option<ElicitationCapabilities>,
1628    /// **UNSTABLE**
1629    ///
1630    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1631    ///
1632    /// NES (Next Edit Suggestions) capabilities supported by the client.
1633    #[cfg(feature = "unstable_nes")]
1634    #[serde_as(deserialize_as = "DefaultOnError")]
1635    #[schemars(extend("x-deserialize-default-on-error" = true))]
1636    #[serde(default)]
1637    pub nes: Option<ClientNesCapabilities>,
1638    /// **UNSTABLE**
1639    ///
1640    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1641    ///
1642    /// The position encodings supported by the client, in order of preference.
1643    #[cfg(feature = "unstable_nes")]
1644    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1645    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1646    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1647    pub position_encodings: Vec<PositionEncodingKind>,
1648
1649    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1650    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1651    /// these keys.
1652    ///
1653    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1654    #[serde(rename = "_meta")]
1655    pub meta: Option<Meta>,
1656}
1657
1658impl ClientCapabilities {
1659    /// Builds an empty [`ClientCapabilities`]; use builder methods to advertise supported sub-capabilities.
1660    #[must_use]
1661    pub fn new() -> Self {
1662        Self::default()
1663    }
1664
1665    /// File system capabilities supported by the client.
1666    /// Determines which file operations the agent can request.
1667    #[must_use]
1668    pub fn fs(mut self, fs: FileSystemCapabilities) -> Self {
1669        self.fs = fs;
1670        self
1671    }
1672
1673    /// Whether the Client support all `terminal/*` methods.
1674    #[must_use]
1675    pub fn terminal(mut self, terminal: bool) -> Self {
1676        self.terminal = terminal;
1677        self
1678    }
1679
1680    /// **UNSTABLE**
1681    ///
1682    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1683    ///
1684    /// Whether the client supports `plan_update` and `plan_removed` session updates.
1685    ///
1686    /// Omitted means the client does not advertise support.
1687    /// Supplying `{}` means the client can receive both update types.
1688    #[cfg(feature = "unstable_plan_operations")]
1689    #[must_use]
1690    pub fn plan(mut self, plan: impl IntoOption<PlanCapabilities>) -> Self {
1691        self.plan = plan.into_option();
1692        self
1693    }
1694
1695    /// **UNSTABLE**
1696    ///
1697    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1698    ///
1699    /// Authentication capabilities supported by the client.
1700    /// Determines which authentication method types the agent may include
1701    /// in its `InitializeResponse`.
1702    #[cfg(feature = "unstable_auth_methods")]
1703    #[must_use]
1704    pub fn auth(mut self, auth: AuthCapabilities) -> Self {
1705        self.auth = auth;
1706        self
1707    }
1708
1709    /// **UNSTABLE**
1710    ///
1711    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1712    ///
1713    /// Elicitation capabilities supported by the client.
1714    /// Determines which elicitation modes the agent may use.
1715    #[cfg(feature = "unstable_elicitation")]
1716    #[must_use]
1717    pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
1718        self.elicitation = elicitation.into_option();
1719        self
1720    }
1721
1722    /// **UNSTABLE**
1723    ///
1724    /// NES (Next Edit Suggestions) capabilities supported by the client.
1725    #[cfg(feature = "unstable_nes")]
1726    #[must_use]
1727    pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
1728        self.nes = nes.into_option();
1729        self
1730    }
1731
1732    /// **UNSTABLE**
1733    ///
1734    /// The position encodings supported by the client, in order of preference.
1735    #[cfg(feature = "unstable_nes")]
1736    #[must_use]
1737    pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
1738        self.position_encodings = position_encodings;
1739        self
1740    }
1741
1742    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1743    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1744    /// these keys.
1745    ///
1746    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1747    #[must_use]
1748    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1749        self.meta = meta.into_option();
1750        self
1751    }
1752}
1753
1754/// **UNSTABLE**
1755///
1756/// This capability is not part of the spec yet, and may be removed or changed at any point.
1757///
1758/// Authentication capabilities supported by the client.
1759///
1760/// Advertised during initialization to inform the agent which authentication
1761/// method types the client can handle. This governs opt-in types that require
1762/// additional client-side support.
1763#[cfg(feature = "unstable_auth_methods")]
1764#[skip_serializing_none]
1765#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1766#[serde(rename_all = "camelCase")]
1767#[non_exhaustive]
1768pub struct AuthCapabilities {
1769    /// Whether the client supports `terminal` authentication methods.
1770    ///
1771    /// When `true`, the agent may include `terminal` entries in its authentication methods.
1772    #[serde(default)]
1773    pub terminal: bool,
1774    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1775    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1776    /// these keys.
1777    ///
1778    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1779    #[serde(rename = "_meta")]
1780    pub meta: Option<Meta>,
1781}
1782
1783#[cfg(feature = "unstable_auth_methods")]
1784impl AuthCapabilities {
1785    /// Builds an empty [`AuthCapabilities`]; use builder methods to advertise supported sub-capabilities.
1786    #[must_use]
1787    pub fn new() -> Self {
1788        Self::default()
1789    }
1790
1791    /// Whether the client supports `terminal` authentication methods.
1792    ///
1793    /// When `true`, the agent may include `AuthMethod::Terminal`
1794    /// entries in its authentication methods.
1795    #[must_use]
1796    pub fn terminal(mut self, terminal: bool) -> Self {
1797        self.terminal = terminal;
1798        self
1799    }
1800
1801    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1802    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1803    /// these keys.
1804    ///
1805    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1806    #[must_use]
1807    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1808        self.meta = meta.into_option();
1809        self
1810    }
1811}
1812
1813/// File system capabilities that a client may support.
1814///
1815/// See protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initialization#filesystem)
1816#[skip_serializing_none]
1817#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1818#[serde(rename_all = "camelCase")]
1819#[non_exhaustive]
1820pub struct FileSystemCapabilities {
1821    /// Whether the Client supports `fs/read_text_file` requests.
1822    #[serde(default)]
1823    pub read_text_file: bool,
1824    /// Whether the Client supports `fs/write_text_file` requests.
1825    #[serde(default)]
1826    pub write_text_file: bool,
1827    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1828    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1829    /// these keys.
1830    ///
1831    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1832    #[serde(rename = "_meta")]
1833    pub meta: Option<Meta>,
1834}
1835
1836impl FileSystemCapabilities {
1837    /// Builds an empty [`FileSystemCapabilities`]; use builder methods to advertise supported sub-capabilities.
1838    #[must_use]
1839    pub fn new() -> Self {
1840        Self::default()
1841    }
1842
1843    /// Whether the Client supports `fs/read_text_file` requests.
1844    #[must_use]
1845    pub fn read_text_file(mut self, read_text_file: bool) -> Self {
1846        self.read_text_file = read_text_file;
1847        self
1848    }
1849
1850    /// Whether the Client supports `fs/write_text_file` requests.
1851    #[must_use]
1852    pub fn write_text_file(mut self, write_text_file: bool) -> Self {
1853        self.write_text_file = write_text_file;
1854        self
1855    }
1856
1857    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1858    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1859    /// these keys.
1860    ///
1861    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1862    #[must_use]
1863    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1864        self.meta = meta.into_option();
1865        self
1866    }
1867}
1868
1869// Method schema
1870
1871/// Names of all methods that clients handle.
1872///
1873/// Provides a centralized definition of method names used in the protocol.
1874#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1875#[non_exhaustive]
1876pub struct ClientMethodNames {
1877    /// Method for requesting permission from the user.
1878    pub session_request_permission: &'static str,
1879    /// Notification for session updates.
1880    pub session_update: &'static str,
1881    /// Method for writing text files.
1882    pub fs_write_text_file: &'static str,
1883    /// Method for reading text files.
1884    pub fs_read_text_file: &'static str,
1885    /// Method for creating new terminals.
1886    pub terminal_create: &'static str,
1887    /// Method for getting terminals output.
1888    pub terminal_output: &'static str,
1889    /// Method for releasing a terminal.
1890    pub terminal_release: &'static str,
1891    /// Method for waiting for a terminal to finish.
1892    pub terminal_wait_for_exit: &'static str,
1893    /// Method for killing a terminal.
1894    pub terminal_kill: &'static str,
1895    /// Method for opening an MCP-over-ACP connection.
1896    #[cfg(feature = "unstable_mcp_over_acp")]
1897    pub mcp_connect: &'static str,
1898    /// Method for exchanging MCP-over-ACP messages.
1899    #[cfg(feature = "unstable_mcp_over_acp")]
1900    pub mcp_message: &'static str,
1901    /// Method for closing an MCP-over-ACP connection.
1902    #[cfg(feature = "unstable_mcp_over_acp")]
1903    pub mcp_disconnect: &'static str,
1904    /// Method for elicitation.
1905    #[cfg(feature = "unstable_elicitation")]
1906    pub elicitation_create: &'static str,
1907    /// Notification for elicitation completion.
1908    #[cfg(feature = "unstable_elicitation")]
1909    pub elicitation_complete: &'static str,
1910}
1911
1912/// Constant containing all client method names.
1913pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
1914    session_update: SESSION_UPDATE_NOTIFICATION,
1915    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
1916    fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
1917    fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
1918    terminal_create: TERMINAL_CREATE_METHOD_NAME,
1919    terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
1920    terminal_release: TERMINAL_RELEASE_METHOD_NAME,
1921    terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
1922    terminal_kill: TERMINAL_KILL_METHOD_NAME,
1923    #[cfg(feature = "unstable_mcp_over_acp")]
1924    mcp_connect: MCP_CONNECT_METHOD_NAME,
1925    #[cfg(feature = "unstable_mcp_over_acp")]
1926    mcp_message: MCP_MESSAGE_METHOD_NAME,
1927    #[cfg(feature = "unstable_mcp_over_acp")]
1928    mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
1929    #[cfg(feature = "unstable_elicitation")]
1930    elicitation_create: ELICITATION_CREATE_METHOD_NAME,
1931    #[cfg(feature = "unstable_elicitation")]
1932    elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
1933};
1934
1935/// Notification name for session updates.
1936pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
1937/// Method name for requesting user permission.
1938pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
1939/// Method name for writing text files.
1940pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
1941/// Method name for reading text files.
1942pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
1943/// Method name for creating a new terminal.
1944pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
1945/// Method for getting terminals output.
1946pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
1947/// Method for releasing a terminal.
1948pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
1949/// Method for waiting for a terminal to finish.
1950pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
1951/// Method for killing a terminal.
1952pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
1953/// Method name for elicitation.
1954#[cfg(feature = "unstable_elicitation")]
1955pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
1956/// Notification name for elicitation completion.
1957#[cfg(feature = "unstable_elicitation")]
1958pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
1959
1960/// All possible requests that an agent can send to a client.
1961///
1962/// This enum is used internally for routing RPC requests. You typically won't need
1963/// to use this directly.
1964///
1965/// This enum encompasses all method calls from agent to client.
1966#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1967#[serde(untagged)]
1968#[schemars(inline)]
1969#[non_exhaustive]
1970#[allow(clippy::large_enum_variant)]
1971pub enum AgentRequest {
1972    /// Writes content to a text file in the client's file system.
1973    ///
1974    /// Only available if the client advertises the `fs.writeTextFile` capability.
1975    /// Allows the agent to create or modify files within the client's environment.
1976    ///
1977    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
1978    WriteTextFileRequest(WriteTextFileRequest),
1979    /// Reads content from a text file in the client's file system.
1980    ///
1981    /// Only available if the client advertises the `fs.readTextFile` capability.
1982    /// Allows the agent to access file contents within the client's environment.
1983    ///
1984    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
1985    ReadTextFileRequest(ReadTextFileRequest),
1986    /// Requests permission from the user for a tool call operation.
1987    ///
1988    /// Called by the agent when it needs user authorization before executing
1989    /// a potentially sensitive operation. The client should present the options
1990    /// to the user and return their decision.
1991    ///
1992    /// If the client cancels the prompt turn via `session/cancel`, it MUST
1993    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
1994    ///
1995    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
1996    RequestPermissionRequest(RequestPermissionRequest),
1997    /// Executes a command in a new terminal
1998    ///
1999    /// Only available if the `terminal` Client capability is set to `true`.
2000    ///
2001    /// Returns a `TerminalId` that can be used with other terminal methods
2002    /// to get the current output, wait for exit, and kill the command.
2003    ///
2004    /// The `TerminalId` can also be used to embed the terminal in a tool call
2005    /// by using the `ToolCallContent::Terminal` variant.
2006    ///
2007    /// The Agent is responsible for releasing the terminal by using the `terminal/release`
2008    /// method.
2009    ///
2010    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2011    CreateTerminalRequest(CreateTerminalRequest),
2012    /// Gets the terminal output and exit status
2013    ///
2014    /// Returns the current content in the terminal without waiting for the command to exit.
2015    /// If the command has already exited, the exit status is included.
2016    ///
2017    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2018    TerminalOutputRequest(TerminalOutputRequest),
2019    /// Releases a terminal
2020    ///
2021    /// The command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`
2022    /// to wait for the command to exit before releasing the terminal.
2023    ///
2024    /// After release, the `TerminalId` can no longer be used with other `terminal/*` methods,
2025    /// but tool calls that already contain it, continue to display its output.
2026    ///
2027    /// The `terminal/kill` method can be used to terminate the command without releasing
2028    /// the terminal, allowing the Agent to call `terminal/output` and other methods.
2029    ///
2030    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2031    ReleaseTerminalRequest(ReleaseTerminalRequest),
2032    /// Waits for the terminal command to exit and return its exit status
2033    ///
2034    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2035    WaitForTerminalExitRequest(WaitForTerminalExitRequest),
2036    /// Kills the terminal command without releasing the terminal
2037    ///
2038    /// While `terminal/release` will also kill the command, this method will keep
2039    /// the `TerminalId` valid so it can be used with other methods.
2040    ///
2041    /// This method can be helpful when implementing command timeouts which terminate
2042    /// the command as soon as elapsed, and then get the final output so it can be sent
2043    /// to the model.
2044    ///
2045    /// Note: Call `terminal/release` when `TerminalId` is no longer needed.
2046    ///
2047    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2048    KillTerminalRequest(KillTerminalRequest),
2049    /// **UNSTABLE**
2050    ///
2051    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2052    ///
2053    /// Requests structured user input via a form or URL.
2054    #[cfg(feature = "unstable_elicitation")]
2055    CreateElicitationRequest(CreateElicitationRequest),
2056    /// **UNSTABLE**
2057    ///
2058    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2059    ///
2060    /// Opens an MCP-over-ACP connection.
2061    #[cfg(feature = "unstable_mcp_over_acp")]
2062    ConnectMcpRequest(ConnectMcpRequest),
2063    /// **UNSTABLE**
2064    ///
2065    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2066    ///
2067    /// Exchanges an MCP-over-ACP message.
2068    #[cfg(feature = "unstable_mcp_over_acp")]
2069    MessageMcpRequest(MessageMcpRequest),
2070    /// **UNSTABLE**
2071    ///
2072    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2073    ///
2074    /// Closes an MCP-over-ACP connection.
2075    #[cfg(feature = "unstable_mcp_over_acp")]
2076    DisconnectMcpRequest(DisconnectMcpRequest),
2077    /// Handles extension method requests from the agent.
2078    ///
2079    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
2080    /// Extension methods provide a way to add custom functionality while maintaining
2081    /// protocol compatibility.
2082    ///
2083    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2084    ExtMethodRequest(ExtRequest),
2085}
2086
2087impl AgentRequest {
2088    /// Returns the corresponding method name of the request.
2089    #[must_use]
2090    pub fn method(&self) -> &str {
2091        match self {
2092            Self::WriteTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_write_text_file,
2093            Self::ReadTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_read_text_file,
2094            Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
2095            Self::CreateTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_create,
2096            Self::TerminalOutputRequest(_) => CLIENT_METHOD_NAMES.terminal_output,
2097            Self::ReleaseTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_release,
2098            Self::WaitForTerminalExitRequest(_) => CLIENT_METHOD_NAMES.terminal_wait_for_exit,
2099            Self::KillTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_kill,
2100            #[cfg(feature = "unstable_elicitation")]
2101            Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
2102            #[cfg(feature = "unstable_mcp_over_acp")]
2103            Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
2104            #[cfg(feature = "unstable_mcp_over_acp")]
2105            Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
2106            #[cfg(feature = "unstable_mcp_over_acp")]
2107            Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
2108            Self::ExtMethodRequest(ext_request) => &ext_request.method,
2109        }
2110    }
2111}
2112
2113/// All possible responses that a client can send to an agent.
2114///
2115/// This enum is used internally for routing RPC responses. You typically won't need
2116/// to use this directly - the responses are handled automatically by the connection.
2117///
2118/// These are responses to the corresponding `AgentRequest` variants.
2119#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
2120#[serde(untagged)]
2121#[schemars(inline)]
2122#[non_exhaustive]
2123pub enum ClientResponse {
2124    /// Successful result returned for a `fs/write_text_file` request.
2125    WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
2126    /// Successful result returned for a `fs/read_text_file` request.
2127    ReadTextFileResponse(ReadTextFileResponse),
2128    /// Successful result returned for a `session/request_permission` request.
2129    RequestPermissionResponse(RequestPermissionResponse),
2130    /// Successful result returned for a `terminal/create` request.
2131    CreateTerminalResponse(CreateTerminalResponse),
2132    /// Successful result returned for a `terminal/output` request.
2133    TerminalOutputResponse(TerminalOutputResponse),
2134    /// Successful result returned for a `terminal/release` request.
2135    ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
2136    /// Successful result returned for a `terminal/wait_for_exit` request.
2137    WaitForTerminalExitResponse(WaitForTerminalExitResponse),
2138    /// Successful result returned for a `terminal/kill` request.
2139    KillTerminalResponse(#[serde(default)] KillTerminalResponse),
2140    /// Successful result returned for a `elicitation/create` request.
2141    #[cfg(feature = "unstable_elicitation")]
2142    CreateElicitationResponse(CreateElicitationResponse),
2143    /// Successful result returned for a `mcp/connect` request.
2144    #[cfg(feature = "unstable_mcp_over_acp")]
2145    ConnectMcpResponse(ConnectMcpResponse),
2146    /// Successful result returned for a `mcp/disconnect` request.
2147    #[cfg(feature = "unstable_mcp_over_acp")]
2148    DisconnectMcpResponse(#[serde(default)] DisconnectMcpResponse),
2149    /// Successful result returned by an extension method outside the core ACP method set.
2150    ExtMethodResponse(ExtResponse),
2151    /// Successful result returned by an MCP-over-ACP `mcp/message` request.
2152    #[cfg(feature = "unstable_mcp_over_acp")]
2153    MessageMcpResponse(MessageMcpResponse),
2154}
2155
2156/// All possible notifications that an agent can send to a client.
2157///
2158/// This enum is used internally for routing RPC notifications. You typically won't need
2159/// to use this directly.
2160///
2161/// Notifications do not expect a response.
2162#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
2163#[serde(untagged)]
2164#[expect(clippy::large_enum_variant)]
2165#[schemars(inline)]
2166#[non_exhaustive]
2167pub enum AgentNotification {
2168    /// Handles session update notifications from the agent.
2169    ///
2170    /// This is a notification endpoint (no response expected) that receives
2171    /// real-time updates about session progress, including message chunks,
2172    /// tool calls, and execution plans.
2173    ///
2174    /// Note: Clients SHOULD continue accepting tool call updates even after
2175    /// sending a `session/cancel` notification, as the agent may send final
2176    /// updates before responding with the cancelled stop reason.
2177    ///
2178    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
2179    SessionNotification(SessionNotification),
2180    /// **UNSTABLE**
2181    ///
2182    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2183    ///
2184    /// Notification that a URL-based elicitation has completed.
2185    #[cfg(feature = "unstable_elicitation")]
2186    CompleteElicitationNotification(CompleteElicitationNotification),
2187    /// **UNSTABLE**
2188    ///
2189    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2190    ///
2191    /// Receives an MCP-over-ACP notification.
2192    #[cfg(feature = "unstable_mcp_over_acp")]
2193    MessageMcpNotification(MessageMcpNotification),
2194    /// Handles extension notifications from the agent.
2195    ///
2196    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
2197    /// Extension notifications provide a way to send one-way messages for custom functionality
2198    /// while maintaining protocol compatibility.
2199    ///
2200    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2201    ExtNotification(ExtNotification),
2202}
2203
2204impl AgentNotification {
2205    /// Returns the corresponding method name of the notification.
2206    #[must_use]
2207    pub fn method(&self) -> &str {
2208        match self {
2209            Self::SessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
2210            #[cfg(feature = "unstable_elicitation")]
2211            Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
2212            #[cfg(feature = "unstable_mcp_over_acp")]
2213            Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
2214            Self::ExtNotification(ext_notification) => &ext_notification.method,
2215        }
2216    }
2217}
2218
2219#[cfg(test)]
2220mod tests {
2221    use super::*;
2222
2223    #[test]
2224    fn test_serialization_behavior() {
2225        use serde_json::json;
2226
2227        assert_eq!(
2228            serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
2229            SessionInfoUpdate {
2230                title: MaybeUndefined::Undefined,
2231                updated_at: MaybeUndefined::Undefined,
2232                meta: None
2233            }
2234        );
2235        assert_eq!(
2236            serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
2237                .unwrap(),
2238            SessionInfoUpdate {
2239                title: MaybeUndefined::Null,
2240                updated_at: MaybeUndefined::Null,
2241                meta: None
2242            }
2243        );
2244        assert_eq!(
2245            serde_json::from_value::<SessionInfoUpdate>(
2246                json!({"title": "title", "updatedAt": "timestamp"})
2247            )
2248            .unwrap(),
2249            SessionInfoUpdate {
2250                title: MaybeUndefined::Value("title".to_string()),
2251                updated_at: MaybeUndefined::Value("timestamp".to_string()),
2252                meta: None
2253            }
2254        );
2255
2256        assert_eq!(
2257            serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
2258            json!({})
2259        );
2260        assert_eq!(
2261            serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
2262            json!({"title": "title"})
2263        );
2264        assert_eq!(
2265            serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
2266            json!({"title": null})
2267        );
2268        assert_eq!(
2269            serde_json::to_value(
2270                SessionInfoUpdate::new()
2271                    .title("title")
2272                    .title(MaybeUndefined::Undefined)
2273            )
2274            .unwrap(),
2275            json!({})
2276        );
2277    }
2278
2279    #[test]
2280    fn test_content_chunk_message_id_serialization() {
2281        use serde_json::json;
2282
2283        assert_eq!(
2284            serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
2285                ContentBlock::Text(crate::v1::TextContent::new("Hello"))
2286            )))
2287            .unwrap(),
2288            json!({
2289                "sessionUpdate": "agent_message_chunk",
2290                "content": {
2291                    "type": "text",
2292                    "text": "Hello"
2293                }
2294            })
2295        );
2296
2297        assert_eq!(
2298            serde_json::to_value(SessionUpdate::AgentMessageChunk(
2299                ContentChunk::new(ContentBlock::Text(crate::v1::TextContent::new("Hello")))
2300                    .message_id("msg_agent_c42b9")
2301            ))
2302            .unwrap(),
2303            json!({
2304                "sessionUpdate": "agent_message_chunk",
2305                "messageId": "msg_agent_c42b9",
2306                "content": {
2307                    "type": "text",
2308                    "text": "Hello"
2309                }
2310            })
2311        );
2312
2313        let SessionUpdate::AgentMessageChunk(chunk) = serde_json::from_value(json!({
2314            "sessionUpdate": "agent_message_chunk",
2315            "messageId": null,
2316            "content": {
2317                "type": "text",
2318                "text": "Hello"
2319            }
2320        }))
2321        .unwrap() else {
2322            panic!("expected agent message chunk");
2323        };
2324
2325        assert_eq!(chunk.message_id, None);
2326    }
2327
2328    #[test]
2329    fn test_usage_update_serialization() {
2330        use serde_json::json;
2331
2332        assert_eq!(
2333            serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
2334                53_000, 200_000
2335            )))
2336            .unwrap(),
2337            json!({
2338                "sessionUpdate": "usage_update",
2339                "used": 53000,
2340                "size": 200_000
2341            })
2342        );
2343
2344        assert_eq!(
2345            serde_json::to_value(SessionUpdate::UsageUpdate(
2346                UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
2347            ))
2348            .unwrap(),
2349            json!({
2350                "sessionUpdate": "usage_update",
2351                "used": 53000,
2352                "size": 200_000,
2353                "cost": {
2354                    "amount": 0.045,
2355                    "currency": "USD"
2356                }
2357            })
2358        );
2359
2360        let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
2361            "sessionUpdate": "usage_update",
2362            "used": 53000,
2363            "size": 200_000,
2364            "cost": null
2365        }))
2366        .unwrap() else {
2367            panic!("expected usage update");
2368        };
2369
2370        assert_eq!(update.cost, None);
2371    }
2372
2373    #[cfg(feature = "unstable_nes")]
2374    #[test]
2375    fn test_client_capabilities_position_encodings_serialization() {
2376        use serde_json::json;
2377
2378        let capabilities = ClientCapabilities::new().position_encodings(vec![
2379            PositionEncodingKind::Utf32,
2380            PositionEncodingKind::Utf16,
2381        ]);
2382        let json = serde_json::to_value(&capabilities).unwrap();
2383
2384        assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
2385    }
2386
2387    #[cfg(feature = "unstable_plan_operations")]
2388    #[test]
2389    fn test_plan_operations_serialization() {
2390        use serde_json::json;
2391
2392        use crate::v1::{PlanEntry, PlanEntryPriority, PlanEntryStatus, PlanUpdateContent};
2393
2394        let plan_update = SessionUpdate::PlanUpdate(PlanUpdate::new(PlanUpdateContent::items(
2395            "plan-1",
2396            vec![PlanEntry::new(
2397                "Step 1",
2398                PlanEntryPriority::High,
2399                PlanEntryStatus::Pending,
2400            )],
2401        )));
2402
2403        assert_eq!(
2404            serde_json::to_value(plan_update).unwrap(),
2405            json!({
2406                "sessionUpdate": "plan_update",
2407                "plan": {
2408                    "type": "items",
2409                    "id": "plan-1",
2410                    "entries": [
2411                        {
2412                            "content": "Step 1",
2413                            "priority": "high",
2414                            "status": "pending"
2415                        }
2416                    ]
2417                }
2418            })
2419        );
2420
2421        assert_eq!(
2422            serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
2423            json!({
2424                "sessionUpdate": "plan_removed",
2425                "id": "plan-1"
2426            })
2427        );
2428
2429        let capabilities = ClientCapabilities::new().plan(PlanCapabilities::new());
2430        let json = serde_json::to_value(&capabilities).unwrap();
2431        assert_eq!(json["plan"], json!({}));
2432
2433        assert_eq!(
2434            serde_json::from_value::<ClientCapabilities>(json!({ "plan": null }))
2435                .unwrap()
2436                .plan,
2437            None
2438        );
2439    }
2440
2441    #[cfg(feature = "unstable_mcp_over_acp")]
2442    #[test]
2443    fn test_agent_mcp_request_method_names() {
2444        use serde_json::json;
2445
2446        let params: serde_json::Map<String, serde_json::Value> =
2447            [("cursor".to_string(), json!("abc"))].into_iter().collect();
2448
2449        assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
2450        assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
2451        assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
2452
2453        assert_eq!(
2454            AgentRequest::ConnectMcpRequest(ConnectMcpRequest::new("server-1")).method(),
2455            "mcp/connect"
2456        );
2457        assert_eq!(
2458            AgentRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
2459                .method(),
2460            "mcp/message"
2461        );
2462        assert_eq!(
2463            AgentRequest::DisconnectMcpRequest(DisconnectMcpRequest::new("conn-1")).method(),
2464            "mcp/disconnect"
2465        );
2466        assert_eq!(
2467            AgentNotification::MessageMcpNotification(MessageMcpNotification::new(
2468                "conn-1",
2469                "notifications/progress"
2470            ))
2471            .method(),
2472            "mcp/message"
2473        );
2474
2475        assert_eq!(
2476            serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
2477            json!({ "acpId": "server-1" })
2478        );
2479        assert_eq!(
2480            serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
2481            json!({ "connectionId": "conn-1" })
2482        );
2483        assert_eq!(
2484            serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
2485                .unwrap(),
2486            json!({
2487                "connectionId": "conn-1",
2488                "method": "tools/list",
2489                "params": { "cursor": "abc" }
2490            })
2491        );
2492        assert_eq!(
2493            serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
2494            json!({ "connectionId": "conn-1" })
2495        );
2496        assert_eq!(
2497            serde_json::to_value(MessageMcpNotification::new(
2498                "conn-1",
2499                "notifications/progress"
2500            ))
2501            .unwrap(),
2502            json!({
2503                "connectionId": "conn-1",
2504                "method": "notifications/progress"
2505            })
2506        );
2507
2508        let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
2509            "connectionId": "conn-1",
2510            "method": "tools/list",
2511            "params": null
2512        }))
2513        .unwrap();
2514        assert_eq!(request_with_null_params.params, None);
2515    }
2516}