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