Skip to main content

agent_client_protocol_schema/v2/
agent.rs

1//! Methods and notifications the agent handles/receives.
2//!
3//! This module defines the Agent trait and all associated types for implementing
4//! an AI coding agent that follows the Agent Client Protocol (ACP).
5
6use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
7
8#[cfg(feature = "unstable_llm_providers")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12#[cfg(feature = "schemars")]
13use schemars::Schema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
16
17use super::{
18    AbsolutePath, AvailableCommand, ClientCapabilities, ContentBlock, ExtNotification, ExtRequest,
19    ExtResponse, MessageId, Meta, SessionId,
20};
21use crate::{IntoOption, ProtocolVersion};
22
23#[cfg(feature = "unstable_mcp_over_acp")]
24use super::mcp::{MCP_MESSAGE_METHOD_NAME, MessageMcpNotification};
25
26#[cfg(feature = "unstable_nes")]
27use super::{
28    AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification,
29    DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification,
30    DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification,
31    StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse,
32};
33
34#[cfg(feature = "unstable_nes")]
35use super::{
36    DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME,
37    DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME,
38    NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME,
39    NES_SUGGEST_METHOD_NAME,
40};
41
42// Initialize
43
44/// Request parameters for the initialize method.
45///
46/// Sent by the client to establish connection and negotiate capabilities.
47///
48/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
49#[serde_as]
50#[skip_serializing_none]
51#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
54#[serde(rename_all = "camelCase")]
55#[non_exhaustive]
56pub struct InitializeRequest {
57    /// The latest protocol version supported by the client.
58    pub protocol_version: ProtocolVersion,
59    /// Information about the implementation sending this initialize request.
60    pub info: Implementation,
61    /// Capabilities supported by the client.
62    #[serde_as(deserialize_as = "DefaultOnError")]
63    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
64    #[serde(default)]
65    pub capabilities: ClientCapabilities,
66    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
67    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
68    /// these keys.
69    ///
70    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
71    #[serde_as(deserialize_as = "DefaultOnError")]
72    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
73    #[serde(default)]
74    #[serde(rename = "_meta")]
75    pub meta: Option<Meta>,
76}
77
78impl InitializeRequest {
79    /// Builds [`InitializeRequest`] with the required request fields set; optional fields start unset or empty.
80    #[must_use]
81    pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
82        Self {
83            protocol_version,
84            capabilities: ClientCapabilities::default(),
85            info,
86            meta: None,
87        }
88    }
89
90    /// Capabilities supported by the client.
91    #[must_use]
92    pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
93        self.capabilities = capabilities;
94        self
95    }
96
97    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
98    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
99    /// these keys.
100    ///
101    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
102    #[must_use]
103    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
104        self.meta = meta.into_option();
105        self
106    }
107}
108
109/// Response to the `initialize` method.
110///
111/// Contains the negotiated protocol version and agent capabilities.
112///
113/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
114#[serde_as]
115#[skip_serializing_none]
116#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
118#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
119#[serde(rename_all = "camelCase")]
120#[non_exhaustive]
121pub struct InitializeResponse {
122    /// The protocol version the client specified if supported by the agent,
123    /// or the latest protocol version supported by the agent.
124    ///
125    /// The client should disconnect, if it doesn't support this version.
126    pub protocol_version: ProtocolVersion,
127    /// Information about the implementation sending this initialize response.
128    pub info: Implementation,
129    /// Capabilities supported by the agent.
130    #[serde_as(deserialize_as = "DefaultOnError")]
131    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
132    #[serde(default)]
133    pub capabilities: AgentCapabilities,
134    /// Authentication methods supported by the agent.
135    ///
136    /// Optional. Omitted or empty means the agent does not advertise the
137    /// authentication method surface. Supplying one or more valid methods means
138    /// the agent MUST support both `auth/login` and `auth/logout`.
139    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
140    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
141    #[serde(default, skip_serializing_if = "Vec::is_empty")]
142    pub auth_methods: Vec<AuthMethod>,
143    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
144    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
145    /// these keys.
146    ///
147    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
148    #[serde_as(deserialize_as = "DefaultOnError")]
149    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
150    #[serde(default)]
151    #[serde(rename = "_meta")]
152    pub meta: Option<Meta>,
153}
154
155impl InitializeResponse {
156    /// Builds [`InitializeResponse`] with the required response fields set; optional fields start unset or empty.
157    #[must_use]
158    pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
159        Self {
160            protocol_version,
161            capabilities: AgentCapabilities::default(),
162            auth_methods: vec![],
163            info,
164            meta: None,
165        }
166    }
167
168    /// Capabilities supported by the agent.
169    #[must_use]
170    pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
171        self.capabilities = capabilities;
172        self
173    }
174
175    /// Authentication methods supported by the agent.
176    ///
177    /// Supplying one or more valid methods means the agent MUST support both
178    /// `auth/login` and `auth/logout`.
179    #[must_use]
180    pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
181        self.auth_methods = auth_methods;
182        self
183    }
184
185    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
186    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
187    /// these keys.
188    ///
189    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
190    #[must_use]
191    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
192        self.meta = meta.into_option();
193        self
194    }
195}
196
197/// Metadata about the implementation of the client or agent.
198/// Describes the name and version of an ACP implementation, with an optional
199/// title for UI representation.
200#[serde_as]
201#[skip_serializing_none]
202#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
203#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
204#[serde(rename_all = "camelCase")]
205#[non_exhaustive]
206pub struct Implementation {
207    /// Intended for programmatic or logical use, but can be used as a display
208    /// name fallback if title isn’t present.
209    pub name: String,
210    /// Intended for UI and end-user contexts — optimized to be human-readable
211    /// and easily understood.
212    ///
213    /// If not provided, the name should be used for display.
214    #[serde_as(deserialize_as = "DefaultOnError")]
215    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
216    #[serde(default)]
217    pub title: Option<String>,
218    /// Version of the implementation. Can be displayed to the user or used
219    /// for debugging or metrics purposes. (e.g. "1.0.0").
220    pub version: String,
221    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
222    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
223    /// these keys.
224    ///
225    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
226    #[serde_as(deserialize_as = "DefaultOnError")]
227    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
228    #[serde(default)]
229    #[serde(rename = "_meta")]
230    pub meta: Option<Meta>,
231}
232
233impl Implementation {
234    /// Builds [`Implementation`] with the required fields set; optional fields start unset or empty.
235    #[must_use]
236    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
237        Self {
238            name: name.into(),
239            title: None,
240            version: version.into(),
241            meta: None,
242        }
243    }
244
245    /// Intended for UI and end-user contexts — optimized to be human-readable
246    /// and easily understood.
247    ///
248    /// If not provided, the name should be used for display.
249    #[must_use]
250    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
251        self.title = title.into_option();
252        self
253    }
254
255    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
256    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
257    /// these keys.
258    ///
259    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
260    #[must_use]
261    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
262        self.meta = meta.into_option();
263        self
264    }
265}
266
267// Authentication
268
269/// Request parameters for the `auth/login` method.
270///
271/// Specifies which authentication method to use.
272///
273/// Agents MUST support this method when their `initialize` response advertised
274/// at least one valid authentication method. Clients MUST NOT call this method
275/// when `authMethods` was omitted or empty.
276#[serde_as]
277#[skip_serializing_none]
278#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
279#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
280#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
281#[serde(rename_all = "camelCase")]
282#[non_exhaustive]
283pub struct LoginAuthRequest {
284    /// The ID of the authentication method to use.
285    /// Must be one of the methods advertised in the initialize response.
286    pub method_id: AuthMethodId,
287    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
288    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
289    /// these keys.
290    ///
291    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
292    #[serde_as(deserialize_as = "DefaultOnError")]
293    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
294    #[serde(default)]
295    #[serde(rename = "_meta")]
296    pub meta: Option<Meta>,
297}
298
299impl LoginAuthRequest {
300    /// Builds [`LoginAuthRequest`] with the required request fields set; optional fields start unset or empty.
301    #[must_use]
302    pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
303        Self {
304            method_id: method_id.into(),
305            meta: None,
306        }
307    }
308
309    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
310    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
311    /// these keys.
312    ///
313    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
314    #[must_use]
315    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
316        self.meta = meta.into_option();
317        self
318    }
319}
320
321crate::serde_util::default_on_null! {
322    /// Response to the `auth/login` method.
323    #[serde_as]
324    #[skip_serializing_none]
325    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
326    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
327    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
328    #[serde(rename_all = "camelCase")]
329    #[non_exhaustive]
330    pub struct LoginAuthResponse {
331        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
332        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
333        /// these keys.
334        ///
335        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
336        #[serde_as(deserialize_as = "DefaultOnError")]
337        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
338        #[serde(default)]
339        #[serde(rename = "_meta")]
340        pub meta: Option<Meta>,
341    }
342}
343
344impl LoginAuthResponse {
345    /// Builds [`LoginAuthResponse`] with the required response fields set; optional fields start unset or empty.
346    #[must_use]
347    pub fn new() -> Self {
348        Self::default()
349    }
350
351    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
352    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
353    /// these keys.
354    ///
355    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
356    #[must_use]
357    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
358        self.meta = meta.into_option();
359        self
360    }
361}
362
363// Logout
364
365crate::serde_util::default_on_null! {
366    /// Request parameters for the `auth/logout` method.
367    ///
368    /// Terminates the current authenticated session.
369    ///
370    /// Agents MUST support this method when their `initialize` response advertised
371    /// at least one valid authentication method. Clients MUST NOT call this method
372    /// when `authMethods` was omitted or empty.
373    #[serde_as]
374    #[skip_serializing_none]
375    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
376    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
377    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
378    #[serde(rename_all = "camelCase")]
379    #[non_exhaustive]
380    pub struct LogoutAuthRequest {
381        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
382        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
383        /// these keys.
384        ///
385        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
386        #[serde_as(deserialize_as = "DefaultOnError")]
387        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
388        #[serde(default)]
389        #[serde(rename = "_meta")]
390        pub meta: Option<Meta>,
391    }
392}
393
394impl LogoutAuthRequest {
395    /// Builds [`LogoutAuthRequest`] with the required request fields set; optional fields start unset or empty.
396    #[must_use]
397    pub fn new() -> Self {
398        Self::default()
399    }
400
401    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
402    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
403    /// these keys.
404    ///
405    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
406    #[must_use]
407    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
408        self.meta = meta.into_option();
409        self
410    }
411}
412
413crate::serde_util::default_on_null! {
414    /// Response to the `auth/logout` method.
415    #[serde_as]
416    #[skip_serializing_none]
417    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
418    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
419    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
420    #[serde(rename_all = "camelCase")]
421    #[non_exhaustive]
422    pub struct LogoutAuthResponse {
423        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
424        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
425        /// these keys.
426        ///
427        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
428        #[serde_as(deserialize_as = "DefaultOnError")]
429        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
430        #[serde(default)]
431        #[serde(rename = "_meta")]
432        pub meta: Option<Meta>,
433    }
434}
435
436impl LogoutAuthResponse {
437    /// Builds [`LogoutAuthResponse`] with the required response fields set; optional fields start unset or empty.
438    #[must_use]
439    pub fn new() -> Self {
440        Self::default()
441    }
442
443    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
444    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
445    /// these keys.
446    ///
447    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
448    #[must_use]
449    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
450        self.meta = meta.into_option();
451        self
452    }
453}
454
455/// Authentication-related extension capabilities supported by the agent.
456///
457/// This object does not advertise support for `auth/login` or `auth/logout`.
458/// Those methods are advertised by a non-empty `authMethods` list in the
459/// `initialize` response.
460#[serde_as]
461#[skip_serializing_none]
462#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
463#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
464#[serde(rename_all = "camelCase")]
465#[non_exhaustive]
466pub struct AgentAuthCapabilities {
467    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
468    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
469    /// these keys.
470    ///
471    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
472    #[serde_as(deserialize_as = "DefaultOnError")]
473    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
474    #[serde(default)]
475    #[serde(rename = "_meta")]
476    pub meta: Option<Meta>,
477}
478
479impl AgentAuthCapabilities {
480    /// Builds an empty [`AgentAuthCapabilities`].
481    #[must_use]
482    pub fn new() -> Self {
483        Self::default()
484    }
485
486    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
487    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
488    /// these keys.
489    ///
490    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
491    #[must_use]
492    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
493        self.meta = meta.into_option();
494        self
495    }
496}
497
498/// Typed identifier used for auth method values on the wire.
499#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
500#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
501#[serde(transparent)]
502#[from(forward)]
503#[non_exhaustive]
504pub struct AuthMethodId(pub Arc<str>);
505
506impl AuthMethodId {
507    /// Wraps a protocol string as a typed [`AuthMethodId`].
508    #[must_use]
509    pub fn new(id: impl Into<Self>) -> Self {
510        id.into()
511    }
512}
513
514/// Describes an available authentication method.
515///
516/// The `type` field acts as the discriminator in the serialized JSON form.
517#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
518#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
519#[serde(tag = "type", rename_all = "snake_case")]
520#[non_exhaustive]
521pub enum AuthMethod {
522    /// Client runs the configured agent program as a separate interactive
523    /// process, without passing this method to `auth/login`.
524    Terminal(AuthMethodTerminal),
525    /// Agent handles authentication itself through `auth/login`.
526    ///
527    /// The `type` discriminator value is `agent`.
528    Agent(AuthMethodAgent),
529    /// Custom or future authentication method.
530    ///
531    /// Values beginning with `_` are reserved for implementation-specific
532    /// extensions. Unknown values that do not begin with `_` are reserved for
533    /// future ACP variants.
534    ///
535    /// Clients that do not understand this method type should preserve the raw
536    /// payload when storing, replaying, proxying, or forwarding initialization
537    /// data, and otherwise ignore the method or display it generically.
538    #[serde(untagged)]
539    Other(OtherAuthMethod),
540}
541
542impl AuthMethod {
543    /// The unique identifier for this authentication method.
544    #[must_use]
545    pub fn method_id(&self) -> &AuthMethodId {
546        match self {
547            Self::Agent(a) => &a.method_id,
548            Self::Other(a) => &a.method_id,
549            Self::Terminal(t) => &t.method_id,
550        }
551    }
552
553    /// The human-readable name of this authentication method.
554    #[must_use]
555    pub fn name(&self) -> &str {
556        match self {
557            Self::Agent(a) => &a.name,
558            Self::Other(a) => &a.name,
559            Self::Terminal(t) => &t.name,
560        }
561    }
562
563    /// Optional description providing more details about this authentication method.
564    #[must_use]
565    pub fn description(&self) -> Option<&str> {
566        match self {
567            Self::Agent(a) => a.description.as_deref(),
568            Self::Other(a) => a.description.as_deref(),
569            Self::Terminal(t) => t.description.as_deref(),
570        }
571    }
572
573    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
574    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
575    /// these keys.
576    ///
577    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
578    #[must_use]
579    pub fn meta(&self) -> Option<&Meta> {
580        match self {
581            Self::Agent(a) => a.meta.as_ref(),
582            Self::Other(a) => a.meta.as_ref(),
583            Self::Terminal(t) => t.meta.as_ref(),
584        }
585    }
586}
587
588/// Custom or future authentication method payload.
589#[serde_as]
590#[skip_serializing_none]
591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
592#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
593#[cfg_attr(feature = "schemars", schemars(inline))]
594#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
595#[serde(rename_all = "camelCase")]
596#[non_exhaustive]
597pub struct OtherAuthMethod {
598    /// Custom or future authentication method type.
599    ///
600    /// Values beginning with `_` are reserved for implementation-specific
601    /// extensions. Unknown values that do not begin with `_` are reserved for
602    /// future ACP variants.
603    #[serde(rename = "type")]
604    pub type_: String,
605    /// Unique identifier for this authentication method.
606    pub method_id: AuthMethodId,
607    /// Human-readable name of the authentication method.
608    pub name: String,
609    /// Optional description providing more details about this authentication method.
610    #[serde_as(deserialize_as = "DefaultOnError")]
611    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
612    #[serde(default)]
613    pub description: Option<String>,
614    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
615    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
616    /// these keys.
617    ///
618    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
619    #[serde_as(deserialize_as = "DefaultOnError")]
620    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
621    #[serde(default)]
622    #[serde(rename = "_meta")]
623    pub meta: Option<Meta>,
624    /// Additional fields from the unknown authentication method payload.
625    #[serde(flatten)]
626    pub fields: BTreeMap<String, serde_json::Value>,
627}
628
629impl OtherAuthMethod {
630    /// Builds [`OtherAuthMethod`] from an unknown discriminator and preserves the remaining extension fields.
631    #[must_use]
632    pub fn new(
633        type_: impl Into<String>,
634        method_id: impl Into<AuthMethodId>,
635        name: impl Into<String>,
636        mut fields: BTreeMap<String, serde_json::Value>,
637    ) -> Self {
638        fields.remove("type");
639        fields.remove("methodId");
640        fields.remove("name");
641        fields.remove("description");
642        fields.remove("_meta");
643        Self {
644            type_: type_.into(),
645            method_id: method_id.into(),
646            name: name.into(),
647            description: None,
648            meta: None,
649            fields,
650        }
651    }
652
653    /// Optional description providing more details about this authentication method.
654    #[must_use]
655    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
656        self.description = description.into_option();
657        self
658    }
659
660    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
661    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
662    /// these keys.
663    ///
664    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
665    #[must_use]
666    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
667        self.meta = meta.into_option();
668        self
669    }
670}
671
672impl<'de> Deserialize<'de> for OtherAuthMethod {
673    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
674    where
675        D: serde::Deserializer<'de>,
676    {
677        #[derive(Deserialize)]
678        #[serde(rename_all = "camelCase")]
679        struct RawOtherAuthMethod {
680            #[serde(rename = "type")]
681            type_: String,
682            method_id: AuthMethodId,
683            name: String,
684            description: Option<String>,
685            #[serde(rename = "_meta")]
686            meta: Option<Meta>,
687            #[serde(flatten)]
688            fields: BTreeMap<String, serde_json::Value>,
689        }
690
691        let raw = RawOtherAuthMethod::deserialize(deserializer)?;
692        if is_known_auth_method_type(&raw.type_) {
693            return Err(serde::de::Error::custom(format!(
694                "known authentication method `{}` did not match its schema",
695                raw.type_
696            )));
697        }
698
699        Ok(Self {
700            type_: raw.type_,
701            method_id: raw.method_id,
702            name: raw.name,
703            description: raw.description,
704            meta: raw.meta,
705            fields: raw.fields,
706        })
707    }
708}
709
710fn is_known_auth_method_type(type_: &str) -> bool {
711    matches!(type_, "agent" | "terminal")
712}
713
714#[cfg(feature = "schemars")]
715fn other_auth_method_schema(schema: &mut Schema) {
716    super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
717}
718
719/// Agent handles authentication itself through `auth/login`.
720///
721/// The `type` discriminator value is `agent`.
722#[serde_as]
723#[skip_serializing_none]
724#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
725#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
726#[serde(rename_all = "camelCase")]
727#[non_exhaustive]
728pub struct AuthMethodAgent {
729    /// Unique identifier for this authentication method.
730    pub method_id: AuthMethodId,
731    /// Human-readable name of the authentication method.
732    pub name: String,
733    /// Optional description providing more details about this authentication method.
734    #[serde_as(deserialize_as = "DefaultOnError")]
735    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
736    #[serde(default)]
737    pub description: Option<String>,
738    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
739    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
740    /// these keys.
741    ///
742    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
743    #[serde_as(deserialize_as = "DefaultOnError")]
744    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
745    #[serde(default)]
746    #[serde(rename = "_meta")]
747    pub meta: Option<Meta>,
748}
749
750impl AuthMethodAgent {
751    /// Builds [`AuthMethodAgent`] with the required fields set; optional fields start unset or empty.
752    #[must_use]
753    pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
754        Self {
755            method_id: method_id.into(),
756            name: name.into(),
757            description: None,
758            meta: None,
759        }
760    }
761
762    /// Optional description providing more details about this authentication method.
763    #[must_use]
764    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
765        self.description = description.into_option();
766        self
767    }
768
769    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
770    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
771    /// these keys.
772    ///
773    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
774    #[must_use]
775    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
776        self.meta = meta.into_option();
777        self
778    }
779}
780
781/// Terminal-based authentication method.
782///
783/// The client runs the configured agent program as a separate interactive
784/// process for the user to authenticate via a TUI. Agents MUST advertise this
785/// method only when the client enabled its terminal authentication capability.
786/// A zero exit status signals success; any other termination signals failure.
787/// The client MUST NOT pass this method to `auth/login`.
788#[serde_as]
789#[skip_serializing_none]
790#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
791#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
792#[serde(rename_all = "camelCase")]
793#[non_exhaustive]
794pub struct AuthMethodTerminal {
795    /// Unique identifier for this authentication method.
796    pub method_id: AuthMethodId,
797    /// Human-readable name of the authentication method.
798    pub name: String,
799    /// Optional description providing more details about this authentication method.
800    #[serde_as(deserialize_as = "DefaultOnError")]
801    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
802    #[serde(default)]
803    pub description: Option<String>,
804    /// Additional arguments to append to the configured agent invocation for terminal auth.
805    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
806    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
807    #[serde(default, skip_serializing_if = "Vec::is_empty")]
808    pub args: Vec<String>,
809    /// Additional environment variables to set on the configured agent invocation for terminal auth.
810    /// Names MUST be unique. These values override same-named variables in the
811    /// base launch configuration.
812    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
813    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
814    #[serde(default, skip_serializing_if = "Vec::is_empty")]
815    pub env: Vec<EnvVariable>,
816    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
817    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
818    /// these keys.
819    ///
820    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
821    #[serde_as(deserialize_as = "DefaultOnError")]
822    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
823    #[serde(default)]
824    #[serde(rename = "_meta")]
825    pub meta: Option<Meta>,
826}
827
828impl AuthMethodTerminal {
829    /// Builds [`AuthMethodTerminal`] with the required fields set; optional fields start unset or empty.
830    #[must_use]
831    pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
832        Self {
833            method_id: method_id.into(),
834            name: name.into(),
835            description: None,
836            args: Vec::new(),
837            env: Vec::new(),
838            meta: None,
839        }
840    }
841
842    /// Additional arguments to append to the configured agent invocation for terminal auth.
843    #[must_use]
844    pub fn args(mut self, args: Vec<String>) -> Self {
845        self.args = args;
846        self
847    }
848
849    /// Additional environment variables to set on the configured agent invocation for terminal auth.
850    /// Names MUST be unique. These values override same-named variables in the
851    /// base launch configuration.
852    #[must_use]
853    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
854        self.env = env;
855        self
856    }
857
858    /// Optional description providing more details about this authentication method.
859    #[must_use]
860    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
861        self.description = description.into_option();
862        self
863    }
864
865    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
866    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
867    /// these keys.
868    ///
869    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
870    #[must_use]
871    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
872        self.meta = meta.into_option();
873        self
874    }
875}
876
877// New session
878
879/// Request parameters for creating a new session.
880///
881/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)
882#[serde_as]
883#[skip_serializing_none]
884#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
885#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
886#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
887#[serde(rename_all = "camelCase")]
888#[non_exhaustive]
889pub struct NewSessionRequest {
890    /// The working directory for this session. Must be an absolute path.
891    pub cwd: AbsolutePath,
892    /// Additional workspace roots for this session. Each path must be absolute.
893    ///
894    /// These expand the session's workspace scope without changing `cwd`, which
895    /// remains the base for relative paths. When omitted or empty, no
896    /// additional roots are activated for the new session.
897    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
898    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
899    #[serde(default, skip_serializing_if = "Vec::is_empty")]
900    pub additional_directories: Vec<AbsolutePath>,
901    /// List of MCP (Model Context Protocol) servers the agent should connect to.
902    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
903    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
904    #[serde(default, skip_serializing_if = "Vec::is_empty")]
905    pub mcp_servers: Vec<McpServer>,
906    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
907    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
908    /// these keys.
909    ///
910    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
911    #[serde_as(deserialize_as = "DefaultOnError")]
912    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
913    #[serde(default)]
914    #[serde(rename = "_meta")]
915    pub meta: Option<Meta>,
916}
917
918impl NewSessionRequest {
919    /// Builds [`NewSessionRequest`] with the required request fields set; optional fields start unset or empty.
920    #[must_use]
921    pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
922        Self {
923            cwd: cwd.into(),
924            additional_directories: vec![],
925            mcp_servers: vec![],
926            meta: None,
927        }
928    }
929
930    /// Additional workspace roots for this session. Each path must be absolute.
931    #[must_use]
932    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
933    where
934        I: IntoIterator<Item = P>,
935        P: Into<AbsolutePath>,
936    {
937        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
938        self
939    }
940
941    /// List of MCP (Model Context Protocol) servers the agent should connect to.
942    #[must_use]
943    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
944        self.mcp_servers = mcp_servers;
945        self
946    }
947
948    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
949    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
950    /// these keys.
951    ///
952    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
953    #[must_use]
954    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
955        self.meta = meta.into_option();
956        self
957    }
958}
959
960/// Response from creating a new session.
961///
962/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)
963#[serde_as]
964#[skip_serializing_none]
965#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
966#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
967#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
968#[serde(rename_all = "camelCase")]
969#[non_exhaustive]
970pub struct NewSessionResponse {
971    /// Unique identifier for the created session.
972    ///
973    /// Used in all subsequent requests for this conversation.
974    pub session_id: SessionId,
975    /// Initial session configuration options.
976    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
977    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
978    #[serde(default, skip_serializing_if = "Vec::is_empty")]
979    pub config_options: Vec<SessionConfigOption>,
980    /// Initial commands the agent can execute in this session.
981    ///
982    /// Optional. Omitted or empty means no initial commands are advertised.
983    /// Senders MUST use an array, not `null`; receivers treat `null` like omission.
984    /// Later `available_commands_update` notifications replace this list.
985    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
986    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
987    #[serde(default, skip_serializing_if = "Vec::is_empty")]
988    pub available_commands: Vec<AvailableCommand>,
989    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
990    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
991    /// these keys.
992    ///
993    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
994    #[serde_as(deserialize_as = "DefaultOnError")]
995    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
996    #[serde(default)]
997    #[serde(rename = "_meta")]
998    pub meta: Option<Meta>,
999}
1000
1001impl NewSessionResponse {
1002    /// Builds [`NewSessionResponse`] with the required response fields set; optional fields start unset or empty.
1003    #[must_use]
1004    pub fn new(session_id: impl Into<SessionId>) -> Self {
1005        Self {
1006            session_id: session_id.into(),
1007            config_options: Vec::new(),
1008            available_commands: Vec::new(),
1009            meta: None,
1010        }
1011    }
1012
1013    /// Initial session configuration options.
1014    #[must_use]
1015    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1016        self.config_options = config_options;
1017        self
1018    }
1019
1020    /// Initial commands the agent can execute in this session.
1021    #[must_use]
1022    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1023        self.available_commands = available_commands;
1024        self
1025    }
1026
1027    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1028    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1029    /// these keys.
1030    ///
1031    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1032    #[must_use]
1033    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1034        self.meta = meta.into_option();
1035        self
1036    }
1037}
1038
1039// Fork session
1040
1041/// **UNSTABLE**
1042///
1043/// This capability is not part of the spec yet, and may be removed or changed at any point.
1044///
1045/// Request parameters for forking an existing session.
1046///
1047/// Creates a new session based on the context of an existing one, allowing
1048/// operations like generating summaries without affecting the original session's history.
1049///
1050/// Only available if the Agent supports the `session.fork` capability.
1051#[cfg(feature = "unstable_session_fork")]
1052#[serde_as]
1053#[skip_serializing_none]
1054#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1055#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1056#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1057#[serde(rename_all = "camelCase")]
1058#[non_exhaustive]
1059pub struct ForkSessionRequest {
1060    /// The ID of the session to fork.
1061    pub session_id: SessionId,
1062    /// The working directory for this session. Must be an absolute path.
1063    pub cwd: AbsolutePath,
1064    /// Additional workspace roots to activate for this session. Each path must be absolute.
1065    ///
1066    /// When omitted or empty, no additional roots are activated. When non-empty,
1067    /// this is the complete resulting additional-root list for the forked
1068    /// session.
1069    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1070    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1071    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1072    pub additional_directories: Vec<AbsolutePath>,
1073    /// List of MCP servers to connect to for this session.
1074    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1075    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1076    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1077    pub mcp_servers: Vec<McpServer>,
1078    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1079    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1080    /// these keys.
1081    ///
1082    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1083    #[serde_as(deserialize_as = "DefaultOnError")]
1084    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1085    #[serde(default)]
1086    #[serde(rename = "_meta")]
1087    pub meta: Option<Meta>,
1088}
1089
1090#[cfg(feature = "unstable_session_fork")]
1091impl ForkSessionRequest {
1092    /// Builds [`ForkSessionRequest`] with the required request fields set; optional fields start unset or empty.
1093    #[must_use]
1094    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1095        Self {
1096            session_id: session_id.into(),
1097            cwd: cwd.into(),
1098            additional_directories: vec![],
1099            mcp_servers: vec![],
1100            meta: None,
1101        }
1102    }
1103
1104    /// Additional workspace roots to activate for this session. Each path must be absolute.
1105    #[must_use]
1106    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1107    where
1108        I: IntoIterator<Item = P>,
1109        P: Into<AbsolutePath>,
1110    {
1111        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1112        self
1113    }
1114
1115    /// List of MCP servers to connect to for this session.
1116    #[must_use]
1117    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1118        self.mcp_servers = mcp_servers;
1119        self
1120    }
1121
1122    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1123    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1124    /// these keys.
1125    ///
1126    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1127    #[must_use]
1128    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1129        self.meta = meta.into_option();
1130        self
1131    }
1132}
1133
1134/// **UNSTABLE**
1135///
1136/// This capability is not part of the spec yet, and may be removed or changed at any point.
1137///
1138/// Response from forking an existing session.
1139#[cfg(feature = "unstable_session_fork")]
1140#[serde_as]
1141#[skip_serializing_none]
1142#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1144#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1145#[serde(rename_all = "camelCase")]
1146#[non_exhaustive]
1147pub struct ForkSessionResponse {
1148    /// Unique identifier for the newly created forked session.
1149    pub session_id: SessionId,
1150    /// Initial session configuration options.
1151    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1152    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1153    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1154    pub config_options: Vec<SessionConfigOption>,
1155    /// Initial commands the agent can execute in this session.
1156    ///
1157    /// Optional. Omitted or empty means no initial commands are advertised.
1158    /// Senders MUST use an array, not `null`; receivers treat `null` like omission.
1159    /// Later `available_commands_update` notifications replace this list.
1160    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1161    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1162    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1163    pub available_commands: Vec<AvailableCommand>,
1164    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1165    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1166    /// these keys.
1167    ///
1168    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1169    #[serde_as(deserialize_as = "DefaultOnError")]
1170    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1171    #[serde(default)]
1172    #[serde(rename = "_meta")]
1173    pub meta: Option<Meta>,
1174}
1175
1176#[cfg(feature = "unstable_session_fork")]
1177impl ForkSessionResponse {
1178    /// Builds [`ForkSessionResponse`] with the required response fields set; optional fields start unset or empty.
1179    #[must_use]
1180    pub fn new(session_id: impl Into<SessionId>) -> Self {
1181        Self {
1182            session_id: session_id.into(),
1183            config_options: Vec::new(),
1184            available_commands: Vec::new(),
1185            meta: None,
1186        }
1187    }
1188
1189    /// Initial session configuration options.
1190    #[must_use]
1191    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1192        self.config_options = config_options;
1193        self
1194    }
1195
1196    /// Initial commands the agent can execute in this session.
1197    #[must_use]
1198    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1199        self.available_commands = available_commands;
1200        self
1201    }
1202
1203    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1204    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1205    /// these keys.
1206    ///
1207    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1208    #[must_use]
1209    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1210        self.meta = meta.into_option();
1211        self
1212    }
1213}
1214
1215// Resume session
1216
1217/// Request parameters for resuming an existing session.
1218///
1219/// Resumes an existing session and optionally replays retained conversation
1220/// history according to `replayFrom`.
1221#[serde_as]
1222#[skip_serializing_none]
1223#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1225#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1226#[serde(rename_all = "camelCase")]
1227#[non_exhaustive]
1228pub struct ResumeSessionRequest {
1229    /// The ID of the session to resume.
1230    pub session_id: SessionId,
1231    /// The working directory for this session. Must be an absolute path.
1232    pub cwd: AbsolutePath,
1233    /// Additional workspace roots to activate for this session. Each path must be absolute.
1234    ///
1235    /// When omitted or empty, no additional roots are activated. When non-empty,
1236    /// this is the complete resulting additional-root list for the resumed
1237    /// session. It may differ from any previously used or reported list as long as
1238    /// the request `cwd` matches the session's `cwd`.
1239    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1240    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1241    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1242    pub additional_directories: Vec<AbsolutePath>,
1243    /// List of MCP servers to connect to for this session.
1244    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1245    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1246    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1247    pub mcp_servers: Vec<McpServer>,
1248    /// Inclusive cursor describing where conversation replay should begin.
1249    ///
1250    /// Optional. Omitted or `null` both mean the Agent should resume without
1251    /// replaying previous conversation history. Replay cursors are inclusive:
1252    /// replay includes the position identified by the cursor. Supplying
1253    /// `{ "type": "start" }` means the Agent should replay all retained
1254    /// conversation history before responding.
1255    #[serde_as(deserialize_as = "DefaultOnError")]
1256    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1257    #[serde(default)]
1258    pub replay_from: Option<ReplayFrom>,
1259    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1260    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1261    /// these keys.
1262    ///
1263    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1264    #[serde_as(deserialize_as = "DefaultOnError")]
1265    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1266    #[serde(default)]
1267    #[serde(rename = "_meta")]
1268    pub meta: Option<Meta>,
1269}
1270
1271impl ResumeSessionRequest {
1272    /// Builds [`ResumeSessionRequest`] with the required request fields set; optional fields start unset or empty.
1273    #[must_use]
1274    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1275        Self {
1276            session_id: session_id.into(),
1277            cwd: cwd.into(),
1278            additional_directories: vec![],
1279            mcp_servers: vec![],
1280            replay_from: None,
1281            meta: None,
1282        }
1283    }
1284
1285    /// Additional workspace roots to activate for this session. Each path must be absolute.
1286    #[must_use]
1287    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1288    where
1289        I: IntoIterator<Item = P>,
1290        P: Into<AbsolutePath>,
1291    {
1292        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1293        self
1294    }
1295
1296    /// List of MCP servers to connect to for this session.
1297    #[must_use]
1298    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1299        self.mcp_servers = mcp_servers;
1300        self
1301    }
1302
1303    /// Inclusive cursor describing where conversation replay should begin.
1304    ///
1305    /// Omitted or `null` both mean the Agent should resume without replaying
1306    /// previous conversation history. Replay cursors are inclusive: replay
1307    /// includes the position identified by the cursor. Supplying
1308    /// `{ "type": "start" }` means the Agent should replay all retained
1309    /// conversation history before responding.
1310    #[must_use]
1311    pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1312        self.replay_from = replay_from.into_option();
1313        self
1314    }
1315
1316    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1317    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1318    /// these keys.
1319    ///
1320    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1321    #[must_use]
1322    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1323        self.meta = meta.into_option();
1324        self
1325    }
1326}
1327
1328/// Inclusive cursor describing where replayed session history should begin.
1329///
1330/// Replay includes the position identified by the cursor.
1331#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1332#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1333#[serde(tag = "type", rename_all = "snake_case")]
1334#[non_exhaustive]
1335pub enum ReplayFrom {
1336    /// Replay all retained conversation history from its first replayable entry.
1337    Start(ReplayFromStart),
1338    /// Custom or future replay cursor.
1339    ///
1340    /// Values beginning with `_` are reserved for implementation-specific
1341    /// extensions. Unknown values that do not begin with `_` are reserved for
1342    /// future ACP variants.
1343    ///
1344    /// Receivers that do not understand this cursor should preserve the raw
1345    /// payload when storing, replaying, proxying, or forwarding requests, and
1346    /// otherwise reject the request rather than guessing where to replay from.
1347    #[serde(untagged)]
1348    Other(OtherReplayFrom),
1349}
1350
1351impl From<ReplayFromStart> for ReplayFrom {
1352    fn from(replay_from: ReplayFromStart) -> Self {
1353        Self::Start(replay_from)
1354    }
1355}
1356
1357/// Inclusive replay cursor requesting replay from the start of retained conversation history.
1358#[serde_as]
1359#[skip_serializing_none]
1360#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1361#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1362#[serde(rename_all = "camelCase")]
1363#[non_exhaustive]
1364pub struct ReplayFromStart {
1365    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1366    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1367    /// these keys.
1368    ///
1369    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1370    #[serde_as(deserialize_as = "DefaultOnError")]
1371    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1372    #[serde(default)]
1373    #[serde(rename = "_meta")]
1374    pub meta: Option<Meta>,
1375}
1376
1377impl ReplayFromStart {
1378    /// Builds [`ReplayFromStart`].
1379    #[must_use]
1380    pub fn new() -> Self {
1381        Self::default()
1382    }
1383
1384    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1385    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1386    /// these keys.
1387    ///
1388    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1389    #[must_use]
1390    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1391        self.meta = meta.into_option();
1392        self
1393    }
1394}
1395
1396/// Custom or future replay cursor payload.
1397#[serde_as]
1398#[skip_serializing_none]
1399#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1400#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1401#[cfg_attr(feature = "schemars", schemars(inline))]
1402#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1403#[serde(rename_all = "camelCase")]
1404#[non_exhaustive]
1405pub struct OtherReplayFrom {
1406    /// Custom or future replay cursor type.
1407    ///
1408    /// Values beginning with `_` are reserved for implementation-specific
1409    /// extensions. Unknown values that do not begin with `_` are reserved for
1410    /// future ACP variants.
1411    #[serde(rename = "type")]
1412    pub type_: String,
1413    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1414    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1415    /// these keys.
1416    ///
1417    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1418    #[serde_as(deserialize_as = "DefaultOnError")]
1419    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1420    #[serde(default)]
1421    #[serde(rename = "_meta")]
1422    pub meta: Option<Meta>,
1423    /// Additional fields from the unknown replay cursor payload.
1424    #[serde(flatten)]
1425    pub fields: BTreeMap<String, serde_json::Value>,
1426}
1427
1428impl OtherReplayFrom {
1429    /// Builds [`OtherReplayFrom`] from an unknown discriminator and preserves the remaining extension fields.
1430    #[must_use]
1431    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1432        fields.remove("type");
1433        fields.remove("_meta");
1434        Self {
1435            type_: type_.into(),
1436            meta: None,
1437            fields,
1438        }
1439    }
1440
1441    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1442    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1443    /// these keys.
1444    ///
1445    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1446    #[must_use]
1447    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1448        self.meta = meta.into_option();
1449        self
1450    }
1451}
1452
1453impl<'de> Deserialize<'de> for OtherReplayFrom {
1454    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1455    where
1456        D: serde::Deserializer<'de>,
1457    {
1458        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1459        let type_ = fields
1460            .remove("type")
1461            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1462        let serde_json::Value::String(type_) = type_ else {
1463            return Err(serde::de::Error::custom("`type` must be a string"));
1464        };
1465
1466        if is_known_replay_from_type(&type_) {
1467            return Err(serde::de::Error::custom(format!(
1468                "known replay cursor `{type_}` did not match its schema"
1469            )));
1470        }
1471
1472        let meta = fields
1473            .remove("_meta")
1474            .and_then(|value| serde_json::from_value(value).ok());
1475
1476        Ok(Self {
1477            type_,
1478            meta,
1479            fields,
1480        })
1481    }
1482}
1483
1484fn is_known_replay_from_type(type_: &str) -> bool {
1485    matches!(type_, "start")
1486}
1487
1488#[cfg(feature = "schemars")]
1489fn other_replay_from_schema(schema: &mut Schema) {
1490    super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1491}
1492
1493crate::serde_util::default_on_null! {
1494    /// Response from resuming an existing session.
1495    #[serde_as]
1496    #[skip_serializing_none]
1497    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1498    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1499    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1500    #[serde(rename_all = "camelCase")]
1501    #[non_exhaustive]
1502    pub struct ResumeSessionResponse {
1503        /// Initial session configuration options.
1504        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1505        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1506        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1507        pub config_options: Vec<SessionConfigOption>,
1508        /// Initial commands the agent can execute in this session.
1509        ///
1510        /// Optional. Omitted or empty means no initial commands are advertised.
1511        /// Senders MUST use an array, not `null`; receivers treat `null` like omission.
1512        /// Later `available_commands_update` notifications replace this list.
1513        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1514        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1515        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1516        pub available_commands: Vec<AvailableCommand>,
1517        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1518        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1519        /// these keys.
1520        ///
1521        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1522        #[serde_as(deserialize_as = "DefaultOnError")]
1523        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1524        #[serde(default)]
1525        #[serde(rename = "_meta")]
1526        pub meta: Option<Meta>,
1527    }
1528}
1529
1530impl ResumeSessionResponse {
1531    /// Builds [`ResumeSessionResponse`] with the required response fields set; optional fields start unset or empty.
1532    #[must_use]
1533    pub fn new() -> Self {
1534        Self::default()
1535    }
1536
1537    /// Initial session configuration options.
1538    #[must_use]
1539    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1540        self.config_options = config_options;
1541        self
1542    }
1543
1544    /// Initial commands the agent can execute in this session.
1545    #[must_use]
1546    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1547        self.available_commands = available_commands;
1548        self
1549    }
1550
1551    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1552    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1553    /// these keys.
1554    ///
1555    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1556    #[must_use]
1557    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1558        self.meta = meta.into_option();
1559        self
1560    }
1561}
1562
1563// Close session
1564
1565/// Request parameters for closing an active session.
1566///
1567/// The agent **must** cancel any ongoing work related to the session (treat it
1568/// as if `session/cancel` was called) and then free up any resources associated
1569/// with the session.
1570#[serde_as]
1571#[skip_serializing_none]
1572#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1573#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1574#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1575#[serde(rename_all = "camelCase")]
1576#[non_exhaustive]
1577pub struct CloseSessionRequest {
1578    /// The ID of the session to close.
1579    pub session_id: SessionId,
1580    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1581    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1582    /// these keys.
1583    ///
1584    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1585    #[serde_as(deserialize_as = "DefaultOnError")]
1586    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1587    #[serde(default)]
1588    #[serde(rename = "_meta")]
1589    pub meta: Option<Meta>,
1590}
1591
1592impl CloseSessionRequest {
1593    /// Builds [`CloseSessionRequest`] with the required request fields set; optional fields start unset or empty.
1594    #[must_use]
1595    pub fn new(session_id: impl Into<SessionId>) -> Self {
1596        Self {
1597            session_id: session_id.into(),
1598            meta: None,
1599        }
1600    }
1601
1602    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1603    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1604    /// these keys.
1605    ///
1606    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1607    #[must_use]
1608    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1609        self.meta = meta.into_option();
1610        self
1611    }
1612}
1613
1614crate::serde_util::default_on_null! {
1615    /// Response from closing a session.
1616    #[serde_as]
1617    #[skip_serializing_none]
1618    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1619    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1620    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
1621    #[serde(rename_all = "camelCase")]
1622    #[non_exhaustive]
1623    pub struct CloseSessionResponse {
1624        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1625        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1626        /// these keys.
1627        ///
1628        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1629        #[serde_as(deserialize_as = "DefaultOnError")]
1630        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1631        #[serde(default)]
1632        #[serde(rename = "_meta")]
1633        pub meta: Option<Meta>,
1634    }
1635}
1636
1637impl CloseSessionResponse {
1638    /// Builds [`CloseSessionResponse`] with the required response fields set; optional fields start unset or empty.
1639    #[must_use]
1640    pub fn new() -> Self {
1641        Self::default()
1642    }
1643
1644    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1645    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1646    /// these keys.
1647    ///
1648    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1649    #[must_use]
1650    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1651        self.meta = meta.into_option();
1652        self
1653    }
1654}
1655
1656// List sessions
1657
1658/// An opaque cursor used to paginate `session/list` results.
1659#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1660#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
1661#[serde(transparent)]
1662#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
1663#[non_exhaustive]
1664pub struct SessionListCursor(pub Arc<str>);
1665
1666impl SessionListCursor {
1667    /// Wraps a protocol string as a typed [`SessionListCursor`].
1668    #[must_use]
1669    pub fn new(cursor: impl Into<Self>) -> Self {
1670        cursor.into()
1671    }
1672}
1673
1674impl AsRef<str> for SessionListCursor {
1675    fn as_ref(&self) -> &str {
1676        &self.0
1677    }
1678}
1679
1680impl From<&String> for SessionListCursor {
1681    fn from(cursor: &String) -> Self {
1682        Self(cursor.as_str().into())
1683    }
1684}
1685
1686macro_rules! impl_session_list_cursor_option_conversion {
1687    ($source:ty) => {
1688        impl IntoOption<SessionListCursor> for $source {
1689            fn into_option(self) -> Option<SessionListCursor> {
1690                Some(self.into())
1691            }
1692        }
1693    };
1694}
1695
1696impl_session_list_cursor_option_conversion!(Arc<str>);
1697impl_session_list_cursor_option_conversion!(String);
1698impl_session_list_cursor_option_conversion!(&str);
1699impl_session_list_cursor_option_conversion!(&mut str);
1700impl_session_list_cursor_option_conversion!(&String);
1701impl_session_list_cursor_option_conversion!(Box<str>);
1702impl_session_list_cursor_option_conversion!(Cow<'_, str>);
1703
1704crate::serde_util::default_on_null! {
1705    /// Request parameters for listing existing sessions.
1706    #[serde_as]
1707    #[skip_serializing_none]
1708    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1709    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1710    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1711    #[serde(rename_all = "camelCase")]
1712    #[non_exhaustive]
1713    pub struct ListSessionsRequest {
1714        /// Filter sessions by working directory. Must be an absolute path.
1715        #[serde(default)]
1716        pub cwd: Option<AbsolutePath>,
1717        /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
1718        #[serde(default)]
1719        pub cursor: Option<SessionListCursor>,
1720        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1721        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1722        /// these keys.
1723        ///
1724        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1725        #[serde_as(deserialize_as = "DefaultOnError")]
1726        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1727        #[serde(default)]
1728        #[serde(rename = "_meta")]
1729        pub meta: Option<Meta>,
1730    }
1731}
1732
1733impl ListSessionsRequest {
1734    /// Builds [`ListSessionsRequest`] with the required request fields set; optional fields start unset or empty.
1735    #[must_use]
1736    pub fn new() -> Self {
1737        Self::default()
1738    }
1739
1740    /// Filter sessions by working directory. Must be an absolute path.
1741    #[must_use]
1742    pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
1743        self.cwd = cwd.into_option();
1744        self
1745    }
1746
1747    /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
1748    #[must_use]
1749    pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
1750        self.cursor = cursor.into_option();
1751        self
1752    }
1753
1754    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1755    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1756    /// these keys.
1757    ///
1758    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1759    #[must_use]
1760    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1761        self.meta = meta.into_option();
1762        self
1763    }
1764}
1765
1766/// Response from listing sessions.
1767#[serde_as]
1768#[skip_serializing_none]
1769#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1770#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1771#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
1772#[serde(rename_all = "camelCase")]
1773#[non_exhaustive]
1774pub struct ListSessionsResponse {
1775    /// Array of session information objects.
1776    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1777    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1778    pub sessions: Vec<SessionInfo>,
1779    /// Opaque cursor token. If present, pass this in the next request's cursor parameter
1780    /// to fetch the next page. If absent, there are no more results.
1781    #[serde_as(deserialize_as = "DefaultOnError")]
1782    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1783    #[serde(default)]
1784    pub next_cursor: Option<SessionListCursor>,
1785    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1786    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1787    /// these keys.
1788    ///
1789    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1790    #[serde_as(deserialize_as = "DefaultOnError")]
1791    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1792    #[serde(default)]
1793    #[serde(rename = "_meta")]
1794    pub meta: Option<Meta>,
1795}
1796
1797impl ListSessionsResponse {
1798    /// Builds [`ListSessionsResponse`] with the required response fields set; optional fields start unset or empty.
1799    #[must_use]
1800    pub fn new(sessions: Vec<SessionInfo>) -> Self {
1801        Self {
1802            sessions,
1803            next_cursor: None,
1804            meta: None,
1805        }
1806    }
1807
1808    /// Sets or clears the optional `nextCursor` field.
1809    #[must_use]
1810    pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
1811        self.next_cursor = next_cursor.into_option();
1812        self
1813    }
1814
1815    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1816    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1817    /// these keys.
1818    ///
1819    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1820    #[must_use]
1821    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1822        self.meta = meta.into_option();
1823        self
1824    }
1825}
1826
1827// Delete session
1828
1829/// Request parameters for deleting an existing session from `session/list`.
1830///
1831/// Only available if the Agent supports the `session.delete` capability.
1832#[serde_as]
1833#[skip_serializing_none]
1834#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1835#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1836#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1837#[serde(rename_all = "camelCase")]
1838#[non_exhaustive]
1839pub struct DeleteSessionRequest {
1840    /// The ID of the session to delete.
1841    pub session_id: SessionId,
1842    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1843    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1844    /// these keys.
1845    ///
1846    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1847    #[serde_as(deserialize_as = "DefaultOnError")]
1848    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1849    #[serde(default)]
1850    #[serde(rename = "_meta")]
1851    pub meta: Option<Meta>,
1852}
1853
1854impl DeleteSessionRequest {
1855    /// Builds [`DeleteSessionRequest`] with the required request fields set; optional fields start unset or empty.
1856    #[must_use]
1857    pub fn new(session_id: impl Into<SessionId>) -> Self {
1858        Self {
1859            session_id: session_id.into(),
1860            meta: None,
1861        }
1862    }
1863
1864    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1865    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1866    /// these keys.
1867    ///
1868    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1869    #[must_use]
1870    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1871        self.meta = meta.into_option();
1872        self
1873    }
1874}
1875
1876crate::serde_util::default_on_null! {
1877    /// Response from deleting a session.
1878    #[serde_as]
1879    #[skip_serializing_none]
1880    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1881    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1882    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
1883    #[serde(rename_all = "camelCase")]
1884    #[non_exhaustive]
1885    pub struct DeleteSessionResponse {
1886        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1887        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1888        /// these keys.
1889        ///
1890        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1891        #[serde_as(deserialize_as = "DefaultOnError")]
1892        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1893        #[serde(default)]
1894        #[serde(rename = "_meta")]
1895        pub meta: Option<Meta>,
1896    }
1897}
1898
1899impl DeleteSessionResponse {
1900    /// Builds [`DeleteSessionResponse`] with the required response fields set; optional fields start unset or empty.
1901    #[must_use]
1902    pub fn new() -> Self {
1903        Self::default()
1904    }
1905
1906    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1907    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1908    /// these keys.
1909    ///
1910    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1911    #[must_use]
1912    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1913        self.meta = meta.into_option();
1914        self
1915    }
1916}
1917
1918/// Information about a session returned by session/list
1919#[serde_as]
1920#[skip_serializing_none]
1921#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1922#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1923#[serde(rename_all = "camelCase")]
1924#[non_exhaustive]
1925pub struct SessionInfo {
1926    /// Unique identifier for the session
1927    pub session_id: SessionId,
1928    /// The working directory for this session. Must be an absolute path.
1929    pub cwd: AbsolutePath,
1930    /// Additional workspace roots reported for this session. Each path must be absolute.
1931    ///
1932    /// When present, this is the complete ordered additional-root list reported
1933    /// by the Agent. Omitted and empty values are equivalent: the response
1934    /// reports no additional roots.
1935    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1936    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1937    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1938    pub additional_directories: Vec<AbsolutePath>,
1939
1940    /// Human-readable title for the session
1941    #[serde_as(deserialize_as = "DefaultOnError")]
1942    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1943    #[serde(default)]
1944    pub title: Option<String>,
1945    /// RFC 3339 timestamp of last activity.
1946    #[serde_as(deserialize_as = "DefaultOnError")]
1947    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
1948    #[serde(default)]
1949    pub updated_at: Option<String>,
1950    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1951    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1952    /// these keys.
1953    ///
1954    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1955    #[serde_as(deserialize_as = "DefaultOnError")]
1956    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1957    #[serde(default)]
1958    #[serde(rename = "_meta")]
1959    pub meta: Option<Meta>,
1960}
1961
1962impl SessionInfo {
1963    /// Builds [`SessionInfo`] with the required fields set; optional fields start unset or empty.
1964    #[must_use]
1965    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1966        Self {
1967            session_id: session_id.into(),
1968            cwd: cwd.into(),
1969            additional_directories: vec![],
1970            title: None,
1971            updated_at: None,
1972            meta: None,
1973        }
1974    }
1975
1976    /// Additional workspace roots reported for this session. Each path must be absolute.
1977    #[must_use]
1978    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1979    where
1980        I: IntoIterator<Item = P>,
1981        P: Into<AbsolutePath>,
1982    {
1983        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1984        self
1985    }
1986
1987    /// Human-readable title for the session
1988    #[must_use]
1989    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
1990        self.title = title.into_option();
1991        self
1992    }
1993
1994    /// RFC 3339 timestamp of last activity.
1995    #[must_use]
1996    pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
1997        self.updated_at = updated_at.into_option();
1998        self
1999    }
2000
2001    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2002    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2003    /// these keys.
2004    ///
2005    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2006    #[must_use]
2007    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2008        self.meta = meta.into_option();
2009        self
2010    }
2011}
2012
2013// Session config options
2014
2015/// Unique identifier for a session configuration option.
2016#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2017#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2018#[serde(transparent)]
2019#[from(forward)]
2020#[non_exhaustive]
2021pub struct SessionConfigId(pub Arc<str>);
2022
2023impl SessionConfigId {
2024    /// Wraps a protocol string as a typed [`SessionConfigId`].
2025    #[must_use]
2026    pub fn new(id: impl Into<Self>) -> Self {
2027        id.into()
2028    }
2029}
2030
2031/// Unique identifier for a session configuration option value.
2032#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2033#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2034#[serde(transparent)]
2035#[from(forward)]
2036#[non_exhaustive]
2037pub struct SessionConfigValueId(pub Arc<str>);
2038
2039impl SessionConfigValueId {
2040    /// Wraps a protocol string as a typed [`SessionConfigValueId`].
2041    #[must_use]
2042    pub fn new(id: impl Into<Self>) -> Self {
2043        id.into()
2044    }
2045}
2046
2047/// Unique identifier for a session configuration option value group.
2048#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2049#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
2050#[serde(transparent)]
2051#[from(forward)]
2052#[non_exhaustive]
2053pub struct SessionConfigGroupId(pub Arc<str>);
2054
2055impl SessionConfigGroupId {
2056    /// Wraps a protocol string as a typed [`SessionConfigGroupId`].
2057    #[must_use]
2058    pub fn new(id: impl Into<Self>) -> Self {
2059        id.into()
2060    }
2061}
2062
2063/// A possible value for a session configuration option.
2064#[serde_as]
2065#[skip_serializing_none]
2066#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2067#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2068#[serde(rename_all = "camelCase")]
2069#[non_exhaustive]
2070pub struct SessionConfigSelectOption {
2071    /// Unique identifier for this option value.
2072    pub value: SessionConfigValueId,
2073    /// Human-readable label for this option value.
2074    pub name: String,
2075    /// Optional description for this option value.
2076    #[serde_as(deserialize_as = "DefaultOnError")]
2077    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2078    #[serde(default)]
2079    pub description: Option<String>,
2080    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2081    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2082    /// these keys.
2083    ///
2084    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2085    #[serde_as(deserialize_as = "DefaultOnError")]
2086    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2087    #[serde(default)]
2088    #[serde(rename = "_meta")]
2089    pub meta: Option<Meta>,
2090}
2091
2092impl SessionConfigSelectOption {
2093    /// Builds [`SessionConfigSelectOption`] with the required fields set; optional fields start unset or empty.
2094    #[must_use]
2095    pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
2096        Self {
2097            value: value.into(),
2098            name: name.into(),
2099            description: None,
2100            meta: None,
2101        }
2102    }
2103
2104    /// Sets or clears the optional `description` field.
2105    #[must_use]
2106    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2107        self.description = description.into_option();
2108        self
2109    }
2110
2111    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2112    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2113    /// these keys.
2114    ///
2115    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2116    #[must_use]
2117    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2118        self.meta = meta.into_option();
2119        self
2120    }
2121}
2122
2123/// A group of possible values for a session configuration option.
2124#[serde_as]
2125#[skip_serializing_none]
2126#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2127#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2128#[serde(rename_all = "camelCase")]
2129#[non_exhaustive]
2130pub struct SessionConfigSelectGroup {
2131    /// Unique identifier for this group.
2132    pub group_id: SessionConfigGroupId,
2133    /// Human-readable label for this group.
2134    pub name: String,
2135    /// The set of option values in this group.
2136    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2137    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2138    pub options: Vec<SessionConfigSelectOption>,
2139    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2140    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2141    /// these keys.
2142    ///
2143    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2144    #[serde_as(deserialize_as = "DefaultOnError")]
2145    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2146    #[serde(default)]
2147    #[serde(rename = "_meta")]
2148    pub meta: Option<Meta>,
2149}
2150
2151impl SessionConfigSelectGroup {
2152    /// Builds [`SessionConfigSelectGroup`] with the required fields set; optional fields start unset or empty.
2153    #[must_use]
2154    pub fn new(
2155        group_id: impl Into<SessionConfigGroupId>,
2156        name: impl Into<String>,
2157        options: Vec<SessionConfigSelectOption>,
2158    ) -> Self {
2159        Self {
2160            group_id: group_id.into(),
2161            name: name.into(),
2162            options,
2163            meta: None,
2164        }
2165    }
2166
2167    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2168    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2169    /// these keys.
2170    ///
2171    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2172    #[must_use]
2173    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2174        self.meta = meta.into_option();
2175        self
2176    }
2177}
2178
2179/// Possible values for a session configuration option.
2180#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2182#[serde(untagged)]
2183#[non_exhaustive]
2184pub enum SessionConfigSelectOptions {
2185    /// A flat list of options with no grouping.
2186    Ungrouped(Vec<SessionConfigSelectOption>),
2187    /// A list of options grouped under headers.
2188    Grouped(Vec<SessionConfigSelectGroup>),
2189}
2190
2191impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
2192    fn from(options: Vec<SessionConfigSelectOption>) -> Self {
2193        SessionConfigSelectOptions::Ungrouped(options)
2194    }
2195}
2196
2197impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
2198    fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
2199        SessionConfigSelectOptions::Grouped(groups)
2200    }
2201}
2202
2203/// A single-value selector (dropdown) session configuration option payload.
2204#[skip_serializing_none]
2205#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2206#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2207#[serde(rename_all = "camelCase")]
2208#[non_exhaustive]
2209pub struct SessionConfigSelect {
2210    /// The currently selected value.
2211    pub current_value: SessionConfigValueId,
2212    /// The set of selectable options.
2213    pub options: SessionConfigSelectOptions,
2214}
2215
2216impl SessionConfigSelect {
2217    /// Builds [`SessionConfigSelect`] with the required fields set; optional fields start unset or empty.
2218    #[must_use]
2219    pub fn new(
2220        current_value: impl Into<SessionConfigValueId>,
2221        options: impl Into<SessionConfigSelectOptions>,
2222    ) -> Self {
2223        Self {
2224            current_value: current_value.into(),
2225            options: options.into(),
2226        }
2227    }
2228}
2229
2230/// A boolean on/off toggle session configuration option payload.
2231#[skip_serializing_none]
2232#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2234#[serde(rename_all = "camelCase")]
2235#[non_exhaustive]
2236pub struct SessionConfigBoolean {
2237    /// The current value of the boolean option.
2238    pub current_value: bool,
2239}
2240
2241impl SessionConfigBoolean {
2242    /// Builds [`SessionConfigBoolean`] with the required fields set; optional fields start unset or empty.
2243    #[must_use]
2244    pub fn new(current_value: bool) -> Self {
2245        Self { current_value }
2246    }
2247}
2248
2249/// Semantic category for a session configuration option.
2250///
2251/// This is intended to help Clients distinguish broadly common selectors (e.g. model selector vs
2252/// session mode selector vs thought/reasoning level) for UX purposes (keyboard shortcuts, icons,
2253/// placement). It MUST NOT be required for correctness. Clients MUST handle missing or unknown
2254/// categories gracefully.
2255///
2256/// Category names beginning with `_` are free for custom use, like other ACP extension methods.
2257/// Category names that do not begin with `_` are reserved for the ACP spec.
2258#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2259#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2260#[serde(rename_all = "snake_case")]
2261#[non_exhaustive]
2262pub enum SessionConfigOptionCategory {
2263    /// Session mode selector.
2264    Mode,
2265    /// Model selector.
2266    Model,
2267    /// Model-related configuration parameter.
2268    ModelConfig,
2269    /// Thought/reasoning level selector.
2270    ThoughtLevel,
2271    /// Custom or future category.
2272    ///
2273    /// Values beginning with `_` are reserved for implementation-specific
2274    /// extensions. Unknown values that do not begin with `_` are reserved for
2275    /// future ACP variants.
2276    #[serde(untagged)]
2277    Other(String),
2278}
2279
2280/// Type-specific session configuration option payload.
2281#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2282#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2283#[serde(tag = "type", rename_all = "snake_case")]
2284#[non_exhaustive]
2285pub enum SessionConfigKind {
2286    /// Single-value selector (dropdown).
2287    Select(SessionConfigSelect),
2288    /// Boolean on/off toggle.
2289    Boolean(SessionConfigBoolean),
2290    /// Custom or future session configuration option payload.
2291    ///
2292    /// Values beginning with `_` are reserved for implementation-specific
2293    /// extensions. Unknown values that do not begin with `_` are reserved for
2294    /// future ACP variants.
2295    ///
2296    /// Clients that do not understand this option type should preserve the raw
2297    /// payload when storing, replaying, proxying, or forwarding configuration
2298    /// data, and otherwise ignore the option or display it generically.
2299    #[serde(untagged)]
2300    Other(OtherSessionConfigKind),
2301}
2302
2303/// Custom or future session configuration option payload.
2304#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2305#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2306#[cfg_attr(feature = "schemars", schemars(inline))]
2307#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
2308#[serde(rename_all = "camelCase")]
2309#[non_exhaustive]
2310pub struct OtherSessionConfigKind {
2311    /// Custom or future session configuration option type.
2312    ///
2313    /// Values beginning with `_` are reserved for implementation-specific
2314    /// extensions. Unknown values that do not begin with `_` are reserved for
2315    /// future ACP variants.
2316    #[serde(rename = "type")]
2317    pub type_: String,
2318    /// Additional fields from the unknown session configuration option payload.
2319    #[serde(flatten)]
2320    pub fields: BTreeMap<String, serde_json::Value>,
2321}
2322
2323impl OtherSessionConfigKind {
2324    /// Builds [`OtherSessionConfigKind`] from an unknown discriminator and preserves the remaining extension fields.
2325    #[must_use]
2326    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2327        fields.remove("type");
2328        fields.remove("_meta");
2329        Self {
2330            type_: type_.into(),
2331            fields,
2332        }
2333    }
2334}
2335
2336impl<'de> Deserialize<'de> for OtherSessionConfigKind {
2337    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2338    where
2339        D: serde::Deserializer<'de>,
2340    {
2341        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2342        let type_ = fields
2343            .remove("type")
2344            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2345        let serde_json::Value::String(type_) = type_ else {
2346            return Err(serde::de::Error::custom("`type` must be a string"));
2347        };
2348
2349        if is_known_session_config_kind_type(&type_) {
2350            return Err(serde::de::Error::custom(format!(
2351                "known session configuration option `{type_}` did not match its schema"
2352            )));
2353        }
2354
2355        Ok(Self { type_, fields })
2356    }
2357}
2358
2359fn is_known_session_config_kind_type(type_: &str) -> bool {
2360    matches!(type_, "select" | "boolean")
2361}
2362
2363#[cfg(feature = "schemars")]
2364fn other_session_config_kind_schema(schema: &mut Schema) {
2365    super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
2366}
2367
2368/// A session configuration option selector and its current state.
2369#[serde_as]
2370#[skip_serializing_none]
2371#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2372#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2373#[serde(rename_all = "camelCase")]
2374#[non_exhaustive]
2375pub struct SessionConfigOption {
2376    /// Unique identifier for the configuration option.
2377    pub config_id: SessionConfigId,
2378    /// Human-readable label for the option.
2379    pub name: String,
2380    /// Optional description for the Client to display to the user.
2381    #[serde_as(deserialize_as = "DefaultOnError")]
2382    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2383    #[serde(default)]
2384    pub description: Option<String>,
2385    /// Optional semantic category for this option (UX only).
2386    #[serde_as(deserialize_as = "DefaultOnError")]
2387    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2388    #[serde(default)]
2389    pub category: Option<SessionConfigOptionCategory>,
2390    /// Type-specific fields for this configuration option.
2391    #[serde(flatten)]
2392    pub kind: SessionConfigKind,
2393    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2394    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2395    /// these keys.
2396    ///
2397    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2398    #[serde_as(deserialize_as = "DefaultOnError")]
2399    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2400    #[serde(default)]
2401    #[serde(rename = "_meta")]
2402    pub meta: Option<Meta>,
2403}
2404
2405impl SessionConfigOption {
2406    /// Builds [`SessionConfigOption`] with the required fields set; optional fields start unset or empty.
2407    #[must_use]
2408    pub fn new(
2409        config_id: impl Into<SessionConfigId>,
2410        name: impl Into<String>,
2411        kind: SessionConfigKind,
2412    ) -> Self {
2413        Self {
2414            config_id: config_id.into(),
2415            name: name.into(),
2416            description: None,
2417            category: None,
2418            kind,
2419            meta: None,
2420        }
2421    }
2422
2423    /// Builds a select-style session configuration option with its current value and choices.
2424    #[must_use]
2425    pub fn select(
2426        config_id: impl Into<SessionConfigId>,
2427        name: impl Into<String>,
2428        current_value: impl Into<SessionConfigValueId>,
2429        options: impl Into<SessionConfigSelectOptions>,
2430    ) -> Self {
2431        Self::new(
2432            config_id,
2433            name,
2434            SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
2435        )
2436    }
2437
2438    /// Builds a boolean-style session configuration option with its current value.
2439    #[must_use]
2440    pub fn boolean(
2441        config_id: impl Into<SessionConfigId>,
2442        name: impl Into<String>,
2443        current_value: bool,
2444    ) -> Self {
2445        Self::new(
2446            config_id,
2447            name,
2448            SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
2449        )
2450    }
2451
2452    /// Sets or clears the optional `description` field.
2453    #[must_use]
2454    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
2455        self.description = description.into_option();
2456        self
2457    }
2458
2459    /// Sets or clears the optional `category` field.
2460    #[must_use]
2461    pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
2462        self.category = category.into_option();
2463        self
2464    }
2465
2466    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2467    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2468    /// these keys.
2469    ///
2470    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2471    #[must_use]
2472    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2473        self.meta = meta.into_option();
2474        self
2475    }
2476}
2477
2478/// The value to set for a session configuration option.
2479///
2480/// The `type` field acts as the discriminator in the serialized JSON form.
2481///
2482/// The `type` discriminator describes the *shape* of the value, not the option
2483/// kind. For example every option kind that picks from a list of ids
2484/// (`select`, `radio`, …) would use [`Id`](Self::Id), while a future freeform
2485/// text option would get its own variant.
2486#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2487#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2488#[serde(tag = "type", rename_all = "snake_case")]
2489#[non_exhaustive]
2490pub enum SessionConfigOptionValue {
2491    /// A [`SessionConfigValueId`] string value (`type: "id"`).
2492    Id {
2493        /// The value ID.
2494        value: SessionConfigValueId,
2495    },
2496    /// A boolean value (`type: "boolean"`).
2497    Boolean {
2498        /// The boolean value.
2499        value: bool,
2500    },
2501    /// Custom or future session configuration option value payload.
2502    ///
2503    /// Values beginning with `_` are reserved for implementation-specific
2504    /// extensions. Unknown values that do not begin with `_` are reserved for
2505    /// future ACP variants.
2506    #[serde(untagged)]
2507    Other(OtherSessionConfigOptionValue),
2508}
2509
2510/// Custom or future session configuration option value payload.
2511#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2512#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2513#[cfg_attr(feature = "schemars", schemars(inline))]
2514#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
2515#[serde(rename_all = "camelCase")]
2516#[non_exhaustive]
2517pub struct OtherSessionConfigOptionValue {
2518    /// Custom or future session configuration option value type.
2519    ///
2520    /// Values beginning with `_` are reserved for implementation-specific
2521    /// extensions. Unknown values that do not begin with `_` are reserved for
2522    /// future ACP variants.
2523    #[serde(rename = "type")]
2524    pub type_: String,
2525    /// Raw value payload for the custom or future value type.
2526    pub value: serde_json::Value,
2527    /// Additional fields from the unknown session configuration option value payload.
2528    #[serde(flatten)]
2529    pub fields: BTreeMap<String, serde_json::Value>,
2530}
2531
2532impl OtherSessionConfigOptionValue {
2533    /// Builds [`OtherSessionConfigOptionValue`] from an unknown discriminator and preserves the remaining extension fields.
2534    #[must_use]
2535    pub fn new(
2536        type_: impl Into<String>,
2537        value: serde_json::Value,
2538        mut fields: BTreeMap<String, serde_json::Value>,
2539    ) -> Self {
2540        fields.remove("type");
2541        fields.remove("value");
2542        fields.remove("_meta");
2543        Self {
2544            type_: type_.into(),
2545            value,
2546            fields,
2547        }
2548    }
2549}
2550
2551impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
2552    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2553    where
2554        D: serde::Deserializer<'de>,
2555    {
2556        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2557        let type_ = fields
2558            .remove("type")
2559            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2560        let serde_json::Value::String(type_) = type_ else {
2561            return Err(serde::de::Error::custom("`type` must be a string"));
2562        };
2563
2564        if is_known_session_config_option_value_type(&type_) {
2565            return Err(serde::de::Error::custom(format!(
2566                "known session configuration option value `{type_}` did not match its schema"
2567            )));
2568        }
2569
2570        let value = fields
2571            .remove("value")
2572            .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2573
2574        Ok(Self {
2575            type_,
2576            value,
2577            fields,
2578        })
2579    }
2580}
2581
2582impl<'de> Deserialize<'de> for SessionConfigOptionValue {
2583    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2584    where
2585        D: serde::Deserializer<'de>,
2586    {
2587        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2588        let type_ = fields.remove("type");
2589        let value = fields
2590            .remove("value")
2591            .ok_or_else(|| serde::de::Error::missing_field("value"))?;
2592
2593        let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
2594
2595        let serde_json::Value::String(type_) = type_ else {
2596            return Err(serde::de::Error::custom("`type` must be a string"));
2597        };
2598
2599        match type_.as_str() {
2600            "id" => {
2601                let value = serde_json::from_value(value).map_err(|error| {
2602                    serde::de::Error::custom(format!(
2603                        "`value` must be a string for `type: id`: {error}"
2604                    ))
2605                })?;
2606                Ok(Self::Id { value })
2607            }
2608            "boolean" => {
2609                let value = serde_json::from_value(value).map_err(|error| {
2610                    serde::de::Error::custom(format!(
2611                        "`value` must be a boolean for `type: boolean`: {error}"
2612                    ))
2613                })?;
2614                Ok(Self::Boolean { value })
2615            }
2616            _ => Ok(Self::Other(OtherSessionConfigOptionValue {
2617                type_,
2618                value,
2619                fields,
2620            })),
2621        }
2622    }
2623}
2624
2625fn is_known_session_config_option_value_type(type_: &str) -> bool {
2626    matches!(type_, "id" | "boolean")
2627}
2628
2629#[cfg(feature = "schemars")]
2630fn other_session_config_option_value_schema(schema: &mut Schema) {
2631    super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
2632}
2633
2634impl SessionConfigOptionValue {
2635    /// Create an id option value (used by `select` and other id-based option types).
2636    #[must_use]
2637    pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
2638        Self::Id { value: id.into() }
2639    }
2640
2641    /// Create a boolean option value.
2642    #[must_use]
2643    pub fn boolean(val: bool) -> Self {
2644        Self::Boolean { value: val }
2645    }
2646
2647    /// Return the inner [`SessionConfigValueId`] if this is a
2648    /// [`Id`](Self::Id) value.
2649    #[must_use]
2650    pub fn as_id(&self) -> Option<&SessionConfigValueId> {
2651        match self {
2652            Self::Id { value } => Some(value),
2653            _ => None,
2654        }
2655    }
2656
2657    /// Return the inner [`bool`] if this is a [`Boolean`](Self::Boolean) value.
2658    #[must_use]
2659    pub fn as_bool(&self) -> Option<bool> {
2660        match self {
2661            Self::Boolean { value } => Some(*value),
2662            _ => None,
2663        }
2664    }
2665}
2666
2667impl From<SessionConfigValueId> for SessionConfigOptionValue {
2668    fn from(value: SessionConfigValueId) -> Self {
2669        Self::Id { value }
2670    }
2671}
2672
2673impl From<bool> for SessionConfigOptionValue {
2674    fn from(value: bool) -> Self {
2675        Self::Boolean { value }
2676    }
2677}
2678
2679impl From<&str> for SessionConfigOptionValue {
2680    fn from(value: &str) -> Self {
2681        Self::Id {
2682            value: SessionConfigValueId::new(value),
2683        }
2684    }
2685}
2686
2687/// Request parameters for setting a session configuration option.
2688#[serde_as]
2689#[skip_serializing_none]
2690#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2691#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2692#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2693#[serde(rename_all = "camelCase")]
2694#[non_exhaustive]
2695pub struct SetSessionConfigOptionRequest {
2696    /// The ID of the session to set the configuration option for.
2697    pub session_id: SessionId,
2698    /// The ID of the configuration option to set.
2699    pub config_id: SessionConfigId,
2700    /// The value to set, including a `type` discriminator and the raw `value`.
2701    ///
2702    /// Payloads must send `type: "id"` for id-based options.
2703    #[serde(flatten)]
2704    pub value: SessionConfigOptionValue,
2705    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2706    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2707    /// these keys.
2708    ///
2709    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2710    #[serde_as(deserialize_as = "DefaultOnError")]
2711    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2712    #[serde(default)]
2713    #[serde(rename = "_meta")]
2714    pub meta: Option<Meta>,
2715}
2716
2717impl SetSessionConfigOptionRequest {
2718    /// Builds [`SetSessionConfigOptionRequest`] with the required request fields set; optional fields start unset or empty.
2719    #[must_use]
2720    pub fn new(
2721        session_id: impl Into<SessionId>,
2722        config_id: impl Into<SessionConfigId>,
2723        value: impl Into<SessionConfigOptionValue>,
2724    ) -> Self {
2725        Self {
2726            session_id: session_id.into(),
2727            config_id: config_id.into(),
2728            value: value.into(),
2729            meta: None,
2730        }
2731    }
2732
2733    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2734    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2735    /// these keys.
2736    ///
2737    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2738    #[must_use]
2739    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2740        self.meta = meta.into_option();
2741        self
2742    }
2743}
2744
2745/// Response to `session/set_config_option` method.
2746#[serde_as]
2747#[skip_serializing_none]
2748#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2749#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2750#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
2751#[serde(rename_all = "camelCase")]
2752#[non_exhaustive]
2753pub struct SetSessionConfigOptionResponse {
2754    /// The full set of configuration options and their current values.
2755    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2756    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2757    pub config_options: Vec<SessionConfigOption>,
2758    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2759    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2760    /// these keys.
2761    ///
2762    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2763    #[serde_as(deserialize_as = "DefaultOnError")]
2764    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2765    #[serde(default)]
2766    #[serde(rename = "_meta")]
2767    pub meta: Option<Meta>,
2768}
2769
2770impl SetSessionConfigOptionResponse {
2771    /// Builds [`SetSessionConfigOptionResponse`] with the required response fields set; optional fields start unset or empty.
2772    #[must_use]
2773    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
2774        Self {
2775            config_options,
2776            meta: None,
2777        }
2778    }
2779
2780    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2781    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2782    /// these keys.
2783    ///
2784    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2785    #[must_use]
2786    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2787        self.meta = meta.into_option();
2788        self
2789    }
2790}
2791
2792// MCP
2793
2794/// Configuration for connecting to an MCP (Model Context Protocol) server.
2795///
2796/// MCP servers provide tools and context that the agent can use when
2797/// processing prompts.
2798///
2799/// See protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/session-setup#mcp-servers)
2800#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2801#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2802#[serde(tag = "type", rename_all = "snake_case")]
2803#[non_exhaustive]
2804pub enum McpServer {
2805    /// HTTP transport configuration
2806    ///
2807    /// Only available when the Agent capabilities include `session.mcp.http`.
2808    Http(McpServerHttp),
2809    /// **UNSTABLE**
2810    ///
2811    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2812    ///
2813    /// ACP transport configuration
2814    ///
2815    /// Only available when the Agent capabilities include `session.mcp.acp`.
2816    /// The MCP server is provided by an ACP component and communicates over the ACP channel.
2817    #[cfg(feature = "unstable_mcp_over_acp")]
2818    Acp(McpServerAcp),
2819    /// Stdio transport configuration
2820    ///
2821    /// Only available when the Agent capabilities include `session.mcp.stdio`.
2822    Stdio(McpServerStdio),
2823    /// Custom or future MCP server transport configuration.
2824    ///
2825    /// Values beginning with `_` are reserved for implementation-specific
2826    /// extensions. Unknown values that do not begin with `_` are reserved for
2827    /// future ACP variants.
2828    ///
2829    /// Receivers that do not understand this transport should preserve the raw
2830    /// payload when storing, replaying, proxying, or forwarding session setup
2831    /// data, and otherwise ignore it or reject the server configuration.
2832    #[serde(untagged)]
2833    Other(OtherMcpServer),
2834}
2835
2836/// Custom or future MCP server transport payload.
2837#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2838#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
2839#[cfg_attr(feature = "schemars", schemars(inline))]
2840#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
2841#[serde(rename_all = "camelCase")]
2842#[non_exhaustive]
2843pub struct OtherMcpServer {
2844    /// Custom or future MCP server transport type.
2845    ///
2846    /// Values beginning with `_` are reserved for implementation-specific
2847    /// extensions. Unknown values that do not begin with `_` are reserved for
2848    /// future ACP variants.
2849    #[serde(rename = "type")]
2850    pub type_: String,
2851    /// Additional fields from the unknown MCP server transport payload.
2852    #[serde(flatten)]
2853    pub fields: BTreeMap<String, serde_json::Value>,
2854}
2855
2856impl OtherMcpServer {
2857    /// Builds [`OtherMcpServer`] from an unknown discriminator and preserves the remaining extension fields.
2858    #[must_use]
2859    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2860        fields.remove("type");
2861        Self {
2862            type_: type_.into(),
2863            fields,
2864        }
2865    }
2866}
2867
2868impl<'de> Deserialize<'de> for OtherMcpServer {
2869    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2870    where
2871        D: serde::Deserializer<'de>,
2872    {
2873        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2874        let type_ = fields
2875            .remove("type")
2876            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
2877        let serde_json::Value::String(type_) = type_ else {
2878            return Err(serde::de::Error::custom("`type` must be a string"));
2879        };
2880
2881        if is_known_mcp_server_type(&type_) {
2882            return Err(serde::de::Error::custom(format!(
2883                "known MCP server transport `{type_}` did not match its schema"
2884            )));
2885        }
2886
2887        Ok(Self { type_, fields })
2888    }
2889}
2890
2891fn is_known_mcp_server_type(type_: &str) -> bool {
2892    match type_ {
2893        "http" | "stdio" => true,
2894        #[cfg(feature = "unstable_mcp_over_acp")]
2895        "acp" => true,
2896        _ => false,
2897    }
2898}
2899
2900#[cfg(feature = "schemars")]
2901fn other_mcp_server_schema(schema: &mut Schema) {
2902    super::schema_util::reject_known_string_discriminators(
2903        schema,
2904        "type",
2905        &[
2906            "http",
2907            "stdio",
2908            #[cfg(feature = "unstable_mcp_over_acp")]
2909            "acp",
2910        ],
2911    );
2912}
2913
2914/// HTTP transport configuration for MCP.
2915#[serde_as]
2916#[skip_serializing_none]
2917#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2918#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2919#[serde(rename_all = "camelCase")]
2920#[non_exhaustive]
2921pub struct McpServerHttp {
2922    /// Human-readable name identifying this MCP server.
2923    pub name: String,
2924    /// URL to the MCP server.
2925    #[cfg_attr(feature = "schemars", schemars(url))]
2926    pub url: String,
2927    /// HTTP headers to set when making requests to the MCP server.
2928    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2929    pub headers: Vec<HttpHeader>,
2930    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2931    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2932    /// these keys.
2933    ///
2934    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2935    #[serde_as(deserialize_as = "DefaultOnError")]
2936    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2937    #[serde(default)]
2938    #[serde(rename = "_meta")]
2939    pub meta: Option<Meta>,
2940}
2941
2942impl McpServerHttp {
2943    /// Builds [`McpServerHttp`] with the required fields set; optional fields start unset or empty.
2944    #[must_use]
2945    pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
2946        Self {
2947            name: name.into(),
2948            url: url.into(),
2949            headers: Vec::new(),
2950            meta: None,
2951        }
2952    }
2953
2954    /// HTTP headers to set when making requests to the MCP server.
2955    #[must_use]
2956    pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
2957        self.headers = headers;
2958        self
2959    }
2960
2961    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2962    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2963    /// these keys.
2964    ///
2965    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2966    #[must_use]
2967    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2968        self.meta = meta.into_option();
2969        self
2970    }
2971}
2972
2973/// **UNSTABLE**
2974///
2975/// This capability is not part of the spec yet, and may be removed or changed at any point.
2976///
2977/// Unique identifier for an MCP server using the ACP transport.
2978///
2979/// The value is opaque and generated by the ACP component providing the MCP server. It is
2980/// used by `mcp/message` to route requests to the component that declared the server.
2981#[cfg(feature = "unstable_mcp_over_acp")]
2982#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2983#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2984#[serde(transparent)]
2985#[from(forward)]
2986#[non_exhaustive]
2987pub struct McpServerAcpId(pub Arc<str>);
2988
2989#[cfg(feature = "unstable_mcp_over_acp")]
2990impl McpServerAcpId {
2991    /// Wraps a protocol string as a typed [`McpServerAcpId`].
2992    #[must_use]
2993    pub fn new(id: impl Into<Self>) -> Self {
2994        id.into()
2995    }
2996}
2997
2998/// **UNSTABLE**
2999///
3000/// This capability is not part of the spec yet, and may be removed or changed at any point.
3001///
3002/// ACP transport configuration for MCP.
3003///
3004/// The MCP server is provided by an ACP component and communicates over the ACP channel
3005/// using `mcp/message`.
3006#[serde_as]
3007#[skip_serializing_none]
3008#[cfg(feature = "unstable_mcp_over_acp")]
3009#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3010#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3011#[serde(rename_all = "camelCase")]
3012#[non_exhaustive]
3013pub struct McpServerAcp {
3014    /// Human-readable name identifying this MCP server.
3015    pub name: String,
3016    /// Unique identifier for this MCP server, generated by the component providing it.
3017    ///
3018    /// Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible
3019    /// on the same ACP connection.
3020    pub server_id: McpServerAcpId,
3021    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3022    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3023    /// these keys.
3024    ///
3025    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3026    #[serde_as(deserialize_as = "DefaultOnError")]
3027    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3028    #[serde(default)]
3029    #[serde(rename = "_meta")]
3030    pub meta: Option<Meta>,
3031}
3032
3033#[cfg(feature = "unstable_mcp_over_acp")]
3034impl McpServerAcp {
3035    /// Builds [`McpServerAcp`] with the required fields set; optional fields start unset or empty.
3036    #[must_use]
3037    pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
3038        Self {
3039            name: name.into(),
3040            server_id: server_id.into(),
3041            meta: None,
3042        }
3043    }
3044
3045    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3046    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3047    /// these keys.
3048    ///
3049    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3050    #[must_use]
3051    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3052        self.meta = meta.into_option();
3053        self
3054    }
3055}
3056
3057/// Stdio transport configuration for MCP.
3058#[serde_as]
3059#[skip_serializing_none]
3060#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3061#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3062#[serde(rename_all = "camelCase")]
3063#[non_exhaustive]
3064pub struct McpServerStdio {
3065    /// Human-readable name identifying this MCP server.
3066    pub name: String,
3067    /// Absolute path to the MCP server executable.
3068    pub command: AbsolutePath,
3069    /// Command-line arguments to pass to the MCP server.
3070    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3071    pub args: Vec<String>,
3072    /// Environment variables to set when launching the MCP server.
3073    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3074    pub env: Vec<EnvVariable>,
3075    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3076    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3077    /// these keys.
3078    ///
3079    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3080    #[serde_as(deserialize_as = "DefaultOnError")]
3081    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3082    #[serde(default)]
3083    #[serde(rename = "_meta")]
3084    pub meta: Option<Meta>,
3085}
3086
3087impl McpServerStdio {
3088    /// Builds [`McpServerStdio`] with the required fields set; optional fields start unset or empty.
3089    #[must_use]
3090    pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
3091        Self {
3092            name: name.into(),
3093            command: command.into(),
3094            args: Vec::new(),
3095            env: Vec::new(),
3096            meta: None,
3097        }
3098    }
3099
3100    /// Command-line arguments to pass to the MCP server.
3101    #[must_use]
3102    pub fn args(mut self, args: Vec<String>) -> Self {
3103        self.args = args;
3104        self
3105    }
3106
3107    /// Environment variables to set when launching the MCP server.
3108    #[must_use]
3109    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
3110        self.env = env;
3111        self
3112    }
3113
3114    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3115    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3116    /// these keys.
3117    ///
3118    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3119    #[must_use]
3120    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3121        self.meta = meta.into_option();
3122        self
3123    }
3124}
3125
3126/// An environment variable to set when launching a process.
3127#[serde_as]
3128#[skip_serializing_none]
3129#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3131#[serde(rename_all = "camelCase")]
3132#[non_exhaustive]
3133pub struct EnvVariable {
3134    /// The name of the environment variable.
3135    pub name: String,
3136    /// The value to set for the environment variable.
3137    pub value: String,
3138    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3139    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3140    /// these keys.
3141    ///
3142    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3143    #[serde_as(deserialize_as = "DefaultOnError")]
3144    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3145    #[serde(default)]
3146    #[serde(rename = "_meta")]
3147    pub meta: Option<Meta>,
3148}
3149
3150impl EnvVariable {
3151    /// Builds [`EnvVariable`] with the required fields set; optional fields start unset or empty.
3152    #[must_use]
3153    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3154        Self {
3155            name: name.into(),
3156            value: value.into(),
3157            meta: None,
3158        }
3159    }
3160
3161    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3162    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3163    /// these keys.
3164    ///
3165    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3166    #[must_use]
3167    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3168        self.meta = meta.into_option();
3169        self
3170    }
3171}
3172
3173/// An HTTP header to set when making requests to the MCP server.
3174#[serde_as]
3175#[skip_serializing_none]
3176#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3177#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3178#[serde(rename_all = "camelCase")]
3179#[non_exhaustive]
3180pub struct HttpHeader {
3181    /// The name of the HTTP header.
3182    pub name: String,
3183    /// The value to set for the HTTP header.
3184    pub value: String,
3185    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3186    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3187    /// these keys.
3188    ///
3189    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3190    #[serde_as(deserialize_as = "DefaultOnError")]
3191    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3192    #[serde(default)]
3193    #[serde(rename = "_meta")]
3194    pub meta: Option<Meta>,
3195}
3196
3197impl HttpHeader {
3198    /// Builds [`HttpHeader`] with the required fields set; optional fields start unset or empty.
3199    #[must_use]
3200    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3201        Self {
3202            name: name.into(),
3203            value: value.into(),
3204            meta: None,
3205        }
3206    }
3207
3208    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3209    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3210    /// these keys.
3211    ///
3212    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3213    #[must_use]
3214    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3215        self.meta = meta.into_option();
3216        self
3217    }
3218}
3219
3220// Prompt
3221
3222/// Request parameters for sending a user prompt to the agent.
3223///
3224/// Contains the user's message and any additional context.
3225///
3226/// See protocol docs: [User Message](https://agentclientprotocol.com/protocol/prompt-lifecycle#1-user-message)
3227#[serde_as]
3228#[skip_serializing_none]
3229#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3230#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3231#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3232#[serde(rename_all = "camelCase")]
3233#[non_exhaustive]
3234pub struct PromptRequest {
3235    /// The ID of the session to send this user message to
3236    pub session_id: SessionId,
3237    /// The blocks of content that compose the user's message.
3238    ///
3239    /// As a baseline, the Agent MUST support [`ContentBlock::Text`] and [`ContentBlock::ResourceLink`],
3240    /// while other variants are optionally enabled via [`PromptCapabilities`].
3241    ///
3242    /// The Client MUST adapt its interface according to [`PromptCapabilities`].
3243    ///
3244    /// The client MAY include referenced pieces of context as either
3245    /// [`ContentBlock::Resource`] or [`ContentBlock::ResourceLink`].
3246    ///
3247    /// When available, [`ContentBlock::Resource`] is preferred
3248    /// as it avoids extra round-trips and allows the message to include
3249    /// pieces of context from sources the agent may not have access to.
3250    pub prompt: Vec<ContentBlock>,
3251    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3252    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3253    /// these keys.
3254    ///
3255    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3256    #[serde_as(deserialize_as = "DefaultOnError")]
3257    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3258    #[serde(default)]
3259    #[serde(rename = "_meta")]
3260    pub meta: Option<Meta>,
3261}
3262
3263impl PromptRequest {
3264    /// Builds [`PromptRequest`] with the required request fields set; optional fields start unset or empty.
3265    #[must_use]
3266    pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
3267        Self {
3268            session_id: session_id.into(),
3269            prompt,
3270            meta: None,
3271        }
3272    }
3273
3274    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3275    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3276    /// these keys.
3277    ///
3278    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3279    #[must_use]
3280    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3281        self.meta = meta.into_option();
3282        self
3283    }
3284}
3285
3286/// Response acknowledging that a user prompt was inserted into the ACP conversation.
3287///
3288/// This response does not indicate that the prompt was merely received or queued, nor that the
3289/// agent has finished processing it.
3290/// Processing and completion are reported through `state_update` session updates.
3291///
3292/// See protocol docs: [Prompt Accepted](https://agentclientprotocol.com/protocol/prompt-lifecycle#2-prompt-accepted)
3293#[serde_as]
3294#[skip_serializing_none]
3295#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3297#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
3298#[serde(rename_all = "camelCase")]
3299#[non_exhaustive]
3300pub struct PromptResponse {
3301    /// Identifies the user message inserted into the ACP conversation.
3302    ///
3303    /// Required and non-null. Omission and explicit `null` are both invalid.
3304    ///
3305    /// The corresponding user-message session update carries this same identifier and may arrive
3306    /// before or after this response. Agents must echo the message during the live session, but are
3307    /// not required to retain it. If retained and replayed, the message keeps this identifier.
3308    pub message_id: MessageId,
3309    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3310    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3311    /// these keys.
3312    ///
3313    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3314    #[serde_as(deserialize_as = "DefaultOnError")]
3315    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3316    #[serde(default)]
3317    #[serde(rename = "_meta")]
3318    pub meta: Option<Meta>,
3319}
3320
3321impl PromptResponse {
3322    /// Builds [`PromptResponse`] with the required response fields set; optional fields start unset or empty.
3323    #[must_use]
3324    pub fn new(message_id: impl Into<MessageId>) -> Self {
3325        Self {
3326            message_id: message_id.into(),
3327            meta: None,
3328        }
3329    }
3330
3331    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3332    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3333    /// these keys.
3334    ///
3335    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3336    #[must_use]
3337    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3338        self.meta = meta.into_option();
3339        self
3340    }
3341}
3342
3343/// Reasons why an agent stops active session work.
3344///
3345/// See protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/prompt-lifecycle#stop-reasons)
3346#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3347#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
3348#[serde(rename_all = "snake_case")]
3349#[non_exhaustive]
3350pub enum StopReason {
3351    /// The active work ended successfully.
3352    EndTurn,
3353    /// The active work ended because the agent reached the maximum number of tokens.
3354    MaxTokens,
3355    /// The active work ended because the agent reached the maximum number of
3356    /// allowed agent requests before returning idle.
3357    MaxTurnRequests,
3358    /// The active work ended because the agent refused to continue. The user
3359    /// prompt and everything that comes after it won't be included in the next
3360    /// prompt, so this should be reflected in the UI.
3361    Refusal,
3362    /// Active session work was cancelled by the client via `session/cancel`.
3363    ///
3364    /// Agents should report this stop reason on an idle `state_update` session update
3365    /// when cancellation succeeds, even if cancellation causes exceptions in
3366    /// underlying operations.
3367    Cancelled,
3368    /// Custom or future stop reason.
3369    ///
3370    /// Values beginning with `_` are reserved for implementation-specific
3371    /// extensions. Unknown values that do not begin with `_` are reserved for
3372    /// future ACP variants.
3373    #[serde(untagged)]
3374    Other(String),
3375}
3376
3377/// **UNSTABLE**
3378///
3379/// This capability is not part of the spec yet, and may be removed or changed at any point.
3380///
3381/// Token usage information for completed session work.
3382#[cfg(feature = "unstable_end_turn_token_usage")]
3383#[serde_as]
3384#[skip_serializing_none]
3385#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3386#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3387#[serde(rename_all = "camelCase")]
3388#[non_exhaustive]
3389pub struct Usage {
3390    /// Sum of all token types across session.
3391    pub total_tokens: u64,
3392    /// Total input tokens.
3393    pub input_tokens: u64,
3394    /// Total output tokens.
3395    pub output_tokens: u64,
3396    /// Total thought/reasoning tokens
3397    #[serde_as(deserialize_as = "DefaultOnError")]
3398    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3399    #[serde(default)]
3400    pub thought_tokens: Option<u64>,
3401    /// Total cache read tokens.
3402    #[serde_as(deserialize_as = "DefaultOnError")]
3403    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3404    #[serde(default)]
3405    pub cached_read_tokens: Option<u64>,
3406    /// Total cache write tokens.
3407    #[serde_as(deserialize_as = "DefaultOnError")]
3408    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3409    #[serde(default)]
3410    pub cached_write_tokens: Option<u64>,
3411    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3412    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3413    /// these keys.
3414    ///
3415    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3416    #[serde_as(deserialize_as = "DefaultOnError")]
3417    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3418    #[serde(default)]
3419    #[serde(rename = "_meta")]
3420    pub meta: Option<Meta>,
3421}
3422
3423#[cfg(feature = "unstable_end_turn_token_usage")]
3424impl Usage {
3425    /// Builds [`Usage`] with the required fields set; optional fields start unset or empty.
3426    #[must_use]
3427    pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
3428        Self {
3429            total_tokens,
3430            input_tokens,
3431            output_tokens,
3432            thought_tokens: None,
3433            cached_read_tokens: None,
3434            cached_write_tokens: None,
3435            meta: None,
3436        }
3437    }
3438
3439    /// Total thought/reasoning tokens
3440    #[must_use]
3441    pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
3442        self.thought_tokens = thought_tokens.into_option();
3443        self
3444    }
3445
3446    /// Total cache read tokens.
3447    #[must_use]
3448    pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
3449        self.cached_read_tokens = cached_read_tokens.into_option();
3450        self
3451    }
3452
3453    /// Total cache write tokens.
3454    #[must_use]
3455    pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
3456        self.cached_write_tokens = cached_write_tokens.into_option();
3457        self
3458    }
3459
3460    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3461    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3462    /// these keys.
3463    ///
3464    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3465    #[must_use]
3466    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3467        self.meta = meta.into_option();
3468        self
3469    }
3470}
3471
3472// Providers
3473
3474/// **UNSTABLE**
3475///
3476/// This capability is not part of the spec yet, and may be removed or changed at any point.
3477///
3478/// Well-known API protocol identifiers for LLM providers.
3479///
3480/// Agents and clients MUST handle unknown protocol identifiers gracefully.
3481///
3482/// Protocol names beginning with `_` are free for custom use, like other ACP extension methods.
3483/// Protocol names that do not begin with `_` are reserved for the ACP spec.
3484#[cfg(feature = "unstable_llm_providers")]
3485#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3486#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3487#[serde(rename_all = "snake_case")]
3488#[non_exhaustive]
3489#[expect(clippy::doc_markdown)]
3490pub enum LlmProtocol {
3491    /// Anthropic API protocol.
3492    Anthropic,
3493    /// OpenAI API protocol.
3494    #[serde(rename = "openai")]
3495    OpenAi,
3496    /// Azure OpenAI API protocol.
3497    Azure,
3498    /// Google Vertex AI API protocol.
3499    Vertex,
3500    /// AWS Bedrock API protocol.
3501    Bedrock,
3502    /// Custom or future protocol.
3503    ///
3504    /// Values beginning with `_` are reserved for implementation-specific
3505    /// extensions. Unknown values that do not begin with `_` are reserved for
3506    /// future ACP variants.
3507    #[serde(untagged)]
3508    Other(String),
3509}
3510
3511/// **UNSTABLE**
3512///
3513/// This capability is not part of the spec yet, and may be removed or changed at any point.
3514///
3515/// Current effective non-secret routing configuration for a provider.
3516#[cfg(feature = "unstable_llm_providers")]
3517#[serde_as]
3518#[skip_serializing_none]
3519#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3520#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3521#[serde(rename_all = "camelCase")]
3522#[non_exhaustive]
3523pub struct ProviderCurrentConfig {
3524    /// Protocol currently used by this provider.
3525    pub api_type: LlmProtocol,
3526    /// Base URL currently used by this provider.
3527    #[cfg_attr(feature = "schemars", schemars(url))]
3528    pub base_url: String,
3529    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3530    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3531    /// these keys.
3532    ///
3533    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3534    #[serde_as(deserialize_as = "DefaultOnError")]
3535    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3536    #[serde(default)]
3537    #[serde(rename = "_meta")]
3538    pub meta: Option<Meta>,
3539}
3540
3541#[cfg(feature = "unstable_llm_providers")]
3542impl ProviderCurrentConfig {
3543    /// Builds [`ProviderCurrentConfig`] with the required fields set; optional fields start unset or empty.
3544    #[must_use]
3545    pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
3546        Self {
3547            api_type,
3548            base_url: base_url.into(),
3549            meta: None,
3550        }
3551    }
3552
3553    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3554    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3555    /// these keys.
3556    ///
3557    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3558    #[must_use]
3559    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3560        self.meta = meta.into_option();
3561        self
3562    }
3563}
3564
3565/// **UNSTABLE**
3566///
3567/// This capability is not part of the spec yet, and may be removed or changed at any point.
3568///
3569/// Unique identifier for a configurable LLM provider.
3570#[cfg(feature = "unstable_llm_providers")]
3571#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3572#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
3573#[serde(transparent)]
3574#[from(forward)]
3575#[non_exhaustive]
3576pub struct ProviderId(pub Arc<str>);
3577
3578#[cfg(feature = "unstable_llm_providers")]
3579impl ProviderId {
3580    /// Wraps a protocol string as a typed [`ProviderId`].
3581    #[must_use]
3582    pub fn new(id: impl Into<Self>) -> Self {
3583        id.into()
3584    }
3585}
3586
3587/// **UNSTABLE**
3588///
3589/// This capability is not part of the spec yet, and may be removed or changed at any point.
3590///
3591/// Information about a configurable LLM provider.
3592#[cfg(feature = "unstable_llm_providers")]
3593#[serde_as]
3594#[skip_serializing_none]
3595#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3596#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3597#[serde(rename_all = "camelCase")]
3598#[non_exhaustive]
3599pub struct ProviderInfo {
3600    /// Provider identifier, for example "main" or "openai".
3601    pub provider_id: ProviderId,
3602    /// Supported protocol types for this provider.
3603    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3604    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3605    pub supported: Vec<LlmProtocol>,
3606    /// Whether this provider is mandatory and cannot be disabled via `providers/disable`.
3607    /// If true, clients must not call `providers/disable` for this provider ID.
3608    pub required: bool,
3609    /// Current effective non-secret routing config.
3610    /// Null or omitted means provider is disabled.
3611    #[serde(default)]
3612    pub current: Option<ProviderCurrentConfig>,
3613    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3614    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3615    /// these keys.
3616    ///
3617    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3618    #[serde_as(deserialize_as = "DefaultOnError")]
3619    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3620    #[serde(default)]
3621    #[serde(rename = "_meta")]
3622    pub meta: Option<Meta>,
3623}
3624
3625#[cfg(feature = "unstable_llm_providers")]
3626impl ProviderInfo {
3627    /// Builds [`ProviderInfo`] with the required fields set; optional fields start unset or empty.
3628    #[must_use]
3629    pub fn new(
3630        provider_id: impl Into<ProviderId>,
3631        supported: Vec<LlmProtocol>,
3632        required: bool,
3633        current: impl IntoOption<ProviderCurrentConfig>,
3634    ) -> Self {
3635        Self {
3636            provider_id: provider_id.into(),
3637            supported,
3638            required,
3639            current: current.into_option(),
3640            meta: None,
3641        }
3642    }
3643
3644    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3645    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3646    /// these keys.
3647    ///
3648    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3649    #[must_use]
3650    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3651        self.meta = meta.into_option();
3652        self
3653    }
3654}
3655
3656#[cfg(feature = "unstable_llm_providers")]
3657crate::serde_util::default_on_null! {
3658    /// **UNSTABLE**
3659    ///
3660    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3661    ///
3662    /// Request parameters for `providers/list`.
3663    #[serde_as]
3664    #[skip_serializing_none]
3665    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3666    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3667    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3668    #[serde(rename_all = "camelCase")]
3669    #[non_exhaustive]
3670    pub struct ListProvidersRequest {
3671        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3672        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3673        /// these keys.
3674        ///
3675        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3676        #[serde_as(deserialize_as = "DefaultOnError")]
3677        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3678        #[serde(default)]
3679        #[serde(rename = "_meta")]
3680        pub meta: Option<Meta>,
3681    }
3682}
3683
3684#[cfg(feature = "unstable_llm_providers")]
3685impl ListProvidersRequest {
3686    /// Builds [`ListProvidersRequest`] with the required request fields set; optional fields start unset or empty.
3687    #[must_use]
3688    pub fn new() -> Self {
3689        Self::default()
3690    }
3691
3692    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3693    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3694    /// these keys.
3695    ///
3696    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3697    #[must_use]
3698    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3699        self.meta = meta.into_option();
3700        self
3701    }
3702}
3703
3704/// **UNSTABLE**
3705///
3706/// This capability is not part of the spec yet, and may be removed or changed at any point.
3707///
3708/// Response to `providers/list`.
3709#[cfg(feature = "unstable_llm_providers")]
3710#[serde_as]
3711#[skip_serializing_none]
3712#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3713#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3714#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
3715#[serde(rename_all = "camelCase")]
3716#[non_exhaustive]
3717pub struct ListProvidersResponse {
3718    /// Configurable providers with current routing info suitable for UI display.
3719    pub providers: Vec<ProviderInfo>,
3720    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3721    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3722    /// these keys.
3723    ///
3724    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3725    #[serde_as(deserialize_as = "DefaultOnError")]
3726    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3727    #[serde(default)]
3728    #[serde(rename = "_meta")]
3729    pub meta: Option<Meta>,
3730}
3731
3732#[cfg(feature = "unstable_llm_providers")]
3733impl ListProvidersResponse {
3734    /// Builds [`ListProvidersResponse`] with the required response fields set; optional fields start unset or empty.
3735    #[must_use]
3736    pub fn new(providers: Vec<ProviderInfo>) -> Self {
3737        Self {
3738            providers,
3739            meta: None,
3740        }
3741    }
3742
3743    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3744    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3745    /// these keys.
3746    ///
3747    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3748    #[must_use]
3749    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3750        self.meta = meta.into_option();
3751        self
3752    }
3753}
3754
3755/// **UNSTABLE**
3756///
3757/// This capability is not part of the spec yet, and may be removed or changed at any point.
3758///
3759/// Request parameters for `providers/set`.
3760///
3761/// Replaces the full configuration for one provider ID.
3762#[cfg(feature = "unstable_llm_providers")]
3763#[serde_as]
3764#[skip_serializing_none]
3765#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3766#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3767#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3768#[serde(rename_all = "camelCase")]
3769#[non_exhaustive]
3770pub struct SetProviderRequest {
3771    /// Provider ID to configure.
3772    pub provider_id: ProviderId,
3773    /// Protocol type for this provider.
3774    pub api_type: LlmProtocol,
3775    /// Base URL for requests sent through this provider.
3776    #[cfg_attr(feature = "schemars", schemars(url))]
3777    pub base_url: String,
3778    /// Full headers map for this provider.
3779    /// May include authorization, routing, or other integration-specific headers.
3780    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
3781    pub headers: HashMap<String, String>,
3782    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3783    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3784    /// these keys.
3785    ///
3786    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3787    #[serde_as(deserialize_as = "DefaultOnError")]
3788    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3789    #[serde(default)]
3790    #[serde(rename = "_meta")]
3791    pub meta: Option<Meta>,
3792}
3793
3794#[cfg(feature = "unstable_llm_providers")]
3795impl SetProviderRequest {
3796    /// Builds [`SetProviderRequest`] with the required request fields set; optional fields start unset or empty.
3797    #[must_use]
3798    pub fn new(
3799        provider_id: impl Into<ProviderId>,
3800        api_type: LlmProtocol,
3801        base_url: impl Into<String>,
3802    ) -> Self {
3803        Self {
3804            provider_id: provider_id.into(),
3805            api_type,
3806            base_url: base_url.into(),
3807            headers: HashMap::new(),
3808            meta: None,
3809        }
3810    }
3811
3812    /// Full headers map for this provider.
3813    /// May include authorization, routing, or other integration-specific headers.
3814    #[must_use]
3815    pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
3816        self.headers = headers;
3817        self
3818    }
3819
3820    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3821    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3822    /// these keys.
3823    ///
3824    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3825    #[must_use]
3826    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3827        self.meta = meta.into_option();
3828        self
3829    }
3830}
3831
3832#[cfg(feature = "unstable_llm_providers")]
3833crate::serde_util::default_on_null! {
3834    /// **UNSTABLE**
3835    ///
3836    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3837    ///
3838    /// Response to `providers/set`.
3839    #[serde_as]
3840    #[skip_serializing_none]
3841    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3842    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3843    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
3844    #[serde(rename_all = "camelCase")]
3845    #[non_exhaustive]
3846    pub struct SetProviderResponse {
3847        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3848        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3849        /// these keys.
3850        ///
3851        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3852        #[serde_as(deserialize_as = "DefaultOnError")]
3853        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3854        #[serde(default)]
3855        #[serde(rename = "_meta")]
3856        pub meta: Option<Meta>,
3857    }
3858}
3859
3860#[cfg(feature = "unstable_llm_providers")]
3861impl SetProviderResponse {
3862    /// Builds [`SetProviderResponse`] with the required response fields set; optional fields start unset or empty.
3863    #[must_use]
3864    pub fn new() -> Self {
3865        Self::default()
3866    }
3867
3868    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3869    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3870    /// these keys.
3871    ///
3872    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3873    #[must_use]
3874    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3875        self.meta = meta.into_option();
3876        self
3877    }
3878}
3879
3880/// **UNSTABLE**
3881///
3882/// This capability is not part of the spec yet, and may be removed or changed at any point.
3883///
3884/// Request parameters for `providers/disable`.
3885#[cfg(feature = "unstable_llm_providers")]
3886#[serde_as]
3887#[skip_serializing_none]
3888#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3889#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3890#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3891#[serde(rename_all = "camelCase")]
3892#[non_exhaustive]
3893pub struct DisableProviderRequest {
3894    /// Provider ID to disable.
3895    pub provider_id: ProviderId,
3896    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3897    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3898    /// these keys.
3899    ///
3900    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3901    #[serde_as(deserialize_as = "DefaultOnError")]
3902    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3903    #[serde(default)]
3904    #[serde(rename = "_meta")]
3905    pub meta: Option<Meta>,
3906}
3907
3908#[cfg(feature = "unstable_llm_providers")]
3909impl DisableProviderRequest {
3910    /// Builds [`DisableProviderRequest`] with the required request fields set; optional fields start unset or empty.
3911    #[must_use]
3912    pub fn new(provider_id: impl Into<ProviderId>) -> Self {
3913        Self {
3914            provider_id: provider_id.into(),
3915            meta: None,
3916        }
3917    }
3918
3919    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3920    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3921    /// these keys.
3922    ///
3923    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3924    #[must_use]
3925    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3926        self.meta = meta.into_option();
3927        self
3928    }
3929}
3930
3931#[cfg(feature = "unstable_llm_providers")]
3932crate::serde_util::default_on_null! {
3933    /// **UNSTABLE**
3934    ///
3935    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3936    ///
3937    /// Response to `providers/disable`.
3938    #[serde_as]
3939    #[skip_serializing_none]
3940    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3941    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
3942    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
3943    #[serde(rename_all = "camelCase")]
3944    #[non_exhaustive]
3945    pub struct DisableProviderResponse {
3946        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3947        /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3948        /// these keys.
3949        ///
3950        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3951        #[serde_as(deserialize_as = "DefaultOnError")]
3952        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3953        #[serde(default)]
3954        #[serde(rename = "_meta")]
3955        pub meta: Option<Meta>,
3956    }
3957}
3958
3959#[cfg(feature = "unstable_llm_providers")]
3960impl DisableProviderResponse {
3961    /// Builds [`DisableProviderResponse`] with the required response fields set; optional fields start unset or empty.
3962    #[must_use]
3963    pub fn new() -> Self {
3964        Self::default()
3965    }
3966
3967    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3968    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
3969    /// these keys.
3970    ///
3971    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
3972    #[must_use]
3973    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3974        self.meta = meta.into_option();
3975        self
3976    }
3977}
3978
3979// Capabilities
3980
3981/// Capabilities supported by the agent.
3982///
3983/// Advertised during initialization to inform the client about
3984/// available features and content types.
3985///
3986/// See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol/initialization#agent-capabilities)
3987#[serde_as]
3988#[skip_serializing_none]
3989#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3990#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3991#[serde(rename_all = "camelCase")]
3992#[non_exhaustive]
3993pub struct AgentCapabilities {
3994    /// Session capabilities supported by the agent.
3995    ///
3996    /// Optional. Omitted or `null` both mean the agent does not support the
3997    /// `session/*` method surface. Supplying `{}` means the agent supports the
3998    /// baseline session methods: `session/new`, `session/prompt`,
3999    /// `session/cancel`, and `session/update`.
4000    #[serde_as(deserialize_as = "DefaultOnError")]
4001    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4002    #[serde(default)]
4003    pub session: Option<SessionCapabilities>,
4004    /// Authentication-related extension capabilities supported by the agent.
4005    ///
4006    /// Optional. Omitted or `null` both mean the agent does not advertise any
4007    /// authentication-related extensions. This field does not advertise support
4008    /// for `auth/login` or `auth/logout`; those methods are advertised by a
4009    /// non-empty `authMethods` list in the `initialize` response.
4010    #[serde_as(deserialize_as = "DefaultOnError")]
4011    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4012    #[serde(default)]
4013    pub auth: Option<AgentAuthCapabilities>,
4014    /// **UNSTABLE**
4015    ///
4016    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4017    ///
4018    /// Provider configuration capabilities supported by the agent.
4019    ///
4020    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4021    /// Supplying `{}` means the agent supports provider configuration methods.
4022    #[cfg(feature = "unstable_llm_providers")]
4023    #[serde_as(deserialize_as = "DefaultOnError")]
4024    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4025    #[serde(default)]
4026    pub providers: Option<ProvidersCapabilities>,
4027    /// **UNSTABLE**
4028    ///
4029    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4030    ///
4031    /// NES (Next Edit Suggestions) capabilities supported by the agent.
4032    ///
4033    /// Optional. Omitted or `null` both mean the agent does not advertise support
4034    /// for NES methods.
4035    #[cfg(feature = "unstable_nes")]
4036    #[serde_as(deserialize_as = "DefaultOnError")]
4037    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4038    #[serde(default)]
4039    pub nes: Option<NesCapabilities>,
4040    /// **UNSTABLE**
4041    ///
4042    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4043    ///
4044    /// The position encoding selected by the agent from the client's supported encodings.
4045    #[cfg(feature = "unstable_nes")]
4046    #[serde_as(deserialize_as = "DefaultOnError")]
4047    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4048    #[serde(default)]
4049    pub position_encoding: Option<PositionEncodingKind>,
4050    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4051    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4052    /// these keys.
4053    ///
4054    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4055    #[serde_as(deserialize_as = "DefaultOnError")]
4056    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4057    #[serde(default)]
4058    #[serde(rename = "_meta")]
4059    pub meta: Option<Meta>,
4060}
4061
4062impl AgentCapabilities {
4063    /// Builds an empty [`AgentCapabilities`]; use builder methods to advertise supported sub-capabilities.
4064    #[must_use]
4065    pub fn new() -> Self {
4066        Self::default()
4067    }
4068
4069    /// Session capabilities supported by the agent.
4070    ///
4071    /// Omitted or `null` both mean the agent does not support the `session/*`
4072    /// method surface. Supplying `{}` means the agent supports the baseline
4073    /// session methods: `session/new`, `session/prompt`, `session/cancel`, and
4074    /// `session/update`.
4075    #[must_use]
4076    pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
4077        self.session = session.into_option();
4078        self
4079    }
4080
4081    /// Authentication-related extension capabilities supported by the agent.
4082    ///
4083    /// This field does not advertise support for `auth/login` or `auth/logout`.
4084    #[must_use]
4085    pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
4086        self.auth = auth.into_option();
4087        self
4088    }
4089
4090    /// **UNSTABLE**
4091    ///
4092    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4093    ///
4094    /// Provider configuration capabilities supported by the agent.
4095    #[cfg(feature = "unstable_llm_providers")]
4096    #[must_use]
4097    pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
4098        self.providers = providers.into_option();
4099        self
4100    }
4101
4102    /// **UNSTABLE**
4103    ///
4104    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4105    ///
4106    /// NES (Next Edit Suggestions) capabilities supported by the agent.
4107    #[cfg(feature = "unstable_nes")]
4108    #[must_use]
4109    pub fn nes(mut self, nes: impl IntoOption<NesCapabilities>) -> Self {
4110        self.nes = nes.into_option();
4111        self
4112    }
4113
4114    /// **UNSTABLE**
4115    ///
4116    /// The position encoding selected by the agent from the client's supported encodings.
4117    #[cfg(feature = "unstable_nes")]
4118    #[must_use]
4119    pub fn position_encoding(
4120        mut self,
4121        position_encoding: impl IntoOption<PositionEncodingKind>,
4122    ) -> Self {
4123        self.position_encoding = position_encoding.into_option();
4124        self
4125    }
4126
4127    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4128    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4129    /// these keys.
4130    ///
4131    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4132    #[must_use]
4133    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4134        self.meta = meta.into_option();
4135        self
4136    }
4137}
4138
4139/// **UNSTABLE**
4140///
4141/// This capability is not part of the spec yet, and may be removed or changed at any point.
4142///
4143/// Provider configuration capabilities supported by the agent.
4144///
4145/// Supplying `{}` means the agent supports provider configuration methods.
4146#[cfg(feature = "unstable_llm_providers")]
4147#[serde_as]
4148#[skip_serializing_none]
4149#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4150#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4151#[non_exhaustive]
4152pub struct ProvidersCapabilities {
4153    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4154    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4155    /// these keys.
4156    ///
4157    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4158    #[serde_as(deserialize_as = "DefaultOnError")]
4159    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4160    #[serde(default)]
4161    #[serde(rename = "_meta")]
4162    pub meta: Option<Meta>,
4163}
4164
4165#[cfg(feature = "unstable_llm_providers")]
4166impl ProvidersCapabilities {
4167    /// Builds an empty [`ProvidersCapabilities`]; use builder methods to advertise supported sub-capabilities.
4168    #[must_use]
4169    pub fn new() -> Self {
4170        Self::default()
4171    }
4172
4173    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4174    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4175    /// these keys.
4176    ///
4177    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4178    #[must_use]
4179    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4180        self.meta = meta.into_option();
4181        self
4182    }
4183}
4184
4185/// Session capabilities supported by the agent.
4186///
4187/// Supplying `{}` means the agent supports the baseline session methods:
4188/// `session/new`, `session/list`, `session/resume`, `session/close`,
4189/// `session/prompt`, `session/cancel`, and `session/update`.
4190///
4191/// Agents that support sessions **MAY** support additional session methods,
4192/// prompt content types, and MCP transports by specifying additional
4193/// capabilities.
4194///
4195/// See protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/initialization#session-capabilities)
4196#[serde_as]
4197#[skip_serializing_none]
4198#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4199#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4200#[serde(rename_all = "camelCase")]
4201#[non_exhaustive]
4202pub struct SessionCapabilities {
4203    /// Prompt capabilities supported by the agent in `session/prompt` requests.
4204    ///
4205    /// Optional. Omitted or `null` both mean the agent does not advertise any
4206    /// prompt extensions beyond the baseline text and resource-link content
4207    /// required by `session/prompt`.
4208    #[serde_as(deserialize_as = "DefaultOnError")]
4209    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4210    #[serde(default)]
4211    pub prompt: Option<PromptCapabilities>,
4212    /// MCP capabilities supported by the agent for session lifecycle requests.
4213    ///
4214    /// Optional. Omitted or `null` both mean the agent does not advertise MCP
4215    /// server transport support for sessions.
4216    #[serde_as(deserialize_as = "DefaultOnError")]
4217    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4218    #[serde(default)]
4219    pub mcp: Option<McpCapabilities>,
4220    /// Whether the agent supports `session/delete`.
4221    ///
4222    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4223    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
4224    #[serde_as(deserialize_as = "DefaultOnError")]
4225    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4226    #[serde(default)]
4227    pub delete: Option<SessionDeleteCapabilities>,
4228    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
4229    ///
4230    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4231    /// Supplying `{}` means the agent supports `additionalDirectories` on
4232    /// supported session lifecycle requests.
4233    ///
4234    /// Agents may return `SessionInfo.additionalDirectories` to report the
4235    /// complete ordered additional-root list associated with a listed session.
4236    #[serde_as(deserialize_as = "DefaultOnError")]
4237    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4238    #[serde(default)]
4239    pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
4240    /// **UNSTABLE**
4241    ///
4242    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4243    ///
4244    /// Whether the agent supports `session/fork`.
4245    ///
4246    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4247    /// Supplying `{}` means the agent supports forking sessions.
4248    #[cfg(feature = "unstable_session_fork")]
4249    #[serde_as(deserialize_as = "DefaultOnError")]
4250    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4251    #[serde(default)]
4252    pub fork: Option<SessionForkCapabilities>,
4253    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4254    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4255    /// these keys.
4256    ///
4257    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4258    #[serde_as(deserialize_as = "DefaultOnError")]
4259    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4260    #[serde(default)]
4261    #[serde(rename = "_meta")]
4262    pub meta: Option<Meta>,
4263}
4264
4265impl SessionCapabilities {
4266    /// Builds an empty [`SessionCapabilities`]; use builder methods to advertise supported sub-capabilities.
4267    #[must_use]
4268    pub fn new() -> Self {
4269        Self::default()
4270    }
4271
4272    /// Prompt capabilities supported by the agent in `session/prompt` requests.
4273    ///
4274    /// Omitted or `null` both mean the agent does not advertise any prompt
4275    /// extensions beyond the baseline text and resource-link content required by
4276    /// `session/prompt`.
4277    #[must_use]
4278    pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
4279        self.prompt = prompt.into_option();
4280        self
4281    }
4282
4283    /// MCP capabilities supported by the agent for session lifecycle requests.
4284    ///
4285    /// Omitted or `null` both mean the agent does not advertise MCP server
4286    /// transport support for sessions.
4287    #[must_use]
4288    pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
4289        self.mcp = mcp.into_option();
4290        self
4291    }
4292
4293    /// Whether the agent supports `session/delete`.
4294    ///
4295    /// Omitted or `null` both mean the agent does not advertise support.
4296    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
4297    #[must_use]
4298    pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
4299        self.delete = delete.into_option();
4300        self
4301    }
4302
4303    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
4304    ///
4305    /// Omitted or `null` both mean the agent does not advertise support.
4306    /// Supplying `{}` means the agent supports `additionalDirectories` on
4307    /// supported session lifecycle requests.
4308    ///
4309    /// Agents may return `SessionInfo.additionalDirectories` to report the
4310    /// complete ordered additional-root list associated with a listed session.
4311    #[must_use]
4312    pub fn additional_directories(
4313        mut self,
4314        additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
4315    ) -> Self {
4316        self.additional_directories = additional_directories.into_option();
4317        self
4318    }
4319
4320    #[cfg(feature = "unstable_session_fork")]
4321    /// Whether the agent supports `session/fork`.
4322    ///
4323    /// Omitted or `null` both mean the agent does not advertise support.
4324    /// Supplying `{}` means the agent supports forking sessions.
4325    #[must_use]
4326    pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
4327        self.fork = fork.into_option();
4328        self
4329    }
4330
4331    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4332    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4333    /// these keys.
4334    ///
4335    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4336    #[must_use]
4337    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4338        self.meta = meta.into_option();
4339        self
4340    }
4341}
4342
4343/// Capabilities for the `session/delete` method.
4344///
4345/// Supplying `{}` means the agent supports deleting sessions from `session/list`.
4346#[serde_as]
4347#[skip_serializing_none]
4348#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4349#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4350#[non_exhaustive]
4351pub struct SessionDeleteCapabilities {
4352    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4353    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4354    /// these keys.
4355    ///
4356    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4357    #[serde_as(deserialize_as = "DefaultOnError")]
4358    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4359    #[serde(default)]
4360    #[serde(rename = "_meta")]
4361    pub meta: Option<Meta>,
4362}
4363
4364impl SessionDeleteCapabilities {
4365    /// Builds an empty [`SessionDeleteCapabilities`]; use builder methods to advertise supported sub-capabilities.
4366    #[must_use]
4367    pub fn new() -> Self {
4368        Self::default()
4369    }
4370
4371    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4372    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4373    /// these keys.
4374    ///
4375    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4376    #[must_use]
4377    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4378        self.meta = meta.into_option();
4379        self
4380    }
4381}
4382
4383/// Capabilities for additional session directories support.
4384///
4385/// Supplying `{}` means the agent supports the `additionalDirectories` field on
4386/// supported session lifecycle requests. Agents that also support
4387/// `session/list` may return `SessionInfo.additionalDirectories` to report the
4388/// complete ordered additional-root list associated with a listed session.
4389#[serde_as]
4390#[skip_serializing_none]
4391#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4392#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4393#[non_exhaustive]
4394pub struct SessionAdditionalDirectoriesCapabilities {
4395    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4396    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4397    /// these keys.
4398    ///
4399    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4400    #[serde_as(deserialize_as = "DefaultOnError")]
4401    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4402    #[serde(default)]
4403    #[serde(rename = "_meta")]
4404    pub meta: Option<Meta>,
4405}
4406
4407impl SessionAdditionalDirectoriesCapabilities {
4408    /// Builds an empty [`SessionAdditionalDirectoriesCapabilities`]; use builder methods to advertise supported sub-capabilities.
4409    #[must_use]
4410    pub fn new() -> Self {
4411        Self::default()
4412    }
4413
4414    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4415    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4416    /// these keys.
4417    ///
4418    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4419    #[must_use]
4420    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4421        self.meta = meta.into_option();
4422        self
4423    }
4424}
4425
4426/// **UNSTABLE**
4427///
4428/// This capability is not part of the spec yet, and may be removed or changed at any point.
4429///
4430/// Capabilities for the `session/fork` method.
4431///
4432/// Supplying `{}` means the agent supports forking sessions.
4433#[cfg(feature = "unstable_session_fork")]
4434#[serde_as]
4435#[skip_serializing_none]
4436#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4437#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4438#[non_exhaustive]
4439pub struct SessionForkCapabilities {
4440    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4441    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4442    /// these keys.
4443    ///
4444    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4445    #[serde_as(deserialize_as = "DefaultOnError")]
4446    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4447    #[serde(default)]
4448    #[serde(rename = "_meta")]
4449    pub meta: Option<Meta>,
4450}
4451
4452#[cfg(feature = "unstable_session_fork")]
4453impl SessionForkCapabilities {
4454    /// Builds an empty [`SessionForkCapabilities`]; use builder methods to advertise supported sub-capabilities.
4455    #[must_use]
4456    pub fn new() -> Self {
4457        Self::default()
4458    }
4459
4460    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4461    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4462    /// these keys.
4463    ///
4464    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4465    #[must_use]
4466    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4467        self.meta = meta.into_option();
4468        self
4469    }
4470}
4471
4472/// Prompt capabilities supported by the agent in `session/prompt` requests.
4473///
4474/// Baseline agent functionality requires support for [`ContentBlock::Text`]
4475/// and [`ContentBlock::ResourceLink`] in prompt requests.
4476///
4477/// Other variants must be explicitly opted in to.
4478/// Capabilities for different types of content in prompt requests.
4479///
4480/// Indicates which content types beyond the baseline (text and resource links)
4481/// the agent can process.
4482///
4483/// See protocol docs: [Prompt Capabilities](https://agentclientprotocol.com/protocol/initialization#prompt-capabilities)
4484#[serde_as]
4485#[skip_serializing_none]
4486#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4487#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4488#[serde(rename_all = "camelCase")]
4489#[non_exhaustive]
4490pub struct PromptCapabilities {
4491    /// Agent supports [`ContentBlock::Image`].
4492    ///
4493    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4494    /// Supplying `{}` means the agent supports image content in prompts.
4495    #[serde_as(deserialize_as = "DefaultOnError")]
4496    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4497    #[serde(default)]
4498    pub image: Option<PromptImageCapabilities>,
4499    /// Agent supports [`ContentBlock::Audio`].
4500    ///
4501    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4502    /// Supplying `{}` means the agent supports audio content in prompts.
4503    #[serde_as(deserialize_as = "DefaultOnError")]
4504    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4505    #[serde(default)]
4506    pub audio: Option<PromptAudioCapabilities>,
4507    /// Agent supports embedded context in `session/prompt` requests.
4508    ///
4509    /// When enabled, the Client is allowed to include [`ContentBlock::Resource`]
4510    /// in prompt requests for pieces of context that are referenced in the message.
4511    ///
4512    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4513    /// Supplying `{}` means the agent supports embedded context in prompts.
4514    #[serde_as(deserialize_as = "DefaultOnError")]
4515    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4516    #[serde(default)]
4517    pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
4518    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4519    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4520    /// these keys.
4521    ///
4522    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4523    #[serde_as(deserialize_as = "DefaultOnError")]
4524    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4525    #[serde(default)]
4526    #[serde(rename = "_meta")]
4527    pub meta: Option<Meta>,
4528}
4529
4530impl PromptCapabilities {
4531    /// Builds an empty [`PromptCapabilities`]; use builder methods to advertise supported sub-capabilities.
4532    #[must_use]
4533    pub fn new() -> Self {
4534        Self::default()
4535    }
4536
4537    /// Agent supports [`ContentBlock::Image`].
4538    ///
4539    /// Omitted or `null` both mean the agent does not advertise support.
4540    /// Supplying `{}` means the agent supports image content in prompts.
4541    #[must_use]
4542    pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
4543        self.image = image.into_option();
4544        self
4545    }
4546
4547    /// Agent supports [`ContentBlock::Audio`].
4548    ///
4549    /// Omitted or `null` both mean the agent does not advertise support.
4550    /// Supplying `{}` means the agent supports audio content in prompts.
4551    #[must_use]
4552    pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
4553        self.audio = audio.into_option();
4554        self
4555    }
4556
4557    /// Agent supports embedded context in `session/prompt` requests.
4558    ///
4559    /// When enabled, the Client is allowed to include [`ContentBlock::Resource`]
4560    /// in prompt requests for pieces of context that are referenced in the message.
4561    ///
4562    /// Omitted or `null` both mean the agent does not advertise support.
4563    /// Supplying `{}` means the agent supports embedded context in prompts.
4564    #[must_use]
4565    pub fn embedded_context(
4566        mut self,
4567        embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
4568    ) -> Self {
4569        self.embedded_context = embedded_context.into_option();
4570        self
4571    }
4572
4573    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4574    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4575    /// these keys.
4576    ///
4577    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4578    #[must_use]
4579    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4580        self.meta = meta.into_option();
4581        self
4582    }
4583}
4584
4585/// Capabilities for image content in prompt requests.
4586///
4587/// Supplying `{}` means the agent supports image content in prompts.
4588#[serde_as]
4589#[skip_serializing_none]
4590#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4591#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4592#[non_exhaustive]
4593pub struct PromptImageCapabilities {
4594    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4595    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4596    /// these keys.
4597    ///
4598    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4599    #[serde_as(deserialize_as = "DefaultOnError")]
4600    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4601    #[serde(default)]
4602    #[serde(rename = "_meta")]
4603    pub meta: Option<Meta>,
4604}
4605
4606impl PromptImageCapabilities {
4607    /// Builds an empty [`PromptImageCapabilities`]; use builder methods to advertise supported sub-capabilities.
4608    #[must_use]
4609    pub fn new() -> Self {
4610        Self::default()
4611    }
4612
4613    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4614    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4615    /// these keys.
4616    ///
4617    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4618    #[must_use]
4619    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4620        self.meta = meta.into_option();
4621        self
4622    }
4623}
4624
4625/// Capabilities for audio content in prompt requests.
4626///
4627/// Supplying `{}` means the agent supports audio content in prompts.
4628#[serde_as]
4629#[skip_serializing_none]
4630#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4631#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4632#[non_exhaustive]
4633pub struct PromptAudioCapabilities {
4634    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4635    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4636    /// these keys.
4637    ///
4638    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4639    #[serde_as(deserialize_as = "DefaultOnError")]
4640    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4641    #[serde(default)]
4642    #[serde(rename = "_meta")]
4643    pub meta: Option<Meta>,
4644}
4645
4646impl PromptAudioCapabilities {
4647    /// Builds an empty [`PromptAudioCapabilities`]; use builder methods to advertise supported sub-capabilities.
4648    #[must_use]
4649    pub fn new() -> Self {
4650        Self::default()
4651    }
4652
4653    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4654    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4655    /// these keys.
4656    ///
4657    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4658    #[must_use]
4659    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4660        self.meta = meta.into_option();
4661        self
4662    }
4663}
4664
4665/// Capabilities for embedded context in prompt requests.
4666///
4667/// Supplying `{}` means the agent supports embedded context in prompts.
4668#[serde_as]
4669#[skip_serializing_none]
4670#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4671#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4672#[non_exhaustive]
4673pub struct PromptEmbeddedContextCapabilities {
4674    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4675    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4676    /// these keys.
4677    ///
4678    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4679    #[serde_as(deserialize_as = "DefaultOnError")]
4680    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4681    #[serde(default)]
4682    #[serde(rename = "_meta")]
4683    pub meta: Option<Meta>,
4684}
4685
4686impl PromptEmbeddedContextCapabilities {
4687    /// Builds an empty [`PromptEmbeddedContextCapabilities`]; use builder methods to advertise supported sub-capabilities.
4688    #[must_use]
4689    pub fn new() -> Self {
4690        Self::default()
4691    }
4692
4693    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4694    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4695    /// these keys.
4696    ///
4697    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4698    #[must_use]
4699    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4700        self.meta = meta.into_option();
4701        self
4702    }
4703}
4704
4705/// MCP capabilities supported by the agent for session lifecycle requests.
4706#[serde_as]
4707#[skip_serializing_none]
4708#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4709#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4710#[serde(rename_all = "camelCase")]
4711#[non_exhaustive]
4712pub struct McpCapabilities {
4713    /// Agent supports [`McpServer::Stdio`].
4714    ///
4715    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4716    /// Supplying `{}` means the agent supports stdio MCP server transports.
4717    #[serde_as(deserialize_as = "DefaultOnError")]
4718    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4719    #[serde(default)]
4720    pub stdio: Option<McpStdioCapabilities>,
4721    /// Agent supports [`McpServer::Http`].
4722    ///
4723    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4724    /// Supplying `{}` means the agent supports HTTP MCP server transports.
4725    #[serde_as(deserialize_as = "DefaultOnError")]
4726    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4727    #[serde(default)]
4728    pub http: Option<McpHttpCapabilities>,
4729    /// **UNSTABLE**
4730    ///
4731    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4732    ///
4733    /// Agent supports [`McpServer::Acp`].
4734    ///
4735    /// Optional. Omitted or `null` both mean the agent does not advertise support.
4736    /// Supplying `{}` means the agent supports ACP MCP server transports.
4737    #[cfg(feature = "unstable_mcp_over_acp")]
4738    #[serde_as(deserialize_as = "DefaultOnError")]
4739    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4740    #[serde(default)]
4741    pub acp: Option<McpAcpCapabilities>,
4742    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4743    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4744    /// these keys.
4745    ///
4746    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4747    #[serde_as(deserialize_as = "DefaultOnError")]
4748    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4749    #[serde(default)]
4750    #[serde(rename = "_meta")]
4751    pub meta: Option<Meta>,
4752}
4753
4754impl McpCapabilities {
4755    /// Builds an empty [`McpCapabilities`]; use builder methods to advertise supported sub-capabilities.
4756    #[must_use]
4757    pub fn new() -> Self {
4758        Self::default()
4759    }
4760
4761    /// Agent supports [`McpServer::Stdio`].
4762    ///
4763    /// Omitted or `null` both mean the agent does not advertise support.
4764    /// Supplying `{}` means the agent supports stdio MCP server transports.
4765    #[must_use]
4766    pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
4767        self.stdio = stdio.into_option();
4768        self
4769    }
4770
4771    /// Agent supports [`McpServer::Http`].
4772    ///
4773    /// Omitted or `null` both mean the agent does not advertise support.
4774    /// Supplying `{}` means the agent supports HTTP MCP server transports.
4775    #[must_use]
4776    pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
4777        self.http = http.into_option();
4778        self
4779    }
4780
4781    /// **UNSTABLE**
4782    ///
4783    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4784    ///
4785    /// Agent supports [`McpServer::Acp`].
4786    #[cfg(feature = "unstable_mcp_over_acp")]
4787    ///
4788    /// Omitted or `null` both mean the agent does not advertise support.
4789    /// Supplying `{}` means the agent supports ACP MCP server transports.
4790    #[must_use]
4791    pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
4792        self.acp = acp.into_option();
4793        self
4794    }
4795
4796    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4797    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4798    /// these keys.
4799    ///
4800    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4801    #[must_use]
4802    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4803        self.meta = meta.into_option();
4804        self
4805    }
4806}
4807
4808/// Capabilities for stdio MCP server transports.
4809///
4810/// Supplying `{}` means the agent supports stdio MCP server transports.
4811#[serde_as]
4812#[skip_serializing_none]
4813#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4814#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4815#[non_exhaustive]
4816pub struct McpStdioCapabilities {
4817    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4818    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4819    /// these keys.
4820    ///
4821    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4822    #[serde_as(deserialize_as = "DefaultOnError")]
4823    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4824    #[serde(default)]
4825    #[serde(rename = "_meta")]
4826    pub meta: Option<Meta>,
4827}
4828
4829impl McpStdioCapabilities {
4830    /// Builds an empty [`McpStdioCapabilities`]; use builder methods to advertise supported sub-capabilities.
4831    #[must_use]
4832    pub fn new() -> Self {
4833        Self::default()
4834    }
4835
4836    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4837    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4838    /// these keys.
4839    ///
4840    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4841    #[must_use]
4842    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4843        self.meta = meta.into_option();
4844        self
4845    }
4846}
4847
4848/// Capabilities for HTTP MCP server transports.
4849///
4850/// Supplying `{}` means the agent supports HTTP MCP server transports.
4851#[serde_as]
4852#[skip_serializing_none]
4853#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4854#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4855#[non_exhaustive]
4856pub struct McpHttpCapabilities {
4857    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4858    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4859    /// these keys.
4860    ///
4861    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4862    #[serde_as(deserialize_as = "DefaultOnError")]
4863    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4864    #[serde(default)]
4865    #[serde(rename = "_meta")]
4866    pub meta: Option<Meta>,
4867}
4868
4869impl McpHttpCapabilities {
4870    /// Builds an empty [`McpHttpCapabilities`]; use builder methods to advertise supported sub-capabilities.
4871    #[must_use]
4872    pub fn new() -> Self {
4873        Self::default()
4874    }
4875
4876    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4877    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4878    /// these keys.
4879    ///
4880    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4881    #[must_use]
4882    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4883        self.meta = meta.into_option();
4884        self
4885    }
4886}
4887
4888/// **UNSTABLE**
4889///
4890/// This capability is not part of the spec yet, and may be removed or changed at any point.
4891///
4892/// Capabilities for ACP MCP server transports.
4893///
4894/// Supplying `{}` means the agent supports ACP MCP server transports.
4895#[cfg(feature = "unstable_mcp_over_acp")]
4896#[serde_as]
4897#[skip_serializing_none]
4898#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4899#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4900#[non_exhaustive]
4901pub struct McpAcpCapabilities {
4902    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4903    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4904    /// these keys.
4905    ///
4906    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4907    #[serde_as(deserialize_as = "DefaultOnError")]
4908    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4909    #[serde(default)]
4910    #[serde(rename = "_meta")]
4911    pub meta: Option<Meta>,
4912}
4913
4914#[cfg(feature = "unstable_mcp_over_acp")]
4915impl McpAcpCapabilities {
4916    /// Builds an empty [`McpAcpCapabilities`]; use builder methods to advertise supported sub-capabilities.
4917    #[must_use]
4918    pub fn new() -> Self {
4919        Self::default()
4920    }
4921
4922    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4923    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4924    /// these keys.
4925    ///
4926    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4927    #[must_use]
4928    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4929        self.meta = meta.into_option();
4930        self
4931    }
4932}
4933
4934/// Notification to cancel ongoing operations for a session.
4935///
4936/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-lifecycle#cancellation)
4937#[serde_as]
4938#[skip_serializing_none]
4939#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4940#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4941#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
4942#[serde(rename_all = "camelCase")]
4943#[non_exhaustive]
4944pub struct CancelSessionNotification {
4945    /// The ID of the session to cancel operations for.
4946    pub session_id: SessionId,
4947    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4948    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4949    /// these keys.
4950    ///
4951    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4952    #[serde_as(deserialize_as = "DefaultOnError")]
4953    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4954    #[serde(default)]
4955    #[serde(rename = "_meta")]
4956    pub meta: Option<Meta>,
4957}
4958
4959impl CancelSessionNotification {
4960    /// Builds [`CancelSessionNotification`] with the required notification fields set; optional fields start unset or empty.
4961    #[must_use]
4962    pub fn new(session_id: impl Into<SessionId>) -> Self {
4963        Self {
4964            session_id: session_id.into(),
4965            meta: None,
4966        }
4967    }
4968
4969    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4970    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
4971    /// these keys.
4972    ///
4973    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
4974    #[must_use]
4975    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4976        self.meta = meta.into_option();
4977        self
4978    }
4979}
4980
4981// Method schema
4982
4983/// Names of all methods that agents handle.
4984///
4985/// Provides a centralized definition of method names used in the protocol.
4986#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4987#[non_exhaustive]
4988pub struct AgentMethodNames {
4989    /// Method for initializing the connection.
4990    pub initialize: &'static str,
4991    /// Method for authenticating with the agent.
4992    pub auth_login: &'static str,
4993    /// Method for listing configurable providers.
4994    #[cfg(feature = "unstable_llm_providers")]
4995    pub providers_list: &'static str,
4996    /// Method for setting provider configuration.
4997    #[cfg(feature = "unstable_llm_providers")]
4998    pub providers_set: &'static str,
4999    /// Method for disabling a provider.
5000    #[cfg(feature = "unstable_llm_providers")]
5001    pub providers_disable: &'static str,
5002    /// Method for creating a new session.
5003    pub session_new: &'static str,
5004    /// Method for setting a configuration option for a session.
5005    pub session_set_config_option: &'static str,
5006    /// Method for sending a prompt to the agent.
5007    pub session_prompt: &'static str,
5008    /// Notification for cancelling operations.
5009    pub session_cancel: &'static str,
5010    /// Method for exchanging MCP-over-ACP messages.
5011    #[cfg(feature = "unstable_mcp_over_acp")]
5012    pub mcp_message: &'static str,
5013    /// Method for listing existing sessions.
5014    pub session_list: &'static str,
5015    /// Method for deleting an existing session.
5016    pub session_delete: &'static str,
5017    /// Method for forking an existing session.
5018    #[cfg(feature = "unstable_session_fork")]
5019    pub session_fork: &'static str,
5020    /// Method for resuming an existing session.
5021    pub session_resume: &'static str,
5022    /// Method for closing an active session.
5023    pub session_close: &'static str,
5024    /// Method for logging out of an authenticated session.
5025    pub auth_logout: &'static str,
5026    /// Method for starting an NES session.
5027    #[cfg(feature = "unstable_nes")]
5028    pub nes_start: &'static str,
5029    /// Method for requesting a suggestion.
5030    #[cfg(feature = "unstable_nes")]
5031    pub nes_suggest: &'static str,
5032    /// Notification for accepting a suggestion.
5033    #[cfg(feature = "unstable_nes")]
5034    pub nes_accept: &'static str,
5035    /// Notification for rejecting a suggestion.
5036    #[cfg(feature = "unstable_nes")]
5037    pub nes_reject: &'static str,
5038    /// Method for closing an NES session.
5039    #[cfg(feature = "unstable_nes")]
5040    pub nes_close: &'static str,
5041    /// Notification for document open events.
5042    #[cfg(feature = "unstable_nes")]
5043    pub document_did_open: &'static str,
5044    /// Notification for document change events.
5045    #[cfg(feature = "unstable_nes")]
5046    pub document_did_change: &'static str,
5047    /// Notification for document close events.
5048    #[cfg(feature = "unstable_nes")]
5049    pub document_did_close: &'static str,
5050    /// Notification for document save events.
5051    #[cfg(feature = "unstable_nes")]
5052    pub document_did_save: &'static str,
5053    /// Notification for document focus events.
5054    #[cfg(feature = "unstable_nes")]
5055    pub document_did_focus: &'static str,
5056}
5057
5058/// Constant containing all agent method names.
5059pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
5060    initialize: INITIALIZE_METHOD_NAME,
5061    auth_login: AUTH_LOGIN_METHOD_NAME,
5062    #[cfg(feature = "unstable_llm_providers")]
5063    providers_list: PROVIDERS_LIST_METHOD_NAME,
5064    #[cfg(feature = "unstable_llm_providers")]
5065    providers_set: PROVIDERS_SET_METHOD_NAME,
5066    #[cfg(feature = "unstable_llm_providers")]
5067    providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
5068    session_new: SESSION_NEW_METHOD_NAME,
5069    session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
5070    session_prompt: SESSION_PROMPT_METHOD_NAME,
5071    session_cancel: SESSION_CANCEL_METHOD_NAME,
5072    #[cfg(feature = "unstable_mcp_over_acp")]
5073    mcp_message: MCP_MESSAGE_METHOD_NAME,
5074    session_list: SESSION_LIST_METHOD_NAME,
5075    session_delete: SESSION_DELETE_METHOD_NAME,
5076    #[cfg(feature = "unstable_session_fork")]
5077    session_fork: SESSION_FORK_METHOD_NAME,
5078    session_resume: SESSION_RESUME_METHOD_NAME,
5079    session_close: SESSION_CLOSE_METHOD_NAME,
5080    auth_logout: AUTH_LOGOUT_METHOD_NAME,
5081    #[cfg(feature = "unstable_nes")]
5082    nes_start: NES_START_METHOD_NAME,
5083    #[cfg(feature = "unstable_nes")]
5084    nes_suggest: NES_SUGGEST_METHOD_NAME,
5085    #[cfg(feature = "unstable_nes")]
5086    nes_accept: NES_ACCEPT_METHOD_NAME,
5087    #[cfg(feature = "unstable_nes")]
5088    nes_reject: NES_REJECT_METHOD_NAME,
5089    #[cfg(feature = "unstable_nes")]
5090    nes_close: NES_CLOSE_METHOD_NAME,
5091    #[cfg(feature = "unstable_nes")]
5092    document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME,
5093    #[cfg(feature = "unstable_nes")]
5094    document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME,
5095    #[cfg(feature = "unstable_nes")]
5096    document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME,
5097    #[cfg(feature = "unstable_nes")]
5098    document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME,
5099    #[cfg(feature = "unstable_nes")]
5100    document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME,
5101};
5102
5103/// Method name for the initialize request.
5104pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
5105/// Method name for the `auth/login` request.
5106pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
5107/// Method name for listing configurable providers.
5108#[cfg(feature = "unstable_llm_providers")]
5109pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
5110/// Method name for setting provider configuration.
5111#[cfg(feature = "unstable_llm_providers")]
5112pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
5113/// Method name for disabling a provider.
5114#[cfg(feature = "unstable_llm_providers")]
5115pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
5116/// Method name for creating a new session.
5117pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
5118/// Method name for setting a configuration option for a session.
5119pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
5120/// Method name for sending a prompt.
5121pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
5122/// Method name for the cancel notification.
5123pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
5124/// Method name for listing existing sessions.
5125pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
5126/// Method name for deleting an existing session.
5127pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
5128/// Method name for forking an existing session.
5129#[cfg(feature = "unstable_session_fork")]
5130pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
5131/// Method name for resuming an existing session.
5132pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
5133/// Method name for closing an active session.
5134pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
5135/// Method name for the `auth/logout` request.
5136pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
5137
5138/// All possible requests that a client can send to an agent.
5139///
5140/// This enum is used internally for routing RPC requests. You typically won't need
5141/// to use this directly.
5142///
5143/// This enum encompasses all method calls from client to agent.
5144#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5145#[derive(Clone, Debug, Serialize, Deserialize)]
5146#[serde(untagged)]
5147#[cfg_attr(feature = "schemars", schemars(inline))]
5148#[non_exhaustive]
5149pub enum ClientRequest {
5150    /// Establishes the connection with a client and negotiates protocol capabilities.
5151    ///
5152    /// This method is called once at the beginning of the connection to:
5153    /// - Negotiate the protocol version to use
5154    /// - Exchange capability information between client and agent
5155    /// - Determine available authentication methods
5156    ///
5157    /// The agent should respond with its supported protocol version and capabilities.
5158    ///
5159    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
5160    InitializeRequest(Box<InitializeRequest>),
5161    /// Authenticates the client using the specified authentication method.
5162    ///
5163    /// Agents MUST support this method when their `initialize` response advertised
5164    /// at least one valid authentication method. Clients MUST call this method only
5165    /// with a method whose type defines a protocol-driven login flow, and MUST NOT
5166    /// call it when `authMethods` was omitted or empty.
5167    ///
5168    /// Called when the agent requires authentication before allowing session creation.
5169    /// The client provides the authentication method ID that was advertised during initialization.
5170    ///
5171    /// After successful authentication, the client can proceed to create sessions with
5172    /// `new_session` without receiving an `auth_required` error.
5173    ///
5174    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/initialization)
5175    LoginAuthRequest(Box<LoginAuthRequest>),
5176    /// **UNSTABLE**
5177    ///
5178    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5179    ///
5180    /// Lists providers that can be configured by the client.
5181    #[cfg(feature = "unstable_llm_providers")]
5182    ListProvidersRequest(Box<ListProvidersRequest>),
5183    /// **UNSTABLE**
5184    ///
5185    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5186    ///
5187    /// Replaces the configuration for a provider.
5188    #[cfg(feature = "unstable_llm_providers")]
5189    SetProviderRequest(Box<SetProviderRequest>),
5190    /// **UNSTABLE**
5191    ///
5192    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5193    ///
5194    /// Disables a provider.
5195    #[cfg(feature = "unstable_llm_providers")]
5196    DisableProviderRequest(Box<DisableProviderRequest>),
5197    /// Logs out of the current authenticated state.
5198    ///
5199    /// Agents MUST support this method when their `initialize` response advertised
5200    /// at least one valid authentication method. Clients MUST NOT call this method
5201    /// when `authMethods` was omitted or empty.
5202    ///
5203    /// After a successful logout, authentication-gated requests require the
5204    /// client to complete an advertised authentication flow again. There is no
5205    /// guarantee about the behavior of already running sessions.
5206    LogoutAuthRequest(Box<LogoutAuthRequest>),
5207    /// Creates a new conversation session with the agent.
5208    ///
5209    /// Sessions represent independent conversation contexts with their own history and state.
5210    ///
5211    /// The agent should:
5212    /// - Create a new session context
5213    /// - Connect to any specified MCP servers
5214    /// - Return a unique session ID for future requests
5215    ///
5216    /// May return an `auth_required` error if the agent requires authentication.
5217    ///
5218    /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/session-setup)
5219    NewSessionRequest(Box<NewSessionRequest>),
5220    /// Lists existing sessions known to the agent.
5221    ///
5222    /// The agent should return metadata about sessions with optional filtering and pagination support.
5223    ListSessionsRequest(Box<ListSessionsRequest>),
5224    /// Deletes an existing session from `session/list`.
5225    ///
5226    /// This method is only available if the agent advertises the `session.delete` capability.
5227    DeleteSessionRequest(Box<DeleteSessionRequest>),
5228    #[cfg(feature = "unstable_session_fork")]
5229    /// **UNSTABLE**
5230    ///
5231    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5232    ///
5233    /// Forks an existing session to create a new independent session.
5234    ///
5235    /// This method is only available if the agent advertises the `session.fork` capability.
5236    ///
5237    /// The agent should create a new session with the same conversation context as the
5238    /// original, allowing operations like generating summaries without affecting the
5239    /// original session's history.
5240    ForkSessionRequest(Box<ForkSessionRequest>),
5241    /// Resumes an existing session.
5242    ///
5243    /// The agent should resume the session context, allowing the conversation
5244    /// to continue. If `replayFrom` is set, the agent should replay
5245    /// retained conversation history before responding.
5246    ResumeSessionRequest(Box<ResumeSessionRequest>),
5247    /// Closes an active session and frees up any resources associated with it.
5248    ///
5249    /// The agent must cancel any ongoing work (as if `session/cancel` was called)
5250    /// and then free up any resources associated with the session.
5251    CloseSessionRequest(Box<CloseSessionRequest>),
5252    /// Sets the current value for a session configuration option.
5253    SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
5254    /// Processes a user prompt within a session.
5255    ///
5256    /// Acceptance means insertion into the ACP conversation:
5257    /// - Receives user messages with optional context (files, images, etc.)
5258    /// - Returns the inserted user message's ID without waiting for processing to finish
5259    ///
5260    /// The Agent reports the user message with the same ID through `session/update`;
5261    /// this notification may arrive before or after the response. Processing state,
5262    /// output, tool calls, and completion are also reported through session updates.
5263    ///
5264    /// See protocol docs: [Prompt Lifecycle](https://agentclientprotocol.com/protocol/prompt-lifecycle)
5265    PromptRequest(Box<PromptRequest>),
5266    #[cfg(feature = "unstable_nes")]
5267    /// **UNSTABLE**
5268    ///
5269    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5270    ///
5271    /// Starts an NES session.
5272    StartNesRequest(Box<StartNesRequest>),
5273    #[cfg(feature = "unstable_nes")]
5274    /// **UNSTABLE**
5275    ///
5276    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5277    ///
5278    /// Requests a code suggestion.
5279    SuggestNesRequest(Box<SuggestNesRequest>),
5280    #[cfg(feature = "unstable_nes")]
5281    /// **UNSTABLE**
5282    ///
5283    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5284    ///
5285    /// Closes an active NES session and frees up any resources associated with it.
5286    ///
5287    /// The agent must cancel any ongoing work and then free up any resources
5288    /// associated with the NES session.
5289    CloseNesRequest(Box<CloseNesRequest>),
5290    /// Handles extension method requests from the client.
5291    ///
5292    /// Extension methods provide a way to add custom functionality while maintaining
5293    /// protocol compatibility.
5294    ///
5295    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
5296    ExtMethodRequest(Box<ExtRequest>),
5297}
5298
5299impl ClientRequest {
5300    /// Returns the corresponding method name of the request.
5301    #[must_use]
5302    pub fn method(&self) -> &str {
5303        match self {
5304            Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
5305            Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
5306            #[cfg(feature = "unstable_llm_providers")]
5307            Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
5308            #[cfg(feature = "unstable_llm_providers")]
5309            Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
5310            #[cfg(feature = "unstable_llm_providers")]
5311            Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
5312            Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
5313            Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
5314            Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
5315            Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
5316            #[cfg(feature = "unstable_session_fork")]
5317            Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
5318            Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
5319            Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
5320            Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
5321            Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
5322            #[cfg(feature = "unstable_nes")]
5323            Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start,
5324            #[cfg(feature = "unstable_nes")]
5325            Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest,
5326            #[cfg(feature = "unstable_nes")]
5327            Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close,
5328            Self::ExtMethodRequest(ext_request) => &ext_request.method,
5329        }
5330    }
5331}
5332
5333/// All possible responses that an agent can send to a client.
5334///
5335/// This enum is used internally for routing RPC responses. You typically won't need
5336/// to use this directly - the responses are handled automatically by the connection.
5337///
5338/// These are responses to the corresponding `ClientRequest` variants.
5339#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5340#[derive(Clone, Debug, Serialize, Deserialize)]
5341#[serde(untagged)]
5342#[cfg_attr(feature = "schemars", schemars(inline))]
5343#[non_exhaustive]
5344pub enum AgentResponse {
5345    /// Successful result returned for a `initialize` request.
5346    InitializeResponse(Box<InitializeResponse>),
5347    /// Successful result returned for an `auth/login` request.
5348    LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
5349    /// Successful result returned for a `providers/list` request.
5350    #[cfg(feature = "unstable_llm_providers")]
5351    ListProvidersResponse(Box<ListProvidersResponse>),
5352    /// Successful result returned for a `providers/set` request.
5353    #[cfg(feature = "unstable_llm_providers")]
5354    SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
5355    /// Successful result returned for a `providers/disable` request.
5356    #[cfg(feature = "unstable_llm_providers")]
5357    DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
5358    /// Successful result returned for an `auth/logout` request.
5359    LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
5360    /// Successful result returned for a `session/new` request.
5361    NewSessionResponse(Box<NewSessionResponse>),
5362    /// Successful result returned for a `session/list` request.
5363    ListSessionsResponse(Box<ListSessionsResponse>),
5364    /// Successful result returned for a `session/delete` request.
5365    DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
5366    /// Successful result returned for a `session/fork` request.
5367    #[cfg(feature = "unstable_session_fork")]
5368    ForkSessionResponse(Box<ForkSessionResponse>),
5369    /// Successful result returned for a `session/resume` request.
5370    ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
5371    /// Successful result returned for a `session/close` request.
5372    CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
5373    /// Successful result returned for a `session/set_config_option` request.
5374    SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
5375    /// Successful result returned for a `session/prompt` request.
5376    PromptResponse(Box<PromptResponse>),
5377    /// Successful result returned for a `nes/start` request.
5378    #[cfg(feature = "unstable_nes")]
5379    StartNesResponse(Box<StartNesResponse>),
5380    /// Successful result returned for a `nes/suggest` request.
5381    #[cfg(feature = "unstable_nes")]
5382    SuggestNesResponse(Box<SuggestNesResponse>),
5383    /// Successful result returned for a `nes/close` request.
5384    #[cfg(feature = "unstable_nes")]
5385    CloseNesResponse(#[serde(default)] Box<CloseNesResponse>),
5386    /// Successful result returned by an extension method outside the core ACP method set.
5387    ExtMethodResponse(Box<ExtResponse>),
5388}
5389
5390/// All possible notifications that a client can send to an agent.
5391///
5392/// This enum is used internally for routing RPC notifications. You typically won't need
5393/// to use this directly.
5394///
5395/// Notifications do not expect a response.
5396#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5397#[derive(Clone, Debug, Serialize, Deserialize)]
5398#[serde(untagged)]
5399#[cfg_attr(feature = "schemars", schemars(inline))]
5400#[non_exhaustive]
5401pub enum ClientNotification {
5402    /// Cancels ongoing operations for a session.
5403    ///
5404    /// This is a notification sent by the client to cancel active work in a
5405    /// session.
5406    ///
5407    /// Upon receiving this notification, the Agent SHOULD:
5408    /// - Stop all language model requests as soon as possible
5409    /// - Abort all tool call invocations in progress
5410    /// - Send any pending `session/update` notifications
5411    /// - Report an idle `state_update` with `StopReason::Cancelled` after
5412    ///   cancellation succeeds
5413    ///
5414    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-lifecycle#cancellation)
5415    CancelSessionNotification(Box<CancelSessionNotification>),
5416    #[cfg(feature = "unstable_nes")]
5417    /// **UNSTABLE**
5418    ///
5419    /// Notification sent when a file is opened in the editor.
5420    DidOpenDocumentNotification(Box<DidOpenDocumentNotification>),
5421    #[cfg(feature = "unstable_nes")]
5422    /// **UNSTABLE**
5423    ///
5424    /// Notification sent when a file is edited.
5425    DidChangeDocumentNotification(Box<DidChangeDocumentNotification>),
5426    #[cfg(feature = "unstable_nes")]
5427    /// **UNSTABLE**
5428    ///
5429    /// Notification sent when a file is closed.
5430    DidCloseDocumentNotification(Box<DidCloseDocumentNotification>),
5431    #[cfg(feature = "unstable_nes")]
5432    /// **UNSTABLE**
5433    ///
5434    /// Notification sent when a file is saved.
5435    DidSaveDocumentNotification(Box<DidSaveDocumentNotification>),
5436    #[cfg(feature = "unstable_nes")]
5437    /// **UNSTABLE**
5438    ///
5439    /// Notification sent when a file becomes the active editor tab.
5440    DidFocusDocumentNotification(Box<DidFocusDocumentNotification>),
5441    #[cfg(feature = "unstable_nes")]
5442    /// **UNSTABLE**
5443    ///
5444    /// Notification sent when a suggestion is accepted.
5445    AcceptNesNotification(Box<AcceptNesNotification>),
5446    #[cfg(feature = "unstable_nes")]
5447    /// **UNSTABLE**
5448    ///
5449    /// Notification sent when a suggestion is rejected.
5450    RejectNesNotification(Box<RejectNesNotification>),
5451    /// **UNSTABLE**
5452    ///
5453    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5454    ///
5455    /// Sends an MCP-over-ACP notification.
5456    #[cfg(feature = "unstable_mcp_over_acp")]
5457    MessageMcpNotification(Box<MessageMcpNotification>),
5458    /// Handles extension notifications from the client.
5459    ///
5460    /// Extension notifications provide a way to send one-way messages for custom functionality
5461    /// while maintaining protocol compatibility.
5462    ///
5463    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
5464    ExtNotification(Box<ExtNotification>),
5465}
5466
5467impl ClientNotification {
5468    /// Returns the corresponding method name of the notification.
5469    #[must_use]
5470    pub fn method(&self) -> &str {
5471        match self {
5472            Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
5473            #[cfg(feature = "unstable_nes")]
5474            Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open,
5475            #[cfg(feature = "unstable_nes")]
5476            Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change,
5477            #[cfg(feature = "unstable_nes")]
5478            Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close,
5479            #[cfg(feature = "unstable_nes")]
5480            Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save,
5481            #[cfg(feature = "unstable_nes")]
5482            Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus,
5483            #[cfg(feature = "unstable_nes")]
5484            Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept,
5485            #[cfg(feature = "unstable_nes")]
5486            Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject,
5487            #[cfg(feature = "unstable_mcp_over_acp")]
5488            Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
5489            Self::ExtNotification(ext_notification) => &ext_notification.method,
5490        }
5491    }
5492}
5493
5494#[cfg(test)]
5495mod test_serialization {
5496    use std::path::PathBuf;
5497
5498    use super::*;
5499    use serde_json::json;
5500
5501    fn test_meta() -> Meta {
5502        json!({ "source": "test" }).as_object().unwrap().clone()
5503    }
5504
5505    fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
5506        serde_json::to_string(value)
5507            .unwrap()
5508            .matches("\"_meta\"")
5509            .count()
5510    }
5511
5512    #[test]
5513    fn prompt_response_without_metadata_round_trips() {
5514        let response = PromptResponse::new(MessageId::new("message-1"));
5515        let serialized = serde_json::to_value(&response).unwrap();
5516        assert_eq!(serialized, json!({ "messageId": "message-1" }));
5517        assert_eq!(
5518            serde_json::from_value::<PromptResponse>(serialized).unwrap(),
5519            response
5520        );
5521        assert_eq!(
5522            serde_json::from_value::<PromptResponse>(json!({
5523                "messageId": "message-1",
5524                "_meta": null
5525            }))
5526            .unwrap(),
5527            response
5528        );
5529    }
5530
5531    #[test]
5532    fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
5533        let response = PromptResponse::new("message-1").meta(test_meta());
5534        let serialized = serde_json::to_value(&response).unwrap();
5535        assert_eq!(
5536            serialized,
5537            json!({
5538                "messageId": "message-1",
5539                "_meta": { "source": "test" }
5540            })
5541        );
5542
5543        let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
5544        assert_eq!(deserialized.message_id, MessageId::new("message-1"));
5545        assert_eq!(deserialized.meta, Some(test_meta()));
5546
5547        let malformed_meta: PromptResponse = serde_json::from_value(json!({
5548            "messageId": "message-2",
5549            "_meta": false
5550        }))
5551        .unwrap();
5552        assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
5553        assert_eq!(malformed_meta.meta, None);
5554
5555        for invalid in [
5556            json!({}),
5557            json!({ "messageId": null }),
5558            json!({ "messageId": 1 }),
5559            json!({ "messageId": false }),
5560            json!({ "messageId": {} }),
5561            json!({ "messageId": [] }),
5562        ] {
5563            assert!(
5564                serde_json::from_value::<PromptResponse>(invalid).is_err(),
5565                "missing, null, and non-string message IDs must be rejected"
5566            );
5567        }
5568    }
5569
5570    #[cfg(feature = "schemars")]
5571    #[test]
5572    fn prompt_response_schema_requires_non_null_string_message_id_reference() {
5573        let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
5574
5575        assert_eq!(schema["required"], json!(["messageId"]));
5576        assert_eq!(
5577            schema["properties"]["messageId"]["$ref"],
5578            "#/$defs/MessageId"
5579        );
5580        assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
5581    }
5582
5583    #[test]
5584    fn test_initialize_capabilities_default_on_malformed_values() {
5585        let request: InitializeRequest = serde_json::from_value(json!({
5586            "protocolVersion": 2,
5587            "capabilities": false,
5588            "info": {
5589                "name": "client",
5590                "version": "1.0.0"
5591            }
5592        }))
5593        .unwrap();
5594        assert_eq!(request.capabilities, ClientCapabilities::default());
5595
5596        let response: InitializeResponse = serde_json::from_value(json!({
5597            "protocolVersion": 2,
5598            "capabilities": false,
5599            "info": {
5600                "name": "agent",
5601                "version": "1.0.0"
5602            }
5603        }))
5604        .unwrap();
5605        assert_eq!(response.capabilities, AgentCapabilities::default());
5606    }
5607
5608    #[test]
5609    fn test_agent_capabilities_default_on_malformed_values() {
5610        let capabilities: AgentCapabilities = serde_json::from_value(json!({
5611            "session": false,
5612            "auth": false
5613        }))
5614        .unwrap();
5615
5616        assert!(capabilities.session.is_none());
5617        assert_eq!(capabilities.auth, None);
5618    }
5619
5620    #[test]
5621    fn test_mcp_server_stdio_serialization() {
5622        let server = McpServer::Stdio(
5623            McpServerStdio::new("test-server", "/usr/bin/server")
5624                .args(vec!["--port".to_string(), "3000".to_string()])
5625                .env(vec![EnvVariable::new("API_KEY", "secret123")]),
5626        );
5627
5628        let json = serde_json::to_value(&server).unwrap();
5629        assert_eq!(
5630            json,
5631            json!({
5632                "type": "stdio",
5633                "name": "test-server",
5634                "command": "/usr/bin/server",
5635                "args": ["--port", "3000"],
5636                "env": [
5637                    {
5638                        "name": "API_KEY",
5639                        "value": "secret123"
5640                    }
5641                ]
5642            })
5643        );
5644
5645        let deserialized: McpServer = serde_json::from_value(json).unwrap();
5646        match deserialized {
5647            McpServer::Stdio(McpServerStdio {
5648                name,
5649                command,
5650                args,
5651                env,
5652                meta: _,
5653            }) => {
5654                assert_eq!(name, "test-server");
5655                assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
5656                assert_eq!(args, vec!["--port", "3000"]);
5657                assert_eq!(env.len(), 1);
5658                assert_eq!(env[0].name, "API_KEY");
5659                assert_eq!(env[0].value, "secret123");
5660            }
5661            _ => panic!("Expected Stdio variant"),
5662        }
5663    }
5664
5665    #[test]
5666    fn test_mcp_server_empty_arrays_are_optional() {
5667        let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
5668        assert_eq!(
5669            serde_json::to_value(&stdio).unwrap(),
5670            json!({
5671                "type": "stdio",
5672                "name": "test-server",
5673                "command": "/usr/bin/server"
5674            })
5675        );
5676
5677        let McpServer::Stdio(McpServerStdio { args, env, .. }) =
5678            serde_json::from_value::<McpServer>(json!({
5679                "type": "stdio",
5680                "name": "test-server",
5681                "command": "/usr/bin/server"
5682            }))
5683            .unwrap()
5684        else {
5685            panic!("Expected Stdio variant");
5686        };
5687        assert!(args.is_empty());
5688        assert!(env.is_empty());
5689
5690        let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
5691        assert_eq!(
5692            serde_json::to_value(&http).unwrap(),
5693            json!({
5694                "type": "http",
5695                "name": "http-server",
5696                "url": "https://api.example.com"
5697            })
5698        );
5699
5700        let McpServer::Http(McpServerHttp { headers, .. }) =
5701            serde_json::from_value::<McpServer>(json!({
5702                "type": "http",
5703                "name": "http-server",
5704                "url": "https://api.example.com"
5705            }))
5706            .unwrap()
5707        else {
5708            panic!("Expected Http variant");
5709        };
5710        assert!(headers.is_empty());
5711    }
5712
5713    #[test]
5714    fn test_mcp_server_unknown_transport_serialization() {
5715        let json = json!({
5716            "type": "websocket",
5717            "name": "future-server",
5718            "url": "wss://example.com/mcp",
5719            "protocolVersion": "2026-01-01"
5720        });
5721
5722        let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
5723        let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
5724            panic!("Expected Other variant");
5725        };
5726
5727        assert_eq!(type_, "websocket");
5728        assert_eq!(fields["name"], "future-server");
5729        assert_eq!(fields["url"], "wss://example.com/mcp");
5730        assert_eq!(fields["protocolVersion"], "2026-01-01");
5731        assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
5732    }
5733
5734    #[test]
5735    fn test_mcp_server_stdio_requires_type() {
5736        let result = serde_json::from_value::<McpServer>(json!({
5737            "name": "test-server",
5738            "command": "/usr/bin/server",
5739            "args": [],
5740            "env": []
5741        }));
5742
5743        assert!(result.is_err());
5744    }
5745
5746    #[test]
5747    fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
5748        let result = serde_json::from_value::<McpServer>(json!({
5749            "type": "stdio",
5750            "name": "test-server",
5751            "args": [],
5752            "env": []
5753        }));
5754
5755        assert!(result.is_err());
5756    }
5757
5758    #[test]
5759    fn test_mcp_server_http_serialization() {
5760        let server = McpServer::Http(
5761            McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
5762                HttpHeader::new("Authorization", "Bearer token123"),
5763                HttpHeader::new("Content-Type", "application/json"),
5764            ]),
5765        );
5766
5767        let json = serde_json::to_value(&server).unwrap();
5768        assert_eq!(
5769            json,
5770            json!({
5771                "type": "http",
5772                "name": "http-server",
5773                "url": "https://api.example.com",
5774                "headers": [
5775                    {
5776                        "name": "Authorization",
5777                        "value": "Bearer token123"
5778                    },
5779                    {
5780                        "name": "Content-Type",
5781                        "value": "application/json"
5782                    }
5783                ]
5784            })
5785        );
5786
5787        let deserialized: McpServer = serde_json::from_value(json).unwrap();
5788        match deserialized {
5789            McpServer::Http(McpServerHttp {
5790                name,
5791                url,
5792                headers,
5793                meta: _,
5794            }) => {
5795                assert_eq!(name, "http-server");
5796                assert_eq!(url, "https://api.example.com");
5797                assert_eq!(headers.len(), 2);
5798                assert_eq!(headers[0].name, "Authorization");
5799                assert_eq!(headers[0].value, "Bearer token123");
5800                assert_eq!(headers[1].name, "Content-Type");
5801                assert_eq!(headers[1].value, "application/json");
5802            }
5803            _ => panic!("Expected Http variant"),
5804        }
5805    }
5806
5807    #[cfg(feature = "schemars")]
5808    #[test]
5809    fn mcp_server_http_schema_marks_url_as_uri() {
5810        let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
5811
5812        assert_eq!(schema["properties"]["url"]["format"], "uri");
5813    }
5814
5815    #[cfg(feature = "unstable_mcp_over_acp")]
5816    #[test]
5817    fn test_client_mcp_message_method_names() {
5818        use serde_json::json;
5819
5820        assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
5821
5822        let notification =
5823            MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
5824        assert_eq!(
5825            ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
5826            "mcp/message"
5827        );
5828        assert_eq!(
5829            serde_json::to_value(notification).unwrap(),
5830            json!({
5831                "serverId": "server-1",
5832                "requestId": "req-1",
5833                "method": "notifications/progress"
5834            })
5835        );
5836        let notification: MessageMcpNotification = serde_json::from_value(json!({
5837            "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
5838            "params": null, "_meta": null
5839        }))
5840        .unwrap();
5841        assert_eq!(notification.params, None);
5842        assert_eq!(notification.meta, None);
5843        for key in ["serverId", "requestId", "method"] {
5844            let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
5845            value.as_object_mut().unwrap().remove(key);
5846            assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
5847        }
5848    }
5849
5850    #[test]
5851    fn test_auth_method_names() {
5852        assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
5853        assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
5854
5855        assert_eq!(
5856            ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
5857                .method(),
5858            "auth/login"
5859        );
5860        assert_eq!(
5861            ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
5862            "auth/logout"
5863        );
5864    }
5865
5866    #[test]
5867    fn test_session_config_option_category_known_variants() {
5868        // Test serialization of known variants
5869        assert_eq!(
5870            serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
5871            json!("mode")
5872        );
5873        assert_eq!(
5874            serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
5875            json!("model")
5876        );
5877        assert_eq!(
5878            serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
5879            json!("model_config")
5880        );
5881        assert_eq!(
5882            serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
5883            json!("thought_level")
5884        );
5885
5886        // Test deserialization of known variants
5887        assert_eq!(
5888            serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
5889            SessionConfigOptionCategory::Mode
5890        );
5891        assert_eq!(
5892            serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
5893            SessionConfigOptionCategory::Model
5894        );
5895        assert_eq!(
5896            serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
5897            SessionConfigOptionCategory::ModelConfig
5898        );
5899        assert_eq!(
5900            serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
5901            SessionConfigOptionCategory::ThoughtLevel
5902        );
5903    }
5904
5905    #[test]
5906    fn test_session_config_option_category_unknown_variants() {
5907        // Test that unknown strings are captured in Other variant
5908        let unknown: SessionConfigOptionCategory =
5909            serde_json::from_str("\"some_future_category\"").unwrap();
5910        assert_eq!(
5911            unknown,
5912            SessionConfigOptionCategory::Other("some_future_category".to_string())
5913        );
5914
5915        // Test round-trip of unknown category
5916        let json = serde_json::to_value(&unknown).unwrap();
5917        assert_eq!(json, json!("some_future_category"));
5918    }
5919
5920    #[test]
5921    fn test_session_config_option_category_custom_categories() {
5922        // Category names beginning with `_` are free for custom use
5923        let custom: SessionConfigOptionCategory =
5924            serde_json::from_str("\"_my_custom_category\"").unwrap();
5925        assert_eq!(
5926            custom,
5927            SessionConfigOptionCategory::Other("_my_custom_category".to_string())
5928        );
5929
5930        // Test round-trip preserves the custom category name
5931        let json = serde_json::to_value(&custom).unwrap();
5932        assert_eq!(json, json!("_my_custom_category"));
5933
5934        // Deserialize back and verify
5935        let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
5936        assert_eq!(
5937            deserialized,
5938            SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
5939        );
5940    }
5941
5942    fn test_config_option() -> SessionConfigOption {
5943        SessionConfigOption::select(
5944            "mode",
5945            "Mode",
5946            "ask",
5947            vec![SessionConfigSelectOption::new("ask", "Ask")],
5948        )
5949    }
5950
5951    #[test]
5952    fn test_session_response_initial_state_default_empty_and_skip_serializing() {
5953        assert_eq!(
5954            serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
5955            json!({ "sessionId": "sess" })
5956        );
5957        assert_eq!(
5958            serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
5959            json!({})
5960        );
5961        #[cfg(feature = "unstable_session_fork")]
5962        assert_eq!(
5963            serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
5964            json!({ "sessionId": "fork" })
5965        );
5966
5967        let json = serde_json::to_value(
5968            NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
5969        )
5970        .unwrap();
5971        assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
5972    }
5973
5974    #[test]
5975    fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
5976        let missing: NewSessionResponse =
5977            serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
5978        assert!(missing.config_options.is_empty());
5979
5980        let null: NewSessionResponse = serde_json::from_value(json!({
5981            "sessionId": "sess",
5982            "configOptions": null
5983        }))
5984        .unwrap();
5985        assert!(null.config_options.is_empty());
5986
5987        let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
5988            "sessionId": "sess",
5989            "configOptions": "oops"
5990        }))
5991        .unwrap();
5992        assert!(wrong_shape.config_options.is_empty());
5993
5994        let valid_option = serde_json::to_value(test_config_option()).unwrap();
5995        let mixed: NewSessionResponse = serde_json::from_value(json!({
5996            "sessionId": "sess",
5997            "configOptions": ["oops", valid_option]
5998        }))
5999        .unwrap();
6000        assert_eq!(mixed.config_options.len(), 1);
6001
6002        let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
6003        assert!(resume.config_options.is_empty());
6004        #[cfg(feature = "unstable_session_fork")]
6005        {
6006            let fork: ForkSessionResponse =
6007                serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
6008            assert!(fork.config_options.is_empty());
6009        }
6010    }
6011
6012    #[test]
6013    fn test_session_response_available_commands_roundtrip_with_config_options() {
6014        use super::super::{AvailableCommandInput, TextCommandInput};
6015
6016        let commands = vec![
6017            AvailableCommand::new("web", "Search the web")
6018                .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
6019            AvailableCommand::new("test", "Run project tests"),
6020        ];
6021        let mut expected = json!({
6022            "sessionId": "sess",
6023            "configOptions": [test_config_option()],
6024            "availableCommands": [
6025                {
6026                    "name": "web",
6027                    "description": "Search the web",
6028                    "input": { "type": "text", "hint": "Query" }
6029                },
6030                { "name": "test", "description": "Run project tests" }
6031            ]
6032        });
6033
6034        let new = NewSessionResponse::new("sess")
6035            .config_options(vec![test_config_option()])
6036            .available_commands(commands.clone());
6037        assert_eq!(serde_json::to_value(&new).unwrap(), expected);
6038        assert_eq!(
6039            serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
6040            new
6041        );
6042
6043        expected.as_object_mut().unwrap().remove("sessionId");
6044        let resume = ResumeSessionResponse::new()
6045            .config_options(vec![test_config_option()])
6046            .available_commands(commands.clone());
6047        assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
6048        assert_eq!(
6049            serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
6050            resume
6051        );
6052
6053        #[cfg(feature = "unstable_session_fork")]
6054        {
6055            expected["sessionId"] = json!("fork");
6056            let fork = ForkSessionResponse::new("fork")
6057                .config_options(vec![test_config_option()])
6058                .available_commands(commands);
6059            assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
6060            assert_eq!(
6061                serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
6062                fork
6063            );
6064        }
6065    }
6066
6067    #[test]
6068    fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
6069        let command = AvailableCommand::new("test", "Run project tests");
6070        let config_options = vec![test_config_option()];
6071
6072        for (commands, expected) in [
6073            (None, vec![]),
6074            (Some(json!(null)), vec![]),
6075            (Some(json!([])), vec![]),
6076            (Some(json!("oops")), vec![]),
6077            (Some(json!({})), vec![]),
6078            (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
6079            (
6080                Some(json!([null, command, { "name": "invalid" }])),
6081                vec![command.clone()],
6082            ),
6083        ] {
6084            let mut value = json!({
6085                "sessionId": "sess",
6086                "configOptions": config_options
6087            });
6088            if let Some(commands) = commands {
6089                value["availableCommands"] = commands;
6090            }
6091
6092            let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
6093            assert_eq!(new.available_commands, expected);
6094            assert_eq!(new.config_options, config_options);
6095
6096            #[cfg(feature = "unstable_session_fork")]
6097            {
6098                let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
6099                assert_eq!(fork.available_commands, expected);
6100                assert_eq!(fork.config_options, config_options);
6101            }
6102
6103            value.as_object_mut().unwrap().remove("sessionId");
6104            let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
6105            assert_eq!(resume.available_commands, expected);
6106            assert_eq!(resume.config_options, config_options);
6107        }
6108
6109        let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
6110        assert!(resume.available_commands.is_empty());
6111    }
6112
6113    #[test]
6114    fn test_resume_session_replay_from_serialization() {
6115        assert_eq!(
6116            serde_json::to_value(ResumeSessionRequest::new(
6117                "sess_abc123",
6118                "/home/user/project"
6119            ))
6120            .unwrap(),
6121            json!({
6122                "sessionId": "sess_abc123",
6123                "cwd": "/home/user/project"
6124            })
6125        );
6126        assert_eq!(
6127            serde_json::to_value(
6128                ResumeSessionRequest::new("sess_abc123", "/home/user/project")
6129                    .replay_from(ReplayFrom::from(ReplayFromStart::new()))
6130            )
6131            .unwrap(),
6132            json!({
6133                "sessionId": "sess_abc123",
6134                "cwd": "/home/user/project",
6135                "replayFrom": {
6136                    "type": "start"
6137                }
6138            })
6139        );
6140
6141        let replay: ResumeSessionRequest = serde_json::from_value(json!({
6142            "sessionId": "sess_abc123",
6143            "cwd": "/home/user/project",
6144            "replayFrom": {
6145                "type": "start"
6146            }
6147        }))
6148        .unwrap();
6149        assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
6150
6151        let none: ResumeSessionRequest = serde_json::from_value(json!({
6152            "sessionId": "sess_abc123",
6153            "cwd": "/home/user/project",
6154            "replayFrom": null
6155        }))
6156        .unwrap();
6157        assert!(none.replay_from.is_none());
6158    }
6159
6160    #[test]
6161    fn test_auth_method_agent_serialization() {
6162        let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
6163
6164        let json = serde_json::to_value(&method).unwrap();
6165        assert_eq!(
6166            json,
6167            json!({
6168                "methodId": "default-auth",
6169                "name": "Default Auth",
6170                "type": "agent"
6171            })
6172        );
6173        // description should be omitted when None
6174        assert!(!json.as_object().unwrap().contains_key("description"));
6175
6176        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6177        match deserialized {
6178            AuthMethod::Agent(AuthMethodAgent {
6179                method_id, name, ..
6180            }) => {
6181                assert_eq!(method_id.0.as_ref(), "default-auth");
6182                assert_eq!(name, "Default Auth");
6183            }
6184            _ => panic!("Expected Agent variant"),
6185        }
6186    }
6187
6188    #[test]
6189    fn test_auth_method_agent_deserialization() {
6190        let json = json!({
6191            "methodId": "agent-auth",
6192            "name": "Agent Auth",
6193            "type": "agent"
6194        });
6195
6196        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6197        assert!(matches!(deserialized, AuthMethod::Agent(_)));
6198    }
6199
6200    #[test]
6201    fn test_auth_method_agent_requires_type() {
6202        assert!(
6203            serde_json::from_value::<AuthMethod>(json!({
6204                "methodId": "agent-auth",
6205                "name": "Agent Auth"
6206            }))
6207            .is_err()
6208        );
6209    }
6210
6211    #[test]
6212    fn test_auth_method_agent_rejects_null_type() {
6213        assert!(
6214            serde_json::from_value::<AuthMethod>(json!({
6215                "methodId": "agent-auth",
6216                "name": "Agent Auth",
6217                "type": null
6218            }))
6219            .is_err()
6220        );
6221    }
6222
6223    #[test]
6224    fn test_auth_method_unknown_does_not_hide_malformed_agent() {
6225        assert!(
6226            serde_json::from_value::<AuthMethod>(json!({
6227                "methodId": "agent-auth",
6228                "type": "agent"
6229            }))
6230            .is_err()
6231        );
6232        assert!(
6233            serde_json::from_value::<AuthMethod>(json!({
6234                "methodId": "api-key",
6235                "type": "env_var",
6236                "vars": [{"name": "API_KEY"}]
6237            }))
6238            .is_err()
6239        );
6240    }
6241
6242    #[test]
6243    fn test_auth_method_unknown_variant_roundtrip() {
6244        let method: AuthMethod = serde_json::from_value(json!({
6245            "methodId": "oauth",
6246            "name": "OAuth",
6247            "type": "_oauth",
6248            "authorizationUrl": "https://example.com/auth"
6249        }))
6250        .unwrap();
6251
6252        assert_eq!(method.method_id().0.as_ref(), "oauth");
6253        assert_eq!(method.name(), "OAuth");
6254        let AuthMethod::Other(unknown) = method else {
6255            panic!("expected unknown auth method");
6256        };
6257        assert_eq!(unknown.type_, "_oauth");
6258        assert_eq!(
6259            unknown.fields.get("authorizationUrl"),
6260            Some(&json!("https://example.com/auth"))
6261        );
6262
6263        assert_eq!(
6264            serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
6265            json!({
6266                "methodId": "oauth",
6267                "name": "OAuth",
6268                "type": "_oauth",
6269                "authorizationUrl": "https://example.com/auth"
6270            })
6271        );
6272    }
6273
6274    #[test]
6275    fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
6276        assert!(
6277            serde_json::from_value::<AuthMethod>(json!({
6278                "methodId": "terminal-auth",
6279                "type": "terminal"
6280            }))
6281            .is_err()
6282        );
6283    }
6284
6285    #[test]
6286    fn test_session_delete_serialization() {
6287        assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
6288        assert_eq!(
6289            ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
6290                .method(),
6291            "session/delete"
6292        );
6293        assert_eq!(
6294            serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
6295            json!({
6296                "sessionId": "sess_abc123"
6297            })
6298        );
6299        assert_eq!(
6300            serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
6301            json!({})
6302        );
6303        assert_eq!(
6304            serde_json::to_value(
6305                SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
6306            )
6307            .unwrap(),
6308            json!({
6309                "delete": {}
6310            })
6311        );
6312    }
6313    #[test]
6314    fn test_session_additional_directories_serialization() {
6315        assert_eq!(
6316            serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
6317            json!({
6318                "cwd": "/home/user/project",
6319            })
6320        );
6321        assert_eq!(
6322            serde_json::to_value(
6323                NewSessionRequest::new("/home/user/project").additional_directories(vec![
6324                    PathBuf::from("/home/user/shared-lib"),
6325                    PathBuf::from("/home/user/product-docs"),
6326                ])
6327            )
6328            .unwrap(),
6329            json!({
6330                "cwd": "/home/user/project",
6331                "additionalDirectories": [
6332                    "/home/user/shared-lib",
6333                    "/home/user/product-docs"
6334                ],
6335            })
6336        );
6337        assert_eq!(
6338            serde_json::to_value(ResumeSessionRequest::new(
6339                "sess_abc123",
6340                "/home/user/project"
6341            ))
6342            .unwrap(),
6343            json!({
6344                "sessionId": "sess_abc123",
6345                "cwd": "/home/user/project",
6346            })
6347        );
6348        assert_eq!(
6349            serde_json::from_value::<ResumeSessionRequest>(json!({
6350                "sessionId": "sess_abc123",
6351                "cwd": "/home/user/project"
6352            }))
6353            .unwrap()
6354            .mcp_servers,
6355            Vec::<McpServer>::new()
6356        );
6357        assert_eq!(
6358            serde_json::from_value::<ResumeSessionRequest>(json!({
6359                "sessionId": "sess_abc123",
6360                "cwd": "/home/user/project",
6361                "mcpServers": null
6362            }))
6363            .unwrap()
6364            .mcp_servers,
6365            Vec::<McpServer>::new()
6366        );
6367        assert_eq!(
6368            serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
6369            json!({
6370                "sessionId": "sess_abc123",
6371                "cwd": "/home/user/project"
6372            })
6373        );
6374        assert_eq!(
6375            serde_json::to_value(
6376                SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
6377                    PathBuf::from("/home/user/shared-lib"),
6378                    PathBuf::from("/home/user/product-docs"),
6379                ])
6380            )
6381            .unwrap(),
6382            json!({
6383                "sessionId": "sess_abc123",
6384                "cwd": "/home/user/project",
6385                "additionalDirectories": [
6386                    "/home/user/shared-lib",
6387                    "/home/user/product-docs"
6388                ]
6389            })
6390        );
6391        assert_eq!(
6392            serde_json::from_value::<SessionInfo>(json!({
6393                "sessionId": "sess_abc123",
6394                "cwd": "/home/user/project"
6395            }))
6396            .unwrap()
6397            .additional_directories,
6398            Vec::<AbsolutePath>::new()
6399        );
6400    }
6401    #[test]
6402    fn test_session_additional_directories_capabilities_serialization() {
6403        assert_eq!(
6404            serde_json::to_value(
6405                SessionCapabilities::new()
6406                    .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
6407            )
6408            .unwrap(),
6409            json!({
6410                "additionalDirectories": {}
6411            })
6412        );
6413    }
6414
6415    #[test]
6416    fn test_auth_method_terminal_serialization() {
6417        let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
6418
6419        let json = serde_json::to_value(&method).unwrap();
6420        assert_eq!(
6421            json,
6422            json!({
6423                "methodId": "tui-auth",
6424                "name": "Terminal Auth",
6425                "type": "terminal"
6426            })
6427        );
6428        // args and env should be omitted when empty
6429        assert!(!json.as_object().unwrap().contains_key("args"));
6430        assert!(!json.as_object().unwrap().contains_key("env"));
6431
6432        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6433        match deserialized {
6434            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6435                assert!(args.is_empty());
6436                assert!(env.is_empty());
6437            }
6438            _ => panic!("Expected Terminal variant"),
6439        }
6440    }
6441
6442    #[test]
6443    fn test_auth_method_terminal_with_args_and_env_serialization() {
6444        let method = AuthMethod::Terminal(
6445            AuthMethodTerminal::new("tui-auth", "Terminal Auth")
6446                .args(vec!["--interactive".to_string(), "--color".to_string()])
6447                .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
6448        );
6449
6450        let json = serde_json::to_value(&method).unwrap();
6451        assert_eq!(
6452            json,
6453            json!({
6454                "methodId": "tui-auth",
6455                "name": "Terminal Auth",
6456                "type": "terminal",
6457                "args": ["--interactive", "--color"],
6458                "env": [
6459                    {
6460                        "name": "TERM",
6461                        "value": "xterm-256color"
6462                    }
6463                ]
6464            })
6465        );
6466
6467        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
6468        match deserialized {
6469            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
6470                assert_eq!(args, vec!["--interactive", "--color"]);
6471                assert_eq!(env.len(), 1);
6472                assert_eq!(env[0].name, "TERM");
6473                assert_eq!(env[0].value, "xterm-256color");
6474            }
6475            _ => panic!("Expected Terminal variant"),
6476        }
6477    }
6478
6479    #[test]
6480    fn test_session_config_option_id_serialize() {
6481        let val = SessionConfigOptionValue::id("model-1");
6482        let json = serde_json::to_value(&val).unwrap();
6483        assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
6484    }
6485
6486    #[test]
6487    fn test_session_config_option_value_boolean_serialize() {
6488        let val = SessionConfigOptionValue::boolean(true);
6489        let json = serde_json::to_value(&val).unwrap();
6490        assert_eq!(json, json!({ "type": "boolean", "value": true }));
6491    }
6492
6493    #[test]
6494    fn test_session_config_option_value_deserialize_id() {
6495        let json = json!({ "type": "id", "value": "model-1" });
6496        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6497        assert_eq!(val, SessionConfigOptionValue::id("model-1"));
6498        assert_eq!(val.as_id().unwrap().to_string(), "model-1");
6499    }
6500
6501    #[test]
6502    fn test_session_config_option_value_deserialize_requires_type() {
6503        let json = json!({ "value": "model-1" });
6504        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6505        assert!(result.is_err());
6506    }
6507
6508    #[test]
6509    fn test_session_config_option_value_deserialize_boolean() {
6510        let json = json!({ "type": "boolean", "value": true });
6511        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6512        assert_eq!(val, SessionConfigOptionValue::boolean(true));
6513        assert_eq!(val.as_bool(), Some(true));
6514    }
6515
6516    #[test]
6517    fn test_session_config_option_value_deserialize_boolean_false() {
6518        let json = json!({ "type": "boolean", "value": false });
6519        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6520        assert_eq!(val, SessionConfigOptionValue::boolean(false));
6521        assert_eq!(val.as_bool(), Some(false));
6522    }
6523
6524    #[test]
6525    fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
6526        let json = json!({
6527            "type": "text",
6528            "value": "freeform input",
6529            "maxLength": 200
6530        });
6531        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6532        let SessionConfigOptionValue::Other(unknown) = val else {
6533            panic!("Expected Other variant");
6534        };
6535        assert_eq!(unknown.type_, "text");
6536        assert_eq!(unknown.value, json!("freeform input"));
6537        assert_eq!(unknown.fields["maxLength"], json!(200));
6538    }
6539
6540    #[test]
6541    fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
6542        let json = json!({
6543            "type": "range",
6544            "value": { "min": 1, "max": 5 }
6545        });
6546        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6547        let SessionConfigOptionValue::Other(unknown) = val else {
6548            panic!("Expected Other variant");
6549        };
6550        assert_eq!(unknown.type_, "range");
6551        assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
6552    }
6553
6554    #[test]
6555    fn test_session_config_option_value_roundtrip_id() {
6556        let original = SessionConfigOptionValue::id("option-a");
6557        let json = serde_json::to_value(&original).unwrap();
6558        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6559        assert_eq!(original, roundtripped);
6560    }
6561
6562    #[test]
6563    fn test_session_config_option_value_roundtrip_boolean() {
6564        let original = SessionConfigOptionValue::boolean(false);
6565        let json = serde_json::to_value(&original).unwrap();
6566        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6567        assert_eq!(original, roundtripped);
6568    }
6569
6570    #[test]
6571    fn test_session_config_option_value_roundtrip_other() {
6572        let mut fields = BTreeMap::new();
6573        fields.insert("maxLength".to_string(), json!(200));
6574        let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
6575            "text",
6576            json!("freeform input"),
6577            fields,
6578        ));
6579        let json = serde_json::to_value(&original).unwrap();
6580        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
6581        assert_eq!(original, roundtripped);
6582    }
6583
6584    #[test]
6585    fn test_session_config_option_value_type_mismatch_boolean_with_string() {
6586        let json = json!({ "type": "boolean", "value": "not a bool" });
6587        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
6588        assert!(result.is_err());
6589    }
6590
6591    #[test]
6592    fn test_session_config_option_value_from_impls() {
6593        let from_str: SessionConfigOptionValue = "model-1".into();
6594        assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
6595
6596        let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
6597        assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
6598
6599        let from_bool: SessionConfigOptionValue = true.into();
6600        assert_eq!(from_bool.as_bool(), Some(true));
6601    }
6602
6603    #[test]
6604    fn test_set_session_config_option_request_id() {
6605        let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
6606        let json = serde_json::to_value(&req).unwrap();
6607        assert_eq!(
6608            json,
6609            json!({
6610                "sessionId": "sess_1",
6611                "configId": "model",
6612                "type": "id",
6613                "value": "model-1"
6614            })
6615        );
6616    }
6617
6618    #[test]
6619    fn test_set_session_config_option_request_boolean() {
6620        let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
6621        let json = serde_json::to_value(&req).unwrap();
6622        assert_eq!(
6623            json,
6624            json!({
6625                "sessionId": "sess_1",
6626                "configId": "brave_mode",
6627                "type": "boolean",
6628                "value": true
6629            })
6630        );
6631    }
6632
6633    #[test]
6634    fn test_set_session_config_option_request_deserialize_requires_type() {
6635        let json = json!({
6636            "sessionId": "sess_1",
6637            "configId": "model",
6638            "value": "model-1"
6639        });
6640        let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
6641        assert!(result.is_err());
6642    }
6643
6644    #[test]
6645    fn test_set_session_config_option_request_deserialize_boolean() {
6646        let json = json!({
6647            "sessionId": "sess_1",
6648            "configId": "brave_mode",
6649            "type": "boolean",
6650            "value": true
6651        });
6652        let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6653        assert_eq!(req.value.as_bool(), Some(true));
6654    }
6655
6656    #[test]
6657    fn test_set_session_config_option_request_roundtrip_id() {
6658        let original = SetSessionConfigOptionRequest::new("s", "c", "v");
6659        let json = serde_json::to_value(&original).unwrap();
6660        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6661        assert_eq!(original, roundtripped);
6662    }
6663
6664    #[test]
6665    fn test_set_session_config_option_request_roundtrip_boolean() {
6666        let original = SetSessionConfigOptionRequest::new("s", "c", false);
6667        let json = serde_json::to_value(&original).unwrap();
6668        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
6669        assert_eq!(original, roundtripped);
6670    }
6671
6672    #[test]
6673    fn test_session_config_boolean_serialization() {
6674        let cfg = SessionConfigBoolean::new(true);
6675        let json = serde_json::to_value(&cfg).unwrap();
6676        assert_eq!(json, json!({ "currentValue": true }));
6677
6678        let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
6679        assert!(deserialized.current_value);
6680    }
6681
6682    #[test]
6683    fn test_session_config_option_boolean_variant() {
6684        let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
6685            .description("Skip confirmation prompts")
6686            .meta(test_meta());
6687        assert_eq!(serialized_meta_key_count(&opt), 1);
6688
6689        let json = serde_json::to_value(&opt).unwrap();
6690        assert_eq!(
6691            json,
6692            json!({
6693                "configId": "brave_mode",
6694                "name": "Brave Mode",
6695                "description": "Skip confirmation prompts",
6696                "type": "boolean",
6697                "currentValue": false,
6698                "_meta": {
6699                    "source": "test"
6700                }
6701            })
6702        );
6703
6704        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6705        assert_eq!(deserialized.config_id.to_string(), "brave_mode");
6706        assert_eq!(deserialized.name, "Brave Mode");
6707        match deserialized.kind {
6708            SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
6709            _ => panic!("Expected Boolean kind"),
6710        }
6711    }
6712
6713    #[test]
6714    fn test_session_config_option_select_still_works() {
6715        // Make sure existing select options are unaffected
6716        let opt = SessionConfigOption::select(
6717            "model",
6718            "Model",
6719            "model-1",
6720            vec![
6721                SessionConfigSelectOption::new("model-1", "Model 1"),
6722                SessionConfigSelectOption::new("model-2", "Model 2"),
6723            ],
6724        )
6725        .meta(test_meta());
6726        assert_eq!(serialized_meta_key_count(&opt), 1);
6727
6728        let json = serde_json::to_value(&opt).unwrap();
6729        assert_eq!(json["type"], "select");
6730        assert_eq!(json["currentValue"], "model-1");
6731        assert_eq!(json["options"].as_array().unwrap().len(), 2);
6732        assert_eq!(json["_meta"]["source"], "test");
6733
6734        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
6735        match deserialized.kind {
6736            SessionConfigKind::Select(ref s) => {
6737                assert_eq!(s.current_value.to_string(), "model-1");
6738            }
6739            _ => panic!("Expected Select kind"),
6740        }
6741    }
6742
6743    #[test]
6744    fn test_session_config_option_unknown_kind_roundtrip() {
6745        let option: SessionConfigOption = serde_json::from_value(json!({
6746            "configId": "verbosity",
6747            "name": "Verbosity",
6748            "type": "_slider",
6749            "currentValue": 3,
6750            "min": 0,
6751            "max": 5,
6752            "_meta": {
6753                "source": "test"
6754            }
6755        }))
6756        .unwrap();
6757
6758        assert_eq!(option.config_id.to_string(), "verbosity");
6759        assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
6760        let SessionConfigKind::Other(unknown) = &option.kind else {
6761            panic!("expected unknown config kind");
6762        };
6763        assert_eq!(unknown.type_, "_slider");
6764        assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
6765        assert!(!unknown.fields.contains_key("_meta"));
6766        assert_eq!(serialized_meta_key_count(&option), 1);
6767
6768        let json = serde_json::to_value(&option).unwrap();
6769        assert_eq!(json["type"], "_slider");
6770        assert_eq!(json["currentValue"], 3);
6771        assert_eq!(json["min"], 0);
6772        assert_eq!(json["max"], 5);
6773        assert_eq!(json["_meta"]["source"], "test");
6774    }
6775
6776    #[test]
6777    fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
6778        let mut fields = std::collections::BTreeMap::new();
6779        fields.insert("currentValue".to_string(), json!(3));
6780        fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
6781
6782        let option = SessionConfigOption::new(
6783            "verbosity",
6784            "Verbosity",
6785            SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
6786        )
6787        .meta(test_meta());
6788
6789        let SessionConfigKind::Other(unknown) = &option.kind else {
6790            panic!("expected unknown config kind");
6791        };
6792        assert!(!unknown.fields.contains_key("_meta"));
6793        assert_eq!(serialized_meta_key_count(&option), 1);
6794
6795        let json = serde_json::to_value(&option).unwrap();
6796        assert_eq!(json["type"], "_slider");
6797        assert_eq!(json["currentValue"], 3);
6798        assert_eq!(json["_meta"]["source"], "test");
6799    }
6800
6801    #[test]
6802    fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
6803        assert!(
6804            serde_json::from_value::<SessionConfigOption>(json!({
6805                "configId": "model",
6806                "name": "Model",
6807                "type": "select"
6808            }))
6809            .is_err()
6810        );
6811    }
6812
6813    #[cfg(feature = "unstable_llm_providers")]
6814    #[test]
6815    fn test_llm_protocol_known_variants() {
6816        assert_eq!(
6817            serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
6818            json!("anthropic")
6819        );
6820        assert_eq!(
6821            serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
6822            json!("openai")
6823        );
6824        assert_eq!(
6825            serde_json::to_value(&LlmProtocol::Azure).unwrap(),
6826            json!("azure")
6827        );
6828        assert_eq!(
6829            serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
6830            json!("vertex")
6831        );
6832        assert_eq!(
6833            serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
6834            json!("bedrock")
6835        );
6836
6837        assert_eq!(
6838            serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
6839            LlmProtocol::Anthropic
6840        );
6841        assert_eq!(
6842            serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
6843            LlmProtocol::OpenAi
6844        );
6845        assert_eq!(
6846            serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
6847            LlmProtocol::Azure
6848        );
6849        assert_eq!(
6850            serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
6851            LlmProtocol::Vertex
6852        );
6853        assert_eq!(
6854            serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
6855            LlmProtocol::Bedrock
6856        );
6857    }
6858
6859    #[cfg(feature = "unstable_llm_providers")]
6860    #[test]
6861    fn test_llm_protocol_unknown_variant() {
6862        let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
6863        assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
6864
6865        let json = serde_json::to_value(&unknown).unwrap();
6866        assert_eq!(json, json!("cohere"));
6867    }
6868
6869    #[cfg(feature = "unstable_llm_providers")]
6870    #[test]
6871    fn test_provider_current_config_serialization() {
6872        let config =
6873            ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
6874
6875        let json = serde_json::to_value(&config).unwrap();
6876        assert_eq!(
6877            json,
6878            json!({
6879                "apiType": "anthropic",
6880                "baseUrl": "https://api.anthropic.com"
6881            })
6882        );
6883
6884        let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
6885        assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
6886        assert_eq!(deserialized.base_url, "https://api.anthropic.com");
6887    }
6888
6889    #[cfg(feature = "unstable_llm_providers")]
6890    #[test]
6891    fn test_provider_info_with_current_config() {
6892        let info = ProviderInfo::new(
6893            "main",
6894            vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
6895            true,
6896            Some(ProviderCurrentConfig::new(
6897                LlmProtocol::Anthropic,
6898                "https://api.anthropic.com",
6899            )),
6900        );
6901
6902        let json = serde_json::to_value(&info).unwrap();
6903        assert_eq!(
6904            json,
6905            json!({
6906                "providerId": "main",
6907                "supported": ["anthropic", "openai"],
6908                "required": true,
6909                "current": {
6910                    "apiType": "anthropic",
6911                    "baseUrl": "https://api.anthropic.com"
6912                }
6913            })
6914        );
6915
6916        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6917        assert_eq!(deserialized.provider_id.to_string(), "main");
6918        assert_eq!(deserialized.supported.len(), 2);
6919        assert!(deserialized.required);
6920        assert!(deserialized.current.is_some());
6921        assert_eq!(
6922            deserialized.current.as_ref().unwrap().api_type,
6923            LlmProtocol::Anthropic
6924        );
6925    }
6926
6927    #[cfg(feature = "unstable_llm_providers")]
6928    #[test]
6929    fn test_provider_info_disabled() {
6930        let info = ProviderInfo::new(
6931            "secondary",
6932            vec![LlmProtocol::OpenAi],
6933            false,
6934            None::<ProviderCurrentConfig>,
6935        );
6936
6937        let json = serde_json::to_value(&info).unwrap();
6938        assert_eq!(
6939            json,
6940            json!({
6941                "providerId": "secondary",
6942                "supported": ["openai"],
6943                "required": false
6944            })
6945        );
6946
6947        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6948        assert_eq!(deserialized.provider_id.to_string(), "secondary");
6949        assert!(!deserialized.required);
6950        assert!(deserialized.current.is_none());
6951    }
6952
6953    #[cfg(feature = "unstable_llm_providers")]
6954    #[test]
6955    fn test_provider_info_missing_current_defaults_to_none() {
6956        // current is optional; omitting it should decode as None
6957        let json = json!({
6958            "providerId": "main",
6959            "supported": ["anthropic"],
6960            "required": true
6961        });
6962        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6963        assert!(deserialized.current.is_none());
6964    }
6965
6966    #[cfg(feature = "unstable_llm_providers")]
6967    #[test]
6968    fn test_provider_info_explicit_null_current_decodes_to_none() {
6969        // current: null and an omitted current are equivalent on the wire;
6970        // both must deserialize into None so the disabled state is preserved
6971        // regardless of which form the peer chose to send.
6972        let json = json!({
6973            "providerId": "main",
6974            "supported": ["anthropic"],
6975            "required": true,
6976            "current": null
6977        });
6978        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
6979        assert!(deserialized.current.is_none());
6980    }
6981
6982    #[cfg(feature = "unstable_llm_providers")]
6983    #[test]
6984    fn test_list_providers_response_serialization() {
6985        let response = ListProvidersResponse::new(vec![ProviderInfo::new(
6986            "main",
6987            vec![LlmProtocol::Anthropic],
6988            true,
6989            Some(ProviderCurrentConfig::new(
6990                LlmProtocol::Anthropic,
6991                "https://api.anthropic.com",
6992            )),
6993        )]);
6994
6995        let json = serde_json::to_value(&response).unwrap();
6996        assert_eq!(json["providers"].as_array().unwrap().len(), 1);
6997        assert_eq!(json["providers"][0]["providerId"], "main");
6998
6999        let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
7000        assert_eq!(deserialized.providers.len(), 1);
7001    }
7002
7003    #[cfg(feature = "unstable_llm_providers")]
7004    #[test]
7005    fn test_set_provider_request_serialization() {
7006        use std::collections::HashMap;
7007
7008        let mut headers = HashMap::new();
7009        headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
7010
7011        let request =
7012            SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
7013                .headers(headers);
7014
7015        let json = serde_json::to_value(&request).unwrap();
7016        assert_eq!(
7017            json,
7018            json!({
7019                "providerId": "main",
7020                "apiType": "openai",
7021                "baseUrl": "https://api.openai.com/v1",
7022                "headers": {
7023                    "Authorization": "Bearer sk-test"
7024                }
7025            })
7026        );
7027
7028        let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
7029        assert_eq!(deserialized.provider_id.to_string(), "main");
7030        assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
7031        assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
7032        assert_eq!(deserialized.headers.len(), 1);
7033        assert_eq!(
7034            deserialized.headers.get("Authorization").unwrap(),
7035            "Bearer sk-test"
7036        );
7037    }
7038
7039    #[cfg(feature = "unstable_llm_providers")]
7040    #[test]
7041    fn test_set_provider_request_omits_empty_headers() {
7042        let request =
7043            SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
7044
7045        let json = serde_json::to_value(&request).unwrap();
7046        // headers should be omitted when empty
7047        assert!(!json.as_object().unwrap().contains_key("headers"));
7048    }
7049
7050    #[cfg(feature = "unstable_llm_providers")]
7051    #[test]
7052    fn test_disable_provider_request_serialization() {
7053        let request = DisableProviderRequest::new("secondary");
7054
7055        let json = serde_json::to_value(&request).unwrap();
7056        assert_eq!(json, json!({ "providerId": "secondary" }));
7057
7058        let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
7059        assert_eq!(deserialized.provider_id.to_string(), "secondary");
7060    }
7061
7062    #[cfg(feature = "unstable_llm_providers")]
7063    #[test]
7064    fn test_providers_capabilities_serialization() {
7065        let caps = ProvidersCapabilities::new();
7066
7067        let json = serde_json::to_value(&caps).unwrap();
7068        assert_eq!(json, json!({}));
7069
7070        let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
7071        assert!(deserialized.meta.is_none());
7072    }
7073
7074    #[cfg(feature = "unstable_llm_providers")]
7075    #[test]
7076    fn test_agent_capabilities_with_providers() {
7077        let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
7078
7079        let json = serde_json::to_value(&caps).unwrap();
7080        assert_eq!(json["providers"], json!({}));
7081
7082        let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
7083        assert!(deserialized.providers.is_some());
7084    }
7085
7086    #[test]
7087    fn test_agent_capabilities_session_is_explicit() {
7088        let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
7089        assert!(json.get("session").is_none());
7090
7091        let caps = AgentCapabilities::new().session(
7092            SessionCapabilities::new()
7093                .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
7094                .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
7095        );
7096
7097        assert_eq!(
7098            serde_json::to_value(&caps).unwrap(),
7099            json!({
7100                "session": {
7101                    "prompt": {
7102                        "image": {}
7103                    },
7104                    "mcp": {
7105                        "stdio": {}
7106                    }
7107                }
7108            })
7109        );
7110
7111        let deserialized: AgentCapabilities = serde_json::from_value(json!({
7112            "session": false
7113        }))
7114        .unwrap();
7115        assert!(deserialized.session.is_none());
7116    }
7117
7118    #[test]
7119    fn test_prompt_capabilities_serialize_supported_content_as_objects() {
7120        let caps = PromptCapabilities::new()
7121            .image(PromptImageCapabilities::new())
7122            .audio(PromptAudioCapabilities::new())
7123            .embedded_context(PromptEmbeddedContextCapabilities::new());
7124
7125        assert_eq!(
7126            serde_json::to_value(&caps).unwrap(),
7127            json!({
7128                "image": {},
7129                "audio": {},
7130                "embeddedContext": {}
7131            })
7132        );
7133
7134        let deserialized: PromptCapabilities = serde_json::from_value(json!({
7135            "image": null,
7136            "audio": false,
7137            "embeddedContext": {}
7138        }))
7139        .unwrap();
7140        assert!(deserialized.image.is_none());
7141        assert!(deserialized.audio.is_none());
7142        assert!(deserialized.embedded_context.is_some());
7143    }
7144
7145    #[test]
7146    fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
7147        let caps = McpCapabilities::new()
7148            .stdio(McpStdioCapabilities::new())
7149            .http(McpHttpCapabilities::new());
7150
7151        assert_eq!(
7152            serde_json::to_value(&caps).unwrap(),
7153            json!({
7154                "stdio": {},
7155                "http": {}
7156            })
7157        );
7158
7159        let deserialized: McpCapabilities = serde_json::from_value(json!({
7160            "stdio": null,
7161            "http": false
7162        }))
7163        .unwrap();
7164        assert!(deserialized.stdio.is_none());
7165        assert!(deserialized.http.is_none());
7166    }
7167
7168    #[cfg(feature = "unstable_mcp_over_acp")]
7169    #[test]
7170    fn test_mcp_capabilities_serialize_acp_support_as_object() {
7171        let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
7172
7173        assert_eq!(
7174            serde_json::to_value(&caps).unwrap(),
7175            json!({
7176                "acp": {}
7177            })
7178        );
7179    }
7180
7181    #[test]
7182    fn prompt_request_rejects_malformed_content_block() {
7183        use serde_json::json;
7184
7185        assert!(
7186            serde_json::from_value::<PromptRequest>(json!({
7187                "sessionId": "sess-1",
7188                "prompt": [{"type": "text"}]
7189            }))
7190            .is_err()
7191        );
7192    }
7193
7194    #[test]
7195    fn prompt_request_rejects_non_array_prompt() {
7196        use serde_json::json;
7197
7198        assert!(
7199            serde_json::from_value::<PromptRequest>(json!({
7200                "sessionId": "sess-1",
7201                "prompt": "hello"
7202            }))
7203            .is_err()
7204        );
7205    }
7206}