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