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 serde::{Deserialize, Serialize};
10use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
11
12use super::{
13    CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse,
14    ElicitationCapabilities,
15};
16use crate::{IntoMaybeUndefined, IntoOption, MaybeUndefined, SkipListener};
17
18use super::{
19    ContentBlock, EnvVariable, ExtNotification, ExtRequest, ExtResponse, Meta, Plan,
20    SessionConfigOption, SessionId, SessionModeId, ToolCall, ToolCallUpdate,
21};
22#[cfg(feature = "unstable_plan_operations")]
23use super::{PlanCapabilities, PlanRemoved, PlanUpdate};
24
25#[cfg(feature = "unstable_mcp_over_acp")]
26use super::mcp::{
27    ConnectMcpRequest, ConnectMcpResponse, DisconnectMcpRequest, DisconnectMcpResponse,
28    MCP_CONNECT_METHOD_NAME, MCP_DISCONNECT_METHOD_NAME, MCP_MESSAGE_METHOD_NAME,
29    MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
30};
31
32#[cfg(feature = "unstable_nes")]
33use super::{ClientNesCapabilities, PositionEncodingKind};
34
35// Session updates
36
37/// Notification containing a session update from the agent.
38///
39/// Used to stream real-time progress and results during prompt processing.
40///
41/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
42#[serde_as]
43#[skip_serializing_none]
44#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
46#[cfg_attr(feature = "schemars", 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_as(deserialize_as = "DefaultOnError")]
60    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
61    #[serde(default)]
62    #[serde(rename = "_meta")]
63    pub meta: Option<Meta>,
64}
65
66impl SessionNotification {
67    /// Builds [`SessionNotification`] with the required notification fields set; optional fields start unset or empty.
68    #[must_use]
69    pub fn new(session_id: impl Into<SessionId>, update: SessionUpdate) -> Self {
70        Self {
71            session_id: session_id.into(),
72            update,
73            meta: None,
74        }
75    }
76
77    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
78    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
79    /// these keys.
80    ///
81    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
82    #[must_use]
83    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
84        self.meta = meta.into_option();
85        self
86    }
87}
88
89/// Different types of updates that can be sent during session processing.
90///
91/// These updates provide real-time feedback about the agent's progress.
92///
93/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
94#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
96#[serde(tag = "sessionUpdate", rename_all = "snake_case")]
97#[cfg_attr(feature = "schemars", schemars(extend("discriminator" = {"propertyName": "sessionUpdate"})))]
98#[non_exhaustive]
99pub enum SessionUpdate {
100    /// A chunk of the user's message being streamed.
101    UserMessageChunk(ContentChunk),
102    /// A chunk of the agent's response being streamed.
103    AgentMessageChunk(ContentChunk),
104    /// A chunk of the agent's internal reasoning being streamed.
105    AgentThoughtChunk(ContentChunk),
106    /// Notification that a new tool call has been initiated.
107    ToolCall(ToolCall),
108    /// Update on the status or results of a tool call.
109    ToolCallUpdate(ToolCallUpdate),
110    /// The agent's execution plan for complex tasks.
111    /// See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)
112    Plan(Plan),
113    /// **UNSTABLE**
114    ///
115    /// This capability is not part of the spec yet, and may be removed or changed at any point.
116    ///
117    /// A content update for a plan identified by ID.
118    #[cfg(feature = "unstable_plan_operations")]
119    PlanUpdate(PlanUpdate),
120    /// **UNSTABLE**
121    ///
122    /// This capability is not part of the spec yet, and may be removed or changed at any point.
123    ///
124    /// Removal notice for a plan identified by ID.
125    #[cfg(feature = "unstable_plan_operations")]
126    PlanRemoved(PlanRemoved),
127    /// Available commands are ready or have changed
128    AvailableCommandsUpdate(AvailableCommandsUpdate),
129    /// The current mode of the session has changed
130    ///
131    /// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
132    CurrentModeUpdate(CurrentModeUpdate),
133    /// Session configuration options have been updated.
134    ConfigOptionUpdate(ConfigOptionUpdate),
135    /// Session metadata has been updated (title, timestamps, custom metadata)
136    SessionInfoUpdate(SessionInfoUpdate),
137    /// Context window and cost update for the session.
138    UsageUpdate(UsageUpdate),
139    /// **UNSTABLE**
140    ///
141    /// This capability is not part of the spec yet, and may be removed or changed at any point.
142    ///
143    /// Advisory information for the user that is not part of session history.
144    ///
145    /// No Client capability is required. Clients that do not understand or
146    /// present notices may ignore them.
147    #[cfg(feature = "unstable_session_notices")]
148    Notice(Notice),
149    /// **UNSTABLE**
150    ///
151    /// This capability is not part of the spec yet, and may be removed or changed at any point.
152    ///
153    /// A context compaction has been created or updated.
154    ///
155    /// Agents MUST only send this update when the Client advertised
156    /// [`ClientSessionCapabilities::compaction`].
157    #[cfg(feature = "unstable_session_compaction")]
158    CompactionUpdate(CompactionUpdate),
159    /// **UNSTABLE**
160    ///
161    /// This capability is not part of the spec yet, and may be removed or changed at any point.
162    ///
163    /// A content block appended to a context compaction's retained summary.
164    ///
165    /// Agents MUST only send this update when the Client advertised
166    /// [`ClientSessionCapabilities::compaction`].
167    #[cfg(feature = "unstable_session_compaction")]
168    CompactionSummaryChunk(CompactionSummaryChunk),
169}
170
171/// **UNSTABLE**
172///
173/// This capability is not part of the spec yet, and may be removed or changed at any point.
174///
175/// Severity hint for a session notice.
176#[cfg(feature = "unstable_session_notices")]
177#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
178#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
179#[serde(rename_all = "snake_case")]
180#[non_exhaustive]
181pub enum NoticeSeverity {
182    /// Informational notice.
183    Info,
184    /// Warning notice.
185    Warning,
186    /// Error notice.
187    Error,
188    /// Custom or future notice severity.
189    ///
190    /// Values beginning with `_` are reserved for implementation-specific
191    /// extensions. Other unknown values are reserved for future ACP severities.
192    #[serde(untagged)]
193    Other(String),
194}
195
196/// **UNSTABLE**
197///
198/// This capability is not part of the spec yet, and may be removed or changed at any point.
199///
200/// Fire-and-forget advisory information for the user.
201///
202/// Notices are live events rather than session history. Agents must not rely on
203/// a notice being received, displayed, or seen by the user.
204/// No Client capability is required, and unsupported Clients may ignore notices.
205///
206/// See RFD: [Session Notices](https://agentclientprotocol.com/rfds/session-notices)
207#[cfg(feature = "unstable_session_notices")]
208#[serde_as]
209#[skip_serializing_none]
210#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
212#[serde(rename_all = "camelCase")]
213#[non_exhaustive]
214pub struct Notice {
215    /// Presentation severity hint.
216    pub severity: NoticeSeverity,
217    /// Required non-empty plain-text title that can stand alone.
218    #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
219    pub title: String,
220    /// Optional plain-text detail or guidance.
221    ///
222    /// Omitted and `null` are equivalent and mean no description was supplied.
223    #[serde_as(deserialize_as = "DefaultOnError")]
224    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
225    #[serde(default)]
226    pub description: Option<String>,
227    /// Metadata scoped to this notice.
228    ///
229    /// Omitted and `null` are equivalent and mean no metadata was supplied.
230    #[serde_as(deserialize_as = "DefaultOnError")]
231    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
232    #[serde(default, rename = "_meta")]
233    pub meta: Option<Meta>,
234}
235
236#[cfg(feature = "unstable_session_notices")]
237impl Notice {
238    /// Builds a notice with the required fields set and optional fields omitted.
239    #[must_use]
240    pub fn new(severity: NoticeSeverity, title: impl Into<String>) -> Self {
241        Self {
242            severity,
243            title: title.into(),
244            description: None,
245            meta: None,
246        }
247    }
248
249    /// Sets or clears the optional description.
250    #[must_use]
251    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
252        self.description = description.into_option();
253        self
254    }
255
256    /// Sets or clears notice-scoped metadata.
257    #[must_use]
258    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
259        self.meta = meta.into_option();
260        self
261    }
262}
263
264/// **UNSTABLE**
265///
266/// This capability is not part of the spec yet, and may be removed or changed at any point.
267///
268/// Unique identifier for a context compaction within a session.
269#[cfg(feature = "unstable_session_compaction")]
270#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
271#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
272#[serde(transparent)]
273#[from(Arc<str>, String, &'static str)]
274#[non_exhaustive]
275pub struct CompactionId(pub Arc<str>);
276
277#[cfg(feature = "unstable_session_compaction")]
278impl CompactionId {
279    /// Wraps a protocol string as a typed [`CompactionId`].
280    #[must_use]
281    pub fn new(id: impl Into<Arc<str>>) -> Self {
282        Self(id.into())
283    }
284}
285
286/// **UNSTABLE**
287///
288/// This capability is not part of the spec yet, and may be removed or changed at any point.
289///
290/// Lifecycle state of a context compaction.
291#[cfg(feature = "unstable_session_compaction")]
292#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
293#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
294#[serde(rename_all = "snake_case")]
295#[non_exhaustive]
296pub enum CompactionStatus {
297    /// Compaction has started and has not finished.
298    InProgress,
299    /// Compaction finished successfully.
300    Completed,
301    /// Compaction finished unsuccessfully.
302    Failed,
303    /// Compaction was cancelled before it finished.
304    Cancelled,
305    /// Custom or future compaction status.
306    ///
307    /// Values beginning with `_` are reserved for implementation-specific
308    /// extensions. Other unknown values are reserved for future ACP statuses.
309    #[serde(untagged)]
310    Other(String),
311}
312
313/// **UNSTABLE**
314///
315/// This capability is not part of the spec yet, and may be removed or changed at any point.
316///
317/// A context compaction upsert. The first update fixes the compaction's
318/// timeline position. Later updates with the same ID patch that entity in place.
319/// Agents MUST only send this update when the Client advertised
320/// [`ClientSessionCapabilities::compaction`].
321///
322/// `summary`, `error`, and `_meta` have patch semantics: omission leaves the
323/// stored value unchanged, `null` clears it, and a concrete value replaces it.
324/// `summary: []` also clears the retained summary. A non-empty summary is only
325/// valid with `completed`; `error` is only valid with `failed`.
326#[cfg(feature = "unstable_session_compaction")]
327#[serde_as]
328#[skip_serializing_none]
329#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
330#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
331#[serde(rename_all = "camelCase")]
332#[non_exhaustive]
333pub struct CompactionUpdate {
334    /// The Agent-owned ID of this compaction, unique within the session.
335    pub compaction_id: CompactionId,
336    /// Current lifecycle status.
337    pub status: CompactionStatus,
338    /// Complete replacement user-displayable summary retained by the compaction.
339    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
340    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
341    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
342    pub summary: MaybeUndefined<Vec<ContentBlock>>,
343    /// Human-readable description of why the compaction failed.
344    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
345    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
346    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
347    pub error: MaybeUndefined<String>,
348    /// Extensible metadata patch for this compaction.
349    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
350    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
351    #[serde(
352        rename = "_meta",
353        default,
354        skip_serializing_if = "MaybeUndefined::is_undefined"
355    )]
356    pub meta: MaybeUndefined<Meta>,
357}
358
359#[cfg(feature = "unstable_session_compaction")]
360impl CompactionUpdate {
361    /// Builds a compaction update with optional patch fields omitted.
362    #[must_use]
363    pub fn new(compaction_id: impl Into<CompactionId>, status: CompactionStatus) -> Self {
364        Self {
365            compaction_id: compaction_id.into(),
366            status,
367            summary: MaybeUndefined::Undefined,
368            error: MaybeUndefined::Undefined,
369            meta: MaybeUndefined::Undefined,
370        }
371    }
372
373    /// Sets, clears, or omits the complete retained summary patch.
374    #[must_use]
375    pub fn summary(mut self, summary: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
376        self.summary = summary.into_maybe_undefined();
377        self
378    }
379
380    /// Sets, clears, or omits the failure description patch.
381    #[must_use]
382    pub fn error(mut self, error: impl IntoMaybeUndefined<String>) -> Self {
383        self.error = error.into_maybe_undefined();
384        self
385    }
386
387    /// Sets, clears, or omits the metadata patch.
388    #[must_use]
389    pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
390        self.meta = meta.into_maybe_undefined();
391        self
392    }
393}
394
395/// **UNSTABLE**
396///
397/// This capability is not part of the spec yet, and may be removed or changed at any point.
398///
399/// A content block appended to the retained summary of an in-progress
400/// compaction. Agents send chunks only after an `in_progress` update and before
401/// the terminal update for the same ID. Agents MUST only send this update when
402/// the Client advertised [`ClientSessionCapabilities::compaction`].
403#[cfg(feature = "unstable_session_compaction")]
404#[serde_as]
405#[skip_serializing_none]
406#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
407#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
408#[serde(rename_all = "camelCase")]
409#[non_exhaustive]
410pub struct CompactionSummaryChunk {
411    /// ID of the compaction whose summary receives this content.
412    pub compaction_id: CompactionId,
413    /// One content block to append.
414    pub content: ContentBlock,
415    /// Metadata scoped to this chunk. Omission and `null` both mean absent.
416    #[serde_as(deserialize_as = "DefaultOnError")]
417    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
418    #[serde(default, rename = "_meta")]
419    pub meta: Option<Meta>,
420}
421
422#[cfg(feature = "unstable_session_compaction")]
423impl CompactionSummaryChunk {
424    /// Builds a summary chunk without metadata.
425    #[must_use]
426    pub fn new(compaction_id: impl Into<CompactionId>, content: ContentBlock) -> Self {
427        Self {
428            compaction_id: compaction_id.into(),
429            content,
430            meta: None,
431        }
432    }
433
434    /// Sets or clears chunk-scoped metadata.
435    #[must_use]
436    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
437        self.meta = meta.into_option();
438        self
439    }
440}
441
442/// The current mode of the session has changed
443///
444/// See protocol docs: [Session Modes](https://agentclientprotocol.com/protocol/session-modes)
445#[serde_as]
446#[skip_serializing_none]
447#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
448#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
449#[serde(rename_all = "camelCase")]
450#[non_exhaustive]
451pub struct CurrentModeUpdate {
452    /// The ID of the current mode
453    pub current_mode_id: SessionModeId,
454    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
455    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
456    /// these keys.
457    ///
458    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
459    #[serde_as(deserialize_as = "DefaultOnError")]
460    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
461    #[serde(default)]
462    #[serde(rename = "_meta")]
463    pub meta: Option<Meta>,
464}
465
466impl CurrentModeUpdate {
467    /// Builds [`CurrentModeUpdate`] with the required fields set; optional fields start unset or empty.
468    #[must_use]
469    pub fn new(current_mode_id: impl Into<SessionModeId>) -> Self {
470        Self {
471            current_mode_id: current_mode_id.into(),
472            meta: None,
473        }
474    }
475
476    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
477    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
478    /// these keys.
479    ///
480    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
481    #[must_use]
482    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
483        self.meta = meta.into_option();
484        self
485    }
486}
487
488/// Session configuration options have been updated.
489#[serde_as]
490#[skip_serializing_none]
491#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
492#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
493#[serde(rename_all = "camelCase")]
494#[non_exhaustive]
495pub struct ConfigOptionUpdate {
496    /// The full set of configuration options and their current values.
497    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
498    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
499    pub config_options: Vec<SessionConfigOption>,
500    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
501    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
502    /// these keys.
503    ///
504    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
505    #[serde_as(deserialize_as = "DefaultOnError")]
506    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
507    #[serde(default)]
508    #[serde(rename = "_meta")]
509    pub meta: Option<Meta>,
510}
511
512impl ConfigOptionUpdate {
513    /// Builds [`ConfigOptionUpdate`] with the required fields set; optional fields start unset or empty.
514    #[must_use]
515    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
516        Self {
517            config_options,
518            meta: None,
519        }
520    }
521
522    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
523    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
524    /// these keys.
525    ///
526    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
527    #[must_use]
528    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
529        self.meta = meta.into_option();
530        self
531    }
532}
533
534/// Update to session metadata. All fields are optional to support partial updates.
535///
536/// Agents send this notification to update session information like title or custom metadata.
537/// This allows clients to display dynamic session names and track session state changes.
538#[serde_as]
539#[skip_serializing_none]
540#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
541#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
542#[serde(rename_all = "camelCase")]
543#[non_exhaustive]
544pub struct SessionInfoUpdate {
545    /// Human-readable title for the session. Set to null to clear.
546    #[serde_as(deserialize_as = "DefaultOnError")]
547    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
548    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
549    pub title: MaybeUndefined<String>,
550    /// ISO 8601 timestamp of last activity. Set to null to clear.
551    #[serde_as(deserialize_as = "DefaultOnError")]
552    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
553    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
554    pub updated_at: MaybeUndefined<String>,
555    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
556    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
557    /// these keys.
558    ///
559    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
560    #[serde_as(deserialize_as = "DefaultOnError")]
561    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
562    #[serde(default)]
563    #[serde(rename = "_meta")]
564    pub meta: Option<Meta>,
565}
566
567impl SessionInfoUpdate {
568    /// Builds [`SessionInfoUpdate`] with the required fields set; optional fields start unset or empty.
569    #[must_use]
570    pub fn new() -> Self {
571        Self::default()
572    }
573
574    /// Human-readable title for the session. Set to null to clear.
575    #[must_use]
576    pub fn title(mut self, title: impl IntoMaybeUndefined<String>) -> Self {
577        self.title = title.into_maybe_undefined();
578        self
579    }
580
581    /// ISO 8601 timestamp of last activity. Set to null to clear.
582    #[must_use]
583    pub fn updated_at(mut self, updated_at: impl IntoMaybeUndefined<String>) -> Self {
584        self.updated_at = updated_at.into_maybe_undefined();
585        self
586    }
587
588    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
589    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
590    /// these keys.
591    ///
592    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
593    #[must_use]
594    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
595        self.meta = meta.into_option();
596        self
597    }
598}
599
600/// Context window and cost update for a session.
601#[serde_as]
602#[skip_serializing_none]
603#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
604#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
605#[serde(rename_all = "camelCase")]
606#[non_exhaustive]
607pub struct UsageUpdate {
608    /// Tokens currently in context.
609    pub used: u64,
610    /// Total context window size in tokens.
611    pub size: u64,
612    /// Cumulative session cost (optional).
613    #[serde_as(deserialize_as = "DefaultOnError")]
614    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
615    #[serde(default)]
616    pub cost: Option<Cost>,
617    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
618    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
619    /// these keys.
620    ///
621    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
622    #[serde_as(deserialize_as = "DefaultOnError")]
623    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
624    #[serde(default)]
625    #[serde(rename = "_meta")]
626    pub meta: Option<Meta>,
627}
628
629impl UsageUpdate {
630    /// Builds [`UsageUpdate`] with the required fields set; optional fields start unset or empty.
631    #[must_use]
632    pub fn new(used: u64, size: u64) -> Self {
633        Self {
634            used,
635            size,
636            cost: None,
637            meta: None,
638        }
639    }
640
641    /// Cumulative session cost (optional).
642    #[must_use]
643    pub fn cost(mut self, cost: impl IntoOption<Cost>) -> Self {
644        self.cost = cost.into_option();
645        self
646    }
647
648    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
649    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
650    /// these keys.
651    ///
652    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
653    #[must_use]
654    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
655        self.meta = meta.into_option();
656        self
657    }
658}
659
660/// Cost information for a session.
661#[serde_as]
662#[skip_serializing_none]
663#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
664#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
665#[serde(rename_all = "camelCase")]
666#[non_exhaustive]
667pub struct Cost {
668    /// Total cumulative cost for session.
669    pub amount: f64,
670    /// ISO 4217 currency code (e.g., "USD", "EUR").
671    pub currency: String,
672    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
673    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
674    /// these keys.
675    ///
676    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
677    #[serde_as(deserialize_as = "DefaultOnError")]
678    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
679    #[serde(default)]
680    #[serde(rename = "_meta")]
681    pub meta: Option<Meta>,
682}
683
684impl Cost {
685    /// Builds [`Cost`] with the required fields set; optional fields start unset or empty.
686    #[must_use]
687    pub fn new(amount: f64, currency: impl Into<String>) -> Self {
688        Self {
689            amount,
690            currency: currency.into(),
691            meta: None,
692        }
693    }
694
695    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
696    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
697    /// these keys.
698    ///
699    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
700    #[must_use]
701    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
702        self.meta = meta.into_option();
703        self
704    }
705}
706
707/// A streamed item of content
708#[serde_as]
709#[skip_serializing_none]
710#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
711#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
712#[serde(rename_all = "camelCase")]
713#[non_exhaustive]
714pub struct ContentChunk {
715    /// A single item of content
716    pub content: ContentBlock,
717    /// A unique identifier for the message this chunk belongs to.
718    ///
719    /// All chunks belonging to the same message share the same `messageId`.
720    /// A change in `messageId` indicates a new message has started.
721    #[serde_as(deserialize_as = "DefaultOnError")]
722    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
723    #[serde(default)]
724    pub message_id: Option<MessageId>,
725    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
726    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
727    /// these keys.
728    ///
729    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
730    #[serde_as(deserialize_as = "DefaultOnError")]
731    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
732    #[serde(default)]
733    #[serde(rename = "_meta")]
734    pub meta: Option<Meta>,
735}
736
737impl ContentChunk {
738    /// Builds [`ContentChunk`] with the required fields set; optional fields start unset or empty.
739    #[must_use]
740    pub fn new(content: ContentBlock) -> Self {
741        Self {
742            content,
743            message_id: None,
744            meta: None,
745        }
746    }
747
748    /// A unique identifier for the message this chunk belongs to.
749    ///
750    /// All chunks belonging to the same message share the same `messageId`.
751    /// A change in `messageId` indicates a new message has started.
752    #[must_use]
753    pub fn message_id(mut self, message_id: impl IntoOption<MessageId>) -> Self {
754        self.message_id = message_id.into_option();
755        self
756    }
757
758    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
759    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
760    /// these keys.
761    ///
762    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
763    #[must_use]
764    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
765        self.meta = meta.into_option();
766        self
767    }
768}
769
770/// Unique identifier for a message within a session.
771#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
772#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
773#[serde(transparent)]
774#[from(Arc<str>, String, &'static str)]
775#[non_exhaustive]
776pub struct MessageId(pub Arc<str>);
777
778impl MessageId {
779    /// Wraps a protocol string as a typed [`MessageId`].
780    #[must_use]
781    pub fn new(id: impl Into<Arc<str>>) -> Self {
782        Self(id.into())
783    }
784}
785
786impl IntoOption<MessageId> for &str {
787    fn into_option(self) -> Option<MessageId> {
788        Some(MessageId::new(self))
789    }
790}
791
792/// Available commands are ready or have changed
793#[serde_as]
794#[skip_serializing_none]
795#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
796#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
797#[serde(rename_all = "camelCase")]
798#[non_exhaustive]
799pub struct AvailableCommandsUpdate {
800    /// Commands the agent can execute
801    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
802    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
803    pub available_commands: Vec<AvailableCommand>,
804    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
805    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
806    /// these keys.
807    ///
808    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
809    #[serde_as(deserialize_as = "DefaultOnError")]
810    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
811    #[serde(default)]
812    #[serde(rename = "_meta")]
813    pub meta: Option<Meta>,
814}
815
816impl AvailableCommandsUpdate {
817    /// Builds [`AvailableCommandsUpdate`] with the required fields set; optional fields start unset or empty.
818    #[must_use]
819    pub fn new(available_commands: Vec<AvailableCommand>) -> Self {
820        Self {
821            available_commands,
822            meta: None,
823        }
824    }
825
826    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
827    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
828    /// these keys.
829    ///
830    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
831    #[must_use]
832    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
833        self.meta = meta.into_option();
834        self
835    }
836}
837
838/// Information about a command.
839#[serde_as]
840#[skip_serializing_none]
841#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
842#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
843#[serde(rename_all = "camelCase")]
844#[non_exhaustive]
845pub struct AvailableCommand {
846    /// Command name (e.g., `create_plan`, `research_codebase`).
847    pub name: String,
848    /// Human-readable description of what the command does.
849    pub description: String,
850    /// Input for the command if required
851    #[serde_as(deserialize_as = "DefaultOnError")]
852    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
853    #[serde(default)]
854    pub input: Option<AvailableCommandInput>,
855    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
856    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
857    /// these keys.
858    ///
859    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
860    #[serde_as(deserialize_as = "DefaultOnError")]
861    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
862    #[serde(default)]
863    #[serde(rename = "_meta")]
864    pub meta: Option<Meta>,
865}
866
867impl AvailableCommand {
868    /// Builds [`AvailableCommand`] with the required fields set; optional fields start unset or empty.
869    #[must_use]
870    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
871        Self {
872            name: name.into(),
873            description: description.into(),
874            input: None,
875            meta: None,
876        }
877    }
878
879    /// Input for the command if required
880    #[must_use]
881    pub fn input(mut self, input: impl IntoOption<AvailableCommandInput>) -> Self {
882        self.input = input.into_option();
883        self
884    }
885
886    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
887    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
888    /// these keys.
889    ///
890    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
891    #[must_use]
892    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
893        self.meta = meta.into_option();
894        self
895    }
896}
897
898/// The input specification for a command.
899#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
900#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
901#[serde(untagged, rename_all = "camelCase")]
902#[non_exhaustive]
903pub enum AvailableCommandInput {
904    /// All text that was typed after the command name is provided as input.
905    Unstructured(UnstructuredCommandInput),
906}
907
908/// All text that was typed after the command name is provided as input.
909#[serde_as]
910#[skip_serializing_none]
911#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
912#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
913#[serde(rename_all = "camelCase")]
914#[non_exhaustive]
915pub struct UnstructuredCommandInput {
916    /// A hint to display when the input hasn't been provided yet
917    pub hint: String,
918    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
919    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
920    /// these keys.
921    ///
922    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
923    #[serde_as(deserialize_as = "DefaultOnError")]
924    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
925    #[serde(default)]
926    #[serde(rename = "_meta")]
927    pub meta: Option<Meta>,
928}
929
930impl UnstructuredCommandInput {
931    /// Builds [`UnstructuredCommandInput`] with the required fields set; optional fields start unset or empty.
932    #[must_use]
933    pub fn new(hint: impl Into<String>) -> Self {
934        Self {
935            hint: hint.into(),
936            meta: None,
937        }
938    }
939
940    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
941    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
942    /// these keys.
943    ///
944    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
945    #[must_use]
946    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
947        self.meta = meta.into_option();
948        self
949    }
950}
951
952// Permission
953
954/// Request for user permission to execute a tool call.
955///
956/// Sent when the agent needs authorization before performing a sensitive operation.
957///
958/// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
959#[serde_as]
960#[skip_serializing_none]
961#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
962#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
963#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME)))]
964#[serde(rename_all = "camelCase")]
965#[non_exhaustive]
966pub struct RequestPermissionRequest {
967    /// The session ID for this request.
968    pub session_id: SessionId,
969    /// Details about the tool call requiring permission.
970    pub tool_call: ToolCallUpdate,
971    /// Available permission options for the user to choose from.
972    pub options: Vec<PermissionOption>,
973    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
974    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
975    /// these keys.
976    ///
977    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
978    #[serde_as(deserialize_as = "DefaultOnError")]
979    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
980    #[serde(default)]
981    #[serde(rename = "_meta")]
982    pub meta: Option<Meta>,
983}
984
985impl RequestPermissionRequest {
986    /// Builds [`RequestPermissionRequest`] with the required request fields set; optional fields start unset or empty.
987    #[must_use]
988    pub fn new(
989        session_id: impl Into<SessionId>,
990        tool_call: ToolCallUpdate,
991        options: Vec<PermissionOption>,
992    ) -> Self {
993        Self {
994            session_id: session_id.into(),
995            tool_call,
996            options,
997            meta: None,
998        }
999    }
1000
1001    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1002    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1003    /// these keys.
1004    ///
1005    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1006    #[must_use]
1007    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1008        self.meta = meta.into_option();
1009        self
1010    }
1011}
1012
1013/// An option presented to the user when requesting permission.
1014#[serde_as]
1015#[skip_serializing_none]
1016#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1017#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1018#[serde(rename_all = "camelCase")]
1019#[non_exhaustive]
1020pub struct PermissionOption {
1021    /// Unique identifier for this permission option.
1022    pub option_id: PermissionOptionId,
1023    /// Human-readable label to display to the user.
1024    pub name: String,
1025    /// Hint about the nature of this permission option.
1026    pub kind: PermissionOptionKind,
1027    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1028    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1029    /// these keys.
1030    ///
1031    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1032    #[serde_as(deserialize_as = "DefaultOnError")]
1033    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1034    #[serde(default)]
1035    #[serde(rename = "_meta")]
1036    pub meta: Option<Meta>,
1037}
1038
1039impl PermissionOption {
1040    /// Builds [`PermissionOption`] with the required fields set; optional fields start unset or empty.
1041    #[must_use]
1042    pub fn new(
1043        option_id: impl Into<PermissionOptionId>,
1044        name: impl Into<String>,
1045        kind: PermissionOptionKind,
1046    ) -> Self {
1047        Self {
1048            option_id: option_id.into(),
1049            name: name.into(),
1050            kind,
1051            meta: None,
1052        }
1053    }
1054
1055    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1056    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1057    /// these keys.
1058    ///
1059    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1060    #[must_use]
1061    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1062        self.meta = meta.into_option();
1063        self
1064    }
1065}
1066
1067/// Unique identifier for a permission option.
1068#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1069#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1070#[serde(transparent)]
1071#[from(Arc<str>, String, &'static str)]
1072#[non_exhaustive]
1073pub struct PermissionOptionId(pub Arc<str>);
1074
1075impl PermissionOptionId {
1076    /// Wraps a protocol string as a typed [`PermissionOptionId`].
1077    #[must_use]
1078    pub fn new(id: impl Into<Arc<str>>) -> Self {
1079        Self(id.into())
1080    }
1081}
1082
1083/// The type of permission option being presented to the user.
1084///
1085/// Helps clients choose appropriate icons and UI treatment.
1086#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1087#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1088#[serde(rename_all = "snake_case")]
1089#[non_exhaustive]
1090pub enum PermissionOptionKind {
1091    /// Allow this operation only this time.
1092    AllowOnce,
1093    /// Allow this operation and remember the choice.
1094    AllowAlways,
1095    /// Reject this operation only this time.
1096    RejectOnce,
1097    /// Reject this operation and remember the choice.
1098    RejectAlways,
1099}
1100
1101/// Response to a permission request.
1102#[serde_as]
1103#[skip_serializing_none]
1104#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1105#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1106#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME)))]
1107#[serde(rename_all = "camelCase")]
1108#[non_exhaustive]
1109pub struct RequestPermissionResponse {
1110    /// The user's decision on the permission request.
1111    // This extra-level is unfortunately needed because the output must be an object
1112    pub outcome: RequestPermissionOutcome,
1113    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1114    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1115    /// these keys.
1116    ///
1117    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1118    #[serde_as(deserialize_as = "DefaultOnError")]
1119    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1120    #[serde(default)]
1121    #[serde(rename = "_meta")]
1122    pub meta: Option<Meta>,
1123}
1124
1125impl RequestPermissionResponse {
1126    /// Builds [`RequestPermissionResponse`] with the required response fields set; optional fields start unset or empty.
1127    #[must_use]
1128    pub fn new(outcome: RequestPermissionOutcome) -> Self {
1129        Self {
1130            outcome,
1131            meta: None,
1132        }
1133    }
1134
1135    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1136    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1137    /// these keys.
1138    ///
1139    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1140    #[must_use]
1141    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1142        self.meta = meta.into_option();
1143        self
1144    }
1145}
1146
1147/// The outcome of a permission request.
1148#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1149#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1150#[serde(tag = "outcome", rename_all = "snake_case")]
1151#[cfg_attr(feature = "schemars", schemars(extend("discriminator" = {"propertyName": "outcome"})))]
1152#[non_exhaustive]
1153pub enum RequestPermissionOutcome {
1154    /// The prompt turn was cancelled before the user responded.
1155    ///
1156    /// When a client sends a `session/cancel` notification to cancel an ongoing
1157    /// prompt turn, it MUST respond to all pending `session/request_permission`
1158    /// requests with this `Cancelled` outcome.
1159    ///
1160    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)
1161    Cancelled,
1162    /// The user selected one of the provided options.
1163    #[serde(rename_all = "camelCase")]
1164    Selected(SelectedPermissionOutcome),
1165}
1166
1167/// The user selected one of the provided options.
1168#[serde_as]
1169#[skip_serializing_none]
1170#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1172#[serde(rename_all = "camelCase")]
1173#[non_exhaustive]
1174pub struct SelectedPermissionOutcome {
1175    /// The ID of the option the user selected.
1176    pub option_id: PermissionOptionId,
1177    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1178    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1179    /// these keys.
1180    ///
1181    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1182    #[serde_as(deserialize_as = "DefaultOnError")]
1183    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1184    #[serde(default)]
1185    #[serde(rename = "_meta")]
1186    pub meta: Option<Meta>,
1187}
1188
1189impl SelectedPermissionOutcome {
1190    /// Builds [`SelectedPermissionOutcome`] with the required fields set; optional fields start unset or empty.
1191    #[must_use]
1192    pub fn new(option_id: impl Into<PermissionOptionId>) -> Self {
1193        Self {
1194            option_id: option_id.into(),
1195            meta: None,
1196        }
1197    }
1198
1199    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1200    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1201    /// these keys.
1202    ///
1203    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1204    #[must_use]
1205    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1206        self.meta = meta.into_option();
1207        self
1208    }
1209}
1210
1211// Write text file
1212
1213/// Request to write content to a text file.
1214///
1215/// Only available if the client supports the `fs.writeTextFile` capability.
1216#[serde_as]
1217#[skip_serializing_none]
1218#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1220#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME)))]
1221#[serde(rename_all = "camelCase")]
1222#[non_exhaustive]
1223pub struct WriteTextFileRequest {
1224    /// The session ID for this request.
1225    pub session_id: SessionId,
1226    /// Absolute path to the file to write.
1227    pub path: PathBuf,
1228    /// The text content to write to the file.
1229    pub content: String,
1230    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1231    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1232    /// these keys.
1233    ///
1234    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1235    #[serde_as(deserialize_as = "DefaultOnError")]
1236    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1237    #[serde(default)]
1238    #[serde(rename = "_meta")]
1239    pub meta: Option<Meta>,
1240}
1241
1242impl WriteTextFileRequest {
1243    /// Builds [`WriteTextFileRequest`] with the required request fields set; optional fields start unset or empty.
1244    #[must_use]
1245    pub fn new(
1246        session_id: impl Into<SessionId>,
1247        path: impl Into<PathBuf>,
1248        content: impl Into<String>,
1249    ) -> Self {
1250        Self {
1251            session_id: session_id.into(),
1252            path: path.into(),
1253            content: content.into(),
1254            meta: None,
1255        }
1256    }
1257
1258    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1259    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1260    /// these keys.
1261    ///
1262    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1263    #[must_use]
1264    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1265        self.meta = meta.into_option();
1266        self
1267    }
1268}
1269
1270/// Response to `fs/write_text_file`
1271#[serde_as]
1272#[skip_serializing_none]
1273#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1274#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1275#[serde(rename_all = "camelCase")]
1276#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME)))]
1277#[non_exhaustive]
1278pub struct WriteTextFileResponse {
1279    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1280    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1281    /// these keys.
1282    ///
1283    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1284    #[serde_as(deserialize_as = "DefaultOnError")]
1285    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1286    #[serde(default)]
1287    #[serde(rename = "_meta")]
1288    pub meta: Option<Meta>,
1289}
1290
1291impl WriteTextFileResponse {
1292    /// Builds [`WriteTextFileResponse`] with the required response fields set; optional fields start unset or empty.
1293    #[must_use]
1294    pub fn new() -> Self {
1295        Self::default()
1296    }
1297
1298    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1299    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1300    /// these keys.
1301    ///
1302    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1303    #[must_use]
1304    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1305        self.meta = meta.into_option();
1306        self
1307    }
1308}
1309
1310// Read text file
1311
1312/// Request to read content from a text file.
1313///
1314/// Only available if the client supports the `fs.readTextFile` capability.
1315#[serde_as]
1316#[skip_serializing_none]
1317#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1318#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1319#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1320#[serde(rename_all = "camelCase")]
1321#[non_exhaustive]
1322pub struct ReadTextFileRequest {
1323    /// The session ID for this request.
1324    pub session_id: SessionId,
1325    /// Absolute path to the file to read.
1326    pub path: PathBuf,
1327    /// Line number to start reading from (1-based).
1328    #[serde_as(deserialize_as = "DefaultOnError")]
1329    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1330    #[serde(default)]
1331    pub line: Option<u32>,
1332    /// Maximum number of lines to read.
1333    #[serde_as(deserialize_as = "DefaultOnError")]
1334    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1335    #[serde(default)]
1336    pub limit: Option<u32>,
1337    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1338    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1339    /// these keys.
1340    ///
1341    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1342    #[serde_as(deserialize_as = "DefaultOnError")]
1343    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1344    #[serde(default)]
1345    #[serde(rename = "_meta")]
1346    pub meta: Option<Meta>,
1347}
1348
1349impl ReadTextFileRequest {
1350    /// Builds [`ReadTextFileRequest`] with the required request fields set; optional fields start unset or empty.
1351    #[must_use]
1352    pub fn new(session_id: impl Into<SessionId>, path: impl Into<PathBuf>) -> Self {
1353        Self {
1354            session_id: session_id.into(),
1355            path: path.into(),
1356            line: None,
1357            limit: None,
1358            meta: None,
1359        }
1360    }
1361
1362    /// Line number to start reading from (1-based).
1363    #[must_use]
1364    pub fn line(mut self, line: impl IntoOption<u32>) -> Self {
1365        self.line = line.into_option();
1366        self
1367    }
1368
1369    /// Maximum number of lines to read.
1370    #[must_use]
1371    pub fn limit(mut self, limit: impl IntoOption<u32>) -> Self {
1372        self.limit = limit.into_option();
1373        self
1374    }
1375
1376    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1377    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1378    /// these keys.
1379    ///
1380    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1381    #[must_use]
1382    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1383        self.meta = meta.into_option();
1384        self
1385    }
1386}
1387
1388/// Response containing the contents of a text file.
1389#[serde_as]
1390#[skip_serializing_none]
1391#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1392#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1393#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1394#[serde(rename_all = "camelCase")]
1395#[non_exhaustive]
1396pub struct ReadTextFileResponse {
1397    /// Content payload returned by this response.
1398    pub content: String,
1399    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1400    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1401    /// these keys.
1402    ///
1403    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1404    #[serde_as(deserialize_as = "DefaultOnError")]
1405    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1406    #[serde(default)]
1407    #[serde(rename = "_meta")]
1408    pub meta: Option<Meta>,
1409}
1410
1411impl ReadTextFileResponse {
1412    /// Builds [`ReadTextFileResponse`] with the required response fields set; optional fields start unset or empty.
1413    #[must_use]
1414    pub fn new(content: impl Into<String>) -> Self {
1415        Self {
1416            content: content.into(),
1417            meta: None,
1418        }
1419    }
1420
1421    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1422    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1423    /// these keys.
1424    ///
1425    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1426    #[must_use]
1427    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1428        self.meta = meta.into_option();
1429        self
1430    }
1431}
1432
1433// Terminals
1434
1435/// Typed identifier used for terminal values on the wire.
1436#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1437#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1438#[serde(transparent)]
1439#[from(Arc<str>, String, &'static str)]
1440#[non_exhaustive]
1441pub struct TerminalId(pub Arc<str>);
1442
1443impl TerminalId {
1444    /// Wraps a protocol string as a typed [`TerminalId`].
1445    #[must_use]
1446    pub fn new(id: impl Into<Arc<str>>) -> Self {
1447        Self(id.into())
1448    }
1449}
1450
1451/// Request to create a new terminal and execute a command.
1452#[serde_as]
1453#[skip_serializing_none]
1454#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1455#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1456#[serde(rename_all = "camelCase")]
1457#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1458#[non_exhaustive]
1459pub struct CreateTerminalRequest {
1460    /// The session ID for this request.
1461    pub session_id: SessionId,
1462    /// The command to execute.
1463    pub command: String,
1464    /// Array of command arguments.
1465    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1466    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1467    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1468    pub args: Vec<String>,
1469    /// Environment variables for the command.
1470    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1471    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1472    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1473    pub env: Vec<EnvVariable>,
1474    /// Working directory for the command. Must be an absolute path.
1475    #[serde_as(deserialize_as = "DefaultOnError")]
1476    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1477    #[serde(default)]
1478    pub cwd: Option<PathBuf>,
1479    /// Maximum number of output bytes to retain.
1480    ///
1481    /// When the limit is exceeded, the Client truncates from the beginning of the output
1482    /// to stay within the limit.
1483    ///
1484    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
1485    /// string output, even if this means the retained output is slightly less than the
1486    /// specified limit.
1487    #[serde_as(deserialize_as = "DefaultOnError")]
1488    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1489    #[serde(default)]
1490    pub output_byte_limit: Option<u64>,
1491    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1492    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1493    /// these keys.
1494    ///
1495    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1496    #[serde_as(deserialize_as = "DefaultOnError")]
1497    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1498    #[serde(default)]
1499    #[serde(rename = "_meta")]
1500    pub meta: Option<Meta>,
1501}
1502
1503impl CreateTerminalRequest {
1504    /// Builds [`CreateTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1505    #[must_use]
1506    pub fn new(session_id: impl Into<SessionId>, command: impl Into<String>) -> Self {
1507        Self {
1508            session_id: session_id.into(),
1509            command: command.into(),
1510            args: Vec::new(),
1511            env: Vec::new(),
1512            cwd: None,
1513            output_byte_limit: None,
1514            meta: None,
1515        }
1516    }
1517
1518    /// Array of command arguments.
1519    #[must_use]
1520    pub fn args(mut self, args: Vec<String>) -> Self {
1521        self.args = args;
1522        self
1523    }
1524
1525    /// Environment variables for the command.
1526    #[must_use]
1527    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1528        self.env = env;
1529        self
1530    }
1531
1532    /// Working directory for the command. Must be an absolute path.
1533    #[must_use]
1534    pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1535        self.cwd = cwd.into_option();
1536        self
1537    }
1538
1539    /// Maximum number of output bytes to retain.
1540    ///
1541    /// When the limit is exceeded, the Client truncates from the beginning of the output
1542    /// to stay within the limit.
1543    ///
1544    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
1545    /// string output, even if this means the retained output is slightly less than the
1546    /// specified limit.
1547    #[must_use]
1548    pub fn output_byte_limit(mut self, output_byte_limit: impl IntoOption<u64>) -> Self {
1549        self.output_byte_limit = output_byte_limit.into_option();
1550        self
1551    }
1552
1553    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1554    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1555    /// these keys.
1556    ///
1557    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1558    #[must_use]
1559    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1560        self.meta = meta.into_option();
1561        self
1562    }
1563}
1564
1565/// Response containing the ID of the created terminal.
1566#[serde_as]
1567#[skip_serializing_none]
1568#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1569#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1570#[serde(rename_all = "camelCase")]
1571#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1572#[non_exhaustive]
1573pub struct CreateTerminalResponse {
1574    /// The unique identifier for the created terminal.
1575    pub terminal_id: TerminalId,
1576    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1577    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1578    /// these keys.
1579    ///
1580    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1581    #[serde_as(deserialize_as = "DefaultOnError")]
1582    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1583    #[serde(default)]
1584    #[serde(rename = "_meta")]
1585    pub meta: Option<Meta>,
1586}
1587
1588impl CreateTerminalResponse {
1589    /// Builds [`CreateTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1590    #[must_use]
1591    pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
1592        Self {
1593            terminal_id: terminal_id.into(),
1594            meta: None,
1595        }
1596    }
1597
1598    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1599    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1600    /// these keys.
1601    ///
1602    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1603    #[must_use]
1604    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1605        self.meta = meta.into_option();
1606        self
1607    }
1608}
1609
1610/// Request to get the current output and status of a terminal.
1611#[serde_as]
1612#[skip_serializing_none]
1613#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1614#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1615#[serde(rename_all = "camelCase")]
1616#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1617#[non_exhaustive]
1618pub struct TerminalOutputRequest {
1619    /// The session ID for this request.
1620    pub session_id: SessionId,
1621    /// The ID of the terminal to get output from.
1622    pub terminal_id: TerminalId,
1623    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1624    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1625    /// these keys.
1626    ///
1627    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1628    #[serde_as(deserialize_as = "DefaultOnError")]
1629    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1630    #[serde(default)]
1631    #[serde(rename = "_meta")]
1632    pub meta: Option<Meta>,
1633}
1634
1635impl TerminalOutputRequest {
1636    /// Builds [`TerminalOutputRequest`] with the required request fields set; optional fields start unset or empty.
1637    #[must_use]
1638    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1639        Self {
1640            session_id: session_id.into(),
1641            terminal_id: terminal_id.into(),
1642            meta: None,
1643        }
1644    }
1645
1646    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1647    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1648    /// these keys.
1649    ///
1650    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1651    #[must_use]
1652    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1653        self.meta = meta.into_option();
1654        self
1655    }
1656}
1657
1658/// Response containing the terminal output and exit status.
1659#[serde_as]
1660#[skip_serializing_none]
1661#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1662#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1663#[serde(rename_all = "camelCase")]
1664#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1665#[non_exhaustive]
1666pub struct TerminalOutputResponse {
1667    /// The terminal output captured so far.
1668    pub output: String,
1669    /// Whether the output was truncated due to byte limits.
1670    pub truncated: bool,
1671    /// Exit status if the command has completed.
1672    #[serde_as(deserialize_as = "DefaultOnError")]
1673    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1674    #[serde(default)]
1675    pub exit_status: Option<TerminalExitStatus>,
1676    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1677    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1678    /// these keys.
1679    ///
1680    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1681    #[serde_as(deserialize_as = "DefaultOnError")]
1682    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1683    #[serde(default)]
1684    #[serde(rename = "_meta")]
1685    pub meta: Option<Meta>,
1686}
1687
1688impl TerminalOutputResponse {
1689    /// Builds [`TerminalOutputResponse`] with the required response fields set; optional fields start unset or empty.
1690    #[must_use]
1691    pub fn new(output: impl Into<String>, truncated: bool) -> Self {
1692        Self {
1693            output: output.into(),
1694            truncated,
1695            exit_status: None,
1696            meta: None,
1697        }
1698    }
1699
1700    /// Exit status if the command has completed.
1701    #[must_use]
1702    pub fn exit_status(mut self, exit_status: impl IntoOption<TerminalExitStatus>) -> Self {
1703        self.exit_status = exit_status.into_option();
1704        self
1705    }
1706
1707    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1708    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1709    /// these keys.
1710    ///
1711    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1712    #[must_use]
1713    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1714        self.meta = meta.into_option();
1715        self
1716    }
1717}
1718
1719/// Request to release a terminal and free its resources.
1720#[serde_as]
1721#[skip_serializing_none]
1722#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1723#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1724#[serde(rename_all = "camelCase")]
1725#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1726#[non_exhaustive]
1727pub struct ReleaseTerminalRequest {
1728    /// The session ID for this request.
1729    pub session_id: SessionId,
1730    /// The ID of the terminal to release.
1731    pub terminal_id: TerminalId,
1732    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1733    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1734    /// these keys.
1735    ///
1736    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1737    #[serde_as(deserialize_as = "DefaultOnError")]
1738    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1739    #[serde(default)]
1740    #[serde(rename = "_meta")]
1741    pub meta: Option<Meta>,
1742}
1743
1744impl ReleaseTerminalRequest {
1745    /// Builds [`ReleaseTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1746    #[must_use]
1747    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1748        Self {
1749            session_id: session_id.into(),
1750            terminal_id: terminal_id.into(),
1751            meta: None,
1752        }
1753    }
1754
1755    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1756    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1757    /// these keys.
1758    ///
1759    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1760    #[must_use]
1761    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1762        self.meta = meta.into_option();
1763        self
1764    }
1765}
1766
1767/// Response to terminal/release method
1768#[serde_as]
1769#[skip_serializing_none]
1770#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1771#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1772#[serde(rename_all = "camelCase")]
1773#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1774#[non_exhaustive]
1775pub struct ReleaseTerminalResponse {
1776    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1777    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1778    /// these keys.
1779    ///
1780    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1781    #[serde_as(deserialize_as = "DefaultOnError")]
1782    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1783    #[serde(default)]
1784    #[serde(rename = "_meta")]
1785    pub meta: Option<Meta>,
1786}
1787
1788impl ReleaseTerminalResponse {
1789    /// Builds [`ReleaseTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1790    #[must_use]
1791    pub fn new() -> Self {
1792        Self::default()
1793    }
1794
1795    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1796    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1797    /// these keys.
1798    ///
1799    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1800    #[must_use]
1801    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1802        self.meta = meta.into_option();
1803        self
1804    }
1805}
1806
1807/// Request to kill a terminal without releasing it.
1808#[serde_as]
1809#[skip_serializing_none]
1810#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1811#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1812#[serde(rename_all = "camelCase")]
1813#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1814#[non_exhaustive]
1815pub struct KillTerminalRequest {
1816    /// The session ID for this request.
1817    pub session_id: SessionId,
1818    /// The ID of the terminal to kill.
1819    pub terminal_id: TerminalId,
1820    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1821    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1822    /// these keys.
1823    ///
1824    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1825    #[serde_as(deserialize_as = "DefaultOnError")]
1826    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1827    #[serde(default)]
1828    #[serde(rename = "_meta")]
1829    pub meta: Option<Meta>,
1830}
1831
1832impl KillTerminalRequest {
1833    /// Builds [`KillTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1834    #[must_use]
1835    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1836        Self {
1837            session_id: session_id.into(),
1838            terminal_id: terminal_id.into(),
1839            meta: None,
1840        }
1841    }
1842
1843    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1844    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1845    /// these keys.
1846    ///
1847    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1848    #[must_use]
1849    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1850        self.meta = meta.into_option();
1851        self
1852    }
1853}
1854
1855/// Response to `terminal/kill` method
1856#[serde_as]
1857#[skip_serializing_none]
1858#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1859#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1860#[serde(rename_all = "camelCase")]
1861#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1862#[non_exhaustive]
1863pub struct KillTerminalResponse {
1864    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1865    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1866    /// these keys.
1867    ///
1868    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1869    #[serde_as(deserialize_as = "DefaultOnError")]
1870    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1871    #[serde(default)]
1872    #[serde(rename = "_meta")]
1873    pub meta: Option<Meta>,
1874}
1875
1876impl KillTerminalResponse {
1877    /// Builds [`KillTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1878    #[must_use]
1879    pub fn new() -> Self {
1880        Self::default()
1881    }
1882
1883    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1884    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1885    /// these keys.
1886    ///
1887    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1888    #[must_use]
1889    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1890        self.meta = meta.into_option();
1891        self
1892    }
1893}
1894
1895/// Request to wait for a terminal command to exit.
1896#[serde_as]
1897#[skip_serializing_none]
1898#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1899#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1900#[serde(rename_all = "camelCase")]
1901#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1902#[non_exhaustive]
1903pub struct WaitForTerminalExitRequest {
1904    /// The session ID for this request.
1905    pub session_id: SessionId,
1906    /// The ID of the terminal to wait for.
1907    pub terminal_id: TerminalId,
1908    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1909    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1910    /// these keys.
1911    ///
1912    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1913    #[serde_as(deserialize_as = "DefaultOnError")]
1914    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1915    #[serde(default)]
1916    #[serde(rename = "_meta")]
1917    pub meta: Option<Meta>,
1918}
1919
1920impl WaitForTerminalExitRequest {
1921    /// Builds [`WaitForTerminalExitRequest`] with the required request fields set; optional fields start unset or empty.
1922    #[must_use]
1923    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1924        Self {
1925            session_id: session_id.into(),
1926            terminal_id: terminal_id.into(),
1927            meta: None,
1928        }
1929    }
1930
1931    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1932    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1933    /// these keys.
1934    ///
1935    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1936    #[must_use]
1937    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1938        self.meta = meta.into_option();
1939        self
1940    }
1941}
1942
1943/// Response containing the exit status of a terminal command.
1944#[serde_as]
1945#[skip_serializing_none]
1946#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1947#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1948#[serde(rename_all = "camelCase")]
1949#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1950#[non_exhaustive]
1951pub struct WaitForTerminalExitResponse {
1952    /// The exit status of the terminal command.
1953    #[serde(flatten)]
1954    pub exit_status: TerminalExitStatus,
1955    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1956    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1957    /// these keys.
1958    ///
1959    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1960    #[serde_as(deserialize_as = "DefaultOnError")]
1961    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1962    #[serde(default)]
1963    #[serde(rename = "_meta")]
1964    pub meta: Option<Meta>,
1965}
1966
1967impl WaitForTerminalExitResponse {
1968    /// Builds [`WaitForTerminalExitResponse`] with the required response fields set; optional fields start unset or empty.
1969    #[must_use]
1970    pub fn new(exit_status: TerminalExitStatus) -> Self {
1971        Self {
1972            exit_status,
1973            meta: None,
1974        }
1975    }
1976
1977    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1978    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1979    /// these keys.
1980    ///
1981    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1982    #[must_use]
1983    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1984        self.meta = meta.into_option();
1985        self
1986    }
1987}
1988
1989/// Exit status of a terminal command.
1990#[serde_as]
1991#[skip_serializing_none]
1992#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1993#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1994#[serde(rename_all = "camelCase")]
1995#[non_exhaustive]
1996pub struct TerminalExitStatus {
1997    /// The process exit code (may be null if terminated by signal).
1998    #[serde_as(deserialize_as = "DefaultOnError")]
1999    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2000    #[serde(default)]
2001    pub exit_code: Option<u32>,
2002    /// The signal that terminated the process (may be null if exited normally).
2003    #[serde_as(deserialize_as = "DefaultOnError")]
2004    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2005    #[serde(default)]
2006    pub signal: Option<String>,
2007    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2008    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2009    /// these keys.
2010    ///
2011    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2012    #[serde_as(deserialize_as = "DefaultOnError")]
2013    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2014    #[serde(default)]
2015    #[serde(rename = "_meta")]
2016    pub meta: Option<Meta>,
2017}
2018
2019impl TerminalExitStatus {
2020    /// Builds [`TerminalExitStatus`] with the required fields set; optional fields start unset or empty.
2021    #[must_use]
2022    pub fn new() -> Self {
2023        Self::default()
2024    }
2025
2026    /// The process exit code (may be null if terminated by signal).
2027    #[must_use]
2028    pub fn exit_code(mut self, exit_code: impl IntoOption<u32>) -> Self {
2029        self.exit_code = exit_code.into_option();
2030        self
2031    }
2032
2033    /// The signal that terminated the process (may be null if exited normally).
2034    #[must_use]
2035    pub fn signal(mut self, signal: impl IntoOption<String>) -> Self {
2036        self.signal = signal.into_option();
2037        self
2038    }
2039
2040    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2041    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2042    /// these keys.
2043    ///
2044    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2045    #[must_use]
2046    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2047        self.meta = meta.into_option();
2048        self
2049    }
2050}
2051
2052// Capabilities
2053
2054/// Capabilities supported by the client.
2055///
2056/// Advertised during initialization to inform the agent about
2057/// available features and methods.
2058///
2059/// See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/initialization#client-capabilities)
2060#[serde_as]
2061#[skip_serializing_none]
2062#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2063#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2064#[serde(rename_all = "camelCase")]
2065#[non_exhaustive]
2066pub struct ClientCapabilities {
2067    /// File system capabilities supported by the client.
2068    /// Determines which file operations the agent can request.
2069    #[serde_as(deserialize_as = "DefaultOnError")]
2070    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2071    #[serde(default)]
2072    pub fs: FileSystemCapabilities,
2073    /// Whether the Client support all `terminal/*` methods.
2074    #[serde_as(deserialize_as = "DefaultOnError")]
2075    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2076    #[serde(default)]
2077    pub terminal: bool,
2078    /// Session-related capabilities supported by the client.
2079    ///
2080    /// Optional. Omitted or `null` both mean the client does not advertise any
2081    /// session-related extensions.
2082    #[serde_as(deserialize_as = "DefaultOnError")]
2083    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2084    #[serde(default)]
2085    pub session: Option<ClientSessionCapabilities>,
2086    /// **UNSTABLE**
2087    ///
2088    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2089    ///
2090    /// Whether the client supports `plan_update` and `plan_removed` session updates.
2091    ///
2092    /// Optional. Omitted or `null` both mean the client does not advertise support.
2093    /// Supplying `{}` means the client can receive both update types.
2094    #[cfg(feature = "unstable_plan_operations")]
2095    #[serde_as(deserialize_as = "DefaultOnError")]
2096    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2097    #[serde(default)]
2098    pub plan: Option<PlanCapabilities>,
2099    /// Authentication capabilities supported by the client.
2100    /// Determines which authentication method types the agent may include
2101    /// in its `InitializeResponse`.
2102    #[serde_as(deserialize_as = "DefaultOnError")]
2103    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2104    #[serde(default)]
2105    pub auth: AuthCapabilities,
2106    /// Elicitation capabilities supported by the client.
2107    /// Determines which elicitation modes the agent may use.
2108    ///
2109    /// Optional. Omitted or `null` both mean the client does not advertise
2110    /// elicitation support.
2111    #[serde_as(deserialize_as = "DefaultOnError")]
2112    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2113    #[serde(default)]
2114    pub elicitation: Option<ElicitationCapabilities>,
2115    /// **UNSTABLE**
2116    ///
2117    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2118    ///
2119    /// NES (Next Edit Suggestions) capabilities supported by the client.
2120    ///
2121    /// Optional. Omitted or `null` both mean the client does not advertise any
2122    /// NES suggestion-kind extensions.
2123    #[cfg(feature = "unstable_nes")]
2124    #[serde_as(deserialize_as = "DefaultOnError")]
2125    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2126    #[serde(default)]
2127    pub nes: Option<ClientNesCapabilities>,
2128    /// **UNSTABLE**
2129    ///
2130    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2131    ///
2132    /// The position encodings supported by the client, in order of preference.
2133    #[cfg(feature = "unstable_nes")]
2134    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2135    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2136    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2137    pub position_encodings: Vec<PositionEncodingKind>,
2138
2139    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2140    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2141    /// these keys.
2142    ///
2143    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2144    #[serde_as(deserialize_as = "DefaultOnError")]
2145    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2146    #[serde(default)]
2147    #[serde(rename = "_meta")]
2148    pub meta: Option<Meta>,
2149}
2150
2151impl ClientCapabilities {
2152    /// Builds an empty [`ClientCapabilities`]; use builder methods to advertise supported sub-capabilities.
2153    #[must_use]
2154    pub fn new() -> Self {
2155        Self::default()
2156    }
2157
2158    /// File system capabilities supported by the client.
2159    /// Determines which file operations the agent can request.
2160    #[must_use]
2161    pub fn fs(mut self, fs: FileSystemCapabilities) -> Self {
2162        self.fs = fs;
2163        self
2164    }
2165
2166    /// Whether the Client support all `terminal/*` methods.
2167    #[must_use]
2168    pub fn terminal(mut self, terminal: bool) -> Self {
2169        self.terminal = terminal;
2170        self
2171    }
2172
2173    /// Session-related capabilities supported by the client.
2174    #[must_use]
2175    pub fn session(mut self, session: impl IntoOption<ClientSessionCapabilities>) -> Self {
2176        self.session = session.into_option();
2177        self
2178    }
2179
2180    /// **UNSTABLE**
2181    ///
2182    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2183    ///
2184    /// Whether the client supports `plan_update` and `plan_removed` session updates.
2185    ///
2186    /// Omitted or `null` both mean the client does not advertise support.
2187    /// Supplying `{}` means the client can receive both update types.
2188    #[cfg(feature = "unstable_plan_operations")]
2189    #[must_use]
2190    pub fn plan(mut self, plan: impl IntoOption<PlanCapabilities>) -> Self {
2191        self.plan = plan.into_option();
2192        self
2193    }
2194
2195    /// Authentication capabilities supported by the client.
2196    /// Determines which authentication method types the agent may include
2197    /// in its `InitializeResponse`.
2198    #[must_use]
2199    pub fn auth(mut self, auth: AuthCapabilities) -> Self {
2200        self.auth = auth;
2201        self
2202    }
2203
2204    /// Elicitation capabilities supported by the client.
2205    /// Determines which elicitation modes the agent may use.
2206    #[must_use]
2207    pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
2208        self.elicitation = elicitation.into_option();
2209        self
2210    }
2211
2212    /// **UNSTABLE**
2213    ///
2214    /// NES (Next Edit Suggestions) capabilities supported by the client.
2215    #[cfg(feature = "unstable_nes")]
2216    #[must_use]
2217    pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
2218        self.nes = nes.into_option();
2219        self
2220    }
2221
2222    /// **UNSTABLE**
2223    ///
2224    /// The position encodings supported by the client, in order of preference.
2225    #[cfg(feature = "unstable_nes")]
2226    #[must_use]
2227    pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
2228        self.position_encodings = position_encodings;
2229        self
2230    }
2231
2232    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2233    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2234    /// these keys.
2235    ///
2236    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2237    #[must_use]
2238    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2239        self.meta = meta.into_option();
2240        self
2241    }
2242}
2243
2244/// Session-related capabilities supported by the client.
2245#[serde_as]
2246#[skip_serializing_none]
2247#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2248#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2249#[serde(rename_all = "camelCase")]
2250#[non_exhaustive]
2251pub struct ClientSessionCapabilities {
2252    /// **UNSTABLE**
2253    ///
2254    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2255    ///
2256    /// Support for ID-addressed context compaction updates. Omitted or `null`
2257    /// means unsupported; `{}` advertises the complete compaction contract.
2258    #[cfg(feature = "unstable_session_compaction")]
2259    #[serde_as(deserialize_as = "DefaultOnError")]
2260    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2261    #[serde(default)]
2262    pub compaction: Option<CompactionCapabilities>,
2263    /// Config option capabilities supported by the client.
2264    ///
2265    /// Omitted or `null` both mean the client does not advertise support for any
2266    /// config option extensions.
2267    #[serde_as(deserialize_as = "DefaultOnError")]
2268    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2269    #[serde(default)]
2270    pub config_options: Option<SessionConfigOptionsCapabilities>,
2271    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2272    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2273    /// these keys.
2274    ///
2275    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2276    #[serde_as(deserialize_as = "DefaultOnError")]
2277    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2278    #[serde(default)]
2279    #[serde(rename = "_meta")]
2280    pub meta: Option<Meta>,
2281}
2282
2283impl ClientSessionCapabilities {
2284    /// Builds an empty [`ClientSessionCapabilities`]; use builder methods to advertise supported sub-capabilities.
2285    #[must_use]
2286    pub fn new() -> Self {
2287        Self::default()
2288    }
2289
2290    /// Advertises support for ID-addressed context compaction updates.
2291    #[cfg(feature = "unstable_session_compaction")]
2292    #[must_use]
2293    pub fn compaction(mut self, compaction: impl IntoOption<CompactionCapabilities>) -> Self {
2294        self.compaction = compaction.into_option();
2295        self
2296    }
2297
2298    /// Config option capabilities supported by the client.
2299    ///
2300    /// Omitted or `null` both mean the client does not advertise support for any
2301    /// config option extensions.
2302    #[must_use]
2303    pub fn config_options(
2304        mut self,
2305        config_options: impl IntoOption<SessionConfigOptionsCapabilities>,
2306    ) -> Self {
2307        self.config_options = config_options.into_option();
2308        self
2309    }
2310
2311    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2312    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2313    /// these keys.
2314    ///
2315    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2316    #[must_use]
2317    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2318        self.meta = meta.into_option();
2319        self
2320    }
2321}
2322
2323/// **UNSTABLE**
2324///
2325/// This capability is not part of the spec yet, and may be removed or changed at any point.
2326///
2327/// Client support for ID-addressed context compaction updates.
2328#[cfg(feature = "unstable_session_compaction")]
2329#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2330#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2331#[serde(rename_all = "camelCase")]
2332#[non_exhaustive]
2333pub struct CompactionCapabilities {}
2334
2335#[cfg(feature = "unstable_session_compaction")]
2336impl CompactionCapabilities {
2337    /// Advertises the complete compaction update contract.
2338    #[must_use]
2339    pub fn new() -> Self {
2340        Self {}
2341    }
2342}
2343
2344/// Session configuration option capabilities supported by the client.
2345#[serde_as]
2346#[skip_serializing_none]
2347#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2348#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2349#[serde(rename_all = "camelCase")]
2350#[non_exhaustive]
2351pub struct SessionConfigOptionsCapabilities {
2352    /// Whether the client supports boolean session configuration options.
2353    ///
2354    /// Optional. Omitted or `null` both mean the client does not advertise support.
2355    /// Supplying `{}` means agents may include `type: "boolean"` entries in
2356    /// `configOptions`, and the client may send `session/set_config_option`
2357    /// requests with `type: "boolean"` and a boolean `value`.
2358    #[serde_as(deserialize_as = "DefaultOnError")]
2359    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2360    #[serde(default)]
2361    pub boolean: Option<BooleanConfigOptionCapabilities>,
2362    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2363    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2364    /// these keys.
2365    ///
2366    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2367    #[serde_as(deserialize_as = "DefaultOnError")]
2368    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2369    #[serde(default)]
2370    #[serde(rename = "_meta")]
2371    pub meta: Option<Meta>,
2372}
2373
2374impl SessionConfigOptionsCapabilities {
2375    /// Builds an empty [`SessionConfigOptionsCapabilities`]; use builder methods to advertise supported sub-capabilities.
2376    #[must_use]
2377    pub fn new() -> Self {
2378        Self::default()
2379    }
2380
2381    /// Whether the client supports boolean session configuration options.
2382    ///
2383    /// Omitted or `null` both mean the client does not advertise support.
2384    /// Supplying `{}` means agents may include `type: "boolean"` entries in
2385    /// `configOptions`, and the client may send `session/set_config_option`
2386    /// requests with `type: "boolean"` and a boolean `value`.
2387    #[must_use]
2388    pub fn boolean(mut self, boolean: impl IntoOption<BooleanConfigOptionCapabilities>) -> Self {
2389        self.boolean = boolean.into_option();
2390        self
2391    }
2392
2393    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2394    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2395    /// these keys.
2396    ///
2397    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2398    #[must_use]
2399    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2400        self.meta = meta.into_option();
2401        self
2402    }
2403}
2404
2405/// Capabilities for boolean session configuration options.
2406///
2407/// Supplying `{}` means the client supports boolean session configuration options.
2408#[serde_as]
2409#[skip_serializing_none]
2410#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2411#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2412#[non_exhaustive]
2413pub struct BooleanConfigOptionCapabilities {
2414    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2415    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2416    /// these keys.
2417    ///
2418    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2419    #[serde_as(deserialize_as = "DefaultOnError")]
2420    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2421    #[serde(default)]
2422    #[serde(rename = "_meta")]
2423    pub meta: Option<Meta>,
2424}
2425
2426impl BooleanConfigOptionCapabilities {
2427    /// Builds an empty [`BooleanConfigOptionCapabilities`]; use builder methods to advertise supported sub-capabilities.
2428    #[must_use]
2429    pub fn new() -> Self {
2430        Self::default()
2431    }
2432
2433    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2434    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2435    /// these keys.
2436    ///
2437    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2438    #[must_use]
2439    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2440        self.meta = meta.into_option();
2441        self
2442    }
2443}
2444
2445/// Authentication capabilities supported by the client.
2446///
2447/// Advertised during initialization to inform the agent which authentication
2448/// method types the client can handle. This governs opt-in types that require
2449/// additional client-side support.
2450#[serde_as]
2451#[skip_serializing_none]
2452#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2453#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2454#[serde(rename_all = "camelCase")]
2455#[non_exhaustive]
2456pub struct AuthCapabilities {
2457    /// Whether the client supports `terminal` authentication methods.
2458    ///
2459    /// The client should set this to `true` only when it can reproduce the
2460    /// configured agent invocation in an interactive terminal. When `true`, the
2461    /// agent may include `terminal` entries in its authentication methods.
2462    #[serde_as(deserialize_as = "DefaultOnError")]
2463    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2464    #[serde(default)]
2465    pub terminal: bool,
2466    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2467    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2468    /// these keys.
2469    ///
2470    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2471    #[serde_as(deserialize_as = "DefaultOnError")]
2472    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2473    #[serde(default)]
2474    #[serde(rename = "_meta")]
2475    pub meta: Option<Meta>,
2476}
2477
2478impl AuthCapabilities {
2479    /// Builds an empty [`AuthCapabilities`]; use builder methods to advertise supported sub-capabilities.
2480    #[must_use]
2481    pub fn new() -> Self {
2482        Self::default()
2483    }
2484
2485    /// Whether the client supports `terminal` authentication methods.
2486    ///
2487    /// The client should set this to `true` only when it can reproduce the
2488    /// configured agent invocation in an interactive terminal. When `true`, the
2489    /// agent may include `AuthMethod::Terminal` entries in its authentication
2490    /// methods.
2491    #[must_use]
2492    pub fn terminal(mut self, terminal: bool) -> Self {
2493        self.terminal = terminal;
2494        self
2495    }
2496
2497    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2498    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2499    /// these keys.
2500    ///
2501    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2502    #[must_use]
2503    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2504        self.meta = meta.into_option();
2505        self
2506    }
2507}
2508
2509/// File system capabilities that a client may support.
2510///
2511/// See protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initialization#filesystem)
2512#[serde_as]
2513#[skip_serializing_none]
2514#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2515#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2516#[serde(rename_all = "camelCase")]
2517#[non_exhaustive]
2518pub struct FileSystemCapabilities {
2519    /// Whether the Client supports `fs/read_text_file` requests.
2520    #[serde_as(deserialize_as = "DefaultOnError")]
2521    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2522    #[serde(default)]
2523    pub read_text_file: bool,
2524    /// Whether the Client supports `fs/write_text_file` requests.
2525    #[serde_as(deserialize_as = "DefaultOnError")]
2526    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2527    #[serde(default)]
2528    pub write_text_file: bool,
2529    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2530    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2531    /// these keys.
2532    ///
2533    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2534    #[serde_as(deserialize_as = "DefaultOnError")]
2535    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2536    #[serde(default)]
2537    #[serde(rename = "_meta")]
2538    pub meta: Option<Meta>,
2539}
2540
2541impl FileSystemCapabilities {
2542    /// Builds an empty [`FileSystemCapabilities`]; use builder methods to advertise supported sub-capabilities.
2543    #[must_use]
2544    pub fn new() -> Self {
2545        Self::default()
2546    }
2547
2548    /// Whether the Client supports `fs/read_text_file` requests.
2549    #[must_use]
2550    pub fn read_text_file(mut self, read_text_file: bool) -> Self {
2551        self.read_text_file = read_text_file;
2552        self
2553    }
2554
2555    /// Whether the Client supports `fs/write_text_file` requests.
2556    #[must_use]
2557    pub fn write_text_file(mut self, write_text_file: bool) -> Self {
2558        self.write_text_file = write_text_file;
2559        self
2560    }
2561
2562    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2563    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2564    /// these keys.
2565    ///
2566    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2567    #[must_use]
2568    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2569        self.meta = meta.into_option();
2570        self
2571    }
2572}
2573
2574// Method schema
2575
2576/// Names of all methods that clients handle.
2577///
2578/// Provides a centralized definition of method names used in the protocol.
2579#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2580#[non_exhaustive]
2581pub struct ClientMethodNames {
2582    /// Method for requesting permission from the user.
2583    pub session_request_permission: &'static str,
2584    /// Notification for session updates.
2585    pub session_update: &'static str,
2586    /// Method for writing text files.
2587    pub fs_write_text_file: &'static str,
2588    /// Method for reading text files.
2589    pub fs_read_text_file: &'static str,
2590    /// Method for creating new terminals.
2591    pub terminal_create: &'static str,
2592    /// Method for getting terminals output.
2593    pub terminal_output: &'static str,
2594    /// Method for releasing a terminal.
2595    pub terminal_release: &'static str,
2596    /// Method for waiting for a terminal to finish.
2597    pub terminal_wait_for_exit: &'static str,
2598    /// Method for killing a terminal.
2599    pub terminal_kill: &'static str,
2600    /// Method for opening an MCP-over-ACP connection.
2601    #[cfg(feature = "unstable_mcp_over_acp")]
2602    pub mcp_connect: &'static str,
2603    /// Method for exchanging MCP-over-ACP messages.
2604    #[cfg(feature = "unstable_mcp_over_acp")]
2605    pub mcp_message: &'static str,
2606    /// Method for closing an MCP-over-ACP connection.
2607    #[cfg(feature = "unstable_mcp_over_acp")]
2608    pub mcp_disconnect: &'static str,
2609    /// Method for elicitation.
2610    pub elicitation_create: &'static str,
2611    /// Notification for elicitation completion.
2612    pub elicitation_complete: &'static str,
2613}
2614
2615/// Constant containing all client method names.
2616pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
2617    session_update: SESSION_UPDATE_NOTIFICATION,
2618    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
2619    fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
2620    fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
2621    terminal_create: TERMINAL_CREATE_METHOD_NAME,
2622    terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
2623    terminal_release: TERMINAL_RELEASE_METHOD_NAME,
2624    terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
2625    terminal_kill: TERMINAL_KILL_METHOD_NAME,
2626    #[cfg(feature = "unstable_mcp_over_acp")]
2627    mcp_connect: MCP_CONNECT_METHOD_NAME,
2628    #[cfg(feature = "unstable_mcp_over_acp")]
2629    mcp_message: MCP_MESSAGE_METHOD_NAME,
2630    #[cfg(feature = "unstable_mcp_over_acp")]
2631    mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
2632    elicitation_create: ELICITATION_CREATE_METHOD_NAME,
2633    elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
2634};
2635
2636/// Notification name for session updates.
2637pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
2638/// Method name for requesting user permission.
2639pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
2640/// Method name for writing text files.
2641pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
2642/// Method name for reading text files.
2643pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
2644/// Method name for creating a new terminal.
2645pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
2646/// Method for getting terminals output.
2647pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
2648/// Method for releasing a terminal.
2649pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
2650/// Method for waiting for a terminal to finish.
2651pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
2652/// Method for killing a terminal.
2653pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
2654/// Method name for elicitation.
2655pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
2656/// Notification name for elicitation completion.
2657pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
2658
2659/// All possible requests that an agent can send to a client.
2660///
2661/// This enum is used internally for routing RPC requests. You typically won't need
2662/// to use this directly.
2663///
2664/// This enum encompasses all method calls from agent to client.
2665#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2666#[derive(Clone, Debug, Serialize, Deserialize)]
2667#[serde(untagged)]
2668#[cfg_attr(feature = "schemars", schemars(inline))]
2669#[non_exhaustive]
2670#[allow(clippy::large_enum_variant)]
2671pub enum AgentRequest {
2672    /// Writes content to a text file in the client's file system.
2673    ///
2674    /// Only available if the client advertises the `fs.writeTextFile` capability.
2675    /// Allows the agent to create or modify files within the client's environment.
2676    ///
2677    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
2678    WriteTextFileRequest(WriteTextFileRequest),
2679    /// Reads content from a text file in the client's file system.
2680    ///
2681    /// Only available if the client advertises the `fs.readTextFile` capability.
2682    /// Allows the agent to access file contents within the client's environment.
2683    ///
2684    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
2685    ReadTextFileRequest(ReadTextFileRequest),
2686    /// Requests permission from the user for a tool call operation.
2687    ///
2688    /// Called by the agent when it needs user authorization before executing
2689    /// a potentially sensitive operation. The client should present the options
2690    /// to the user and return their decision.
2691    ///
2692    /// If the client cancels the prompt turn via `session/cancel`, it MUST
2693    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
2694    ///
2695    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
2696    RequestPermissionRequest(RequestPermissionRequest),
2697    /// Executes a command in a new terminal
2698    ///
2699    /// Only available if the `terminal` Client capability is set to `true`.
2700    ///
2701    /// Returns a `TerminalId` that can be used with other terminal methods
2702    /// to get the current output, wait for exit, and kill the command.
2703    ///
2704    /// The `TerminalId` can also be used to embed the terminal in a tool call
2705    /// by using the `ToolCallContent::Terminal` variant.
2706    ///
2707    /// The Agent is responsible for releasing the terminal by using the `terminal/release`
2708    /// method.
2709    ///
2710    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2711    CreateTerminalRequest(CreateTerminalRequest),
2712    /// Gets the terminal output and exit status
2713    ///
2714    /// Returns the current content in the terminal without waiting for the command to exit.
2715    /// If the command has already exited, the exit status is included.
2716    ///
2717    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2718    TerminalOutputRequest(TerminalOutputRequest),
2719    /// Releases a terminal
2720    ///
2721    /// The command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`
2722    /// to wait for the command to exit before releasing the terminal.
2723    ///
2724    /// After release, the `TerminalId` can no longer be used with other `terminal/*` methods,
2725    /// but tool calls that already contain it, continue to display its output.
2726    ///
2727    /// The `terminal/kill` method can be used to terminate the command without releasing
2728    /// the terminal, allowing the Agent to call `terminal/output` and other methods.
2729    ///
2730    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2731    ReleaseTerminalRequest(ReleaseTerminalRequest),
2732    /// Waits for the terminal command to exit and return its exit status
2733    ///
2734    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2735    WaitForTerminalExitRequest(WaitForTerminalExitRequest),
2736    /// Kills the terminal command without releasing the terminal
2737    ///
2738    /// While `terminal/release` will also kill the command, this method will keep
2739    /// the `TerminalId` valid so it can be used with other methods.
2740    ///
2741    /// This method can be helpful when implementing command timeouts which terminate
2742    /// the command as soon as elapsed, and then get the final output so it can be sent
2743    /// to the model.
2744    ///
2745    /// Note: Call `terminal/release` when `TerminalId` is no longer needed.
2746    ///
2747    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2748    KillTerminalRequest(KillTerminalRequest),
2749    /// Requests structured user input via a form or URL.
2750    ///
2751    /// See protocol docs: [Elicitation](https://agentclientprotocol.com/protocol/elicitation)
2752    CreateElicitationRequest(CreateElicitationRequest),
2753    /// **UNSTABLE**
2754    ///
2755    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2756    ///
2757    /// Opens an MCP-over-ACP connection.
2758    #[cfg(feature = "unstable_mcp_over_acp")]
2759    ConnectMcpRequest(ConnectMcpRequest),
2760    /// **UNSTABLE**
2761    ///
2762    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2763    ///
2764    /// Exchanges an MCP-over-ACP message.
2765    #[cfg(feature = "unstable_mcp_over_acp")]
2766    MessageMcpRequest(MessageMcpRequest),
2767    /// **UNSTABLE**
2768    ///
2769    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2770    ///
2771    /// Closes an MCP-over-ACP connection.
2772    #[cfg(feature = "unstable_mcp_over_acp")]
2773    DisconnectMcpRequest(DisconnectMcpRequest),
2774    /// Handles extension method requests from the agent.
2775    ///
2776    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
2777    /// Extension methods provide a way to add custom functionality while maintaining
2778    /// protocol compatibility.
2779    ///
2780    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2781    ExtMethodRequest(ExtRequest),
2782}
2783
2784impl AgentRequest {
2785    /// Returns the corresponding method name of the request.
2786    #[must_use]
2787    pub fn method(&self) -> &str {
2788        match self {
2789            Self::WriteTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_write_text_file,
2790            Self::ReadTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_read_text_file,
2791            Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
2792            Self::CreateTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_create,
2793            Self::TerminalOutputRequest(_) => CLIENT_METHOD_NAMES.terminal_output,
2794            Self::ReleaseTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_release,
2795            Self::WaitForTerminalExitRequest(_) => CLIENT_METHOD_NAMES.terminal_wait_for_exit,
2796            Self::KillTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_kill,
2797            Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
2798            #[cfg(feature = "unstable_mcp_over_acp")]
2799            Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
2800            #[cfg(feature = "unstable_mcp_over_acp")]
2801            Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
2802            #[cfg(feature = "unstable_mcp_over_acp")]
2803            Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
2804            Self::ExtMethodRequest(ext_request) => &ext_request.method,
2805        }
2806    }
2807}
2808
2809/// All possible responses that a client can send to an agent.
2810///
2811/// This enum is used internally for routing RPC responses. You typically won't need
2812/// to use this directly - the responses are handled automatically by the connection.
2813///
2814/// These are responses to the corresponding `AgentRequest` variants.
2815#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2816#[derive(Clone, Debug, Serialize, Deserialize)]
2817#[serde(untagged)]
2818#[cfg_attr(feature = "schemars", schemars(inline))]
2819#[non_exhaustive]
2820pub enum ClientResponse {
2821    /// Successful result returned for a `fs/write_text_file` request.
2822    WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
2823    /// Successful result returned for a `fs/read_text_file` request.
2824    ReadTextFileResponse(ReadTextFileResponse),
2825    /// Successful result returned for a `session/request_permission` request.
2826    RequestPermissionResponse(RequestPermissionResponse),
2827    /// Successful result returned for a `terminal/create` request.
2828    CreateTerminalResponse(CreateTerminalResponse),
2829    /// Successful result returned for a `terminal/output` request.
2830    TerminalOutputResponse(TerminalOutputResponse),
2831    /// Successful result returned for a `terminal/release` request.
2832    ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
2833    /// Successful result returned for a `terminal/wait_for_exit` request.
2834    WaitForTerminalExitResponse(WaitForTerminalExitResponse),
2835    /// Successful result returned for a `terminal/kill` request.
2836    KillTerminalResponse(#[serde(default)] KillTerminalResponse),
2837    /// Successful result returned for a `elicitation/create` request.
2838    CreateElicitationResponse(CreateElicitationResponse),
2839    /// Successful result returned for a `mcp/connect` request.
2840    #[cfg(feature = "unstable_mcp_over_acp")]
2841    ConnectMcpResponse(ConnectMcpResponse),
2842    /// Successful result returned for a `mcp/disconnect` request.
2843    #[cfg(feature = "unstable_mcp_over_acp")]
2844    DisconnectMcpResponse(#[serde(default)] DisconnectMcpResponse),
2845    /// Successful result returned by an MCP-over-ACP `mcp/message` request.
2846    #[cfg(feature = "unstable_mcp_over_acp")]
2847    MessageMcpResponse(MessageMcpResponse),
2848    /// Successful result returned by an extension method outside the core ACP method set.
2849    ExtMethodResponse(ExtResponse),
2850}
2851
2852/// All possible notifications that an agent can send to a client.
2853///
2854/// This enum is used internally for routing RPC notifications. You typically won't need
2855/// to use this directly.
2856///
2857/// Notifications do not expect a response.
2858#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2859#[derive(Clone, Debug, Serialize, Deserialize)]
2860#[serde(untagged)]
2861#[expect(clippy::large_enum_variant)]
2862#[cfg_attr(feature = "schemars", schemars(inline))]
2863#[non_exhaustive]
2864pub enum AgentNotification {
2865    /// Handles session update notifications from the agent.
2866    ///
2867    /// This is a notification endpoint (no response expected) that receives
2868    /// real-time updates about session progress, including message chunks,
2869    /// tool calls, and execution plans.
2870    ///
2871    /// Note: Clients SHOULD continue accepting tool call updates even after
2872    /// sending a `session/cancel` notification, as the agent may send final
2873    /// updates before responding with the cancelled stop reason.
2874    ///
2875    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
2876    SessionNotification(SessionNotification),
2877    /// Notification that a URL-based elicitation has completed.
2878    ///
2879    /// See protocol docs: [Elicitation](https://agentclientprotocol.com/protocol/elicitation#url-completion)
2880    CompleteElicitationNotification(CompleteElicitationNotification),
2881    /// **UNSTABLE**
2882    ///
2883    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2884    ///
2885    /// Receives an MCP-over-ACP notification.
2886    #[cfg(feature = "unstable_mcp_over_acp")]
2887    MessageMcpNotification(MessageMcpNotification),
2888    /// Handles extension notifications from the agent.
2889    ///
2890    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
2891    /// Extension notifications provide a way to send one-way messages for custom functionality
2892    /// while maintaining protocol compatibility.
2893    ///
2894    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2895    ExtNotification(ExtNotification),
2896}
2897
2898impl AgentNotification {
2899    /// Returns the corresponding method name of the notification.
2900    #[must_use]
2901    pub fn method(&self) -> &str {
2902        match self {
2903            Self::SessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
2904            Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
2905            #[cfg(feature = "unstable_mcp_over_acp")]
2906            Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
2907            Self::ExtNotification(ext_notification) => &ext_notification.method,
2908        }
2909    }
2910}
2911
2912#[cfg(test)]
2913mod tests {
2914    use super::*;
2915
2916    #[cfg(feature = "unstable_session_notices")]
2917    #[test]
2918    fn notice_preserves_wire_shape_nullable_fields_and_open_severity() {
2919        use serde_json::json;
2920
2921        let mut meta = Meta::new();
2922        meta.insert("source".into(), json!("fallback"));
2923        assert_eq!(
2924            serde_json::to_value(SessionUpdate::Notice(
2925                Notice::new(NoticeSeverity::Warning, "MCP server unavailable")
2926                    .description("Continuing without it.")
2927                    .meta(meta),
2928            ))
2929            .unwrap(),
2930            json!({
2931                "sessionUpdate": "notice",
2932                "severity": "warning",
2933                "title": "MCP server unavailable",
2934                "description": "Continuing without it.",
2935                "_meta": { "source": "fallback" }
2936            })
2937        );
2938
2939        assert_eq!(
2940            serde_json::to_value(SessionUpdate::Notice(Notice::new(
2941                NoticeSeverity::Info,
2942                "Indexing workspace",
2943            )))
2944            .unwrap(),
2945            json!({
2946                "sessionUpdate": "notice",
2947                "severity": "info",
2948                "title": "Indexing workspace"
2949            })
2950        );
2951
2952        let SessionUpdate::Notice(notice) = serde_json::from_value(json!({
2953            "sessionUpdate": "notice",
2954            "severity": "critical",
2955            "title": "Provider degraded",
2956            "description": null,
2957            "_meta": null
2958        }))
2959        .unwrap() else {
2960            panic!("expected notice");
2961        };
2962
2963        assert_eq!(
2964            notice.severity,
2965            NoticeSeverity::Other("critical".to_string())
2966        );
2967        assert_eq!(notice.description, None);
2968        assert_eq!(notice.meta, None);
2969        assert_eq!(
2970            serde_json::to_value(SessionUpdate::Notice(notice)).unwrap(),
2971            json!({
2972                "sessionUpdate": "notice",
2973                "severity": "critical",
2974                "title": "Provider degraded"
2975            })
2976        );
2977    }
2978
2979    #[cfg(feature = "unstable_session_notices")]
2980    #[test]
2981    fn notice_requires_non_null_severity_and_title() {
2982        use serde_json::json;
2983
2984        for malformed in [
2985            json!({
2986                "sessionUpdate": "notice",
2987                "severity": "warning"
2988            }),
2989            json!({
2990                "sessionUpdate": "notice",
2991                "severity": "warning",
2992                "title": null
2993            }),
2994            json!({
2995                "sessionUpdate": "notice",
2996                "title": "MCP server unavailable"
2997            }),
2998            json!({
2999                "sessionUpdate": "notice",
3000                "severity": null,
3001                "title": "MCP server unavailable"
3002            }),
3003        ] {
3004            assert!(serde_json::from_value::<SessionUpdate>(malformed).is_err());
3005        }
3006    }
3007
3008    #[cfg(not(feature = "unstable_session_notices"))]
3009    #[test]
3010    fn unsupported_notice_is_rejected_by_closed_update_union() {
3011        use serde_json::json;
3012
3013        assert!(
3014            serde_json::from_value::<SessionUpdate>(json!({
3015                "sessionUpdate": "notice",
3016                "severity": "warning",
3017                "title": "MCP server unavailable"
3018            }))
3019            .is_err()
3020        );
3021    }
3022
3023    #[cfg(feature = "unstable_session_compaction")]
3024    #[test]
3025    fn compaction_updates_preserve_patch_and_open_status_semantics() {
3026        use serde_json::json;
3027
3028        assert_eq!(
3029            serde_json::to_value(SessionUpdate::CompactionUpdate(CompactionUpdate::new(
3030                "cmp_001",
3031                CompactionStatus::InProgress,
3032            )))
3033            .unwrap(),
3034            json!({
3035                "sessionUpdate": "compaction_update",
3036                "compactionId": "cmp_001",
3037                "status": "in_progress"
3038            })
3039        );
3040
3041        let SessionUpdate::CompactionUpdate(update) = serde_json::from_value(json!({
3042            "sessionUpdate": "compaction_update",
3043            "compactionId": "cmp_001",
3044            "status": "paused",
3045            "summary": null,
3046            "error": "waiting"
3047        }))
3048        .unwrap() else {
3049            panic!("expected compaction update");
3050        };
3051        assert_eq!(update.status, CompactionStatus::Other("paused".into()));
3052        assert!(update.summary.is_null());
3053        assert_eq!(update.error.value().map(String::as_str), Some("waiting"));
3054        assert!(update.meta.is_undefined());
3055    }
3056
3057    #[cfg(feature = "unstable_session_compaction")]
3058    #[test]
3059    fn compaction_chunk_and_v1_capability_serialize() {
3060        use serde_json::json;
3061
3062        assert_eq!(
3063            serde_json::to_value(SessionUpdate::CompactionSummaryChunk(
3064                CompactionSummaryChunk::new(
3065                    "cmp_001",
3066                    ContentBlock::Text(crate::v1::TextContent::new("retained")),
3067                ),
3068            ))
3069            .unwrap(),
3070            json!({
3071                "sessionUpdate": "compaction_summary_chunk",
3072                "compactionId": "cmp_001",
3073                "content": { "type": "text", "text": "retained" }
3074            })
3075        );
3076        assert_eq!(
3077            serde_json::to_value(
3078                ClientSessionCapabilities::new().compaction(CompactionCapabilities::new())
3079            )
3080            .unwrap(),
3081            json!({ "compaction": {} })
3082        );
3083        let absent: ClientSessionCapabilities = serde_json::from_value(json!({})).unwrap();
3084        let null: ClientSessionCapabilities =
3085            serde_json::from_value(json!({ "compaction": null })).unwrap();
3086        assert!(absent.compaction.is_none());
3087        assert!(null.compaction.is_none());
3088    }
3089
3090    #[test]
3091    fn test_elicitation_capability_semantics() {
3092        use serde_json::json;
3093
3094        let unsupported: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3095        assert!(unsupported.elicitation.is_none());
3096
3097        let null: ClientCapabilities =
3098            serde_json::from_value(json!({ "elicitation": null })).unwrap();
3099        assert!(null.elicitation.is_none());
3100
3101        let malformed: ClientCapabilities =
3102            serde_json::from_value(json!({ "elicitation": false })).unwrap();
3103        assert!(malformed.elicitation.is_none());
3104
3105        let empty: ClientCapabilities =
3106            serde_json::from_value(json!({ "elicitation": {} })).unwrap();
3107        let empty = empty.elicitation.expect("present capability");
3108        assert!(!empty.supports_form());
3109        assert!(!empty.supports_url());
3110
3111        let form_only: ClientCapabilities = serde_json::from_value(json!({
3112            "elicitation": { "form": {} }
3113        }))
3114        .unwrap();
3115        let form_only = form_only.elicitation.expect("advertised capability");
3116        assert!(form_only.supports_form());
3117        assert!(!form_only.supports_url());
3118
3119        let url_only: ClientCapabilities = serde_json::from_value(json!({
3120            "elicitation": { "url": {} }
3121        }))
3122        .unwrap();
3123        let url_only = url_only.elicitation.expect("advertised capability");
3124        assert!(!url_only.supports_form());
3125        assert!(url_only.supports_url());
3126
3127        let both: ClientCapabilities = serde_json::from_value(json!({
3128            "elicitation": { "form": {}, "url": {} }
3129        }))
3130        .unwrap();
3131        let both = both.elicitation.expect("advertised capability");
3132        assert!(both.supports_form());
3133        assert!(both.supports_url());
3134    }
3135
3136    #[test]
3137    fn test_elicitation_method_routing_and_envelopes() {
3138        use serde_json::json;
3139
3140        assert_eq!(CLIENT_METHOD_NAMES.elicitation_create, "elicitation/create");
3141        assert_eq!(
3142            CLIENT_METHOD_NAMES.elicitation_complete,
3143            "elicitation/complete"
3144        );
3145
3146        let request = AgentRequest::CreateElicitationRequest(CreateElicitationRequest::new(
3147            crate::v1::ElicitationFormMode::new(
3148                crate::v1::ElicitationSessionScope::new("sess_1"),
3149                crate::v1::ElicitationSchema::new(),
3150            ),
3151            "Choose a value",
3152        ));
3153        assert_eq!(request.method(), "elicitation/create");
3154        let method = Arc::from(request.method());
3155        let request = crate::v1::JsonRpcMessage::wrap(crate::v1::Request {
3156            id: crate::v1::RequestId::Number(7),
3157            method,
3158            params: Some(request),
3159        });
3160        assert_eq!(
3161            serde_json::to_value(request).unwrap(),
3162            json!({
3163                "jsonrpc": "2.0",
3164                "id": 7,
3165                "method": "elicitation/create",
3166                "params": {
3167                    "mode": "form",
3168                    "sessionId": "sess_1",
3169                    "message": "Choose a value",
3170                    "requestedSchema": { "type": "object", "properties": {} }
3171                }
3172            })
3173        );
3174
3175        let notification = AgentNotification::CompleteElicitationNotification(
3176            CompleteElicitationNotification::new("elic_1"),
3177        );
3178        assert_eq!(notification.method(), "elicitation/complete");
3179        let method = Arc::from(notification.method());
3180        let notification = crate::v1::JsonRpcMessage::wrap(crate::v1::Notification {
3181            method,
3182            params: Some(notification),
3183        });
3184        assert_eq!(
3185            serde_json::to_value(notification).unwrap(),
3186            json!({
3187                "jsonrpc": "2.0",
3188                "method": "elicitation/complete",
3189                "params": { "elicitationId": "elic_1" }
3190            })
3191        );
3192    }
3193
3194    #[test]
3195    fn test_client_capabilities_default_on_malformed_values() {
3196        use serde_json::json;
3197
3198        let capabilities: ClientCapabilities = serde_json::from_value(json!({
3199            "fs": {
3200                "readTextFile": "yes",
3201                "writeTextFile": true
3202            },
3203            "terminal": {}
3204        }))
3205        .unwrap();
3206
3207        assert!(!capabilities.fs.read_text_file);
3208        assert!(capabilities.fs.write_text_file);
3209        assert!(!capabilities.terminal);
3210
3211        let capabilities: ClientCapabilities = serde_json::from_value(json!({
3212            "fs": false
3213        }))
3214        .unwrap();
3215        assert_eq!(capabilities.fs, FileSystemCapabilities::default());
3216
3217        {
3218            let capabilities: ClientCapabilities = serde_json::from_value(json!({
3219                "auth": false
3220            }))
3221            .unwrap();
3222            assert_eq!(capabilities.auth, AuthCapabilities::default());
3223
3224            let capabilities: AuthCapabilities = serde_json::from_value(json!({
3225                "terminal": {}
3226            }))
3227            .unwrap();
3228            assert!(!capabilities.terminal);
3229        }
3230    }
3231
3232    #[test]
3233    fn test_serialization_behavior() {
3234        use serde_json::json;
3235
3236        assert_eq!(
3237            serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
3238            SessionInfoUpdate {
3239                title: MaybeUndefined::Undefined,
3240                updated_at: MaybeUndefined::Undefined,
3241                meta: None
3242            }
3243        );
3244        assert_eq!(
3245            serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
3246                .unwrap(),
3247            SessionInfoUpdate {
3248                title: MaybeUndefined::Null,
3249                updated_at: MaybeUndefined::Null,
3250                meta: None
3251            }
3252        );
3253        assert_eq!(
3254            serde_json::from_value::<SessionInfoUpdate>(
3255                json!({"title": "title", "updatedAt": "timestamp"})
3256            )
3257            .unwrap(),
3258            SessionInfoUpdate {
3259                title: MaybeUndefined::Value("title".to_string()),
3260                updated_at: MaybeUndefined::Value("timestamp".to_string()),
3261                meta: None
3262            }
3263        );
3264
3265        assert_eq!(
3266            serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
3267            json!({})
3268        );
3269        assert_eq!(
3270            serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
3271            json!({"title": "title"})
3272        );
3273        assert_eq!(
3274            serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
3275            json!({"title": null})
3276        );
3277        assert_eq!(
3278            serde_json::to_value(
3279                SessionInfoUpdate::new()
3280                    .title("title")
3281                    .title(MaybeUndefined::Undefined)
3282            )
3283            .unwrap(),
3284            json!({})
3285        );
3286    }
3287
3288    #[test]
3289    fn test_content_chunk_message_id_serialization() {
3290        use serde_json::json;
3291
3292        assert_eq!(
3293            serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
3294                ContentBlock::Text(crate::v1::TextContent::new("Hello"))
3295            )))
3296            .unwrap(),
3297            json!({
3298                "sessionUpdate": "agent_message_chunk",
3299                "content": {
3300                    "type": "text",
3301                    "text": "Hello"
3302                }
3303            })
3304        );
3305
3306        assert_eq!(
3307            serde_json::to_value(SessionUpdate::AgentMessageChunk(
3308                ContentChunk::new(ContentBlock::Text(crate::v1::TextContent::new("Hello")))
3309                    .message_id("msg_agent_c42b9")
3310            ))
3311            .unwrap(),
3312            json!({
3313                "sessionUpdate": "agent_message_chunk",
3314                "messageId": "msg_agent_c42b9",
3315                "content": {
3316                    "type": "text",
3317                    "text": "Hello"
3318                }
3319            })
3320        );
3321
3322        let SessionUpdate::AgentMessageChunk(chunk) = serde_json::from_value(json!({
3323            "sessionUpdate": "agent_message_chunk",
3324            "messageId": null,
3325            "content": {
3326                "type": "text",
3327                "text": "Hello"
3328            }
3329        }))
3330        .unwrap() else {
3331            panic!("expected agent message chunk");
3332        };
3333
3334        assert_eq!(chunk.message_id, None);
3335    }
3336
3337    #[test]
3338    fn test_usage_update_serialization() {
3339        use serde_json::json;
3340
3341        assert_eq!(
3342            serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
3343                53_000, 200_000
3344            )))
3345            .unwrap(),
3346            json!({
3347                "sessionUpdate": "usage_update",
3348                "used": 53000,
3349                "size": 200_000
3350            })
3351        );
3352
3353        assert_eq!(
3354            serde_json::to_value(SessionUpdate::UsageUpdate(
3355                UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
3356            ))
3357            .unwrap(),
3358            json!({
3359                "sessionUpdate": "usage_update",
3360                "used": 53000,
3361                "size": 200_000,
3362                "cost": {
3363                    "amount": 0.045,
3364                    "currency": "USD"
3365                }
3366            })
3367        );
3368
3369        let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
3370            "sessionUpdate": "usage_update",
3371            "used": 53000,
3372            "size": 200_000,
3373            "cost": null
3374        }))
3375        .unwrap() else {
3376            panic!("expected usage update");
3377        };
3378
3379        assert_eq!(update.cost, None);
3380    }
3381
3382    #[cfg(feature = "unstable_nes")]
3383    #[test]
3384    fn test_client_capabilities_position_encodings_serialization() {
3385        use serde_json::json;
3386
3387        let capabilities = ClientCapabilities::new().position_encodings(vec![
3388            PositionEncodingKind::Utf32,
3389            PositionEncodingKind::Utf16,
3390        ]);
3391        let json = serde_json::to_value(&capabilities).unwrap();
3392
3393        assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
3394    }
3395
3396    #[test]
3397    fn test_client_capabilities_boolean_config_options_serialization() {
3398        use serde_json::json;
3399
3400        let capabilities = ClientCapabilities::new().session(
3401            ClientSessionCapabilities::new().config_options(
3402                SessionConfigOptionsCapabilities::new()
3403                    .boolean(BooleanConfigOptionCapabilities::new()),
3404            ),
3405        );
3406        let json = serde_json::to_value(&capabilities).unwrap();
3407
3408        assert_eq!(json["session"]["configOptions"]["boolean"], json!({}));
3409
3410        let omitted: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3411        assert!(omitted.session.is_none());
3412
3413        let null_session: ClientCapabilities = serde_json::from_value(json!({
3414            "session": null
3415        }))
3416        .unwrap();
3417        assert!(null_session.session.is_none());
3418
3419        let null_config_options: ClientCapabilities = serde_json::from_value(json!({
3420            "session": {
3421                "configOptions": null
3422            }
3423        }))
3424        .unwrap();
3425        assert!(
3426            null_config_options
3427                .session
3428                .and_then(|session| session.config_options)
3429                .is_none()
3430        );
3431
3432        let null_boolean: ClientCapabilities = serde_json::from_value(json!({
3433            "session": {
3434                "configOptions": {
3435                    "boolean": null
3436                }
3437            }
3438        }))
3439        .unwrap();
3440        assert!(
3441            null_boolean
3442                .session
3443                .and_then(|session| session.config_options)
3444                .and_then(|config_options| config_options.boolean)
3445                .is_none()
3446        );
3447    }
3448
3449    #[cfg(feature = "unstable_plan_operations")]
3450    #[test]
3451    fn test_plan_operations_serialization() {
3452        use serde_json::json;
3453
3454        use crate::v1::{PlanEntry, PlanEntryPriority, PlanEntryStatus, PlanUpdateContent};
3455
3456        let plan_update = SessionUpdate::PlanUpdate(PlanUpdate::new(PlanUpdateContent::items(
3457            "plan-1",
3458            vec![PlanEntry::new(
3459                "Step 1",
3460                PlanEntryPriority::High,
3461                PlanEntryStatus::Pending,
3462            )],
3463        )));
3464
3465        assert_eq!(
3466            serde_json::to_value(plan_update).unwrap(),
3467            json!({
3468                "sessionUpdate": "plan_update",
3469                "plan": {
3470                    "type": "items",
3471                    "planId": "plan-1",
3472                    "entries": [
3473                        {
3474                            "content": "Step 1",
3475                            "priority": "high",
3476                            "status": "pending"
3477                        }
3478                    ]
3479                }
3480            })
3481        );
3482
3483        assert_eq!(
3484            serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
3485            json!({
3486                "sessionUpdate": "plan_removed",
3487                "planId": "plan-1"
3488            })
3489        );
3490
3491        let capabilities = ClientCapabilities::new().plan(PlanCapabilities::new());
3492        let json = serde_json::to_value(&capabilities).unwrap();
3493        assert_eq!(json["plan"], json!({}));
3494
3495        assert_eq!(
3496            serde_json::from_value::<ClientCapabilities>(json!({ "plan": null }))
3497                .unwrap()
3498                .plan,
3499            None
3500        );
3501    }
3502
3503    #[cfg(feature = "unstable_mcp_over_acp")]
3504    #[test]
3505    fn test_agent_mcp_request_method_names() {
3506        use serde_json::json;
3507
3508        let params: serde_json::Map<String, serde_json::Value> =
3509            [("cursor".to_string(), json!("abc"))].into_iter().collect();
3510
3511        assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
3512        assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
3513        assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
3514
3515        assert_eq!(
3516            AgentRequest::ConnectMcpRequest(ConnectMcpRequest::new("server-1")).method(),
3517            "mcp/connect"
3518        );
3519        assert_eq!(
3520            AgentRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
3521                .method(),
3522            "mcp/message"
3523        );
3524        assert_eq!(
3525            AgentRequest::DisconnectMcpRequest(DisconnectMcpRequest::new("conn-1")).method(),
3526            "mcp/disconnect"
3527        );
3528        assert_eq!(
3529            AgentNotification::MessageMcpNotification(MessageMcpNotification::new(
3530                "conn-1",
3531                "notifications/progress"
3532            ))
3533            .method(),
3534            "mcp/message"
3535        );
3536
3537        assert_eq!(
3538            serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
3539            json!({ "serverId": "server-1" })
3540        );
3541        assert_eq!(
3542            serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
3543            json!({ "connectionId": "conn-1" })
3544        );
3545        assert_eq!(
3546            serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
3547                .unwrap(),
3548            json!({
3549                "connectionId": "conn-1",
3550                "method": "tools/list",
3551                "params": { "cursor": "abc" }
3552            })
3553        );
3554        assert_eq!(
3555            serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
3556            json!({ "connectionId": "conn-1" })
3557        );
3558        assert_eq!(
3559            serde_json::to_value(MessageMcpNotification::new(
3560                "conn-1",
3561                "notifications/progress"
3562            ))
3563            .unwrap(),
3564            json!({
3565                "connectionId": "conn-1",
3566                "method": "notifications/progress"
3567            })
3568        );
3569
3570        let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
3571            "connectionId": "conn-1",
3572            "method": "tools/list",
3573            "params": null
3574        }))
3575        .unwrap();
3576        assert_eq!(request_with_null_params.params, None);
3577    }
3578
3579    #[test]
3580    fn request_permission_request_rejects_malformed_options() {
3581        use serde_json::json;
3582
3583        assert!(
3584            serde_json::from_value::<RequestPermissionRequest>(json!({
3585                "sessionId": "sess-1",
3586                "toolCall": {"toolCallId": "tc-1"},
3587                "options": "not-an-array"
3588            }))
3589            .is_err()
3590        );
3591        assert!(
3592            serde_json::from_value::<RequestPermissionRequest>(json!({
3593                "sessionId": "sess-1",
3594                "toolCall": {"toolCallId": "tc-1"},
3595                "options": [{"optionId": "allow"}]
3596            }))
3597            .is_err()
3598        );
3599    }
3600}