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