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