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