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