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
1272/// Response to `fs/write_text_file`
1273#[serde_as]
1274#[skip_serializing_none]
1275#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1276#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1277#[serde(rename_all = "camelCase")]
1278#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_WRITE_TEXT_FILE_METHOD_NAME)))]
1279#[non_exhaustive]
1280pub struct WriteTextFileResponse {
1281    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1282    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1283    /// these keys.
1284    ///
1285    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1286    #[serde_as(deserialize_as = "DefaultOnError")]
1287    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1288    #[serde(default)]
1289    #[serde(rename = "_meta")]
1290    pub meta: Option<Meta>,
1291}
1292
1293impl WriteTextFileResponse {
1294    /// Builds [`WriteTextFileResponse`] with the required response fields set; optional fields start unset or empty.
1295    #[must_use]
1296    pub fn new() -> Self {
1297        Self::default()
1298    }
1299
1300    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1301    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1302    /// these keys.
1303    ///
1304    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1305    #[must_use]
1306    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1307        self.meta = meta.into_option();
1308        self
1309    }
1310}
1311
1312// Read text file
1313
1314/// Request to read content from a text file.
1315///
1316/// Only available if the client supports the `fs.readTextFile` capability.
1317#[serde_as]
1318#[skip_serializing_none]
1319#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1320#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1321#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1322#[serde(rename_all = "camelCase")]
1323#[non_exhaustive]
1324pub struct ReadTextFileRequest {
1325    /// The session ID for this request.
1326    pub session_id: SessionId,
1327    /// Absolute path to the file to read.
1328    pub path: PathBuf,
1329    /// Line number to start reading from (1-based).
1330    #[serde_as(deserialize_as = "DefaultOnError")]
1331    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1332    #[serde(default)]
1333    pub line: Option<u32>,
1334    /// Maximum number of lines to read.
1335    #[serde_as(deserialize_as = "DefaultOnError")]
1336    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1337    #[serde(default)]
1338    pub limit: Option<u32>,
1339    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1340    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1341    /// these keys.
1342    ///
1343    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1344    #[serde_as(deserialize_as = "DefaultOnError")]
1345    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1346    #[serde(default)]
1347    #[serde(rename = "_meta")]
1348    pub meta: Option<Meta>,
1349}
1350
1351impl ReadTextFileRequest {
1352    /// Builds [`ReadTextFileRequest`] with the required request fields set; optional fields start unset or empty.
1353    #[must_use]
1354    pub fn new(session_id: impl Into<SessionId>, path: impl Into<PathBuf>) -> Self {
1355        Self {
1356            session_id: session_id.into(),
1357            path: path.into(),
1358            line: None,
1359            limit: None,
1360            meta: None,
1361        }
1362    }
1363
1364    /// Line number to start reading from (1-based).
1365    #[must_use]
1366    pub fn line(mut self, line: impl IntoOption<u32>) -> Self {
1367        self.line = line.into_option();
1368        self
1369    }
1370
1371    /// Maximum number of lines to read.
1372    #[must_use]
1373    pub fn limit(mut self, limit: impl IntoOption<u32>) -> Self {
1374        self.limit = limit.into_option();
1375        self
1376    }
1377
1378    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1379    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1380    /// these keys.
1381    ///
1382    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1383    #[must_use]
1384    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1385        self.meta = meta.into_option();
1386        self
1387    }
1388}
1389
1390/// Response containing the contents of a text file.
1391#[serde_as]
1392#[skip_serializing_none]
1393#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1394#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1395#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = FS_READ_TEXT_FILE_METHOD_NAME)))]
1396#[serde(rename_all = "camelCase")]
1397#[non_exhaustive]
1398pub struct ReadTextFileResponse {
1399    /// Content payload returned by this response.
1400    pub content: String,
1401    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1402    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1403    /// these keys.
1404    ///
1405    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1406    #[serde_as(deserialize_as = "DefaultOnError")]
1407    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1408    #[serde(default)]
1409    #[serde(rename = "_meta")]
1410    pub meta: Option<Meta>,
1411}
1412
1413impl ReadTextFileResponse {
1414    /// Builds [`ReadTextFileResponse`] with the required response fields set; optional fields start unset or empty.
1415    #[must_use]
1416    pub fn new(content: impl Into<String>) -> Self {
1417        Self {
1418            content: content.into(),
1419            meta: None,
1420        }
1421    }
1422
1423    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1424    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1425    /// these keys.
1426    ///
1427    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1428    #[must_use]
1429    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1430        self.meta = meta.into_option();
1431        self
1432    }
1433}
1434
1435// Terminals
1436
1437/// Typed identifier used for terminal values on the wire.
1438#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1440#[serde(transparent)]
1441#[from(Arc<str>, String, &'static str)]
1442#[non_exhaustive]
1443pub struct TerminalId(pub Arc<str>);
1444
1445impl TerminalId {
1446    /// Wraps a protocol string as a typed [`TerminalId`].
1447    #[must_use]
1448    pub fn new(id: impl Into<Arc<str>>) -> Self {
1449        Self(id.into())
1450    }
1451}
1452
1453/// Request to create a new terminal and execute a command.
1454#[serde_as]
1455#[skip_serializing_none]
1456#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1457#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1458#[serde(rename_all = "camelCase")]
1459#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1460#[non_exhaustive]
1461pub struct CreateTerminalRequest {
1462    /// The session ID for this request.
1463    pub session_id: SessionId,
1464    /// The command to execute.
1465    pub command: String,
1466    /// Array of command arguments.
1467    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1468    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1469    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1470    pub args: Vec<String>,
1471    /// Environment variables for the command.
1472    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1473    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1474    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1475    pub env: Vec<EnvVariable>,
1476    /// Working directory for the command. Must be an absolute path.
1477    #[serde_as(deserialize_as = "DefaultOnError")]
1478    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1479    #[serde(default)]
1480    pub cwd: Option<PathBuf>,
1481    /// Maximum number of output bytes to retain.
1482    ///
1483    /// When the limit is exceeded, the Client truncates from the beginning of the output
1484    /// to stay within the limit.
1485    ///
1486    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
1487    /// string output, even if this means the retained output is slightly less than the
1488    /// specified limit.
1489    #[serde_as(deserialize_as = "DefaultOnError")]
1490    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1491    #[serde(default)]
1492    pub output_byte_limit: Option<u64>,
1493    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1494    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1495    /// these keys.
1496    ///
1497    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1498    #[serde_as(deserialize_as = "DefaultOnError")]
1499    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1500    #[serde(default)]
1501    #[serde(rename = "_meta")]
1502    pub meta: Option<Meta>,
1503}
1504
1505impl CreateTerminalRequest {
1506    /// Builds [`CreateTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1507    #[must_use]
1508    pub fn new(session_id: impl Into<SessionId>, command: impl Into<String>) -> Self {
1509        Self {
1510            session_id: session_id.into(),
1511            command: command.into(),
1512            args: Vec::new(),
1513            env: Vec::new(),
1514            cwd: None,
1515            output_byte_limit: None,
1516            meta: None,
1517        }
1518    }
1519
1520    /// Array of command arguments.
1521    #[must_use]
1522    pub fn args(mut self, args: Vec<String>) -> Self {
1523        self.args = args;
1524        self
1525    }
1526
1527    /// Environment variables for the command.
1528    #[must_use]
1529    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1530        self.env = env;
1531        self
1532    }
1533
1534    /// Working directory for the command. Must be an absolute path.
1535    #[must_use]
1536    pub fn cwd(mut self, cwd: impl IntoOption<PathBuf>) -> Self {
1537        self.cwd = cwd.into_option();
1538        self
1539    }
1540
1541    /// Maximum number of output bytes to retain.
1542    ///
1543    /// When the limit is exceeded, the Client truncates from the beginning of the output
1544    /// to stay within the limit.
1545    ///
1546    /// The Client MUST ensure truncation happens at a character boundary to maintain valid
1547    /// string output, even if this means the retained output is slightly less than the
1548    /// specified limit.
1549    #[must_use]
1550    pub fn output_byte_limit(mut self, output_byte_limit: impl IntoOption<u64>) -> Self {
1551        self.output_byte_limit = output_byte_limit.into_option();
1552        self
1553    }
1554
1555    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1556    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1557    /// these keys.
1558    ///
1559    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1560    #[must_use]
1561    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1562        self.meta = meta.into_option();
1563        self
1564    }
1565}
1566
1567/// Response containing the ID of the created terminal.
1568#[serde_as]
1569#[skip_serializing_none]
1570#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1571#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1572#[serde(rename_all = "camelCase")]
1573#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_CREATE_METHOD_NAME)))]
1574#[non_exhaustive]
1575pub struct CreateTerminalResponse {
1576    /// The unique identifier for the created terminal.
1577    pub terminal_id: TerminalId,
1578    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1579    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1580    /// these keys.
1581    ///
1582    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1583    #[serde_as(deserialize_as = "DefaultOnError")]
1584    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1585    #[serde(default)]
1586    #[serde(rename = "_meta")]
1587    pub meta: Option<Meta>,
1588}
1589
1590impl CreateTerminalResponse {
1591    /// Builds [`CreateTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1592    #[must_use]
1593    pub fn new(terminal_id: impl Into<TerminalId>) -> Self {
1594        Self {
1595            terminal_id: terminal_id.into(),
1596            meta: None,
1597        }
1598    }
1599
1600    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1601    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1602    /// these keys.
1603    ///
1604    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1605    #[must_use]
1606    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1607        self.meta = meta.into_option();
1608        self
1609    }
1610}
1611
1612/// Request to get the current output and status of a terminal.
1613#[serde_as]
1614#[skip_serializing_none]
1615#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1616#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1617#[serde(rename_all = "camelCase")]
1618#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1619#[non_exhaustive]
1620pub struct TerminalOutputRequest {
1621    /// The session ID for this request.
1622    pub session_id: SessionId,
1623    /// The ID of the terminal to get output from.
1624    pub terminal_id: TerminalId,
1625    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1626    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1627    /// these keys.
1628    ///
1629    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1630    #[serde_as(deserialize_as = "DefaultOnError")]
1631    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1632    #[serde(default)]
1633    #[serde(rename = "_meta")]
1634    pub meta: Option<Meta>,
1635}
1636
1637impl TerminalOutputRequest {
1638    /// Builds [`TerminalOutputRequest`] with the required request fields set; optional fields start unset or empty.
1639    #[must_use]
1640    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1641        Self {
1642            session_id: session_id.into(),
1643            terminal_id: terminal_id.into(),
1644            meta: None,
1645        }
1646    }
1647
1648    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1649    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1650    /// these keys.
1651    ///
1652    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1653    #[must_use]
1654    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1655        self.meta = meta.into_option();
1656        self
1657    }
1658}
1659
1660/// Response containing the terminal output and exit status.
1661#[serde_as]
1662#[skip_serializing_none]
1663#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1664#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1665#[serde(rename_all = "camelCase")]
1666#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_OUTPUT_METHOD_NAME)))]
1667#[non_exhaustive]
1668pub struct TerminalOutputResponse {
1669    /// The terminal output captured so far.
1670    pub output: String,
1671    /// Whether the output was truncated due to byte limits.
1672    pub truncated: bool,
1673    /// Exit status if the command has completed.
1674    #[serde_as(deserialize_as = "DefaultOnError")]
1675    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1676    #[serde(default)]
1677    pub exit_status: Option<TerminalExitStatus>,
1678    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1679    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1680    /// these keys.
1681    ///
1682    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1683    #[serde_as(deserialize_as = "DefaultOnError")]
1684    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1685    #[serde(default)]
1686    #[serde(rename = "_meta")]
1687    pub meta: Option<Meta>,
1688}
1689
1690impl TerminalOutputResponse {
1691    /// Builds [`TerminalOutputResponse`] with the required response fields set; optional fields start unset or empty.
1692    #[must_use]
1693    pub fn new(output: impl Into<String>, truncated: bool) -> Self {
1694        Self {
1695            output: output.into(),
1696            truncated,
1697            exit_status: None,
1698            meta: None,
1699        }
1700    }
1701
1702    /// Exit status if the command has completed.
1703    #[must_use]
1704    pub fn exit_status(mut self, exit_status: impl IntoOption<TerminalExitStatus>) -> Self {
1705        self.exit_status = exit_status.into_option();
1706        self
1707    }
1708
1709    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1710    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1711    /// these keys.
1712    ///
1713    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1714    #[must_use]
1715    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1716        self.meta = meta.into_option();
1717        self
1718    }
1719}
1720
1721/// Request to release a terminal and free its resources.
1722#[serde_as]
1723#[skip_serializing_none]
1724#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1725#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1726#[serde(rename_all = "camelCase")]
1727#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1728#[non_exhaustive]
1729pub struct ReleaseTerminalRequest {
1730    /// The session ID for this request.
1731    pub session_id: SessionId,
1732    /// The ID of the terminal to release.
1733    pub terminal_id: TerminalId,
1734    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1735    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1736    /// these keys.
1737    ///
1738    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1739    #[serde_as(deserialize_as = "DefaultOnError")]
1740    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1741    #[serde(default)]
1742    #[serde(rename = "_meta")]
1743    pub meta: Option<Meta>,
1744}
1745
1746impl ReleaseTerminalRequest {
1747    /// Builds [`ReleaseTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1748    #[must_use]
1749    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1750        Self {
1751            session_id: session_id.into(),
1752            terminal_id: terminal_id.into(),
1753            meta: None,
1754        }
1755    }
1756
1757    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1758    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1759    /// these keys.
1760    ///
1761    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1762    #[must_use]
1763    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1764        self.meta = meta.into_option();
1765        self
1766    }
1767}
1768
1769/// Response to terminal/release method
1770#[serde_as]
1771#[skip_serializing_none]
1772#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1773#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1774#[serde(rename_all = "camelCase")]
1775#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_RELEASE_METHOD_NAME)))]
1776#[non_exhaustive]
1777pub struct ReleaseTerminalResponse {
1778    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1779    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1780    /// these keys.
1781    ///
1782    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1783    #[serde_as(deserialize_as = "DefaultOnError")]
1784    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1785    #[serde(default)]
1786    #[serde(rename = "_meta")]
1787    pub meta: Option<Meta>,
1788}
1789
1790impl ReleaseTerminalResponse {
1791    /// Builds [`ReleaseTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1792    #[must_use]
1793    pub fn new() -> Self {
1794        Self::default()
1795    }
1796
1797    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1798    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1799    /// these keys.
1800    ///
1801    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1802    #[must_use]
1803    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1804        self.meta = meta.into_option();
1805        self
1806    }
1807}
1808
1809/// Request to kill a terminal without releasing it.
1810#[serde_as]
1811#[skip_serializing_none]
1812#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1813#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1814#[serde(rename_all = "camelCase")]
1815#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1816#[non_exhaustive]
1817pub struct KillTerminalRequest {
1818    /// The session ID for this request.
1819    pub session_id: SessionId,
1820    /// The ID of the terminal to kill.
1821    pub terminal_id: TerminalId,
1822    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1823    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1824    /// these keys.
1825    ///
1826    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1827    #[serde_as(deserialize_as = "DefaultOnError")]
1828    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1829    #[serde(default)]
1830    #[serde(rename = "_meta")]
1831    pub meta: Option<Meta>,
1832}
1833
1834impl KillTerminalRequest {
1835    /// Builds [`KillTerminalRequest`] with the required request fields set; optional fields start unset or empty.
1836    #[must_use]
1837    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1838        Self {
1839            session_id: session_id.into(),
1840            terminal_id: terminal_id.into(),
1841            meta: None,
1842        }
1843    }
1844
1845    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1846    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1847    /// these keys.
1848    ///
1849    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1850    #[must_use]
1851    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1852        self.meta = meta.into_option();
1853        self
1854    }
1855}
1856
1857/// Response to `terminal/kill` method
1858#[serde_as]
1859#[skip_serializing_none]
1860#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1861#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1862#[serde(rename_all = "camelCase")]
1863#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_KILL_METHOD_NAME)))]
1864#[non_exhaustive]
1865pub struct KillTerminalResponse {
1866    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1867    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1868    /// these keys.
1869    ///
1870    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1871    #[serde_as(deserialize_as = "DefaultOnError")]
1872    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1873    #[serde(default)]
1874    #[serde(rename = "_meta")]
1875    pub meta: Option<Meta>,
1876}
1877
1878impl KillTerminalResponse {
1879    /// Builds [`KillTerminalResponse`] with the required response fields set; optional fields start unset or empty.
1880    #[must_use]
1881    pub fn new() -> Self {
1882        Self::default()
1883    }
1884
1885    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1886    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1887    /// these keys.
1888    ///
1889    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1890    #[must_use]
1891    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1892        self.meta = meta.into_option();
1893        self
1894    }
1895}
1896
1897/// Request to wait for a terminal command to exit.
1898#[serde_as]
1899#[skip_serializing_none]
1900#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1901#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1902#[serde(rename_all = "camelCase")]
1903#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1904#[non_exhaustive]
1905pub struct WaitForTerminalExitRequest {
1906    /// The session ID for this request.
1907    pub session_id: SessionId,
1908    /// The ID of the terminal to wait for.
1909    pub terminal_id: TerminalId,
1910    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1911    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1912    /// these keys.
1913    ///
1914    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1915    #[serde_as(deserialize_as = "DefaultOnError")]
1916    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1917    #[serde(default)]
1918    #[serde(rename = "_meta")]
1919    pub meta: Option<Meta>,
1920}
1921
1922impl WaitForTerminalExitRequest {
1923    /// Builds [`WaitForTerminalExitRequest`] with the required request fields set; optional fields start unset or empty.
1924    #[must_use]
1925    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
1926        Self {
1927            session_id: session_id.into(),
1928            terminal_id: terminal_id.into(),
1929            meta: None,
1930        }
1931    }
1932
1933    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1934    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1935    /// these keys.
1936    ///
1937    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1938    #[must_use]
1939    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1940        self.meta = meta.into_option();
1941        self
1942    }
1943}
1944
1945/// Response containing the exit status of a terminal command.
1946#[serde_as]
1947#[skip_serializing_none]
1948#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1949#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1950#[serde(rename_all = "camelCase")]
1951#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = TERMINAL_WAIT_FOR_EXIT_METHOD_NAME)))]
1952#[non_exhaustive]
1953pub struct WaitForTerminalExitResponse {
1954    /// The exit status of the terminal command.
1955    #[serde(flatten)]
1956    pub exit_status: TerminalExitStatus,
1957    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1958    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1959    /// these keys.
1960    ///
1961    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1962    #[serde_as(deserialize_as = "DefaultOnError")]
1963    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1964    #[serde(default)]
1965    #[serde(rename = "_meta")]
1966    pub meta: Option<Meta>,
1967}
1968
1969impl WaitForTerminalExitResponse {
1970    /// Builds [`WaitForTerminalExitResponse`] with the required response fields set; optional fields start unset or empty.
1971    #[must_use]
1972    pub fn new(exit_status: TerminalExitStatus) -> Self {
1973        Self {
1974            exit_status,
1975            meta: None,
1976        }
1977    }
1978
1979    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1980    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1981    /// these keys.
1982    ///
1983    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1984    #[must_use]
1985    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1986        self.meta = meta.into_option();
1987        self
1988    }
1989}
1990
1991/// Exit status of a terminal command.
1992#[serde_as]
1993#[skip_serializing_none]
1994#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1995#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1996#[serde(rename_all = "camelCase")]
1997#[non_exhaustive]
1998pub struct TerminalExitStatus {
1999    /// The process exit code (may be null if terminated by signal).
2000    #[serde_as(deserialize_as = "DefaultOnError")]
2001    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2002    #[serde(default)]
2003    pub exit_code: Option<u32>,
2004    /// The signal that terminated the process (may be null if exited normally).
2005    #[serde_as(deserialize_as = "DefaultOnError")]
2006    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2007    #[serde(default)]
2008    pub signal: Option<String>,
2009    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2010    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2011    /// these keys.
2012    ///
2013    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2014    #[serde_as(deserialize_as = "DefaultOnError")]
2015    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2016    #[serde(default)]
2017    #[serde(rename = "_meta")]
2018    pub meta: Option<Meta>,
2019}
2020
2021impl TerminalExitStatus {
2022    /// Builds [`TerminalExitStatus`] with the required fields set; optional fields start unset or empty.
2023    #[must_use]
2024    pub fn new() -> Self {
2025        Self::default()
2026    }
2027
2028    /// The process exit code (may be null if terminated by signal).
2029    #[must_use]
2030    pub fn exit_code(mut self, exit_code: impl IntoOption<u32>) -> Self {
2031        self.exit_code = exit_code.into_option();
2032        self
2033    }
2034
2035    /// The signal that terminated the process (may be null if exited normally).
2036    #[must_use]
2037    pub fn signal(mut self, signal: impl IntoOption<String>) -> Self {
2038        self.signal = signal.into_option();
2039        self
2040    }
2041
2042    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2043    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2044    /// these keys.
2045    ///
2046    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2047    #[must_use]
2048    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2049        self.meta = meta.into_option();
2050        self
2051    }
2052}
2053
2054// Capabilities
2055
2056/// Capabilities supported by the client.
2057///
2058/// Advertised during initialization to inform the agent about
2059/// available features and methods.
2060///
2061/// See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/initialization#client-capabilities)
2062#[serde_as]
2063#[skip_serializing_none]
2064#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2065#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2066#[serde(rename_all = "camelCase")]
2067#[non_exhaustive]
2068pub struct ClientCapabilities {
2069    /// File system capabilities supported by the client.
2070    /// Determines which file operations the agent can request.
2071    #[serde_as(deserialize_as = "DefaultOnError")]
2072    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2073    #[serde(default)]
2074    pub fs: FileSystemCapabilities,
2075    /// Whether the Client support all `terminal/*` methods.
2076    #[serde_as(deserialize_as = "DefaultOnError")]
2077    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2078    #[serde(default)]
2079    pub terminal: bool,
2080    /// Session-related capabilities supported by the client.
2081    ///
2082    /// Optional. Omitted or `null` both mean the client does not advertise any
2083    /// session-related extensions.
2084    #[serde_as(deserialize_as = "DefaultOnError")]
2085    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2086    #[serde(default)]
2087    pub session: Option<ClientSessionCapabilities>,
2088    /// **UNSTABLE**
2089    ///
2090    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2091    ///
2092    /// Whether the client supports `plan_update` and `plan_removed` session updates.
2093    ///
2094    /// Optional. Omitted or `null` both mean the client does not advertise support.
2095    /// Supplying `{}` means the client can receive both update types.
2096    #[cfg(feature = "unstable_plan_operations")]
2097    #[serde_as(deserialize_as = "DefaultOnError")]
2098    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2099    #[serde(default)]
2100    pub plan: Option<PlanCapabilities>,
2101    /// Authentication capabilities supported by the client.
2102    /// Determines which authentication method types the agent may include
2103    /// in its `InitializeResponse`.
2104    #[serde_as(deserialize_as = "DefaultOnError")]
2105    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2106    #[serde(default)]
2107    pub auth: AuthCapabilities,
2108    /// Elicitation capabilities supported by the client.
2109    /// Determines which elicitation modes the agent may use.
2110    ///
2111    /// Optional. Omitted or `null` both mean the client does not advertise
2112    /// elicitation support.
2113    #[serde_as(deserialize_as = "DefaultOnError")]
2114    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2115    #[serde(default)]
2116    pub elicitation: Option<ElicitationCapabilities>,
2117    /// **UNSTABLE**
2118    ///
2119    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2120    ///
2121    /// NES (Next Edit Suggestions) capabilities supported by the client.
2122    ///
2123    /// Optional. Omitted or `null` both mean the client does not advertise any
2124    /// NES suggestion-kind extensions.
2125    #[cfg(feature = "unstable_nes")]
2126    #[serde_as(deserialize_as = "DefaultOnError")]
2127    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2128    #[serde(default)]
2129    pub nes: Option<ClientNesCapabilities>,
2130    /// **UNSTABLE**
2131    ///
2132    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2133    ///
2134    /// The position encodings supported by the client, in order of preference.
2135    #[cfg(feature = "unstable_nes")]
2136    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2137    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2138    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2139    pub position_encodings: Vec<PositionEncodingKind>,
2140
2141    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2142    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2143    /// these keys.
2144    ///
2145    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2146    #[serde_as(deserialize_as = "DefaultOnError")]
2147    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2148    #[serde(default)]
2149    #[serde(rename = "_meta")]
2150    pub meta: Option<Meta>,
2151}
2152
2153impl ClientCapabilities {
2154    /// Builds an empty [`ClientCapabilities`]; use builder methods to advertise supported sub-capabilities.
2155    #[must_use]
2156    pub fn new() -> Self {
2157        Self::default()
2158    }
2159
2160    /// File system capabilities supported by the client.
2161    /// Determines which file operations the agent can request.
2162    #[must_use]
2163    pub fn fs(mut self, fs: FileSystemCapabilities) -> Self {
2164        self.fs = fs;
2165        self
2166    }
2167
2168    /// Whether the Client support all `terminal/*` methods.
2169    #[must_use]
2170    pub fn terminal(mut self, terminal: bool) -> Self {
2171        self.terminal = terminal;
2172        self
2173    }
2174
2175    /// Session-related capabilities supported by the client.
2176    #[must_use]
2177    pub fn session(mut self, session: impl IntoOption<ClientSessionCapabilities>) -> Self {
2178        self.session = session.into_option();
2179        self
2180    }
2181
2182    /// **UNSTABLE**
2183    ///
2184    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2185    ///
2186    /// Whether the client supports `plan_update` and `plan_removed` session updates.
2187    ///
2188    /// Omitted or `null` both mean the client does not advertise support.
2189    /// Supplying `{}` means the client can receive both update types.
2190    #[cfg(feature = "unstable_plan_operations")]
2191    #[must_use]
2192    pub fn plan(mut self, plan: impl IntoOption<PlanCapabilities>) -> Self {
2193        self.plan = plan.into_option();
2194        self
2195    }
2196
2197    /// Authentication capabilities supported by the client.
2198    /// Determines which authentication method types the agent may include
2199    /// in its `InitializeResponse`.
2200    #[must_use]
2201    pub fn auth(mut self, auth: AuthCapabilities) -> Self {
2202        self.auth = auth;
2203        self
2204    }
2205
2206    /// Elicitation capabilities supported by the client.
2207    /// Determines which elicitation modes the agent may use.
2208    #[must_use]
2209    pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
2210        self.elicitation = elicitation.into_option();
2211        self
2212    }
2213
2214    /// **UNSTABLE**
2215    ///
2216    /// NES (Next Edit Suggestions) capabilities supported by the client.
2217    #[cfg(feature = "unstable_nes")]
2218    #[must_use]
2219    pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
2220        self.nes = nes.into_option();
2221        self
2222    }
2223
2224    /// **UNSTABLE**
2225    ///
2226    /// The position encodings supported by the client, in order of preference.
2227    #[cfg(feature = "unstable_nes")]
2228    #[must_use]
2229    pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
2230        self.position_encodings = position_encodings;
2231        self
2232    }
2233
2234    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2235    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2236    /// these keys.
2237    ///
2238    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2239    #[must_use]
2240    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2241        self.meta = meta.into_option();
2242        self
2243    }
2244}
2245
2246/// Session-related capabilities supported by the client.
2247#[serde_as]
2248#[skip_serializing_none]
2249#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2250#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2251#[serde(rename_all = "camelCase")]
2252#[non_exhaustive]
2253pub struct ClientSessionCapabilities {
2254    /// **UNSTABLE**
2255    ///
2256    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2257    ///
2258    /// Support for ID-addressed context compaction updates. Omitted or `null`
2259    /// means unsupported; `{}` advertises the complete compaction contract.
2260    #[cfg(feature = "unstable_session_compaction")]
2261    #[serde_as(deserialize_as = "DefaultOnError")]
2262    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2263    #[serde(default)]
2264    pub compaction: Option<CompactionCapabilities>,
2265    /// Config option capabilities supported by the client.
2266    ///
2267    /// Omitted or `null` both mean the client does not advertise support for any
2268    /// config option extensions.
2269    #[serde_as(deserialize_as = "DefaultOnError")]
2270    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2271    #[serde(default)]
2272    pub config_options: Option<SessionConfigOptionsCapabilities>,
2273    /// **UNSTABLE**
2274    ///
2275    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2276    ///
2277    /// Support for live advisory `notice` session updates.
2278    ///
2279    /// Optional. Omitted or `null` both mean the client does not advertise support.
2280    /// Supplying `{}` means the client can present notices to the user.
2281    #[cfg(feature = "unstable_session_notices")]
2282    #[serde_as(deserialize_as = "DefaultOnError")]
2283    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2284    #[serde(default)]
2285    pub notices: Option<NoticeCapabilities>,
2286    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2287    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2288    /// these keys.
2289    ///
2290    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2291    #[serde_as(deserialize_as = "DefaultOnError")]
2292    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2293    #[serde(default)]
2294    #[serde(rename = "_meta")]
2295    pub meta: Option<Meta>,
2296}
2297
2298impl ClientSessionCapabilities {
2299    /// Builds an empty [`ClientSessionCapabilities`]; use builder methods to advertise supported sub-capabilities.
2300    #[must_use]
2301    pub fn new() -> Self {
2302        Self::default()
2303    }
2304
2305    /// Advertises support for ID-addressed context compaction updates.
2306    #[cfg(feature = "unstable_session_compaction")]
2307    #[must_use]
2308    pub fn compaction(mut self, compaction: impl IntoOption<CompactionCapabilities>) -> Self {
2309        self.compaction = compaction.into_option();
2310        self
2311    }
2312
2313    /// Config option capabilities supported by the client.
2314    ///
2315    /// Omitted or `null` both mean the client does not advertise support for any
2316    /// config option extensions.
2317    #[must_use]
2318    pub fn config_options(
2319        mut self,
2320        config_options: impl IntoOption<SessionConfigOptionsCapabilities>,
2321    ) -> Self {
2322        self.config_options = config_options.into_option();
2323        self
2324    }
2325
2326    /// Advertises support for presenting live advisory notices to the user.
2327    #[cfg(feature = "unstable_session_notices")]
2328    #[must_use]
2329    pub fn notices(mut self, notices: impl IntoOption<NoticeCapabilities>) -> Self {
2330        self.notices = notices.into_option();
2331        self
2332    }
2333
2334    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2335    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2336    /// these keys.
2337    ///
2338    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2339    #[must_use]
2340    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2341        self.meta = meta.into_option();
2342        self
2343    }
2344}
2345
2346/// **UNSTABLE**
2347///
2348/// This capability is not part of the spec yet, and may be removed or changed at any point.
2349///
2350/// Client support for ID-addressed context compaction updates.
2351#[cfg(feature = "unstable_session_compaction")]
2352#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2353#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2354#[serde(rename_all = "camelCase")]
2355#[non_exhaustive]
2356pub struct CompactionCapabilities {}
2357
2358#[cfg(feature = "unstable_session_compaction")]
2359impl CompactionCapabilities {
2360    /// Advertises the complete compaction update contract.
2361    #[must_use]
2362    pub fn new() -> Self {
2363        Self {}
2364    }
2365}
2366
2367/// **UNSTABLE**
2368///
2369/// This capability is not part of the spec yet, and may be removed or changed at any point.
2370///
2371/// Client support for presenting live advisory notices to the user.
2372#[cfg(feature = "unstable_session_notices")]
2373#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2374#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2375#[serde(rename_all = "camelCase")]
2376#[non_exhaustive]
2377pub struct NoticeCapabilities {}
2378
2379#[cfg(feature = "unstable_session_notices")]
2380impl NoticeCapabilities {
2381    /// Advertises support for presenting live advisory notices to the user.
2382    #[must_use]
2383    pub fn new() -> Self {
2384        Self {}
2385    }
2386}
2387
2388/// Session configuration option capabilities supported by the client.
2389#[serde_as]
2390#[skip_serializing_none]
2391#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2392#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2393#[serde(rename_all = "camelCase")]
2394#[non_exhaustive]
2395pub struct SessionConfigOptionsCapabilities {
2396    /// Whether the client supports boolean session configuration options.
2397    ///
2398    /// Optional. Omitted or `null` both mean the client does not advertise support.
2399    /// Supplying `{}` means agents may include `type: "boolean"` entries in
2400    /// `configOptions`, and the client may send `session/set_config_option`
2401    /// requests with `type: "boolean"` and a boolean `value`.
2402    #[serde_as(deserialize_as = "DefaultOnError")]
2403    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2404    #[serde(default)]
2405    pub boolean: Option<BooleanConfigOptionCapabilities>,
2406    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2407    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2408    /// these keys.
2409    ///
2410    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2411    #[serde_as(deserialize_as = "DefaultOnError")]
2412    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2413    #[serde(default)]
2414    #[serde(rename = "_meta")]
2415    pub meta: Option<Meta>,
2416}
2417
2418impl SessionConfigOptionsCapabilities {
2419    /// Builds an empty [`SessionConfigOptionsCapabilities`]; use builder methods to advertise supported sub-capabilities.
2420    #[must_use]
2421    pub fn new() -> Self {
2422        Self::default()
2423    }
2424
2425    /// Whether the client supports boolean session configuration options.
2426    ///
2427    /// Omitted or `null` both mean the client does not advertise support.
2428    /// Supplying `{}` means agents may include `type: "boolean"` entries in
2429    /// `configOptions`, and the client may send `session/set_config_option`
2430    /// requests with `type: "boolean"` and a boolean `value`.
2431    #[must_use]
2432    pub fn boolean(mut self, boolean: impl IntoOption<BooleanConfigOptionCapabilities>) -> Self {
2433        self.boolean = boolean.into_option();
2434        self
2435    }
2436
2437    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2438    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2439    /// these keys.
2440    ///
2441    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2442    #[must_use]
2443    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2444        self.meta = meta.into_option();
2445        self
2446    }
2447}
2448
2449/// Capabilities for boolean session configuration options.
2450///
2451/// Supplying `{}` means the client supports boolean session configuration options.
2452#[serde_as]
2453#[skip_serializing_none]
2454#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2455#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2456#[non_exhaustive]
2457pub struct BooleanConfigOptionCapabilities {
2458    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2459    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2460    /// these keys.
2461    ///
2462    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2463    #[serde_as(deserialize_as = "DefaultOnError")]
2464    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2465    #[serde(default)]
2466    #[serde(rename = "_meta")]
2467    pub meta: Option<Meta>,
2468}
2469
2470impl BooleanConfigOptionCapabilities {
2471    /// Builds an empty [`BooleanConfigOptionCapabilities`]; use builder methods to advertise supported sub-capabilities.
2472    #[must_use]
2473    pub fn new() -> Self {
2474        Self::default()
2475    }
2476
2477    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2478    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2479    /// these keys.
2480    ///
2481    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2482    #[must_use]
2483    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2484        self.meta = meta.into_option();
2485        self
2486    }
2487}
2488
2489/// Authentication capabilities supported by the client.
2490///
2491/// Advertised during initialization to inform the agent which authentication
2492/// method types the client can handle. This governs opt-in types that require
2493/// additional client-side support.
2494#[serde_as]
2495#[skip_serializing_none]
2496#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2497#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2498#[serde(rename_all = "camelCase")]
2499#[non_exhaustive]
2500pub struct AuthCapabilities {
2501    /// Whether the client supports `terminal` authentication methods.
2502    ///
2503    /// The client should set this to `true` only when it can reproduce the
2504    /// configured agent invocation in an interactive terminal. When `true`, the
2505    /// agent may include `terminal` entries in its authentication methods.
2506    #[serde_as(deserialize_as = "DefaultOnError")]
2507    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2508    #[serde(default)]
2509    pub terminal: bool,
2510    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2511    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2512    /// these keys.
2513    ///
2514    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2515    #[serde_as(deserialize_as = "DefaultOnError")]
2516    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2517    #[serde(default)]
2518    #[serde(rename = "_meta")]
2519    pub meta: Option<Meta>,
2520}
2521
2522impl AuthCapabilities {
2523    /// Builds an empty [`AuthCapabilities`]; use builder methods to advertise supported sub-capabilities.
2524    #[must_use]
2525    pub fn new() -> Self {
2526        Self::default()
2527    }
2528
2529    /// Whether the client supports `terminal` authentication methods.
2530    ///
2531    /// The client should set this to `true` only when it can reproduce the
2532    /// configured agent invocation in an interactive terminal. When `true`, the
2533    /// agent may include `AuthMethod::Terminal` entries in its authentication
2534    /// methods.
2535    #[must_use]
2536    pub fn terminal(mut self, terminal: bool) -> Self {
2537        self.terminal = terminal;
2538        self
2539    }
2540
2541    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2542    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2543    /// these keys.
2544    ///
2545    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2546    #[must_use]
2547    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2548        self.meta = meta.into_option();
2549        self
2550    }
2551}
2552
2553/// File system capabilities that a client may support.
2554///
2555/// See protocol docs: [FileSystem](https://agentclientprotocol.com/protocol/initialization#filesystem)
2556#[serde_as]
2557#[skip_serializing_none]
2558#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2559#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2560#[serde(rename_all = "camelCase")]
2561#[non_exhaustive]
2562pub struct FileSystemCapabilities {
2563    /// Whether the Client supports `fs/read_text_file` requests.
2564    #[serde_as(deserialize_as = "DefaultOnError")]
2565    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2566    #[serde(default)]
2567    pub read_text_file: bool,
2568    /// Whether the Client supports `fs/write_text_file` requests.
2569    #[serde_as(deserialize_as = "DefaultOnError")]
2570    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2571    #[serde(default)]
2572    pub write_text_file: bool,
2573    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2574    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2575    /// these keys.
2576    ///
2577    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2578    #[serde_as(deserialize_as = "DefaultOnError")]
2579    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2580    #[serde(default)]
2581    #[serde(rename = "_meta")]
2582    pub meta: Option<Meta>,
2583}
2584
2585impl FileSystemCapabilities {
2586    /// Builds an empty [`FileSystemCapabilities`]; use builder methods to advertise supported sub-capabilities.
2587    #[must_use]
2588    pub fn new() -> Self {
2589        Self::default()
2590    }
2591
2592    /// Whether the Client supports `fs/read_text_file` requests.
2593    #[must_use]
2594    pub fn read_text_file(mut self, read_text_file: bool) -> Self {
2595        self.read_text_file = read_text_file;
2596        self
2597    }
2598
2599    /// Whether the Client supports `fs/write_text_file` requests.
2600    #[must_use]
2601    pub fn write_text_file(mut self, write_text_file: bool) -> Self {
2602        self.write_text_file = write_text_file;
2603        self
2604    }
2605
2606    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2607    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2608    /// these keys.
2609    ///
2610    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2611    #[must_use]
2612    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2613        self.meta = meta.into_option();
2614        self
2615    }
2616}
2617
2618// Method schema
2619
2620/// Names of all methods that clients handle.
2621///
2622/// Provides a centralized definition of method names used in the protocol.
2623#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2624#[non_exhaustive]
2625pub struct ClientMethodNames {
2626    /// Method for requesting permission from the user.
2627    pub session_request_permission: &'static str,
2628    /// Notification for session updates.
2629    pub session_update: &'static str,
2630    /// Method for writing text files.
2631    pub fs_write_text_file: &'static str,
2632    /// Method for reading text files.
2633    pub fs_read_text_file: &'static str,
2634    /// Method for creating new terminals.
2635    pub terminal_create: &'static str,
2636    /// Method for getting terminals output.
2637    pub terminal_output: &'static str,
2638    /// Method for releasing a terminal.
2639    pub terminal_release: &'static str,
2640    /// Method for waiting for a terminal to finish.
2641    pub terminal_wait_for_exit: &'static str,
2642    /// Method for killing a terminal.
2643    pub terminal_kill: &'static str,
2644    /// Method for opening an MCP-over-ACP connection.
2645    #[cfg(feature = "unstable_mcp_over_acp")]
2646    pub mcp_connect: &'static str,
2647    /// Method for exchanging MCP-over-ACP messages.
2648    #[cfg(feature = "unstable_mcp_over_acp")]
2649    pub mcp_message: &'static str,
2650    /// Method for closing an MCP-over-ACP connection.
2651    #[cfg(feature = "unstable_mcp_over_acp")]
2652    pub mcp_disconnect: &'static str,
2653    /// Method for elicitation.
2654    pub elicitation_create: &'static str,
2655    /// Notification for elicitation completion.
2656    pub elicitation_complete: &'static str,
2657}
2658
2659/// Constant containing all client method names.
2660pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
2661    session_update: SESSION_UPDATE_NOTIFICATION,
2662    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
2663    fs_write_text_file: FS_WRITE_TEXT_FILE_METHOD_NAME,
2664    fs_read_text_file: FS_READ_TEXT_FILE_METHOD_NAME,
2665    terminal_create: TERMINAL_CREATE_METHOD_NAME,
2666    terminal_output: TERMINAL_OUTPUT_METHOD_NAME,
2667    terminal_release: TERMINAL_RELEASE_METHOD_NAME,
2668    terminal_wait_for_exit: TERMINAL_WAIT_FOR_EXIT_METHOD_NAME,
2669    terminal_kill: TERMINAL_KILL_METHOD_NAME,
2670    #[cfg(feature = "unstable_mcp_over_acp")]
2671    mcp_connect: MCP_CONNECT_METHOD_NAME,
2672    #[cfg(feature = "unstable_mcp_over_acp")]
2673    mcp_message: MCP_MESSAGE_METHOD_NAME,
2674    #[cfg(feature = "unstable_mcp_over_acp")]
2675    mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
2676    elicitation_create: ELICITATION_CREATE_METHOD_NAME,
2677    elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
2678};
2679
2680/// Notification name for session updates.
2681pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
2682/// Method name for requesting user permission.
2683pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
2684/// Method name for writing text files.
2685pub(crate) const FS_WRITE_TEXT_FILE_METHOD_NAME: &str = "fs/write_text_file";
2686/// Method name for reading text files.
2687pub(crate) const FS_READ_TEXT_FILE_METHOD_NAME: &str = "fs/read_text_file";
2688/// Method name for creating a new terminal.
2689pub(crate) const TERMINAL_CREATE_METHOD_NAME: &str = "terminal/create";
2690/// Method for getting terminals output.
2691pub(crate) const TERMINAL_OUTPUT_METHOD_NAME: &str = "terminal/output";
2692/// Method for releasing a terminal.
2693pub(crate) const TERMINAL_RELEASE_METHOD_NAME: &str = "terminal/release";
2694/// Method for waiting for a terminal to finish.
2695pub(crate) const TERMINAL_WAIT_FOR_EXIT_METHOD_NAME: &str = "terminal/wait_for_exit";
2696/// Method for killing a terminal.
2697pub(crate) const TERMINAL_KILL_METHOD_NAME: &str = "terminal/kill";
2698/// Method name for elicitation.
2699pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
2700/// Notification name for elicitation completion.
2701pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
2702
2703/// All possible requests that an agent can send to a client.
2704///
2705/// This enum is used internally for routing RPC requests. You typically won't need
2706/// to use this directly.
2707///
2708/// This enum encompasses all method calls from agent to client.
2709#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2710#[derive(Clone, Debug, Serialize, Deserialize)]
2711#[serde(untagged)]
2712#[cfg_attr(feature = "schemars", schemars(inline))]
2713#[non_exhaustive]
2714#[allow(clippy::large_enum_variant)]
2715pub enum AgentRequest {
2716    /// Writes content to a text file in the client's file system.
2717    ///
2718    /// Only available if the client advertises the `fs.writeTextFile` capability.
2719    /// Allows the agent to create or modify files within the client's environment.
2720    ///
2721    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
2722    WriteTextFileRequest(WriteTextFileRequest),
2723    /// Reads content from a text file in the client's file system.
2724    ///
2725    /// Only available if the client advertises the `fs.readTextFile` capability.
2726    /// Allows the agent to access file contents within the client's environment.
2727    ///
2728    /// See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)
2729    ReadTextFileRequest(ReadTextFileRequest),
2730    /// Requests permission from the user for a tool call operation.
2731    ///
2732    /// Called by the agent when it needs user authorization before executing
2733    /// a potentially sensitive operation. The client should present the options
2734    /// to the user and return their decision.
2735    ///
2736    /// If the client cancels the prompt turn via `session/cancel`, it MUST
2737    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
2738    ///
2739    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
2740    RequestPermissionRequest(RequestPermissionRequest),
2741    /// Executes a command in a new terminal
2742    ///
2743    /// Only available if the `terminal` Client capability is set to `true`.
2744    ///
2745    /// Returns a `TerminalId` that can be used with other terminal methods
2746    /// to get the current output, wait for exit, and kill the command.
2747    ///
2748    /// The `TerminalId` can also be used to embed the terminal in a tool call
2749    /// by using the `ToolCallContent::Terminal` variant.
2750    ///
2751    /// The Agent is responsible for releasing the terminal by using the `terminal/release`
2752    /// method.
2753    ///
2754    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2755    CreateTerminalRequest(CreateTerminalRequest),
2756    /// Gets the terminal output and exit status
2757    ///
2758    /// Returns the current content in the terminal without waiting for the command to exit.
2759    /// If the command has already exited, the exit status is included.
2760    ///
2761    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2762    TerminalOutputRequest(TerminalOutputRequest),
2763    /// Releases a terminal
2764    ///
2765    /// The command is killed if it hasn't exited yet. Use `terminal/wait_for_exit`
2766    /// to wait for the command to exit before releasing the terminal.
2767    ///
2768    /// After release, the `TerminalId` can no longer be used with other `terminal/*` methods,
2769    /// but tool calls that already contain it, continue to display its output.
2770    ///
2771    /// The `terminal/kill` method can be used to terminate the command without releasing
2772    /// the terminal, allowing the Agent to call `terminal/output` and other methods.
2773    ///
2774    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2775    ReleaseTerminalRequest(ReleaseTerminalRequest),
2776    /// Waits for the terminal command to exit and return its exit status
2777    ///
2778    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2779    WaitForTerminalExitRequest(WaitForTerminalExitRequest),
2780    /// Kills the terminal command without releasing the terminal
2781    ///
2782    /// While `terminal/release` will also kill the command, this method will keep
2783    /// the `TerminalId` valid so it can be used with other methods.
2784    ///
2785    /// This method can be helpful when implementing command timeouts which terminate
2786    /// the command as soon as elapsed, and then get the final output so it can be sent
2787    /// to the model.
2788    ///
2789    /// Note: Call `terminal/release` when `TerminalId` is no longer needed.
2790    ///
2791    /// See protocol docs: [Terminals](https://agentclientprotocol.com/protocol/terminals)
2792    KillTerminalRequest(KillTerminalRequest),
2793    /// Requests structured user input via a form or URL.
2794    ///
2795    /// See protocol docs: [Elicitation](https://agentclientprotocol.com/protocol/elicitation)
2796    CreateElicitationRequest(CreateElicitationRequest),
2797    /// **UNSTABLE**
2798    ///
2799    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2800    ///
2801    /// Opens an MCP-over-ACP connection.
2802    #[cfg(feature = "unstable_mcp_over_acp")]
2803    ConnectMcpRequest(ConnectMcpRequest),
2804    /// **UNSTABLE**
2805    ///
2806    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2807    ///
2808    /// Exchanges an MCP-over-ACP message.
2809    #[cfg(feature = "unstable_mcp_over_acp")]
2810    MessageMcpRequest(MessageMcpRequest),
2811    /// **UNSTABLE**
2812    ///
2813    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2814    ///
2815    /// Closes an MCP-over-ACP connection.
2816    #[cfg(feature = "unstable_mcp_over_acp")]
2817    DisconnectMcpRequest(DisconnectMcpRequest),
2818    /// Handles extension method requests from the agent.
2819    ///
2820    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
2821    /// Extension methods provide a way to add custom functionality while maintaining
2822    /// protocol compatibility.
2823    ///
2824    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2825    ExtMethodRequest(ExtRequest),
2826}
2827
2828impl AgentRequest {
2829    /// Returns the corresponding method name of the request.
2830    #[must_use]
2831    pub fn method(&self) -> &str {
2832        match self {
2833            Self::WriteTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_write_text_file,
2834            Self::ReadTextFileRequest(_) => CLIENT_METHOD_NAMES.fs_read_text_file,
2835            Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
2836            Self::CreateTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_create,
2837            Self::TerminalOutputRequest(_) => CLIENT_METHOD_NAMES.terminal_output,
2838            Self::ReleaseTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_release,
2839            Self::WaitForTerminalExitRequest(_) => CLIENT_METHOD_NAMES.terminal_wait_for_exit,
2840            Self::KillTerminalRequest(_) => CLIENT_METHOD_NAMES.terminal_kill,
2841            Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
2842            #[cfg(feature = "unstable_mcp_over_acp")]
2843            Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
2844            #[cfg(feature = "unstable_mcp_over_acp")]
2845            Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
2846            #[cfg(feature = "unstable_mcp_over_acp")]
2847            Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
2848            Self::ExtMethodRequest(ext_request) => &ext_request.method,
2849        }
2850    }
2851}
2852
2853/// All possible responses that a client can send to an agent.
2854///
2855/// This enum is used internally for routing RPC responses. You typically won't need
2856/// to use this directly - the responses are handled automatically by the connection.
2857///
2858/// These are responses to the corresponding `AgentRequest` variants.
2859#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2860#[derive(Clone, Debug, Serialize, Deserialize)]
2861#[serde(untagged)]
2862#[cfg_attr(feature = "schemars", schemars(inline))]
2863#[non_exhaustive]
2864pub enum ClientResponse {
2865    /// Successful result returned for a `fs/write_text_file` request.
2866    WriteTextFileResponse(#[serde(default)] WriteTextFileResponse),
2867    /// Successful result returned for a `fs/read_text_file` request.
2868    ReadTextFileResponse(ReadTextFileResponse),
2869    /// Successful result returned for a `session/request_permission` request.
2870    RequestPermissionResponse(RequestPermissionResponse),
2871    /// Successful result returned for a `terminal/create` request.
2872    CreateTerminalResponse(CreateTerminalResponse),
2873    /// Successful result returned for a `terminal/output` request.
2874    TerminalOutputResponse(TerminalOutputResponse),
2875    /// Successful result returned for a `terminal/release` request.
2876    ReleaseTerminalResponse(#[serde(default)] ReleaseTerminalResponse),
2877    /// Successful result returned for a `terminal/wait_for_exit` request.
2878    WaitForTerminalExitResponse(WaitForTerminalExitResponse),
2879    /// Successful result returned for a `terminal/kill` request.
2880    KillTerminalResponse(#[serde(default)] KillTerminalResponse),
2881    /// Successful result returned for a `elicitation/create` request.
2882    CreateElicitationResponse(CreateElicitationResponse),
2883    /// Successful result returned for a `mcp/connect` request.
2884    #[cfg(feature = "unstable_mcp_over_acp")]
2885    ConnectMcpResponse(ConnectMcpResponse),
2886    /// Successful result returned for a `mcp/disconnect` request.
2887    #[cfg(feature = "unstable_mcp_over_acp")]
2888    DisconnectMcpResponse(#[serde(default)] DisconnectMcpResponse),
2889    /// Successful result returned by an MCP-over-ACP `mcp/message` request.
2890    #[cfg(feature = "unstable_mcp_over_acp")]
2891    MessageMcpResponse(MessageMcpResponse),
2892    /// Successful result returned by an extension method outside the core ACP method set.
2893    ExtMethodResponse(ExtResponse),
2894}
2895
2896/// All possible notifications that an agent can send to a client.
2897///
2898/// This enum is used internally for routing RPC notifications. You typically won't need
2899/// to use this directly.
2900///
2901/// Notifications do not expect a response.
2902#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2903#[derive(Clone, Debug, Serialize, Deserialize)]
2904#[serde(untagged)]
2905#[expect(clippy::large_enum_variant)]
2906#[cfg_attr(feature = "schemars", schemars(inline))]
2907#[non_exhaustive]
2908pub enum AgentNotification {
2909    /// Handles session update notifications from the agent.
2910    ///
2911    /// This is a notification endpoint (no response expected) that receives
2912    /// real-time updates about session progress, including message chunks,
2913    /// tool calls, and execution plans.
2914    ///
2915    /// Note: Clients SHOULD continue accepting tool call updates even after
2916    /// sending a `session/cancel` notification, as the agent may send final
2917    /// updates before responding with the cancelled stop reason.
2918    ///
2919    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-turn#3-agent-reports-output)
2920    SessionNotification(SessionNotification),
2921    /// Notification that a URL-based elicitation has completed.
2922    ///
2923    /// See protocol docs: [Elicitation](https://agentclientprotocol.com/protocol/elicitation#url-completion)
2924    CompleteElicitationNotification(CompleteElicitationNotification),
2925    /// **UNSTABLE**
2926    ///
2927    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2928    ///
2929    /// Receives an MCP-over-ACP notification.
2930    #[cfg(feature = "unstable_mcp_over_acp")]
2931    MessageMcpNotification(MessageMcpNotification),
2932    /// Handles extension notifications from the agent.
2933    ///
2934    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
2935    /// Extension notifications provide a way to send one-way messages for custom functionality
2936    /// while maintaining protocol compatibility.
2937    ///
2938    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2939    ExtNotification(ExtNotification),
2940}
2941
2942impl AgentNotification {
2943    /// Returns the corresponding method name of the notification.
2944    #[must_use]
2945    pub fn method(&self) -> &str {
2946        match self {
2947            Self::SessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
2948            Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
2949            #[cfg(feature = "unstable_mcp_over_acp")]
2950            Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
2951            Self::ExtNotification(ext_notification) => &ext_notification.method,
2952        }
2953    }
2954}
2955
2956#[cfg(test)]
2957mod tests {
2958    use super::*;
2959
2960    #[cfg(feature = "unstable_session_notices")]
2961    #[test]
2962    fn notice_preserves_wire_shape_nullable_fields_and_open_severity() {
2963        use serde_json::json;
2964
2965        let mut meta = Meta::new();
2966        meta.insert("source".into(), json!("fallback"));
2967        assert_eq!(
2968            serde_json::to_value(SessionUpdate::Notice(
2969                Notice::new(NoticeSeverity::Warning, "MCP server unavailable")
2970                    .description("Continuing without it.")
2971                    .meta(meta),
2972            ))
2973            .unwrap(),
2974            json!({
2975                "sessionUpdate": "notice",
2976                "severity": "warning",
2977                "title": "MCP server unavailable",
2978                "description": "Continuing without it.",
2979                "_meta": { "source": "fallback" }
2980            })
2981        );
2982
2983        assert_eq!(
2984            serde_json::to_value(SessionUpdate::Notice(Notice::new(
2985                NoticeSeverity::Info,
2986                "Indexing workspace",
2987            )))
2988            .unwrap(),
2989            json!({
2990                "sessionUpdate": "notice",
2991                "severity": "info",
2992                "title": "Indexing workspace"
2993            })
2994        );
2995
2996        let SessionUpdate::Notice(notice) = serde_json::from_value(json!({
2997            "sessionUpdate": "notice",
2998            "severity": "critical",
2999            "title": "Provider degraded",
3000            "description": null,
3001            "_meta": null
3002        }))
3003        .unwrap() else {
3004            panic!("expected notice");
3005        };
3006
3007        assert_eq!(
3008            notice.severity,
3009            NoticeSeverity::Other("critical".to_string())
3010        );
3011        assert_eq!(notice.description, None);
3012        assert_eq!(notice.meta, None);
3013        assert_eq!(
3014            serde_json::to_value(SessionUpdate::Notice(notice)).unwrap(),
3015            json!({
3016                "sessionUpdate": "notice",
3017                "severity": "critical",
3018                "title": "Provider degraded"
3019            })
3020        );
3021    }
3022
3023    #[cfg(feature = "unstable_session_notices")]
3024    #[test]
3025    fn notice_requires_non_null_severity_and_title() {
3026        use serde_json::json;
3027
3028        for malformed in [
3029            json!({
3030                "sessionUpdate": "notice",
3031                "severity": "warning"
3032            }),
3033            json!({
3034                "sessionUpdate": "notice",
3035                "severity": "warning",
3036                "title": null
3037            }),
3038            json!({
3039                "sessionUpdate": "notice",
3040                "title": "MCP server unavailable"
3041            }),
3042            json!({
3043                "sessionUpdate": "notice",
3044                "severity": null,
3045                "title": "MCP server unavailable"
3046            }),
3047        ] {
3048            assert!(serde_json::from_value::<SessionUpdate>(malformed).is_err());
3049        }
3050    }
3051
3052    #[cfg(feature = "unstable_session_notices")]
3053    #[test]
3054    fn notice_capability_advertises_support_only_when_present() {
3055        use serde_json::json;
3056
3057        let capabilities = ClientCapabilities::new()
3058            .session(ClientSessionCapabilities::new().notices(NoticeCapabilities::new()));
3059        let value = serde_json::to_value(&capabilities).unwrap();
3060        assert_eq!(value["session"], json!({ "notices": {} }));
3061        assert_eq!(
3062            serde_json::from_value::<ClientCapabilities>(value).unwrap(),
3063            capabilities
3064        );
3065
3066        for unsupported in [
3067            json!({}),
3068            json!({ "session": null }),
3069            json!({ "session": {} }),
3070            json!({ "session": { "notices": null } }),
3071            json!({ "session": { "notices": false } }),
3072            json!({ "session": { "notices": true } }),
3073            json!({ "session": { "notices": "supported" } }),
3074        ] {
3075            let capabilities: ClientCapabilities = serde_json::from_value(unsupported).unwrap();
3076            assert!(
3077                capabilities
3078                    .session
3079                    .and_then(|session| session.notices)
3080                    .is_none()
3081            );
3082        }
3083
3084        assert_eq!(
3085            serde_json::to_value(
3086                ClientSessionCapabilities::new()
3087                    .notices(NoticeCapabilities::new())
3088                    .notices(None)
3089            )
3090            .unwrap(),
3091            json!({})
3092        );
3093    }
3094
3095    #[cfg(not(feature = "unstable_session_notices"))]
3096    #[test]
3097    fn unsupported_notice_capability_is_ignored() {
3098        use serde_json::json;
3099
3100        let capabilities: ClientSessionCapabilities =
3101            serde_json::from_value(json!({ "notices": {} })).unwrap();
3102        assert_eq!(serde_json::to_value(capabilities).unwrap(), json!({}));
3103    }
3104
3105    #[cfg(not(feature = "unstable_session_notices"))]
3106    #[test]
3107    fn unsupported_notice_is_rejected_by_closed_update_union() {
3108        use serde_json::json;
3109
3110        assert!(
3111            serde_json::from_value::<SessionUpdate>(json!({
3112                "sessionUpdate": "notice",
3113                "severity": "warning",
3114                "title": "MCP server unavailable"
3115            }))
3116            .is_err()
3117        );
3118    }
3119
3120    #[cfg(feature = "unstable_session_compaction")]
3121    #[test]
3122    fn compaction_updates_preserve_patch_and_open_status_semantics() {
3123        use serde_json::json;
3124
3125        assert_eq!(
3126            serde_json::to_value(SessionUpdate::CompactionUpdate(CompactionUpdate::new(
3127                "cmp_001",
3128                CompactionStatus::InProgress,
3129            )))
3130            .unwrap(),
3131            json!({
3132                "sessionUpdate": "compaction_update",
3133                "compactionId": "cmp_001",
3134                "status": "in_progress"
3135            })
3136        );
3137
3138        let SessionUpdate::CompactionUpdate(update) = serde_json::from_value(json!({
3139            "sessionUpdate": "compaction_update",
3140            "compactionId": "cmp_001",
3141            "status": "paused",
3142            "summary": null,
3143            "error": "waiting"
3144        }))
3145        .unwrap() else {
3146            panic!("expected compaction update");
3147        };
3148        assert_eq!(update.status, CompactionStatus::Other("paused".into()));
3149        assert!(update.summary.is_null());
3150        assert_eq!(update.error.value().map(String::as_str), Some("waiting"));
3151        assert!(update.meta.is_undefined());
3152    }
3153
3154    #[cfg(feature = "unstable_session_compaction")]
3155    #[test]
3156    fn compaction_chunk_and_v1_capability_serialize() {
3157        use serde_json::json;
3158
3159        assert_eq!(
3160            serde_json::to_value(SessionUpdate::CompactionSummaryChunk(
3161                CompactionSummaryChunk::new(
3162                    "cmp_001",
3163                    ContentBlock::Text(crate::v1::TextContent::new("retained")),
3164                ),
3165            ))
3166            .unwrap(),
3167            json!({
3168                "sessionUpdate": "compaction_summary_chunk",
3169                "compactionId": "cmp_001",
3170                "content": { "type": "text", "text": "retained" }
3171            })
3172        );
3173        assert_eq!(
3174            serde_json::to_value(
3175                ClientSessionCapabilities::new().compaction(CompactionCapabilities::new())
3176            )
3177            .unwrap(),
3178            json!({ "compaction": {} })
3179        );
3180        let absent: ClientSessionCapabilities = serde_json::from_value(json!({})).unwrap();
3181        let null: ClientSessionCapabilities =
3182            serde_json::from_value(json!({ "compaction": null })).unwrap();
3183        assert!(absent.compaction.is_none());
3184        assert!(null.compaction.is_none());
3185    }
3186
3187    #[test]
3188    fn test_elicitation_capability_semantics() {
3189        use serde_json::json;
3190
3191        let unsupported: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3192        assert!(unsupported.elicitation.is_none());
3193
3194        let null: ClientCapabilities =
3195            serde_json::from_value(json!({ "elicitation": null })).unwrap();
3196        assert!(null.elicitation.is_none());
3197
3198        let malformed: ClientCapabilities =
3199            serde_json::from_value(json!({ "elicitation": false })).unwrap();
3200        assert!(malformed.elicitation.is_none());
3201
3202        let empty: ClientCapabilities =
3203            serde_json::from_value(json!({ "elicitation": {} })).unwrap();
3204        let empty = empty.elicitation.expect("present capability");
3205        assert!(!empty.supports_form());
3206        assert!(!empty.supports_url());
3207
3208        let form_only: ClientCapabilities = serde_json::from_value(json!({
3209            "elicitation": { "form": {} }
3210        }))
3211        .unwrap();
3212        let form_only = form_only.elicitation.expect("advertised capability");
3213        assert!(form_only.supports_form());
3214        assert!(!form_only.supports_url());
3215
3216        let url_only: ClientCapabilities = serde_json::from_value(json!({
3217            "elicitation": { "url": {} }
3218        }))
3219        .unwrap();
3220        let url_only = url_only.elicitation.expect("advertised capability");
3221        assert!(!url_only.supports_form());
3222        assert!(url_only.supports_url());
3223
3224        let both: ClientCapabilities = serde_json::from_value(json!({
3225            "elicitation": { "form": {}, "url": {} }
3226        }))
3227        .unwrap();
3228        let both = both.elicitation.expect("advertised capability");
3229        assert!(both.supports_form());
3230        assert!(both.supports_url());
3231    }
3232
3233    #[test]
3234    fn test_elicitation_method_routing_and_envelopes() {
3235        use serde_json::json;
3236
3237        assert_eq!(CLIENT_METHOD_NAMES.elicitation_create, "elicitation/create");
3238        assert_eq!(
3239            CLIENT_METHOD_NAMES.elicitation_complete,
3240            "elicitation/complete"
3241        );
3242
3243        let request = AgentRequest::CreateElicitationRequest(CreateElicitationRequest::new(
3244            crate::v1::ElicitationFormMode::new(
3245                crate::v1::ElicitationSessionScope::new("sess_1"),
3246                crate::v1::ElicitationSchema::new(),
3247            ),
3248            "Choose a value",
3249        ));
3250        assert_eq!(request.method(), "elicitation/create");
3251        let method = Arc::from(request.method());
3252        let request = crate::v1::JsonRpcMessage::wrap(crate::v1::Request {
3253            id: crate::v1::RequestId::Number(7),
3254            method,
3255            params: Some(request),
3256        });
3257        assert_eq!(
3258            serde_json::to_value(request).unwrap(),
3259            json!({
3260                "jsonrpc": "2.0",
3261                "id": 7,
3262                "method": "elicitation/create",
3263                "params": {
3264                    "mode": "form",
3265                    "sessionId": "sess_1",
3266                    "message": "Choose a value",
3267                    "requestedSchema": { "type": "object", "properties": {} }
3268                }
3269            })
3270        );
3271
3272        let notification = AgentNotification::CompleteElicitationNotification(
3273            CompleteElicitationNotification::new("elic_1"),
3274        );
3275        assert_eq!(notification.method(), "elicitation/complete");
3276        let method = Arc::from(notification.method());
3277        let notification = crate::v1::JsonRpcMessage::wrap(crate::v1::Notification {
3278            method,
3279            params: Some(notification),
3280        });
3281        assert_eq!(
3282            serde_json::to_value(notification).unwrap(),
3283            json!({
3284                "jsonrpc": "2.0",
3285                "method": "elicitation/complete",
3286                "params": { "elicitationId": "elic_1" }
3287            })
3288        );
3289    }
3290
3291    #[test]
3292    fn test_client_capabilities_default_on_malformed_values() {
3293        use serde_json::json;
3294
3295        let capabilities: ClientCapabilities = serde_json::from_value(json!({
3296            "fs": {
3297                "readTextFile": "yes",
3298                "writeTextFile": true
3299            },
3300            "terminal": {}
3301        }))
3302        .unwrap();
3303
3304        assert!(!capabilities.fs.read_text_file);
3305        assert!(capabilities.fs.write_text_file);
3306        assert!(!capabilities.terminal);
3307
3308        let capabilities: ClientCapabilities = serde_json::from_value(json!({
3309            "fs": false
3310        }))
3311        .unwrap();
3312        assert_eq!(capabilities.fs, FileSystemCapabilities::default());
3313
3314        {
3315            let capabilities: ClientCapabilities = serde_json::from_value(json!({
3316                "auth": false
3317            }))
3318            .unwrap();
3319            assert_eq!(capabilities.auth, AuthCapabilities::default());
3320
3321            let capabilities: AuthCapabilities = serde_json::from_value(json!({
3322                "terminal": {}
3323            }))
3324            .unwrap();
3325            assert!(!capabilities.terminal);
3326        }
3327    }
3328
3329    #[test]
3330    fn test_serialization_behavior() {
3331        use serde_json::json;
3332
3333        assert_eq!(
3334            serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
3335            SessionInfoUpdate {
3336                title: MaybeUndefined::Undefined,
3337                updated_at: MaybeUndefined::Undefined,
3338                meta: None
3339            }
3340        );
3341        assert_eq!(
3342            serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
3343                .unwrap(),
3344            SessionInfoUpdate {
3345                title: MaybeUndefined::Null,
3346                updated_at: MaybeUndefined::Null,
3347                meta: None
3348            }
3349        );
3350        assert_eq!(
3351            serde_json::from_value::<SessionInfoUpdate>(
3352                json!({"title": "title", "updatedAt": "timestamp"})
3353            )
3354            .unwrap(),
3355            SessionInfoUpdate {
3356                title: MaybeUndefined::Value("title".to_string()),
3357                updated_at: MaybeUndefined::Value("timestamp".to_string()),
3358                meta: None
3359            }
3360        );
3361
3362        assert_eq!(
3363            serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
3364            json!({})
3365        );
3366        assert_eq!(
3367            serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
3368            json!({"title": "title"})
3369        );
3370        assert_eq!(
3371            serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
3372            json!({"title": null})
3373        );
3374        assert_eq!(
3375            serde_json::to_value(
3376                SessionInfoUpdate::new()
3377                    .title("title")
3378                    .title(MaybeUndefined::Undefined)
3379            )
3380            .unwrap(),
3381            json!({})
3382        );
3383    }
3384
3385    #[test]
3386    fn test_content_chunk_message_id_serialization() {
3387        use serde_json::json;
3388
3389        assert_eq!(
3390            serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
3391                ContentBlock::Text(crate::v1::TextContent::new("Hello"))
3392            )))
3393            .unwrap(),
3394            json!({
3395                "sessionUpdate": "agent_message_chunk",
3396                "content": {
3397                    "type": "text",
3398                    "text": "Hello"
3399                }
3400            })
3401        );
3402
3403        assert_eq!(
3404            serde_json::to_value(SessionUpdate::AgentMessageChunk(
3405                ContentChunk::new(ContentBlock::Text(crate::v1::TextContent::new("Hello")))
3406                    .message_id("msg_agent_c42b9")
3407            ))
3408            .unwrap(),
3409            json!({
3410                "sessionUpdate": "agent_message_chunk",
3411                "messageId": "msg_agent_c42b9",
3412                "content": {
3413                    "type": "text",
3414                    "text": "Hello"
3415                }
3416            })
3417        );
3418
3419        let SessionUpdate::AgentMessageChunk(chunk) = serde_json::from_value(json!({
3420            "sessionUpdate": "agent_message_chunk",
3421            "messageId": null,
3422            "content": {
3423                "type": "text",
3424                "text": "Hello"
3425            }
3426        }))
3427        .unwrap() else {
3428            panic!("expected agent message chunk");
3429        };
3430
3431        assert_eq!(chunk.message_id, None);
3432    }
3433
3434    #[test]
3435    fn test_usage_update_serialization() {
3436        use serde_json::json;
3437
3438        assert_eq!(
3439            serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
3440                53_000, 200_000
3441            )))
3442            .unwrap(),
3443            json!({
3444                "sessionUpdate": "usage_update",
3445                "used": 53000,
3446                "size": 200_000
3447            })
3448        );
3449
3450        assert_eq!(
3451            serde_json::to_value(SessionUpdate::UsageUpdate(
3452                UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
3453            ))
3454            .unwrap(),
3455            json!({
3456                "sessionUpdate": "usage_update",
3457                "used": 53000,
3458                "size": 200_000,
3459                "cost": {
3460                    "amount": 0.045,
3461                    "currency": "USD"
3462                }
3463            })
3464        );
3465
3466        let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
3467            "sessionUpdate": "usage_update",
3468            "used": 53000,
3469            "size": 200_000,
3470            "cost": null
3471        }))
3472        .unwrap() else {
3473            panic!("expected usage update");
3474        };
3475
3476        assert_eq!(update.cost, None);
3477    }
3478
3479    #[cfg(feature = "unstable_nes")]
3480    #[test]
3481    fn test_client_capabilities_position_encodings_serialization() {
3482        use serde_json::json;
3483
3484        let capabilities = ClientCapabilities::new().position_encodings(vec![
3485            PositionEncodingKind::Utf32,
3486            PositionEncodingKind::Utf16,
3487        ]);
3488        let json = serde_json::to_value(&capabilities).unwrap();
3489
3490        assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
3491    }
3492
3493    #[test]
3494    fn test_client_capabilities_boolean_config_options_serialization() {
3495        use serde_json::json;
3496
3497        let capabilities = ClientCapabilities::new().session(
3498            ClientSessionCapabilities::new().config_options(
3499                SessionConfigOptionsCapabilities::new()
3500                    .boolean(BooleanConfigOptionCapabilities::new()),
3501            ),
3502        );
3503        let json = serde_json::to_value(&capabilities).unwrap();
3504
3505        assert_eq!(json["session"]["configOptions"]["boolean"], json!({}));
3506
3507        let omitted: ClientCapabilities = serde_json::from_value(json!({})).unwrap();
3508        assert!(omitted.session.is_none());
3509
3510        let null_session: ClientCapabilities = serde_json::from_value(json!({
3511            "session": null
3512        }))
3513        .unwrap();
3514        assert!(null_session.session.is_none());
3515
3516        let null_config_options: ClientCapabilities = serde_json::from_value(json!({
3517            "session": {
3518                "configOptions": null
3519            }
3520        }))
3521        .unwrap();
3522        assert!(
3523            null_config_options
3524                .session
3525                .and_then(|session| session.config_options)
3526                .is_none()
3527        );
3528
3529        let null_boolean: ClientCapabilities = serde_json::from_value(json!({
3530            "session": {
3531                "configOptions": {
3532                    "boolean": null
3533                }
3534            }
3535        }))
3536        .unwrap();
3537        assert!(
3538            null_boolean
3539                .session
3540                .and_then(|session| session.config_options)
3541                .and_then(|config_options| config_options.boolean)
3542                .is_none()
3543        );
3544    }
3545
3546    #[cfg(feature = "unstable_plan_operations")]
3547    #[test]
3548    fn test_plan_operations_serialization() {
3549        use serde_json::json;
3550
3551        use crate::v1::{PlanEntry, PlanEntryPriority, PlanEntryStatus, PlanUpdateContent};
3552
3553        let plan_update = SessionUpdate::PlanUpdate(PlanUpdate::new(PlanUpdateContent::items(
3554            "plan-1",
3555            vec![PlanEntry::new(
3556                "Step 1",
3557                PlanEntryPriority::High,
3558                PlanEntryStatus::Pending,
3559            )],
3560        )));
3561
3562        assert_eq!(
3563            serde_json::to_value(plan_update).unwrap(),
3564            json!({
3565                "sessionUpdate": "plan_update",
3566                "plan": {
3567                    "type": "items",
3568                    "planId": "plan-1",
3569                    "entries": [
3570                        {
3571                            "content": "Step 1",
3572                            "priority": "high",
3573                            "status": "pending"
3574                        }
3575                    ]
3576                }
3577            })
3578        );
3579
3580        assert_eq!(
3581            serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
3582            json!({
3583                "sessionUpdate": "plan_removed",
3584                "planId": "plan-1"
3585            })
3586        );
3587
3588        let capabilities = ClientCapabilities::new().plan(PlanCapabilities::new());
3589        let json = serde_json::to_value(&capabilities).unwrap();
3590        assert_eq!(json["plan"], json!({}));
3591
3592        assert_eq!(
3593            serde_json::from_value::<ClientCapabilities>(json!({ "plan": null }))
3594                .unwrap()
3595                .plan,
3596            None
3597        );
3598    }
3599
3600    #[cfg(feature = "unstable_mcp_over_acp")]
3601    #[test]
3602    fn test_agent_mcp_request_method_names() {
3603        use serde_json::json;
3604
3605        let params: serde_json::Map<String, serde_json::Value> =
3606            [("cursor".to_string(), json!("abc"))].into_iter().collect();
3607
3608        assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
3609        assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
3610        assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
3611
3612        assert_eq!(
3613            AgentRequest::ConnectMcpRequest(ConnectMcpRequest::new("server-1")).method(),
3614            "mcp/connect"
3615        );
3616        assert_eq!(
3617            AgentRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
3618                .method(),
3619            "mcp/message"
3620        );
3621        assert_eq!(
3622            AgentRequest::DisconnectMcpRequest(DisconnectMcpRequest::new("conn-1")).method(),
3623            "mcp/disconnect"
3624        );
3625        assert_eq!(
3626            AgentNotification::MessageMcpNotification(MessageMcpNotification::new(
3627                "conn-1",
3628                "notifications/progress"
3629            ))
3630            .method(),
3631            "mcp/message"
3632        );
3633
3634        assert_eq!(
3635            serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
3636            json!({ "serverId": "server-1" })
3637        );
3638        assert_eq!(
3639            serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
3640            json!({ "connectionId": "conn-1" })
3641        );
3642        assert_eq!(
3643            serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
3644                .unwrap(),
3645            json!({
3646                "connectionId": "conn-1",
3647                "method": "tools/list",
3648                "params": { "cursor": "abc" }
3649            })
3650        );
3651        assert_eq!(
3652            serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
3653            json!({ "connectionId": "conn-1" })
3654        );
3655        assert_eq!(
3656            serde_json::to_value(MessageMcpNotification::new(
3657                "conn-1",
3658                "notifications/progress"
3659            ))
3660            .unwrap(),
3661            json!({
3662                "connectionId": "conn-1",
3663                "method": "notifications/progress"
3664            })
3665        );
3666
3667        let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
3668            "connectionId": "conn-1",
3669            "method": "tools/list",
3670            "params": null
3671        }))
3672        .unwrap();
3673        assert_eq!(request_with_null_params.params, None);
3674    }
3675
3676    #[test]
3677    fn request_permission_request_rejects_malformed_options() {
3678        use serde_json::json;
3679
3680        assert!(
3681            serde_json::from_value::<RequestPermissionRequest>(json!({
3682                "sessionId": "sess-1",
3683                "toolCall": {"toolCallId": "tc-1"},
3684                "options": "not-an-array"
3685            }))
3686            .is_err()
3687        );
3688        assert!(
3689            serde_json::from_value::<RequestPermissionRequest>(json!({
3690                "sessionId": "sess-1",
3691                "toolCall": {"toolCallId": "tc-1"},
3692                "options": [{"optionId": "allow"}]
3693            }))
3694            .is_err()
3695        );
3696    }
3697}