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