Skip to main content

agent_client_protocol_schema/v2/
agent.rs

1//! Methods and notifications the agent handles/receives.
2//!
3//! This module defines the Agent trait and all associated types for implementing
4//! an AI coding agent that follows the Agent Client Protocol (ACP).
5
6use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
7
8#[cfg(feature = "unstable_llm_providers")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12#[cfg(feature = "schemars")]
13use schemars::Schema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DefaultOnError, DefaultOnNull, VecSkipError, serde_as, skip_serializing_none};
16
17use super::{
18    AbsolutePath, AvailableCommand, ClientCapabilities, ContentBlock, Error, ExtNotification,
19    ExtRequest, ExtResponse, MessageId, Meta, SessionId, TerminalId, ToolCallId,
20};
21use crate::{IntoOption, ProtocolVersion};
22
23#[cfg(feature = "unstable_session_status")]
24use super::StateUpdate;
25
26#[cfg(all(feature = "unstable_auth_status", feature = "schemars"))]
27use super::AUTH_STATUS_UPDATE_NOTIFICATION;
28#[cfg(feature = "unstable_mcp_over_acp")]
29use super::mcp::{MCP_MESSAGE_METHOD_NAME, MessageMcpNotification};
30
31// Initialize
32
33/// Request parameters for the initialize method.
34///
35/// Sent by the client to establish connection and negotiate capabilities.
36///
37/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
38#[serde_as]
39#[skip_serializing_none]
40#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
42#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
43#[serde(rename_all = "camelCase")]
44#[non_exhaustive]
45pub struct InitializeRequest {
46    /// The latest protocol version supported by the client.
47    pub protocol_version: ProtocolVersion,
48    /// Information about the implementation sending this initialize request.
49    pub info: Implementation,
50    /// Capabilities supported by the client.
51    #[serde_as(deserialize_as = "DefaultOnError")]
52    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
53    #[serde(default)]
54    pub capabilities: ClientCapabilities,
55    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
56    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
57    /// Implementations MUST NOT assign meaning to keys they do not recognize.
58    ///
59    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
60    #[serde_as(deserialize_as = "DefaultOnError")]
61    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
62    #[serde(default)]
63    #[serde(rename = "_meta")]
64    pub meta: Option<Meta>,
65}
66
67impl InitializeRequest {
68    /// Builds [`InitializeRequest`] with the required request fields set. Optional fields start unset or empty.
69    #[must_use]
70    pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
71        Self {
72            protocol_version,
73            capabilities: ClientCapabilities::default(),
74            info,
75            meta: None,
76        }
77    }
78
79    /// Capabilities supported by the client.
80    #[must_use]
81    pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
82        self.capabilities = capabilities;
83        self
84    }
85
86    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
87    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
88    /// do not recognize.
89    ///
90    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
91    #[must_use]
92    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
93        self.meta = meta.into_option();
94        self
95    }
96}
97
98/// Response to the `initialize` method.
99///
100/// Contains the negotiated protocol version and agent capabilities.
101///
102/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
103#[serde_as]
104#[skip_serializing_none]
105#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
107#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
108#[serde(rename_all = "camelCase")]
109#[non_exhaustive]
110pub struct InitializeResponse {
111    /// The highest protocol version supported by the agent that is at or below
112    /// the version requested by the client. If no such version exists,
113    /// initialization fails with error code -32022.
114    ///
115    /// The client must check that it supports this version and should disconnect
116    /// if it does not.
117    pub protocol_version: ProtocolVersion,
118    /// Information about the implementation sending this initialize response.
119    pub info: Implementation,
120    /// Capabilities supported by the agent.
121    #[serde_as(deserialize_as = "DefaultOnError")]
122    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
123    #[serde(default)]
124    pub capabilities: AgentCapabilities,
125    /// Authentication methods supported by the agent.
126    ///
127    /// Optional. Omitted or empty means the agent does not advertise the
128    /// `auth/login` and `auth/logout` methods. Supplying one or more methods means
129    /// the agent MUST support both `auth/login` and `auth/logout`.
130    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
131    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
132    #[serde(default, skip_serializing_if = "Vec::is_empty")]
133    pub auth_methods: Vec<AuthMethod>,
134    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
135    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
136    /// Implementations MUST NOT assign meaning to keys they do not recognize.
137    ///
138    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
139    #[serde_as(deserialize_as = "DefaultOnError")]
140    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
141    #[serde(default)]
142    #[serde(rename = "_meta")]
143    pub meta: Option<Meta>,
144}
145
146impl InitializeResponse {
147    /// Builds [`InitializeResponse`] with the required response fields set. Optional fields start unset or empty.
148    #[must_use]
149    pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
150        Self {
151            protocol_version,
152            capabilities: AgentCapabilities::default(),
153            auth_methods: vec![],
154            info,
155            meta: None,
156        }
157    }
158
159    /// Capabilities supported by the agent.
160    #[must_use]
161    pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
162        self.capabilities = capabilities;
163        self
164    }
165
166    /// Authentication methods supported by the agent.
167    ///
168    /// Supplying one or more methods means the agent MUST support both
169    /// `auth/login` and `auth/logout`.
170    #[must_use]
171    pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
172        self.auth_methods = auth_methods;
173        self
174    }
175
176    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
177    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
178    /// do not recognize.
179    ///
180    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
181    #[must_use]
182    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
183        self.meta = meta.into_option();
184        self
185    }
186}
187
188/// Metadata about the implementation of the client or agent.
189/// Describes the name and version of an ACP implementation, with an optional
190/// title for UI representation.
191#[serde_as]
192#[skip_serializing_none]
193#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
195#[serde(rename_all = "camelCase")]
196#[non_exhaustive]
197pub struct Implementation {
198    /// Intended for programmatic or logical use, but can be used as a display
199    /// name fallback if title isn't present.
200    pub name: String,
201    /// Intended for UI and end-user contexts and optimized to be human-readable
202    /// and easily understood.
203    ///
204    /// If not provided, the name should be used for display.
205    ///
206    /// Omitted and `null` are equivalent and mean the `name` is used for display.
207    #[serde_as(deserialize_as = "DefaultOnError")]
208    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
209    #[serde(default)]
210    pub title: Option<String>,
211    /// Version of the implementation. Can be displayed to the user or used
212    /// for debugging or metrics purposes. (e.g. "1.0.0").
213    pub version: String,
214    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
215    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
216    /// Implementations MUST NOT assign meaning to keys they do not recognize.
217    ///
218    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
219    #[serde_as(deserialize_as = "DefaultOnError")]
220    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
221    #[serde(default)]
222    #[serde(rename = "_meta")]
223    pub meta: Option<Meta>,
224}
225
226impl Implementation {
227    /// Builds [`Implementation`] with the required fields set. Optional fields start unset or empty.
228    #[must_use]
229    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
230        Self {
231            name: name.into(),
232            title: None,
233            version: version.into(),
234            meta: None,
235        }
236    }
237
238    /// Intended for UI and end-user contexts and optimized to be human-readable
239    /// and easily understood.
240    ///
241    /// If not provided, the name should be used for display.
242    #[must_use]
243    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
244        self.title = title.into_option();
245        self
246    }
247
248    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
249    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
250    /// do not recognize.
251    ///
252    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
253    #[must_use]
254    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
255        self.meta = meta.into_option();
256        self
257    }
258}
259
260// Authentication
261
262/// Request parameters for the `auth/login` method.
263///
264/// Specifies which authentication method to use.
265///
266/// Agents MUST support this method when their `initialize` response included
267/// a non-empty `authMethods` list. Clients MUST NOT call this method
268/// when `authMethods` was omitted or empty.
269#[serde_as]
270#[skip_serializing_none]
271#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
272#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
273#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
274#[serde(rename_all = "camelCase")]
275#[non_exhaustive]
276pub struct LoginAuthRequest {
277    /// The ID of the authentication method to use.
278    /// Must be one of the methods advertised in the initialize response.
279    pub method_id: AuthMethodId,
280    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
281    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
282    /// Implementations MUST NOT assign meaning to keys they do not recognize.
283    ///
284    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
285    #[serde_as(deserialize_as = "DefaultOnError")]
286    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
287    #[serde(default)]
288    #[serde(rename = "_meta")]
289    pub meta: Option<Meta>,
290}
291
292impl LoginAuthRequest {
293    /// Builds [`LoginAuthRequest`] with the required request fields set. Optional fields start unset or empty.
294    #[must_use]
295    pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
296        Self {
297            method_id: method_id.into(),
298            meta: None,
299        }
300    }
301
302    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
303    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
304    /// do not recognize.
305    ///
306    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
307    #[must_use]
308    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
309        self.meta = meta.into_option();
310        self
311    }
312}
313
314crate::serde_util::default_on_null! {
315    /// Response to the `auth/login` method.
316    #[serde_as]
317    #[skip_serializing_none]
318    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
319    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
320    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
321    #[serde(rename_all = "camelCase")]
322    #[non_exhaustive]
323    pub struct LoginAuthResponse {
324        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
325        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
326        /// Implementations MUST NOT assign meaning to keys they do not recognize.
327        ///
328        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
329        #[serde_as(deserialize_as = "DefaultOnError")]
330        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
331        #[serde(default)]
332        #[serde(rename = "_meta")]
333        pub meta: Option<Meta>,
334    }
335}
336
337impl LoginAuthResponse {
338    /// Builds [`LoginAuthResponse`] with the required response fields set. Optional fields start unset or empty.
339    #[must_use]
340    pub fn new() -> Self {
341        Self::default()
342    }
343
344    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
345    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
346    /// do not recognize.
347    ///
348    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
349    #[must_use]
350    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
351        self.meta = meta.into_option();
352        self
353    }
354}
355
356// Logout
357
358crate::serde_util::default_on_null! {
359    /// Request parameters for the `auth/logout` method.
360    ///
361    /// Terminates the current authenticated session.
362    ///
363    /// Agents MUST support this method when their `initialize` response included
364    /// a non-empty `authMethods` list. Clients MUST NOT call this method
365    /// when `authMethods` was omitted or empty.
366    #[serde_as]
367    #[skip_serializing_none]
368    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
369    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
370    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
371    #[serde(rename_all = "camelCase")]
372    #[non_exhaustive]
373    pub struct LogoutAuthRequest {
374        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
375        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
376        /// Implementations MUST NOT assign meaning to keys they do not recognize.
377        ///
378        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
379        #[serde_as(deserialize_as = "DefaultOnError")]
380        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
381        #[serde(default)]
382        #[serde(rename = "_meta")]
383        pub meta: Option<Meta>,
384    }
385}
386
387impl LogoutAuthRequest {
388    /// Builds [`LogoutAuthRequest`] with the required request fields set. Optional fields start unset or empty.
389    #[must_use]
390    pub fn new() -> Self {
391        Self::default()
392    }
393
394    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
395    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
396    /// do not recognize.
397    ///
398    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
399    #[must_use]
400    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
401        self.meta = meta.into_option();
402        self
403    }
404}
405
406crate::serde_util::default_on_null! {
407    /// Response to the `auth/logout` method.
408    #[serde_as]
409    #[skip_serializing_none]
410    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
411    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
412    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
413    #[serde(rename_all = "camelCase")]
414    #[non_exhaustive]
415    pub struct LogoutAuthResponse {
416        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
417        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
418        /// Implementations MUST NOT assign meaning to keys they do not recognize.
419        ///
420        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
421        #[serde_as(deserialize_as = "DefaultOnError")]
422        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
423        #[serde(default)]
424        #[serde(rename = "_meta")]
425        pub meta: Option<Meta>,
426    }
427}
428
429impl LogoutAuthResponse {
430    /// Builds [`LogoutAuthResponse`] with the required response fields set. Optional fields start unset or empty.
431    #[must_use]
432    pub fn new() -> Self {
433        Self::default()
434    }
435
436    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
437    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
438    /// do not recognize.
439    ///
440    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
441    #[must_use]
442    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
443        self.meta = meta.into_option();
444        self
445    }
446}
447
448// Auth status
449
450#[cfg(feature = "unstable_auth_status")]
451crate::serde_util::default_on_null! {
452    /// **UNSTABLE**
453    ///
454    /// This capability is not part of the spec yet, and may be removed or changed at any point.
455    ///
456    /// Request parameters for the `auth/status` method.
457    ///
458    /// Queries the agent's current authentication state without changing it.
459    ///
460    /// Agents MUST NOT reject it with `auth_required` (`-32000`), since Clients call it to learn
461    /// whether authentication is required.
462    #[serde_as]
463    #[skip_serializing_none]
464    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
465    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
466    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
467    #[serde(rename_all = "camelCase")]
468    #[non_exhaustive]
469    pub struct AuthStatusRequest {
470        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
471        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
472        /// Implementations MUST NOT assign meaning to keys they do not recognize.
473        ///
474        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
475        #[serde_as(deserialize_as = "DefaultOnError")]
476        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
477        #[serde(default)]
478        #[serde(rename = "_meta")]
479        pub meta: Option<Meta>,
480    }
481}
482
483#[cfg(feature = "unstable_auth_status")]
484impl AuthStatusRequest {
485    /// Builds [`AuthStatusRequest`] with the required request fields set. Optional fields start unset or empty.
486    #[must_use]
487    pub fn new() -> Self {
488        Self::default()
489    }
490
491    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
492    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
493    /// do not recognize.
494    ///
495    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
496    #[must_use]
497    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
498        self.meta = meta.into_option();
499        self
500    }
501}
502
503/// **UNSTABLE**
504///
505/// This capability is not part of the spec yet, and may be removed or changed at any point.
506///
507/// Response to the `auth/status` method.
508#[cfg(feature = "unstable_auth_status")]
509#[serde_as]
510#[skip_serializing_none]
511#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
513#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
514#[serde(rename_all = "camelCase")]
515#[non_exhaustive]
516pub struct AuthStatusResponse {
517    /// Whether the agent can start sessions without the user signing in first.
518    ///
519    /// This is the only field clients should act on. Clients MUST treat values they
520    /// do not recognize as `unknown`.
521    pub state: AuthState,
522    /// The accounts the agent has configured, for display.
523    ///
524    /// Optional. Omitted and empty are equivalent and mean no accounts are reported.
525    /// Agents that hold credentials for several providers or accounts SHOULD list each
526    /// of them and mark the ones in effect with `active`.
527    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
528    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
529    #[serde(default, skip_serializing_if = "Vec::is_empty")]
530    pub accounts: Vec<AuthAccount>,
531    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
532    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
533    /// Implementations MUST NOT assign meaning to keys they do not recognize.
534    ///
535    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
536    #[serde_as(deserialize_as = "DefaultOnError")]
537    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
538    #[serde(default)]
539    #[serde(rename = "_meta")]
540    pub meta: Option<Meta>,
541}
542
543#[cfg(feature = "unstable_auth_status")]
544impl AuthStatusResponse {
545    /// Builds [`AuthStatusResponse`] with the required fields set. Optional fields start unset or empty.
546    #[must_use]
547    pub fn new(state: AuthState) -> Self {
548        Self {
549            state,
550            accounts: vec![],
551            meta: None,
552        }
553    }
554
555    /// The accounts the agent has configured, for display.
556    #[must_use]
557    pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
558        self.accounts = accounts;
559        self
560    }
561
562    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
563    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
564    /// do not recognize.
565    ///
566    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
567    #[must_use]
568    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
569        self.meta = meta.into_option();
570        self
571    }
572}
573
574/// **UNSTABLE**
575///
576/// This capability is not part of the spec yet, and may be removed or changed at any point.
577///
578/// Notification sent by the agent when its authentication status changes.
579///
580/// Carries the same fields as the `auth/status` response. Agents that advertise the
581/// `auth.status` capability MAY send it at any time after `initialize`, and SHOULD only
582/// send it when the reported status differs from the last one sent on the connection.
583#[cfg(feature = "unstable_auth_status")]
584#[serde_as]
585#[skip_serializing_none]
586#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
587#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
588#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = AUTH_STATUS_UPDATE_NOTIFICATION)))]
589#[serde(rename_all = "camelCase")]
590#[non_exhaustive]
591pub struct AuthStatusUpdateNotification {
592    /// Whether the agent can start sessions without the user signing in first.
593    ///
594    /// This is the only field clients should act on. Clients MUST treat values they
595    /// do not recognize as `unknown`.
596    pub state: AuthState,
597    /// The accounts the agent has configured, for display.
598    ///
599    /// Optional. Omitted and empty are equivalent and mean no accounts are reported.
600    /// Agents that hold credentials for several providers or accounts SHOULD list each
601    /// of them and mark the ones in effect with `active`.
602    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
603    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
604    #[serde(default, skip_serializing_if = "Vec::is_empty")]
605    pub accounts: Vec<AuthAccount>,
606    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
607    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
608    /// Implementations MUST NOT assign meaning to keys they do not recognize.
609    ///
610    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
611    #[serde_as(deserialize_as = "DefaultOnError")]
612    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
613    #[serde(default)]
614    #[serde(rename = "_meta")]
615    pub meta: Option<Meta>,
616}
617
618#[cfg(feature = "unstable_auth_status")]
619impl AuthStatusUpdateNotification {
620    /// Builds [`AuthStatusUpdateNotification`] with the required fields set. Optional fields start unset or empty.
621    #[must_use]
622    pub fn new(state: AuthState) -> Self {
623        Self {
624            state,
625            accounts: vec![],
626            meta: None,
627        }
628    }
629
630    /// The accounts the agent has configured, for display.
631    #[must_use]
632    pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
633        self.accounts = accounts;
634        self
635    }
636
637    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
638    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
639    /// do not recognize.
640    ///
641    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
642    #[must_use]
643    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
644        self.meta = meta.into_option();
645        self
646    }
647}
648
649/// **UNSTABLE**
650///
651/// This capability is not part of the spec yet, and may be removed or changed at any point.
652///
653/// The agent's overall authentication state.
654#[cfg(feature = "unstable_auth_status")]
655#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
656#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
657#[serde(rename_all = "snake_case")]
658#[non_exhaustive]
659pub enum AuthState {
660    /// Credentials are configured. This does not guarantee that they are valid.
661    Authenticated,
662    /// The user needs to sign in. This includes credentials that are missing,
663    /// expired, or known to be invalid.
664    Unauthenticated,
665    /// No authentication is needed, for example because the agent uses a local model.
666    NotRequired,
667    /// The agent cannot currently determine the state.
668    Unknown,
669    /// Custom or future authentication state.
670    ///
671    /// Values beginning with `_` are reserved for implementation-specific
672    /// extensions. Unknown values that do not begin with `_` are reserved for
673    /// future ACP variants.
674    #[serde(untagged)]
675    Other(String),
676}
677
678/// **UNSTABLE**
679///
680/// This capability is not part of the spec yet, and may be removed or changed at any point.
681///
682/// An account or credential the agent has configured, such as a signed-in user
683/// account or an API key.
684///
685/// All fields are display information only. Clients MUST NOT use them as stable
686/// account identifiers, and SHOULD NOT log them or send them elsewhere, such as to
687/// telemetry.
688#[cfg(feature = "unstable_auth_status")]
689#[serde_as]
690#[skip_serializing_none]
691#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
692#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
693#[serde(rename_all = "camelCase")]
694#[non_exhaustive]
695pub struct AuthAccount {
696    /// Human-readable name for the account, suitable for display on its own,
697    /// for example `Claude Max`, `OpenAI API key`, or `Vertex AI`.
698    pub label: String,
699    /// Whether the agent currently uses this account by default.
700    ///
701    /// Several accounts MAY be active, for example one per provider.
702    ///
703    /// Optional. Omitted and `null` are equivalent and mean the agent does not indicate whether this account is in effect.
704    #[serde_as(deserialize_as = "DefaultOnError")]
705    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
706    #[serde(default)]
707    pub active: Option<bool>,
708    /// Display name of the user, such as a username or full name.
709    ///
710    /// Optional. Omitted and `null` are equivalent and mean no name is reported.
711    #[serde_as(deserialize_as = "DefaultOnError")]
712    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
713    #[serde(default)]
714    pub name: Option<String>,
715    /// Email address of the user.
716    ///
717    /// Optional. Omitted and `null` are equivalent and mean no email is reported.
718    #[serde_as(deserialize_as = "DefaultOnError")]
719    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
720    #[serde(default)]
721    pub email: Option<String>,
722    /// Display name of the organization, team, or workspace the account is using.
723    ///
724    /// Optional. Omitted and `null` are equivalent and mean no organization is reported.
725    #[serde_as(deserialize_as = "DefaultOnError")]
726    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
727    #[serde(default)]
728    pub organization: Option<String>,
729    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
730    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
731    /// Implementations MUST NOT assign meaning to keys they do not recognize.
732    ///
733    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
734    #[serde_as(deserialize_as = "DefaultOnError")]
735    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
736    #[serde(default)]
737    #[serde(rename = "_meta")]
738    pub meta: Option<Meta>,
739}
740
741#[cfg(feature = "unstable_auth_status")]
742impl AuthAccount {
743    /// Builds [`AuthAccount`] with the required fields set. Optional fields start unset or empty.
744    #[must_use]
745    pub fn new(label: impl Into<String>) -> Self {
746        Self {
747            label: label.into(),
748            active: None,
749            name: None,
750            email: None,
751            organization: None,
752            meta: None,
753        }
754    }
755
756    /// Whether the agent currently uses this account by default.
757    #[must_use]
758    pub fn active(mut self, active: impl IntoOption<bool>) -> Self {
759        self.active = active.into_option();
760        self
761    }
762
763    /// Display name of the user, such as a username or full name.
764    #[must_use]
765    pub fn name(mut self, name: impl IntoOption<String>) -> Self {
766        self.name = name.into_option();
767        self
768    }
769
770    /// Email address of the user.
771    #[must_use]
772    pub fn email(mut self, email: impl IntoOption<String>) -> Self {
773        self.email = email.into_option();
774        self
775    }
776
777    /// Display name of the organization, team, or workspace the account is using.
778    #[must_use]
779    pub fn organization(mut self, organization: impl IntoOption<String>) -> Self {
780        self.organization = organization.into_option();
781        self
782    }
783
784    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
785    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
786    /// do not recognize.
787    ///
788    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
789    #[must_use]
790    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
791        self.meta = meta.into_option();
792        self
793    }
794}
795
796/// **UNSTABLE**
797///
798/// This capability is not part of the spec yet, and may be removed or changed at any point.
799///
800/// Auth status capabilities supported by the agent.
801///
802/// Supplying `{}` means the agent supports the `auth/status` method.
803#[cfg(feature = "unstable_auth_status")]
804#[serde_as]
805#[skip_serializing_none]
806#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
807#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
808#[non_exhaustive]
809pub struct AuthStatusCapabilities {
810    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
811    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
812    /// Implementations MUST NOT assign meaning to keys they do not recognize.
813    ///
814    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
815    #[serde_as(deserialize_as = "DefaultOnError")]
816    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
817    #[serde(default)]
818    #[serde(rename = "_meta")]
819    pub meta: Option<Meta>,
820}
821
822#[cfg(feature = "unstable_auth_status")]
823impl AuthStatusCapabilities {
824    /// Builds an empty [`AuthStatusCapabilities`]. Use builder methods to advertise supported sub-capabilities.
825    #[must_use]
826    pub fn new() -> Self {
827        Self::default()
828    }
829
830    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
831    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
832    /// do not recognize.
833    ///
834    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
835    #[must_use]
836    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
837        self.meta = meta.into_option();
838        self
839    }
840}
841
842/// Authentication-related extension capabilities supported by the agent.
843///
844/// This object does not advertise support for `auth/login` or `auth/logout`.
845/// Those methods are advertised by a non-empty `authMethods` list in the
846/// `initialize` response.
847#[serde_as]
848#[skip_serializing_none]
849#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
850#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
851#[serde(rename_all = "camelCase")]
852#[non_exhaustive]
853pub struct AgentAuthCapabilities {
854    /// **UNSTABLE**
855    ///
856    /// This capability is not part of the spec yet, and may be removed or changed at any point.
857    ///
858    /// Whether the agent supports the `auth/status` method.
859    ///
860    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
861    /// Supplying `{}` means the agent supports the `auth/status` method.
862    #[cfg(feature = "unstable_auth_status")]
863    #[serde_as(deserialize_as = "DefaultOnError")]
864    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
865    #[serde(default)]
866    pub status: Option<AuthStatusCapabilities>,
867    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
868    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
869    /// Implementations MUST NOT assign meaning to keys they do not recognize.
870    ///
871    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
872    #[serde_as(deserialize_as = "DefaultOnError")]
873    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
874    #[serde(default)]
875    #[serde(rename = "_meta")]
876    pub meta: Option<Meta>,
877}
878
879impl AgentAuthCapabilities {
880    /// Builds an empty [`AgentAuthCapabilities`].
881    #[must_use]
882    pub fn new() -> Self {
883        Self::default()
884    }
885
886    /// **UNSTABLE**
887    ///
888    /// This capability is not part of the spec yet, and may be removed or changed at any point.
889    ///
890    /// Whether the agent supports the `auth/status` method.
891    #[cfg(feature = "unstable_auth_status")]
892    #[must_use]
893    pub fn status(mut self, status: impl IntoOption<AuthStatusCapabilities>) -> Self {
894        self.status = status.into_option();
895        self
896    }
897
898    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
899    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
900    /// do not recognize.
901    ///
902    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
903    #[must_use]
904    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
905        self.meta = meta.into_option();
906        self
907    }
908}
909
910/// Typed identifier used for auth method values on the wire.
911#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
912#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
913#[serde(transparent)]
914#[from(forward)]
915#[non_exhaustive]
916pub struct AuthMethodId(pub Arc<str>);
917
918impl AuthMethodId {
919    /// Wraps a protocol string as a typed [`AuthMethodId`].
920    #[must_use]
921    pub fn new(id: impl Into<Self>) -> Self {
922        id.into()
923    }
924}
925
926/// Describes an available authentication method.
927///
928/// The `type` field acts as the discriminator in the serialized JSON form.
929#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
930#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
931#[serde(tag = "type", rename_all = "snake_case")]
932#[non_exhaustive]
933pub enum AuthMethod {
934    /// Client runs the configured agent program as a separate interactive
935    /// process, without passing this method to `auth/login`.
936    Terminal(AuthMethodTerminal),
937    /// Agent handles authentication itself through `auth/login`.
938    ///
939    /// The `type` discriminator value is `agent`.
940    Agent(AuthMethodAgent),
941    /// Custom or future authentication method.
942    ///
943    /// Values beginning with `_` are reserved for implementation-specific
944    /// extensions. Unknown values that do not begin with `_` are reserved for
945    /// future ACP variants.
946    ///
947    /// Clients that do not understand this method type should preserve the raw
948    /// payload when storing, replaying, proxying, or forwarding initialization
949    /// data, and otherwise ignore the method or display it generically.
950    #[serde(untagged)]
951    Other(OtherAuthMethod),
952}
953
954impl AuthMethod {
955    /// The unique identifier for this authentication method.
956    #[must_use]
957    pub fn method_id(&self) -> &AuthMethodId {
958        match self {
959            Self::Agent(a) => &a.method_id,
960            Self::Other(a) => &a.method_id,
961            Self::Terminal(t) => &t.method_id,
962        }
963    }
964
965    /// The human-readable name of this authentication method.
966    #[must_use]
967    pub fn name(&self) -> &str {
968        match self {
969            Self::Agent(a) => &a.name,
970            Self::Other(a) => &a.name,
971            Self::Terminal(t) => &t.name,
972        }
973    }
974
975    /// Optional human-readable description of this authentication method.
976    #[must_use]
977    pub fn description(&self) -> Option<&str> {
978        match self {
979            Self::Agent(a) => a.description.as_deref(),
980            Self::Other(a) => a
981                .fields
982                .get("description")
983                .and_then(serde_json::Value::as_str),
984            Self::Terminal(t) => t.description.as_deref(),
985        }
986    }
987
988    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
989    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
990    /// do not recognize.
991    ///
992    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
993    #[must_use]
994    pub fn meta(&self) -> Option<&Meta> {
995        match self {
996            Self::Agent(a) => a.meta.as_ref(),
997            Self::Other(a) => a.fields.get("_meta").and_then(serde_json::Value::as_object),
998            Self::Terminal(t) => t.meta.as_ref(),
999        }
1000    }
1001}
1002
1003/// Custom or future authentication method payload.
1004#[serde_as]
1005#[skip_serializing_none]
1006#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1007#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1008#[cfg_attr(feature = "schemars", schemars(inline))]
1009#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
1010#[serde(rename_all = "camelCase")]
1011#[non_exhaustive]
1012pub struct OtherAuthMethod {
1013    /// Custom or future authentication method type.
1014    ///
1015    /// Values beginning with `_` are reserved for implementation-specific
1016    /// extensions. Unknown values that do not begin with `_` are reserved for
1017    /// future ACP variants.
1018    #[serde(rename = "type")]
1019    pub type_: String,
1020    /// Unique identifier for this authentication method.
1021    pub method_id: AuthMethodId,
1022    /// Human-readable name of the authentication method.
1023    pub name: String,
1024    /// The remaining fields of the unknown authentication method payload, preserved verbatim,
1025    /// including `description` and `_meta`.
1026    #[serde(flatten)]
1027    pub fields: BTreeMap<String, serde_json::Value>,
1028}
1029
1030impl OtherAuthMethod {
1031    /// Builds [`OtherAuthMethod`] from an unknown discriminator and preserves the remaining extension fields.
1032    #[must_use]
1033    pub fn new(
1034        type_: impl Into<String>,
1035        method_id: impl Into<AuthMethodId>,
1036        name: impl Into<String>,
1037        mut fields: BTreeMap<String, serde_json::Value>,
1038    ) -> Self {
1039        fields.remove("type");
1040        fields.remove("methodId");
1041        fields.remove("name");
1042        Self {
1043            type_: type_.into(),
1044            method_id: method_id.into(),
1045            name: name.into(),
1046            fields,
1047        }
1048    }
1049}
1050
1051impl<'de> Deserialize<'de> for OtherAuthMethod {
1052    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1053    where
1054        D: serde::Deserializer<'de>,
1055    {
1056        #[derive(Deserialize)]
1057        #[serde(rename_all = "camelCase")]
1058        struct RawOtherAuthMethod {
1059            #[serde(rename = "type")]
1060            type_: String,
1061            method_id: AuthMethodId,
1062            name: String,
1063            #[serde(flatten)]
1064            fields: BTreeMap<String, serde_json::Value>,
1065        }
1066
1067        let raw = RawOtherAuthMethod::deserialize(deserializer)?;
1068        if is_known_auth_method_type(&raw.type_) {
1069            return Err(serde::de::Error::custom(format!(
1070                "known authentication method `{}` did not match its schema",
1071                raw.type_
1072            )));
1073        }
1074
1075        Ok(Self {
1076            type_: raw.type_,
1077            method_id: raw.method_id,
1078            name: raw.name,
1079            fields: raw.fields,
1080        })
1081    }
1082}
1083
1084fn is_known_auth_method_type(type_: &str) -> bool {
1085    matches!(type_, "agent" | "terminal")
1086}
1087
1088#[cfg(feature = "schemars")]
1089fn other_auth_method_schema(schema: &mut Schema) {
1090    super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
1091}
1092
1093/// Agent handles authentication itself through `auth/login`.
1094///
1095/// The `type` discriminator value is `agent`.
1096#[serde_as]
1097#[skip_serializing_none]
1098#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1099#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1100#[serde(rename_all = "camelCase")]
1101#[non_exhaustive]
1102pub struct AuthMethodAgent {
1103    /// Unique identifier for this authentication method.
1104    pub method_id: AuthMethodId,
1105    /// Human-readable name of the authentication method.
1106    pub name: String,
1107    /// Optional human-readable description of this authentication method.
1108    ///
1109    /// Omitted and `null` are equivalent and mean no description is provided.
1110    #[serde_as(deserialize_as = "DefaultOnError")]
1111    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1112    #[serde(default)]
1113    pub description: Option<String>,
1114    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1115    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1116    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1117    ///
1118    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1119    #[serde_as(deserialize_as = "DefaultOnError")]
1120    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1121    #[serde(default)]
1122    #[serde(rename = "_meta")]
1123    pub meta: Option<Meta>,
1124}
1125
1126impl AuthMethodAgent {
1127    /// Builds [`AuthMethodAgent`] with the required fields set. Optional fields start unset or empty.
1128    #[must_use]
1129    pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1130        Self {
1131            method_id: method_id.into(),
1132            name: name.into(),
1133            description: None,
1134            meta: None,
1135        }
1136    }
1137
1138    /// Optional human-readable description of this authentication method.
1139    #[must_use]
1140    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1141        self.description = description.into_option();
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 assign meaning to keys they
1147    /// do not recognize.
1148    ///
1149    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/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/// Terminal-based authentication method.
1158///
1159/// The client runs the configured agent program as a separate interactive
1160/// process for the user to authenticate via a TUI. Agents MUST advertise this
1161/// method only when the client enabled its terminal authentication capability.
1162/// A zero exit status signals success. Any other termination signals failure.
1163/// The client MUST NOT pass this method to `auth/login`.
1164#[serde_as]
1165#[skip_serializing_none]
1166#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1168#[serde(rename_all = "camelCase")]
1169#[non_exhaustive]
1170pub struct AuthMethodTerminal {
1171    /// Unique identifier for this authentication method.
1172    pub method_id: AuthMethodId,
1173    /// Human-readable name of the authentication method.
1174    pub name: String,
1175    /// Optional human-readable description of this authentication method.
1176    ///
1177    /// Omitted and `null` are equivalent and mean no description is provided.
1178    #[serde_as(deserialize_as = "DefaultOnError")]
1179    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1180    #[serde(default)]
1181    pub description: Option<String>,
1182    /// Additional arguments to append to the configured agent invocation for terminal auth.
1183    #[serde_as(deserialize_as = "DefaultOnNull")]
1184    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1185    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1186    pub args: Vec<String>,
1187    /// Additional environment variables to set on the configured agent invocation for terminal auth.
1188    /// Names MUST be unique. These values override same-named variables in the
1189    /// base launch configuration.
1190    #[serde_as(deserialize_as = "DefaultOnNull")]
1191    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1192    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1193    pub env: Vec<EnvVariable>,
1194    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1195    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1196    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1197    ///
1198    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1199    #[serde_as(deserialize_as = "DefaultOnError")]
1200    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1201    #[serde(default)]
1202    #[serde(rename = "_meta")]
1203    pub meta: Option<Meta>,
1204}
1205
1206impl AuthMethodTerminal {
1207    /// Builds [`AuthMethodTerminal`] with the required fields set. Optional fields start unset or empty.
1208    #[must_use]
1209    pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1210        Self {
1211            method_id: method_id.into(),
1212            name: name.into(),
1213            description: None,
1214            args: Vec::new(),
1215            env: Vec::new(),
1216            meta: None,
1217        }
1218    }
1219
1220    /// Additional arguments to append to the configured agent invocation for terminal auth.
1221    #[must_use]
1222    pub fn args(mut self, args: Vec<String>) -> Self {
1223        self.args = args;
1224        self
1225    }
1226
1227    /// Additional environment variables to set on the configured agent invocation for terminal auth.
1228    /// Names MUST be unique. These values override same-named variables in the
1229    /// base launch configuration.
1230    #[must_use]
1231    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1232        self.env = env;
1233        self
1234    }
1235
1236    /// Optional human-readable description of this authentication method.
1237    #[must_use]
1238    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1239        self.description = description.into_option();
1240        self
1241    }
1242
1243    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1244    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1245    /// do not recognize.
1246    ///
1247    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1248    #[must_use]
1249    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1250        self.meta = meta.into_option();
1251        self
1252    }
1253}
1254
1255// New session
1256
1257/// Request parameters for creating a new session.
1258///
1259/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/v2/session-setup#creating-a-session)
1260#[serde_as]
1261#[skip_serializing_none]
1262#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1264#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1265#[serde(rename_all = "camelCase")]
1266#[non_exhaustive]
1267pub struct NewSessionRequest {
1268    /// The working directory for this session. Must be an absolute path.
1269    pub cwd: AbsolutePath,
1270    /// Additional workspace roots for this session. Each path must be absolute.
1271    ///
1272    /// These expand the session's workspace scope without changing `cwd`, which
1273    /// remains the base for relative paths. When omitted or empty, no
1274    /// additional roots are activated for the new session.
1275    #[serde_as(deserialize_as = "DefaultOnNull")]
1276    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1277    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1278    pub additional_directories: Vec<AbsolutePath>,
1279    /// List of MCP (Model Context Protocol) servers the agent should connect to.
1280    #[serde_as(deserialize_as = "DefaultOnNull")]
1281    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1282    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1283    pub mcp_servers: Vec<McpServer>,
1284    /// System-level instructions for the new session.
1285    ///
1286    /// Optional. Omitted and `null` are equivalent and mean the Agent uses its
1287    /// own default instructions. Clients MUST only send this when the Agent
1288    /// advertises `session.systemPrompt`.
1289    #[serde(default)]
1290    pub system_prompt: Option<SystemPrompt>,
1291    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1292    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1293    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1294    ///
1295    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1296    #[serde_as(deserialize_as = "DefaultOnError")]
1297    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1298    #[serde(default)]
1299    #[serde(rename = "_meta")]
1300    pub meta: Option<Meta>,
1301}
1302
1303impl NewSessionRequest {
1304    /// Builds [`NewSessionRequest`] with the required request fields set. Optional fields start unset or empty.
1305    #[must_use]
1306    pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
1307        Self {
1308            cwd: cwd.into(),
1309            additional_directories: vec![],
1310            mcp_servers: vec![],
1311            system_prompt: None,
1312            meta: None,
1313        }
1314    }
1315
1316    /// Additional workspace roots for this session. Each path must be absolute.
1317    #[must_use]
1318    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1319    where
1320        I: IntoIterator<Item = P>,
1321        P: Into<AbsolutePath>,
1322    {
1323        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1324        self
1325    }
1326
1327    /// List of MCP (Model Context Protocol) servers the agent should connect to.
1328    #[must_use]
1329    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1330        self.mcp_servers = mcp_servers;
1331        self
1332    }
1333
1334    /// System-level instructions for the new session.
1335    #[must_use]
1336    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1337        self.system_prompt = system_prompt.into_option();
1338        self
1339    }
1340
1341    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1342    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1343    /// do not recognize.
1344    ///
1345    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1346    #[must_use]
1347    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1348        self.meta = meta.into_option();
1349        self
1350    }
1351}
1352
1353/// Response from creating a new session.
1354///
1355/// New sessions default to `idle` until a state update reports otherwise.
1356/// Updates may arrive before or after this response. Clients MUST NOT overwrite
1357/// an already-reported state when the response arrives.
1358///
1359/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/v2/session-setup#creating-a-session)
1360#[serde_as]
1361#[skip_serializing_none]
1362#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1363#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1364#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1365#[serde(rename_all = "camelCase")]
1366#[non_exhaustive]
1367pub struct NewSessionResponse {
1368    /// Unique identifier for the created session.
1369    ///
1370    /// Used in all subsequent requests for this conversation.
1371    pub session_id: SessionId,
1372    /// Initial session configuration options.
1373    ///
1374    /// Optional. Omitted or empty means no config options are advertised.
1375    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1376    /// Later `config_option_update` notifications replace this list.
1377    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1378    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1379    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1380    pub config_options: Vec<SessionConfigOption>,
1381    /// Initial commands the agent can execute in this session.
1382    ///
1383    /// Optional. Omitted or empty means no initial commands are advertised.
1384    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1385    /// Later `available_commands_update` notifications replace this list.
1386    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1387    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1388    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1389    pub available_commands: Vec<AvailableCommand>,
1390    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1391    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1392    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1393    ///
1394    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1395    #[serde_as(deserialize_as = "DefaultOnError")]
1396    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1397    #[serde(default)]
1398    #[serde(rename = "_meta")]
1399    pub meta: Option<Meta>,
1400}
1401
1402impl NewSessionResponse {
1403    /// Builds [`NewSessionResponse`] with the required response fields set. Optional fields start unset or empty.
1404    #[must_use]
1405    pub fn new(session_id: impl Into<SessionId>) -> Self {
1406        Self {
1407            session_id: session_id.into(),
1408            config_options: Vec::new(),
1409            available_commands: Vec::new(),
1410            meta: None,
1411        }
1412    }
1413
1414    /// Initial session configuration options.
1415    ///
1416    /// Optional. Omitted or empty means no config options are advertised.
1417    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1418    /// Later `config_option_update` notifications replace this list.
1419    #[must_use]
1420    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1421        self.config_options = config_options;
1422        self
1423    }
1424
1425    /// Initial commands the agent can execute in this session.
1426    #[must_use]
1427    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1428        self.available_commands = available_commands;
1429        self
1430    }
1431
1432    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1433    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1434    /// do not recognize.
1435    ///
1436    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1437    #[must_use]
1438    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1439        self.meta = meta.into_option();
1440        self
1441    }
1442}
1443
1444// Fork session
1445
1446/// **UNSTABLE**
1447///
1448/// This capability is not part of the spec yet, and may be removed or changed at any point.
1449///
1450/// Request parameters for forking an existing session.
1451///
1452/// Creates a new session based on the context of an existing one, allowing
1453/// operations like generating summaries without affecting the original session's history.
1454///
1455/// Only available if the Agent supports the `session.fork` capability.
1456#[cfg(feature = "unstable_session_fork")]
1457#[serde_as]
1458#[skip_serializing_none]
1459#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1460#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1461#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1462#[serde(rename_all = "camelCase")]
1463#[non_exhaustive]
1464pub struct ForkSessionRequest {
1465    /// The ID of the session to fork.
1466    pub session_id: SessionId,
1467    /// The working directory for this session. Must be an absolute path.
1468    pub cwd: AbsolutePath,
1469    /// Additional workspace roots to activate for this session. Each path must be absolute.
1470    ///
1471    /// When omitted or empty, no additional roots are activated. When non-empty,
1472    /// this is the complete resulting additional-root list for the forked
1473    /// session.
1474    #[serde_as(deserialize_as = "DefaultOnNull")]
1475    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1476    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1477    pub additional_directories: Vec<AbsolutePath>,
1478    /// List of MCP servers to connect to for this session.
1479    #[serde_as(deserialize_as = "DefaultOnNull")]
1480    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1481    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1482    pub mcp_servers: Vec<McpServer>,
1483    /// System-level instructions for the forked session.
1484    ///
1485    /// Optional. Omitted and `null` are equivalent. The Agent may then keep a
1486    /// system prompt it stored with the source session or use its default
1487    /// instructions. Clients MUST only send this when the Agent advertises
1488    /// `session.systemPrompt`, and SHOULD send the same value they used for the
1489    /// source session.
1490    #[serde(default)]
1491    pub system_prompt: Option<SystemPrompt>,
1492    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1493    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1494    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1495    ///
1496    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1497    #[serde_as(deserialize_as = "DefaultOnError")]
1498    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1499    #[serde(default)]
1500    #[serde(rename = "_meta")]
1501    pub meta: Option<Meta>,
1502}
1503
1504#[cfg(feature = "unstable_session_fork")]
1505impl ForkSessionRequest {
1506    /// Builds [`ForkSessionRequest`] with the required request fields set. Optional fields start unset or empty.
1507    #[must_use]
1508    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1509        Self {
1510            session_id: session_id.into(),
1511            cwd: cwd.into(),
1512            additional_directories: vec![],
1513            mcp_servers: vec![],
1514            system_prompt: None,
1515            meta: None,
1516        }
1517    }
1518
1519    /// Additional workspace roots to activate for this session. Each path must be absolute.
1520    #[must_use]
1521    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1522    where
1523        I: IntoIterator<Item = P>,
1524        P: Into<AbsolutePath>,
1525    {
1526        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1527        self
1528    }
1529
1530    /// List of MCP servers to connect to for this session.
1531    #[must_use]
1532    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1533        self.mcp_servers = mcp_servers;
1534        self
1535    }
1536
1537    /// System-level instructions for the forked session.
1538    #[must_use]
1539    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1540        self.system_prompt = system_prompt.into_option();
1541        self
1542    }
1543
1544    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1545    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1546    /// do not recognize.
1547    ///
1548    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1549    #[must_use]
1550    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1551        self.meta = meta.into_option();
1552        self
1553    }
1554}
1555
1556/// **UNSTABLE**
1557///
1558/// This capability is not part of the spec yet, and may be removed or changed at any point.
1559///
1560/// Response from forking an existing session.
1561#[cfg(feature = "unstable_session_fork")]
1562#[serde_as]
1563#[skip_serializing_none]
1564#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1565#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1566#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1567#[serde(rename_all = "camelCase")]
1568#[non_exhaustive]
1569pub struct ForkSessionResponse {
1570    /// Unique identifier for the newly created forked session.
1571    pub session_id: SessionId,
1572    /// Initial session configuration options.
1573    ///
1574    /// Optional. Omitted or empty means no config options are advertised.
1575    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1576    /// Later `config_option_update` notifications replace this list.
1577    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1578    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1579    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1580    pub config_options: Vec<SessionConfigOption>,
1581    /// Initial commands the agent can execute in this session.
1582    ///
1583    /// Optional. Omitted or empty means no initial commands are advertised.
1584    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1585    /// Later `available_commands_update` notifications replace this list.
1586    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1587    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1588    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1589    pub available_commands: Vec<AvailableCommand>,
1590    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1591    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1592    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1593    ///
1594    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1595    #[serde_as(deserialize_as = "DefaultOnError")]
1596    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1597    #[serde(default)]
1598    #[serde(rename = "_meta")]
1599    pub meta: Option<Meta>,
1600}
1601
1602#[cfg(feature = "unstable_session_fork")]
1603impl ForkSessionResponse {
1604    /// Builds [`ForkSessionResponse`] with the required response fields set. Optional fields start unset or empty.
1605    #[must_use]
1606    pub fn new(session_id: impl Into<SessionId>) -> Self {
1607        Self {
1608            session_id: session_id.into(),
1609            config_options: Vec::new(),
1610            available_commands: Vec::new(),
1611            meta: None,
1612        }
1613    }
1614
1615    /// Initial session configuration options.
1616    ///
1617    /// Optional. Omitted or empty means no config options are advertised.
1618    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1619    /// Later `config_option_update` notifications replace this list.
1620    #[must_use]
1621    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1622        self.config_options = config_options;
1623        self
1624    }
1625
1626    /// Initial commands the agent can execute in this session.
1627    #[must_use]
1628    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1629        self.available_commands = available_commands;
1630        self
1631    }
1632
1633    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1634    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1635    /// do not recognize.
1636    ///
1637    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1638    #[must_use]
1639    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1640        self.meta = meta.into_option();
1641        self
1642    }
1643}
1644
1645// Resume session
1646
1647/// Request parameters for resuming an existing session.
1648///
1649/// Resumes an existing session and optionally replays retained conversation
1650/// history according to `replayFrom`.
1651#[serde_as]
1652#[skip_serializing_none]
1653#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1654#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1655#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1656#[serde(rename_all = "camelCase")]
1657#[non_exhaustive]
1658pub struct ResumeSessionRequest {
1659    /// The ID of the session to resume.
1660    pub session_id: SessionId,
1661    /// The working directory for this session. Must be an absolute path.
1662    ///
1663    /// Clients MUST send the session's working directory. Agents MAY reject a different directory
1664    /// with `-32602`, and MAY normalize paths when comparing.
1665    pub cwd: AbsolutePath,
1666    /// Additional workspace roots to activate for this session. Each path must be absolute.
1667    ///
1668    /// When omitted or empty, no additional roots are activated. When non-empty,
1669    /// this is the complete resulting additional-root list for the resumed
1670    /// session. It may differ from any previously used or reported list as long as
1671    /// the request `cwd` matches the session's `cwd`.
1672    #[serde_as(deserialize_as = "DefaultOnNull")]
1673    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1674    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1675    pub additional_directories: Vec<AbsolutePath>,
1676    /// List of MCP servers to connect to for this session.
1677    ///
1678    /// The complete set of Client-provided servers. Omitted and `[]` mean none. Agents SHOULD
1679    /// NOT restore servers from earlier requests.
1680    #[serde_as(deserialize_as = "DefaultOnNull")]
1681    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1682    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1683    pub mcp_servers: Vec<McpServer>,
1684    /// Inclusive cursor describing where conversation replay should begin.
1685    ///
1686    /// Optional. Omitted and `null` are equivalent and mean the Agent should resume without
1687    /// replaying previous conversation history. Replay cursors are inclusive:
1688    /// replay includes the position identified by the cursor. Supplying
1689    /// `{ "type": "start" }` means the Agent should replay all retained
1690    /// conversation history before responding.
1691    ///
1692    /// Agents MUST reject unsupported cursor types or malformed cursor values
1693    /// with `-32602` (Invalid params), rather than substituting another cursor
1694    /// or resuming without replay.
1695    #[serde(default)]
1696    pub replay_from: Option<ReplayFrom>,
1697    /// System-level instructions for the resumed session.
1698    ///
1699    /// Optional. Omitted and `null` are equivalent. The Agent may then keep a
1700    /// system prompt it stored with the session or use its default instructions.
1701    /// Clients MUST only send this when the Agent advertises
1702    /// `session.systemPrompt`, and SHOULD send the same value they used when the
1703    /// session was created.
1704    #[serde(default)]
1705    pub system_prompt: Option<SystemPrompt>,
1706    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1707    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1708    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1709    ///
1710    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1711    #[serde_as(deserialize_as = "DefaultOnError")]
1712    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1713    #[serde(default)]
1714    #[serde(rename = "_meta")]
1715    pub meta: Option<Meta>,
1716}
1717
1718impl ResumeSessionRequest {
1719    /// Builds [`ResumeSessionRequest`] with the required request fields set. Optional fields start unset or empty.
1720    #[must_use]
1721    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1722        Self {
1723            session_id: session_id.into(),
1724            cwd: cwd.into(),
1725            additional_directories: vec![],
1726            mcp_servers: vec![],
1727            replay_from: None,
1728            system_prompt: None,
1729            meta: None,
1730        }
1731    }
1732
1733    /// Additional workspace roots to activate for this session. Each path must be absolute.
1734    #[must_use]
1735    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1736    where
1737        I: IntoIterator<Item = P>,
1738        P: Into<AbsolutePath>,
1739    {
1740        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1741        self
1742    }
1743
1744    /// List of MCP servers to connect to for this session.
1745    #[must_use]
1746    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1747        self.mcp_servers = mcp_servers;
1748        self
1749    }
1750
1751    /// Inclusive cursor describing where conversation replay should begin.
1752    ///
1753    /// Omitted and `null` are equivalent and mean the Agent should resume without replaying
1754    /// previous conversation history. Replay cursors are inclusive: replay
1755    /// includes the position identified by the cursor. Supplying
1756    /// `{ "type": "start" }` means the Agent should replay all retained
1757    /// conversation history before responding.
1758    #[must_use]
1759    pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1760        self.replay_from = replay_from.into_option();
1761        self
1762    }
1763
1764    /// System-level instructions for the resumed session.
1765    #[must_use]
1766    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1767        self.system_prompt = system_prompt.into_option();
1768        self
1769    }
1770
1771    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1772    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1773    /// do not recognize.
1774    ///
1775    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1776    #[must_use]
1777    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1778        self.meta = meta.into_option();
1779        self
1780    }
1781}
1782
1783/// Inclusive cursor describing where replayed session history should begin.
1784///
1785/// Replay includes the position identified by the cursor.
1786#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1787#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1788#[serde(tag = "type", rename_all = "snake_case")]
1789#[non_exhaustive]
1790pub enum ReplayFrom {
1791    /// Replay all retained conversation history from its first replayable entry.
1792    Start(ReplayFromStart),
1793    /// Custom or future replay cursor.
1794    ///
1795    /// Values beginning with `_` are reserved for implementation-specific
1796    /// extensions. Unknown values that do not begin with `_` are reserved for
1797    /// future ACP variants.
1798    ///
1799    /// Intermediaries SHOULD preserve the raw payload when storing, replaying,
1800    /// proxying, or forwarding requests. Agents that do not support this cursor
1801    /// MUST reject the request with `-32602` (Invalid params), rather than
1802    /// substituting another cursor or resuming without replay.
1803    #[serde(untagged)]
1804    Other(OtherReplayFrom),
1805}
1806
1807impl From<ReplayFromStart> for ReplayFrom {
1808    fn from(replay_from: ReplayFromStart) -> Self {
1809        Self::Start(replay_from)
1810    }
1811}
1812
1813/// Inclusive replay cursor requesting replay from the start of retained conversation history.
1814#[serde_as]
1815#[skip_serializing_none]
1816#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1817#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1818#[serde(rename_all = "camelCase")]
1819#[non_exhaustive]
1820pub struct ReplayFromStart {
1821    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1822    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1823    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1824    ///
1825    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1826    #[serde_as(deserialize_as = "DefaultOnError")]
1827    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1828    #[serde(default)]
1829    #[serde(rename = "_meta")]
1830    pub meta: Option<Meta>,
1831}
1832
1833impl ReplayFromStart {
1834    /// Builds [`ReplayFromStart`].
1835    #[must_use]
1836    pub fn new() -> Self {
1837        Self::default()
1838    }
1839
1840    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1841    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1842    /// do not recognize.
1843    ///
1844    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1845    #[must_use]
1846    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1847        self.meta = meta.into_option();
1848        self
1849    }
1850}
1851
1852/// Custom or future replay cursor payload.
1853#[serde_as]
1854#[skip_serializing_none]
1855#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1856#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1857#[cfg_attr(feature = "schemars", schemars(inline))]
1858#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1859#[serde(rename_all = "camelCase")]
1860#[non_exhaustive]
1861pub struct OtherReplayFrom {
1862    /// Custom or future replay cursor type.
1863    ///
1864    /// Values beginning with `_` are reserved for implementation-specific
1865    /// extensions. Unknown values that do not begin with `_` are reserved for
1866    /// future ACP variants.
1867    #[serde(rename = "type")]
1868    pub type_: String,
1869    /// The remaining fields of the unknown replay cursor payload, preserved verbatim,
1870    /// including `_meta`.
1871    #[serde(flatten)]
1872    pub fields: BTreeMap<String, serde_json::Value>,
1873}
1874
1875impl OtherReplayFrom {
1876    /// Builds [`OtherReplayFrom`] from an unknown discriminator and preserves the remaining extension fields.
1877    #[must_use]
1878    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1879        fields.remove("type");
1880        Self {
1881            type_: type_.into(),
1882            fields,
1883        }
1884    }
1885}
1886
1887impl<'de> Deserialize<'de> for OtherReplayFrom {
1888    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1889    where
1890        D: serde::Deserializer<'de>,
1891    {
1892        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1893        let type_ = fields
1894            .remove("type")
1895            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1896        let serde_json::Value::String(type_) = type_ else {
1897            return Err(serde::de::Error::custom("`type` must be a string"));
1898        };
1899
1900        if is_known_replay_from_type(&type_) {
1901            return Err(serde::de::Error::custom(format!(
1902                "known replay cursor `{type_}` did not match its schema"
1903            )));
1904        }
1905
1906        Ok(Self { type_, fields })
1907    }
1908}
1909
1910fn is_known_replay_from_type(type_: &str) -> bool {
1911    matches!(type_, "start")
1912}
1913
1914#[cfg(feature = "schemars")]
1915fn other_replay_from_schema(schema: &mut Schema) {
1916    super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1917}
1918
1919crate::serde_util::default_on_null! {
1920    /// Response from resuming an existing session.
1921    ///
1922    /// The agent MUST send a current `state_update` after any requested history
1923    /// replay and before this response, even without replay. The update reports
1924    /// foreground state after resuming. The response alone does not imply `idle`.
1925    ///
1926    /// See protocol docs: [Current-State Synchronization](https://agentclientprotocol.com/protocol/v2/session-setup#current-state-synchronization)
1927    #[serde_as]
1928    #[skip_serializing_none]
1929    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1930    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1931    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1932    #[serde(rename_all = "camelCase")]
1933    #[non_exhaustive]
1934    pub struct ResumeSessionResponse {
1935        /// Initial session configuration options.
1936        ///
1937        /// Optional. Omitted or empty means no config options are advertised.
1938        /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1939        /// Later `config_option_update` notifications replace this list.
1940        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1941        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1942        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1943        pub config_options: Vec<SessionConfigOption>,
1944        /// Initial commands the agent can execute in this session.
1945        ///
1946        /// Optional. Omitted or empty means no initial commands are advertised.
1947        /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1948        /// Later `available_commands_update` notifications replace this list.
1949        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1950        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1951        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1952        pub available_commands: Vec<AvailableCommand>,
1953        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1954        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1955        /// Implementations MUST NOT assign meaning to keys they do not recognize.
1956        ///
1957        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1958        #[serde_as(deserialize_as = "DefaultOnError")]
1959        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1960        #[serde(default)]
1961        #[serde(rename = "_meta")]
1962        pub meta: Option<Meta>,
1963    }
1964}
1965
1966impl ResumeSessionResponse {
1967    /// Builds [`ResumeSessionResponse`] with the required response fields set. Optional fields start unset or empty.
1968    #[must_use]
1969    pub fn new() -> Self {
1970        Self::default()
1971    }
1972
1973    /// Initial session configuration options.
1974    ///
1975    /// Optional. Omitted or empty means no config options are advertised.
1976    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1977    /// Later `config_option_update` notifications replace this list.
1978    #[must_use]
1979    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1980        self.config_options = config_options;
1981        self
1982    }
1983
1984    /// Initial commands the agent can execute in this session.
1985    #[must_use]
1986    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1987        self.available_commands = available_commands;
1988        self
1989    }
1990
1991    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1992    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1993    /// do not recognize.
1994    ///
1995    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1996    #[must_use]
1997    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1998        self.meta = meta.into_option();
1999        self
2000    }
2001}
2002
2003// Close session
2004
2005/// Request parameters for closing an active session.
2006///
2007/// The Agent MUST cancel any ongoing work related to the session as if `session/cancel`
2008/// was called, then free the resources associated with the session. Unlike `session/cancel`,
2009/// closing also stops the session's background work. After the close response, the Agent
2010/// sends no other messages for the session on that connection.
2011///
2012/// Closing a session that does not exist or is not active SHOULD succeed, and Clients SHOULD
2013/// treat a `-32002` response as a successful close.
2014///
2015/// When closing cancels active foreground work, the Agent MUST send the final
2016/// updates for that work and the idle `state_update` with `stopReason: "cancelled"`
2017/// before responding successfully to `session/close`. Closing an idle session
2018/// does not itself require another idle update.
2019///
2020/// Closing also cancels pending `session/prompt` requests whose messages have not
2021/// been inserted, including held prompts. If closing prevents a prompt's user message from being inserted, the
2022/// Agent MUST answer that request with `-32800` (Request Cancelled). If the message
2023/// has already been inserted, the Agent MUST respond successfully with its
2024/// `messageId`. Prompt responses need not precede the close response.
2025#[serde_as]
2026#[skip_serializing_none]
2027#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2028#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2029#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2030#[serde(rename_all = "camelCase")]
2031#[non_exhaustive]
2032pub struct CloseSessionRequest {
2033    /// The ID of the session to close.
2034    pub session_id: SessionId,
2035    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2036    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2037    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2038    ///
2039    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2040    #[serde_as(deserialize_as = "DefaultOnError")]
2041    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2042    #[serde(default)]
2043    #[serde(rename = "_meta")]
2044    pub meta: Option<Meta>,
2045}
2046
2047impl CloseSessionRequest {
2048    /// Builds [`CloseSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2049    #[must_use]
2050    pub fn new(session_id: impl Into<SessionId>) -> Self {
2051        Self {
2052            session_id: session_id.into(),
2053            meta: None,
2054        }
2055    }
2056
2057    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2058    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2059    /// do not recognize.
2060    ///
2061    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2062    #[must_use]
2063    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2064        self.meta = meta.into_option();
2065        self
2066    }
2067}
2068
2069crate::serde_util::default_on_null! {
2070    /// Response from closing a session.
2071    #[serde_as]
2072    #[skip_serializing_none]
2073    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2074    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2075    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2076    #[serde(rename_all = "camelCase")]
2077    #[non_exhaustive]
2078    pub struct CloseSessionResponse {
2079        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2080        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2081        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2082        ///
2083        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2084        #[serde_as(deserialize_as = "DefaultOnError")]
2085        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2086        #[serde(default)]
2087        #[serde(rename = "_meta")]
2088        pub meta: Option<Meta>,
2089    }
2090}
2091
2092impl CloseSessionResponse {
2093    /// Builds [`CloseSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2094    #[must_use]
2095    pub fn new() -> Self {
2096        Self::default()
2097    }
2098
2099    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2100    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2101    /// do not recognize.
2102    ///
2103    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2104    #[must_use]
2105    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2106        self.meta = meta.into_option();
2107        self
2108    }
2109}
2110
2111// Session status
2112
2113/// **UNSTABLE**
2114///
2115/// This capability is not part of the spec yet, and may be removed or changed at any point.
2116///
2117/// Request parameters for checking the status of a session.
2118///
2119/// Only available if the Agent supports the `session.status` capability.
2120///
2121/// `session/status` MUST NOT have side effects. The Agent does not load, resume, create,
2122/// close, or change the session, and does not attach the requesting connection to it.
2123/// A session the Agent does not know gets `-32002` with the requested `sessionId` in `data`.
2124#[cfg(feature = "unstable_session_status")]
2125#[serde_as]
2126#[skip_serializing_none]
2127#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2129#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2130#[serde(rename_all = "camelCase")]
2131#[non_exhaustive]
2132pub struct SessionStatusRequest {
2133    /// The ID of the session to check.
2134    pub session_id: SessionId,
2135    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2136    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2137    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2138    ///
2139    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2140    #[serde_as(deserialize_as = "DefaultOnError")]
2141    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2142    #[serde(default)]
2143    #[serde(rename = "_meta")]
2144    pub meta: Option<Meta>,
2145}
2146
2147#[cfg(feature = "unstable_session_status")]
2148impl SessionStatusRequest {
2149    /// Builds [`SessionStatusRequest`] with the required request fields set. Optional fields start unset or empty.
2150    #[must_use]
2151    pub fn new(session_id: impl Into<SessionId>) -> Self {
2152        Self {
2153            session_id: session_id.into(),
2154            meta: None,
2155        }
2156    }
2157
2158    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2159    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2160    /// do not recognize.
2161    ///
2162    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2163    #[must_use]
2164    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2165        self.meta = meta.into_option();
2166        self
2167    }
2168}
2169
2170/// **UNSTABLE**
2171///
2172/// This capability is not part of the spec yet, and may be removed or changed at any point.
2173///
2174/// Response from checking the status of a session.
2175///
2176/// The result is a snapshot. A session reported `open` may be closed or evicted before
2177/// the Client's next request.
2178#[cfg(feature = "unstable_session_status")]
2179#[serde_as]
2180#[skip_serializing_none]
2181#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2183#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2184#[serde(rename_all = "camelCase")]
2185#[non_exhaustive]
2186pub struct SessionStatusResponse {
2187    /// Whether the Agent has the session in memory.
2188    ///
2189    /// Required. Clients MUST treat a value they do not recognize as `saved`.
2190    pub status: SessionStatus,
2191    /// The session's current foreground state, as the same snapshot the Agent sends in
2192    /// `state_update` notifications.
2193    ///
2194    /// Optional. Agents SHOULD include it when `status` is `open`. Omitted, `null`, and
2195    /// malformed values mean the Agent is not reporting foreground state, and Clients
2196    /// MUST NOT read that as `idle`. This is a snapshot, not a new transition. Clients
2197    /// MUST NOT count an `idle` snapshot as a new completion.
2198    #[serde_as(deserialize_as = "DefaultOnError")]
2199    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2200    #[serde(default)]
2201    pub state: Option<StateUpdate>,
2202    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2203    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2204    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2205    ///
2206    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2207    #[serde_as(deserialize_as = "DefaultOnError")]
2208    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2209    #[serde(default)]
2210    #[serde(rename = "_meta")]
2211    pub meta: Option<Meta>,
2212}
2213
2214#[cfg(feature = "unstable_session_status")]
2215impl SessionStatusResponse {
2216    /// Builds [`SessionStatusResponse`] with the required response fields set. Optional fields start unset or empty.
2217    #[must_use]
2218    pub fn new(status: SessionStatus) -> Self {
2219        Self {
2220            status,
2221            state: None,
2222            meta: None,
2223        }
2224    }
2225
2226    /// The session's current foreground state, as the same snapshot the Agent sends in
2227    /// `state_update` notifications.
2228    #[must_use]
2229    pub fn state(mut self, state: impl IntoOption<StateUpdate>) -> Self {
2230        self.state = state.into_option();
2231        self
2232    }
2233
2234    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2235    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2236    /// do not recognize.
2237    ///
2238    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2239    #[must_use]
2240    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2241        self.meta = meta.into_option();
2242        self
2243    }
2244}
2245
2246/// **UNSTABLE**
2247///
2248/// This capability is not part of the spec yet, and may be removed or changed at any point.
2249///
2250/// Whether the Agent has a session in memory, as reported by `session/status`.
2251#[cfg(feature = "unstable_session_status")]
2252#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2253#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2254#[serde(rename_all = "snake_case")]
2255#[non_exhaustive]
2256pub enum SessionStatus {
2257    /// The Agent has the session in memory, for any connection.
2258    ///
2259    /// This does not mean the requesting connection is attached to it. A Client that did
2260    /// not create or resume the session on its own connection MUST still call
2261    /// `session/resume` before sending `session/prompt` or expecting
2262    /// `session/update` notifications.
2263    Open,
2264    /// The Agent knows the session and can resume it, but does not have it in memory.
2265    ///
2266    /// The Client can call `session/resume` to start it.
2267    Saved,
2268    /// Custom or future session status.
2269    ///
2270    /// Values beginning with `_` are reserved for implementation-specific
2271    /// extensions. Unknown values that do not begin with `_` are reserved for
2272    /// future ACP variants. Clients MUST treat a value they do not recognize as
2273    /// `saved`.
2274    #[serde(untagged)]
2275    Other(String),
2276}
2277
2278// Cancel tool call
2279
2280/// Request parameters for cancelling one tool call.
2281///
2282/// Clients MUST NOT send it unless the tool call currently advertises `capabilities.cancel`.
2283///
2284/// The Agent responds once it has accepted the cancellation. The response does not mean
2285/// the work has stopped. The Agent confirms it with a `tool_call_update` to a final
2286/// status: normally `cancelled`, or `completed` or `failed` if the work finished first.
2287///
2288/// The Agent responds with `-32602` if the tool call is unknown, already final, or not
2289/// cancellable. Cancelling a tool call does not change the foreground state by itself.
2290///
2291/// See protocol docs: [Cancelling Tool Calls](https://agentclientprotocol.com/protocol/v2/tool-calls#cancelling-tool-calls)
2292#[serde_as]
2293#[skip_serializing_none]
2294#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2295#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2296#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2297#[serde(rename_all = "camelCase")]
2298#[non_exhaustive]
2299pub struct CancelToolCallRequest {
2300    /// The ID of the session that owns the tool call.
2301    pub session_id: SessionId,
2302    /// The ID of the tool call to cancel.
2303    pub tool_call_id: ToolCallId,
2304    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2305    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2306    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2307    ///
2308    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2309    #[serde_as(deserialize_as = "DefaultOnError")]
2310    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2311    #[serde(default)]
2312    #[serde(rename = "_meta")]
2313    pub meta: Option<Meta>,
2314}
2315
2316impl CancelToolCallRequest {
2317    /// Builds [`CancelToolCallRequest`] with the required request fields set. Optional fields start unset or empty.
2318    #[must_use]
2319    pub fn new(session_id: impl Into<SessionId>, tool_call_id: impl Into<ToolCallId>) -> Self {
2320        Self {
2321            session_id: session_id.into(),
2322            tool_call_id: tool_call_id.into(),
2323            meta: None,
2324        }
2325    }
2326
2327    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2328    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2329    /// do not recognize.
2330    ///
2331    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2332    #[must_use]
2333    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2334        self.meta = meta.into_option();
2335        self
2336    }
2337}
2338
2339crate::serde_util::default_on_null! {
2340    /// Response from cancelling a tool call.
2341    #[serde_as]
2342    #[skip_serializing_none]
2343    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2344    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2345    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2346    #[serde(rename_all = "camelCase")]
2347    #[non_exhaustive]
2348    pub struct CancelToolCallResponse {
2349        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2350        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2351        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2352        ///
2353        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2354        #[serde_as(deserialize_as = "DefaultOnError")]
2355        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2356        #[serde(default)]
2357        #[serde(rename = "_meta")]
2358        pub meta: Option<Meta>,
2359    }
2360}
2361
2362impl CancelToolCallResponse {
2363    /// Builds [`CancelToolCallResponse`] with the required response fields set. Optional fields start unset or empty.
2364    #[must_use]
2365    pub fn new() -> Self {
2366        Self::default()
2367    }
2368
2369    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2370    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2371    /// do not recognize.
2372    ///
2373    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2374    #[must_use]
2375    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2376        self.meta = meta.into_option();
2377        self
2378    }
2379}
2380
2381// Cancel terminal
2382
2383/// Request parameters for cancelling one terminal.
2384///
2385/// Clients MUST NOT send it unless the terminal currently advertises `capabilities.cancel`.
2386///
2387/// The Agent responds once it has accepted the cancellation. The response does not mean
2388/// the work has stopped. The Agent confirms it with a `terminal_update` that sets
2389/// `exitStatus`. Cancelling stops the process but does not release the terminal, and does
2390/// not change the status of a tool call that references it.
2391///
2392/// The Agent responds with `-32602` if the terminal is unknown, has already exited, or is not
2393/// cancellable. Cancelling a terminal does not change the foreground state by itself.
2394///
2395/// See protocol docs: [Cancelling Terminals](https://agentclientprotocol.com/protocol/v2/tool-calls#cancelling-terminals)
2396#[serde_as]
2397#[skip_serializing_none]
2398#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2399#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2400#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2401#[serde(rename_all = "camelCase")]
2402#[non_exhaustive]
2403pub struct CancelTerminalRequest {
2404    /// The ID of the session that owns the terminal.
2405    pub session_id: SessionId,
2406    /// The ID of the terminal to cancel.
2407    pub terminal_id: TerminalId,
2408    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2409    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2410    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2411    ///
2412    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2413    #[serde_as(deserialize_as = "DefaultOnError")]
2414    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2415    #[serde(default)]
2416    #[serde(rename = "_meta")]
2417    pub meta: Option<Meta>,
2418}
2419
2420impl CancelTerminalRequest {
2421    /// Builds [`CancelTerminalRequest`] with the required request fields set. Optional fields start unset or empty.
2422    #[must_use]
2423    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
2424        Self {
2425            session_id: session_id.into(),
2426            terminal_id: terminal_id.into(),
2427            meta: None,
2428        }
2429    }
2430
2431    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2432    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2433    /// do not recognize.
2434    ///
2435    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2436    #[must_use]
2437    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2438        self.meta = meta.into_option();
2439        self
2440    }
2441}
2442
2443crate::serde_util::default_on_null! {
2444    /// Response from cancelling a terminal.
2445    #[serde_as]
2446    #[skip_serializing_none]
2447    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2448    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2449    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2450    #[serde(rename_all = "camelCase")]
2451    #[non_exhaustive]
2452    pub struct CancelTerminalResponse {
2453        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2454        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2455        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2456        ///
2457        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2458        #[serde_as(deserialize_as = "DefaultOnError")]
2459        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2460        #[serde(default)]
2461        #[serde(rename = "_meta")]
2462        pub meta: Option<Meta>,
2463    }
2464}
2465
2466impl CancelTerminalResponse {
2467    /// Builds [`CancelTerminalResponse`] with the required response fields set. Optional fields start unset or empty.
2468    #[must_use]
2469    pub fn new() -> Self {
2470        Self::default()
2471    }
2472
2473    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2474    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2475    /// do not recognize.
2476    ///
2477    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2478    #[must_use]
2479    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2480        self.meta = meta.into_option();
2481        self
2482    }
2483}
2484
2485// List sessions
2486
2487/// An opaque cursor used to paginate `session/list` results.
2488#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2489#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2490#[serde(transparent)]
2491#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
2492#[non_exhaustive]
2493pub struct SessionListCursor(pub Arc<str>);
2494
2495impl SessionListCursor {
2496    /// Wraps a protocol string as a typed [`SessionListCursor`].
2497    #[must_use]
2498    pub fn new(cursor: impl Into<Self>) -> Self {
2499        cursor.into()
2500    }
2501}
2502
2503impl AsRef<str> for SessionListCursor {
2504    fn as_ref(&self) -> &str {
2505        &self.0
2506    }
2507}
2508
2509impl From<&String> for SessionListCursor {
2510    fn from(cursor: &String) -> Self {
2511        Self(cursor.as_str().into())
2512    }
2513}
2514
2515macro_rules! impl_session_list_cursor_option_conversion {
2516    ($source:ty) => {
2517        impl IntoOption<SessionListCursor> for $source {
2518            fn into_option(self) -> Option<SessionListCursor> {
2519                Some(self.into())
2520            }
2521        }
2522    };
2523}
2524
2525impl_session_list_cursor_option_conversion!(Arc<str>);
2526impl_session_list_cursor_option_conversion!(String);
2527impl_session_list_cursor_option_conversion!(&str);
2528impl_session_list_cursor_option_conversion!(&mut str);
2529impl_session_list_cursor_option_conversion!(&String);
2530impl_session_list_cursor_option_conversion!(Box<str>);
2531impl_session_list_cursor_option_conversion!(Cow<'_, str>);
2532
2533crate::serde_util::default_on_null! {
2534    /// Request parameters for listing existing sessions.
2535    #[serde_as]
2536    #[skip_serializing_none]
2537    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2538    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2539    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2540    #[serde(rename_all = "camelCase")]
2541    #[non_exhaustive]
2542    pub struct ListSessionsRequest {
2543        /// Filter sessions by working directory. Must be an absolute path.
2544        ///
2545        /// Only sessions whose `cwd` is this same directory match. Subdirectories and
2546        /// `additionalDirectories` don't. Agents MAY normalize paths when comparing.
2547        ///
2548        /// Omitted and `null` are equivalent and mean sessions are not filtered by working directory.
2549        #[serde(default)]
2550        pub cwd: Option<AbsolutePath>,
2551        /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
2552        ///
2553        /// Omitted and `null` are equivalent and mean the first page is requested.
2554        #[serde(default)]
2555        pub cursor: Option<SessionListCursor>,
2556        /// **UNSTABLE**
2557        ///
2558        /// This capability is not part of the spec yet, and may be removed or changed at any point.
2559        ///
2560        /// Filter sessions by archive state.
2561        ///
2562        /// Optional. Omitted and `null` are equivalent and mean `exclude`. Only send this when
2563        /// the Agent advertises the `session.archive` capability. The filter combines with `cwd` and
2564        /// applies before pagination. Clients keep the same value when following `nextCursor`.
2565        #[cfg(feature = "unstable_session_archive")]
2566        #[serde(default)]
2567        pub archived: Option<SessionArchiveFilter>,
2568        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2569        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2570        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2571        ///
2572        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2573        #[serde_as(deserialize_as = "DefaultOnError")]
2574        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2575        #[serde(default)]
2576        #[serde(rename = "_meta")]
2577        pub meta: Option<Meta>,
2578    }
2579}
2580
2581impl ListSessionsRequest {
2582    /// Builds [`ListSessionsRequest`] with the required request fields set. Optional fields start unset or empty.
2583    #[must_use]
2584    pub fn new() -> Self {
2585        Self::default()
2586    }
2587
2588    /// Filter sessions by working directory. Must be an absolute path.
2589    #[must_use]
2590    pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
2591        self.cwd = cwd.into_option();
2592        self
2593    }
2594
2595    /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
2596    #[must_use]
2597    pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
2598        self.cursor = cursor.into_option();
2599        self
2600    }
2601
2602    /// **UNSTABLE**
2603    ///
2604    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2605    ///
2606    /// Filter sessions by archive state.
2607    #[cfg(feature = "unstable_session_archive")]
2608    #[must_use]
2609    pub fn archived(mut self, archived: impl IntoOption<SessionArchiveFilter>) -> Self {
2610        self.archived = archived.into_option();
2611        self
2612    }
2613
2614    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2615    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2616    /// do not recognize.
2617    ///
2618    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2619    #[must_use]
2620    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2621        self.meta = meta.into_option();
2622        self
2623    }
2624}
2625
2626/// Response from listing sessions.
2627#[serde_as]
2628#[skip_serializing_none]
2629#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2630#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2631#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2632#[serde(rename_all = "camelCase")]
2633#[non_exhaustive]
2634pub struct ListSessionsResponse {
2635    /// Array of session information objects.
2636    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2637    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2638    pub sessions: Vec<SessionInfo>,
2639    /// Opaque cursor token. If present, pass this in the next request's cursor parameter
2640    /// to fetch the next page. Omitted and `null` are equivalent and mean there are no more
2641    /// results. An empty string is a valid cursor, not the end of results.
2642    #[serde(default)]
2643    pub next_cursor: Option<SessionListCursor>,
2644    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2645    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2646    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2647    ///
2648    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2649    #[serde_as(deserialize_as = "DefaultOnError")]
2650    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2651    #[serde(default)]
2652    #[serde(rename = "_meta")]
2653    pub meta: Option<Meta>,
2654}
2655
2656impl ListSessionsResponse {
2657    /// Builds [`ListSessionsResponse`] with the required response fields set. Optional fields start unset or empty.
2658    #[must_use]
2659    pub fn new(sessions: Vec<SessionInfo>) -> Self {
2660        Self {
2661            sessions,
2662            next_cursor: None,
2663            meta: None,
2664        }
2665    }
2666
2667    /// Sets or clears the optional `nextCursor` field.
2668    #[must_use]
2669    pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
2670        self.next_cursor = next_cursor.into_option();
2671        self
2672    }
2673
2674    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2675    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2676    /// do not recognize.
2677    ///
2678    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2679    #[must_use]
2680    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2681        self.meta = meta.into_option();
2682        self
2683    }
2684}
2685
2686// Delete session
2687
2688/// Request parameters for deleting an existing session from `session/list`.
2689///
2690/// Only available if the Agent supports the `session.delete` capability.
2691#[serde_as]
2692#[skip_serializing_none]
2693#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2694#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2695#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2696#[serde(rename_all = "camelCase")]
2697#[non_exhaustive]
2698pub struct DeleteSessionRequest {
2699    /// The ID of the session to delete.
2700    pub session_id: SessionId,
2701    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2702    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2703    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2704    ///
2705    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2706    #[serde_as(deserialize_as = "DefaultOnError")]
2707    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2708    #[serde(default)]
2709    #[serde(rename = "_meta")]
2710    pub meta: Option<Meta>,
2711}
2712
2713impl DeleteSessionRequest {
2714    /// Builds [`DeleteSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2715    #[must_use]
2716    pub fn new(session_id: impl Into<SessionId>) -> Self {
2717        Self {
2718            session_id: session_id.into(),
2719            meta: None,
2720        }
2721    }
2722
2723    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2724    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2725    /// do not recognize.
2726    ///
2727    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2728    #[must_use]
2729    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2730        self.meta = meta.into_option();
2731        self
2732    }
2733}
2734
2735crate::serde_util::default_on_null! {
2736    /// Response from deleting a session.
2737    #[serde_as]
2738    #[skip_serializing_none]
2739    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2740    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2741    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2742    #[serde(rename_all = "camelCase")]
2743    #[non_exhaustive]
2744    pub struct DeleteSessionResponse {
2745        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2746        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2747        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2748        ///
2749        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2750        #[serde_as(deserialize_as = "DefaultOnError")]
2751        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2752        #[serde(default)]
2753        #[serde(rename = "_meta")]
2754        pub meta: Option<Meta>,
2755    }
2756}
2757
2758impl DeleteSessionResponse {
2759    /// Builds [`DeleteSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2760    #[must_use]
2761    pub fn new() -> Self {
2762        Self::default()
2763    }
2764
2765    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2766    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2767    /// do not recognize.
2768    ///
2769    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2770    #[must_use]
2771    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2772        self.meta = meta.into_option();
2773        self
2774    }
2775}
2776
2777// Archive session
2778
2779/// **UNSTABLE**
2780///
2781/// This capability is not part of the spec yet, and may be removed or changed at any point.
2782///
2783/// Request parameters for archiving a session.
2784///
2785/// Only available if the Agent supports the `session.archive` capability.
2786///
2787/// Archiving hides the session from default `session/list` results. It keeps the session ID
2788/// and saved history. If the session is active, the Agent MUST cancel any ongoing work and
2789/// free the session's resources as if `session/close` had been called, then archive it.
2790/// Archiving an archived session succeeds. A session that does not exist gets `-32002`.
2791///
2792#[cfg(feature = "unstable_session_archive")]
2793#[serde_as]
2794#[skip_serializing_none]
2795#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2796#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2797#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2798#[serde(rename_all = "camelCase")]
2799#[non_exhaustive]
2800pub struct ArchiveSessionRequest {
2801    /// The ID of the session to archive.
2802    pub session_id: SessionId,
2803    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2804    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2805    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2806    ///
2807    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2808    #[serde_as(deserialize_as = "DefaultOnError")]
2809    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2810    #[serde(default)]
2811    #[serde(rename = "_meta")]
2812    pub meta: Option<Meta>,
2813}
2814
2815#[cfg(feature = "unstable_session_archive")]
2816impl ArchiveSessionRequest {
2817    /// Builds [`ArchiveSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2818    #[must_use]
2819    pub fn new(session_id: impl Into<SessionId>) -> Self {
2820        Self {
2821            session_id: session_id.into(),
2822            meta: None,
2823        }
2824    }
2825
2826    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2827    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2828    /// do not recognize.
2829    ///
2830    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2831    #[must_use]
2832    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2833        self.meta = meta.into_option();
2834        self
2835    }
2836}
2837
2838#[cfg(feature = "unstable_session_archive")]
2839crate::serde_util::default_on_null! {
2840    /// **UNSTABLE**
2841    ///
2842    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2843    ///
2844    /// Response from archiving a session.
2845    #[serde_as]
2846    #[skip_serializing_none]
2847    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2848    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2849    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2850    #[serde(rename_all = "camelCase")]
2851    #[non_exhaustive]
2852    pub struct ArchiveSessionResponse {
2853        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2854        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2855        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2856        ///
2857        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2858        #[serde_as(deserialize_as = "DefaultOnError")]
2859        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2860        #[serde(default)]
2861        #[serde(rename = "_meta")]
2862        pub meta: Option<Meta>,
2863    }
2864}
2865
2866#[cfg(feature = "unstable_session_archive")]
2867impl ArchiveSessionResponse {
2868    /// Builds [`ArchiveSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2869    #[must_use]
2870    pub fn new() -> Self {
2871        Self::default()
2872    }
2873
2874    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2875    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2876    /// do not recognize.
2877    ///
2878    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2879    #[must_use]
2880    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2881        self.meta = meta.into_option();
2882        self
2883    }
2884}
2885
2886/// **UNSTABLE**
2887///
2888/// This capability is not part of the spec yet, and may be removed or changed at any point.
2889///
2890/// Request parameters for unarchiving a session.
2891///
2892/// Only available if the Agent supports the `session.archive` capability.
2893///
2894/// Unarchiving returns the session to default `session/list` results with the same ID and
2895/// saved history. It never loads or resumes the session. Clients that want to continue an
2896/// archived session SHOULD unarchive it before calling `session/resume`.
2897/// Unarchiving a session that is not archived succeeds. A session that does not exist
2898/// gets `-32002`.
2899///
2900#[cfg(feature = "unstable_session_archive")]
2901#[serde_as]
2902#[skip_serializing_none]
2903#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2904#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2905#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2906#[serde(rename_all = "camelCase")]
2907#[non_exhaustive]
2908pub struct UnarchiveSessionRequest {
2909    /// The ID of the session to unarchive.
2910    pub session_id: SessionId,
2911    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2912    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2913    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2914    ///
2915    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2916    #[serde_as(deserialize_as = "DefaultOnError")]
2917    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2918    #[serde(default)]
2919    #[serde(rename = "_meta")]
2920    pub meta: Option<Meta>,
2921}
2922
2923#[cfg(feature = "unstable_session_archive")]
2924impl UnarchiveSessionRequest {
2925    /// Builds [`UnarchiveSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2926    #[must_use]
2927    pub fn new(session_id: impl Into<SessionId>) -> Self {
2928        Self {
2929            session_id: session_id.into(),
2930            meta: None,
2931        }
2932    }
2933
2934    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2935    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2936    /// do not recognize.
2937    ///
2938    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2939    #[must_use]
2940    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2941        self.meta = meta.into_option();
2942        self
2943    }
2944}
2945
2946#[cfg(feature = "unstable_session_archive")]
2947crate::serde_util::default_on_null! {
2948    /// **UNSTABLE**
2949    ///
2950    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2951    ///
2952    /// Response from unarchiving a session.
2953    #[serde_as]
2954    #[skip_serializing_none]
2955    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2956    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2957    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2958    #[serde(rename_all = "camelCase")]
2959    #[non_exhaustive]
2960    pub struct UnarchiveSessionResponse {
2961        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2962        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2963        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2964        ///
2965        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2966        #[serde_as(deserialize_as = "DefaultOnError")]
2967        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2968        #[serde(default)]
2969        #[serde(rename = "_meta")]
2970        pub meta: Option<Meta>,
2971    }
2972}
2973
2974#[cfg(feature = "unstable_session_archive")]
2975impl UnarchiveSessionResponse {
2976    /// Builds [`UnarchiveSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2977    #[must_use]
2978    pub fn new() -> Self {
2979        Self::default()
2980    }
2981
2982    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2983    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2984    /// do not recognize.
2985    ///
2986    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2987    #[must_use]
2988    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2989        self.meta = meta.into_option();
2990        self
2991    }
2992}
2993
2994/// **UNSTABLE**
2995///
2996/// This capability is not part of the spec yet, and may be removed or changed at any point.
2997///
2998/// Which sessions `session/list` returns based on their archive state.
2999///
3000/// Unknown values are preserved so they can be stored, proxied, or forwarded. An Agent MUST
3001/// return `-32602` for a value it does not support rather than treating it as `exclude`.
3002#[cfg(feature = "unstable_session_archive")]
3003#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3004#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
3005#[serde(rename_all = "snake_case")]
3006#[non_exhaustive]
3007pub enum SessionArchiveFilter {
3008    /// Only sessions that are not archived. This is the default.
3009    #[default]
3010    Exclude,
3011    /// Both archived sessions and sessions that are not archived.
3012    Include,
3013    /// Only archived sessions.
3014    Only,
3015    /// Custom or future archive filter.
3016    ///
3017    /// Values beginning with `_` are reserved for implementation-specific
3018    /// extensions. Unknown values that do not begin with `_` are reserved for
3019    /// future ACP variants.
3020    #[serde(untagged)]
3021    Other(String),
3022}
3023
3024/// Information about a session returned by session/list
3025#[serde_as]
3026#[skip_serializing_none]
3027#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3028#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3029#[serde(rename_all = "camelCase")]
3030#[non_exhaustive]
3031pub struct SessionInfo {
3032    /// Unique identifier for the session
3033    pub session_id: SessionId,
3034    /// The working directory for this session. Must be an absolute path.
3035    pub cwd: AbsolutePath,
3036    /// Additional workspace roots reported for this session. Each path must be absolute.
3037    ///
3038    /// When present, this is the complete ordered additional-root list reported
3039    /// by the Agent. Omitted and empty values are equivalent: the response
3040    /// reports no additional roots.
3041    #[serde_as(deserialize_as = "DefaultOnNull")]
3042    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
3043    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3044    pub additional_directories: Vec<AbsolutePath>,
3045
3046    /// Human-readable title for the session
3047    ///
3048    /// Omitted and `null` are equivalent and mean no title is provided.
3049    #[serde_as(deserialize_as = "DefaultOnError")]
3050    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3051    #[serde(default)]
3052    pub title: Option<String>,
3053    /// RFC 3339 timestamp of last activity.
3054    ///
3055    /// Omitted and `null` are equivalent and mean the last activity time is not reported.
3056    #[serde_as(deserialize_as = "DefaultOnError")]
3057    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
3058    #[serde(default)]
3059    pub updated_at: Option<String>,
3060    /// **UNSTABLE**
3061    ///
3062    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3063    ///
3064    /// Whether the session is archived.
3065    ///
3066    /// Optional. Omitted and `null` are equivalent and mean the archive state is not reported.
3067    /// Agents that advertise the `session.archive` capability MUST include it on every listed session.
3068    #[cfg(feature = "unstable_session_archive")]
3069    #[serde_as(deserialize_as = "DefaultOnError")]
3070    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3071    #[serde(default)]
3072    pub archived: Option<bool>,
3073    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3074    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3075    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3076    ///
3077    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3078    #[serde_as(deserialize_as = "DefaultOnError")]
3079    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3080    #[serde(default)]
3081    #[serde(rename = "_meta")]
3082    pub meta: Option<Meta>,
3083}
3084
3085impl SessionInfo {
3086    /// Builds [`SessionInfo`] with the required fields set. Optional fields start unset or empty.
3087    #[must_use]
3088    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
3089        Self {
3090            session_id: session_id.into(),
3091            cwd: cwd.into(),
3092            additional_directories: vec![],
3093            title: None,
3094            updated_at: None,
3095            #[cfg(feature = "unstable_session_archive")]
3096            archived: None,
3097            meta: None,
3098        }
3099    }
3100
3101    /// Additional workspace roots reported for this session. Each path must be absolute.
3102    #[must_use]
3103    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
3104    where
3105        I: IntoIterator<Item = P>,
3106        P: Into<AbsolutePath>,
3107    {
3108        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
3109        self
3110    }
3111
3112    /// Human-readable title for the session
3113    #[must_use]
3114    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
3115        self.title = title.into_option();
3116        self
3117    }
3118
3119    /// RFC 3339 timestamp of last activity.
3120    #[must_use]
3121    pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
3122        self.updated_at = updated_at.into_option();
3123        self
3124    }
3125
3126    /// **UNSTABLE**
3127    ///
3128    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3129    ///
3130    /// Whether the session is archived.
3131    #[cfg(feature = "unstable_session_archive")]
3132    #[must_use]
3133    pub fn archived(mut self, archived: impl IntoOption<bool>) -> Self {
3134        self.archived = archived.into_option();
3135        self
3136    }
3137
3138    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3139    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3140    /// do not recognize.
3141    ///
3142    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3143    #[must_use]
3144    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3145        self.meta = meta.into_option();
3146        self
3147    }
3148}
3149
3150// Set session title
3151
3152/// **UNSTABLE**
3153///
3154/// This capability is not part of the spec yet, and may be removed or changed at any point.
3155///
3156/// Request parameters for setting the title of a session.
3157///
3158/// Only available if the Agent supports the `session.title` capability.
3159///
3160/// The session does not need to be active on the connection. Any session the Agent would
3161/// return from `session/list` can be retitled. A session that does not exist gets `-32002`.
3162///
3163/// The Agent responds only after it has stored the title. After a successful response, the
3164/// Agent SHOULD NOT replace the title with one it generates automatically.
3165#[cfg(feature = "unstable_session_set_title")]
3166#[serde_as]
3167#[skip_serializing_none]
3168#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3170#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3171#[serde(rename_all = "camelCase")]
3172#[non_exhaustive]
3173pub struct SetSessionTitleRequest {
3174    /// The ID of the session to retitle.
3175    pub session_id: SessionId,
3176    /// The new title for the session.
3177    ///
3178    /// Required and non-null. Omission and explicit `null` are both invalid. The Agent MAY
3179    /// normalize the title before storing it, for example by trimming whitespace or
3180    /// truncating it. The Agent rejects a title that fails its validation, including one
3181    /// that is empty after normalization, with `-32602`.
3182    pub title: String,
3183    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3184    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3185    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3186    ///
3187    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3188    #[serde_as(deserialize_as = "DefaultOnError")]
3189    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3190    #[serde(default)]
3191    #[serde(rename = "_meta")]
3192    pub meta: Option<Meta>,
3193}
3194
3195#[cfg(feature = "unstable_session_set_title")]
3196impl SetSessionTitleRequest {
3197    /// Builds [`SetSessionTitleRequest`] with the required request fields set. Optional fields start unset or empty.
3198    #[must_use]
3199    pub fn new(session_id: impl Into<SessionId>, title: impl Into<String>) -> Self {
3200        Self {
3201            session_id: session_id.into(),
3202            title: title.into(),
3203            meta: None,
3204        }
3205    }
3206
3207    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3208    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3209    /// do not recognize.
3210    ///
3211    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3212    #[must_use]
3213    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3214        self.meta = meta.into_option();
3215        self
3216    }
3217}
3218
3219/// **UNSTABLE**
3220///
3221/// This capability is not part of the spec yet, and may be removed or changed at any point.
3222///
3223/// Response from setting the title of a session.
3224#[cfg(feature = "unstable_session_set_title")]
3225#[serde_as]
3226#[skip_serializing_none]
3227#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3228#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3229#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3230#[serde(rename_all = "camelCase")]
3231#[non_exhaustive]
3232pub struct SetSessionTitleResponse {
3233    /// The title the Agent stored.
3234    ///
3235    /// This differs from the requested title if the Agent normalized it. Clients SHOULD
3236    /// display this value rather than the one they sent.
3237    pub title: String,
3238    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3239    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3240    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3241    ///
3242    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3243    #[serde_as(deserialize_as = "DefaultOnError")]
3244    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3245    #[serde(default)]
3246    #[serde(rename = "_meta")]
3247    pub meta: Option<Meta>,
3248}
3249
3250#[cfg(feature = "unstable_session_set_title")]
3251impl SetSessionTitleResponse {
3252    /// Builds [`SetSessionTitleResponse`] with the required response fields set. Optional fields start unset or empty.
3253    #[must_use]
3254    pub fn new(title: impl Into<String>) -> Self {
3255        Self {
3256            title: title.into(),
3257            meta: None,
3258        }
3259    }
3260
3261    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3262    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3263    /// do not recognize.
3264    ///
3265    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3266    #[must_use]
3267    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3268        self.meta = meta.into_option();
3269        self
3270    }
3271}
3272
3273// Session config options
3274
3275/// Unique identifier for a session configuration option.
3276#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3277#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3278#[serde(transparent)]
3279#[from(forward)]
3280#[non_exhaustive]
3281pub struct SessionConfigId(pub Arc<str>);
3282
3283impl SessionConfigId {
3284    /// Wraps a protocol string as a typed [`SessionConfigId`].
3285    #[must_use]
3286    pub fn new(id: impl Into<Self>) -> Self {
3287        id.into()
3288    }
3289}
3290
3291/// Unique identifier for a session configuration option value.
3292#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3293#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3294#[serde(transparent)]
3295#[from(forward)]
3296#[non_exhaustive]
3297pub struct SessionConfigValueId(pub Arc<str>);
3298
3299impl SessionConfigValueId {
3300    /// Wraps a protocol string as a typed [`SessionConfigValueId`].
3301    #[must_use]
3302    pub fn new(id: impl Into<Self>) -> Self {
3303        id.into()
3304    }
3305}
3306
3307/// Unique identifier for a session configuration option value group.
3308#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3309#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3310#[serde(transparent)]
3311#[from(forward)]
3312#[non_exhaustive]
3313pub struct SessionConfigGroupId(pub Arc<str>);
3314
3315impl SessionConfigGroupId {
3316    /// Wraps a protocol string as a typed [`SessionConfigGroupId`].
3317    #[must_use]
3318    pub fn new(id: impl Into<Self>) -> Self {
3319        id.into()
3320    }
3321}
3322
3323/// A possible value for a session configuration option.
3324#[serde_as]
3325#[skip_serializing_none]
3326#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3327#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3328#[serde(rename_all = "camelCase")]
3329#[non_exhaustive]
3330pub struct SessionConfigSelectOption {
3331    /// Unique identifier for this option value.
3332    pub value: SessionConfigValueId,
3333    /// Human-readable label for this option value.
3334    pub name: String,
3335    /// Optional description for this option value.
3336    ///
3337    /// Omitted and `null` are equivalent and mean no description is provided.
3338    #[serde_as(deserialize_as = "DefaultOnError")]
3339    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3340    #[serde(default)]
3341    pub description: Option<String>,
3342    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3343    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3344    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3345    ///
3346    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3347    #[serde_as(deserialize_as = "DefaultOnError")]
3348    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3349    #[serde(default)]
3350    #[serde(rename = "_meta")]
3351    pub meta: Option<Meta>,
3352}
3353
3354impl SessionConfigSelectOption {
3355    /// Builds [`SessionConfigSelectOption`] with the required fields set. Optional fields start unset or empty.
3356    #[must_use]
3357    pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
3358        Self {
3359            value: value.into(),
3360            name: name.into(),
3361            description: None,
3362            meta: None,
3363        }
3364    }
3365
3366    /// Sets or clears the optional `description` field.
3367    #[must_use]
3368    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3369        self.description = description.into_option();
3370        self
3371    }
3372
3373    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3374    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3375    /// do not recognize.
3376    ///
3377    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3378    #[must_use]
3379    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3380        self.meta = meta.into_option();
3381        self
3382    }
3383}
3384
3385/// A group of possible values for a session configuration option.
3386#[serde_as]
3387#[skip_serializing_none]
3388#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3389#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3390#[serde(rename_all = "camelCase")]
3391#[non_exhaustive]
3392pub struct SessionConfigSelectGroup {
3393    /// Unique identifier for this group.
3394    pub group_id: SessionConfigGroupId,
3395    /// Human-readable label for this group.
3396    pub name: String,
3397    /// The set of option values in this group. MUST contain at least one value.
3398    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3399    #[cfg_attr(feature = "schemars", schemars(length(min = 1), extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3400    pub options: Vec<SessionConfigSelectOption>,
3401    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3402    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3403    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3404    ///
3405    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3406    #[serde_as(deserialize_as = "DefaultOnError")]
3407    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3408    #[serde(default)]
3409    #[serde(rename = "_meta")]
3410    pub meta: Option<Meta>,
3411}
3412
3413impl SessionConfigSelectGroup {
3414    /// Builds [`SessionConfigSelectGroup`] with the required fields set. Optional fields start unset or empty.
3415    #[must_use]
3416    pub fn new(
3417        group_id: impl Into<SessionConfigGroupId>,
3418        name: impl Into<String>,
3419        options: Vec<SessionConfigSelectOption>,
3420    ) -> Self {
3421        Self {
3422            group_id: group_id.into(),
3423            name: name.into(),
3424            options,
3425            meta: None,
3426        }
3427    }
3428
3429    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3430    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3431    /// do not recognize.
3432    ///
3433    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3434    #[must_use]
3435    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3436        self.meta = meta.into_option();
3437        self
3438    }
3439}
3440
3441/// Possible values for a session configuration option.
3442#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3443#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3444#[serde(untagged)]
3445#[non_exhaustive]
3446pub enum SessionConfigSelectOptions {
3447    /// A flat list of options with no grouping.
3448    Ungrouped(
3449        #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectOption>,
3450    ),
3451    /// A list of options grouped under headers.
3452    Grouped(
3453        #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectGroup>,
3454    ),
3455}
3456
3457impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
3458    fn from(options: Vec<SessionConfigSelectOption>) -> Self {
3459        SessionConfigSelectOptions::Ungrouped(options)
3460    }
3461}
3462
3463impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
3464    fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
3465        SessionConfigSelectOptions::Grouped(groups)
3466    }
3467}
3468
3469/// A single-value selector (dropdown) session configuration option payload.
3470#[skip_serializing_none]
3471#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3472#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3473#[serde(rename_all = "camelCase")]
3474#[non_exhaustive]
3475pub struct SessionConfigSelect {
3476    /// The currently selected value. MUST be one of the `value`s in `options`.
3477    pub current_value: SessionConfigValueId,
3478    /// The set of selectable options. MUST contain at least one value, and each `value` MUST be
3479    /// unique within the option, including across groups.
3480    pub options: SessionConfigSelectOptions,
3481}
3482
3483impl SessionConfigSelect {
3484    /// Builds [`SessionConfigSelect`] with the required fields set. Optional fields start unset or empty.
3485    #[must_use]
3486    pub fn new(
3487        current_value: impl Into<SessionConfigValueId>,
3488        options: impl Into<SessionConfigSelectOptions>,
3489    ) -> Self {
3490        Self {
3491            current_value: current_value.into(),
3492            options: options.into(),
3493        }
3494    }
3495}
3496
3497/// A boolean on/off toggle session configuration option payload.
3498#[skip_serializing_none]
3499#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3500#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3501#[serde(rename_all = "camelCase")]
3502#[non_exhaustive]
3503pub struct SessionConfigBoolean {
3504    /// The current value of the boolean option.
3505    pub current_value: bool,
3506}
3507
3508impl SessionConfigBoolean {
3509    /// Builds [`SessionConfigBoolean`] with the required fields set. Optional fields start unset or empty.
3510    #[must_use]
3511    pub fn new(current_value: bool) -> Self {
3512        Self { current_value }
3513    }
3514}
3515
3516/// Semantic category for a session configuration option.
3517///
3518/// This is intended to help Clients distinguish broadly common selectors (e.g. model selector vs
3519/// session mode selector vs thought/reasoning level) for UX purposes (keyboard shortcuts, icons,
3520/// placement). It MUST NOT be required for correctness. Clients MUST handle missing or unknown
3521/// categories gracefully.
3522///
3523/// Category names beginning with `_` are free for custom use, like other ACP extension methods.
3524/// Category names that do not begin with `_` are reserved for the ACP spec.
3525#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3526#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3527#[serde(rename_all = "snake_case")]
3528#[non_exhaustive]
3529pub enum SessionConfigOptionCategory {
3530    /// Session mode selector.
3531    Mode,
3532    /// Model selector.
3533    Model,
3534    /// Model-related configuration parameter.
3535    ModelConfig,
3536    /// Thought/reasoning level selector.
3537    ThoughtLevel,
3538    /// Custom or future category.
3539    ///
3540    /// Values beginning with `_` are reserved for implementation-specific
3541    /// extensions. Unknown values that do not begin with `_` are reserved for
3542    /// future ACP variants.
3543    #[serde(untagged)]
3544    Other(String),
3545}
3546
3547/// Type-specific session configuration option payload.
3548#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3549#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3550#[serde(tag = "type", rename_all = "snake_case")]
3551#[non_exhaustive]
3552pub enum SessionConfigKind {
3553    /// Single-value selector (dropdown).
3554    Select(SessionConfigSelect),
3555    /// Boolean on/off toggle.
3556    Boolean(SessionConfigBoolean),
3557    /// Custom or future session configuration option payload.
3558    ///
3559    /// Values beginning with `_` are reserved for implementation-specific
3560    /// extensions. Unknown values that do not begin with `_` are reserved for
3561    /// future ACP variants.
3562    ///
3563    /// Clients that do not understand this option type should preserve the raw
3564    /// payload when storing, replaying, proxying, or forwarding configuration
3565    /// data, and otherwise ignore the option. The Agent keeps using its `currentValue`.
3566    #[serde(untagged)]
3567    Other(OtherSessionConfigKind),
3568}
3569
3570/// Custom or future session configuration option payload.
3571#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3572#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3573#[cfg_attr(feature = "schemars", schemars(inline))]
3574#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
3575#[serde(rename_all = "camelCase")]
3576#[non_exhaustive]
3577pub struct OtherSessionConfigKind {
3578    /// Custom or future session configuration option type.
3579    ///
3580    /// Values beginning with `_` are reserved for implementation-specific
3581    /// extensions. Unknown values that do not begin with `_` are reserved for
3582    /// future ACP variants.
3583    #[serde(rename = "type")]
3584    pub type_: String,
3585    /// Additional fields from the unknown session configuration option payload.
3586    #[serde(flatten)]
3587    pub fields: BTreeMap<String, serde_json::Value>,
3588}
3589
3590impl OtherSessionConfigKind {
3591    /// Builds [`OtherSessionConfigKind`] from an unknown discriminator and preserves the remaining extension fields.
3592    #[must_use]
3593    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
3594        fields.remove("type");
3595        fields.remove("_meta");
3596        Self {
3597            type_: type_.into(),
3598            fields,
3599        }
3600    }
3601}
3602
3603impl<'de> Deserialize<'de> for OtherSessionConfigKind {
3604    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3605    where
3606        D: serde::Deserializer<'de>,
3607    {
3608        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3609        let type_ = fields
3610            .remove("type")
3611            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3612        let serde_json::Value::String(type_) = type_ else {
3613            return Err(serde::de::Error::custom("`type` must be a string"));
3614        };
3615
3616        if is_known_session_config_kind_type(&type_) {
3617            return Err(serde::de::Error::custom(format!(
3618                "known session configuration option `{type_}` did not match its schema"
3619            )));
3620        }
3621
3622        Ok(Self { type_, fields })
3623    }
3624}
3625
3626fn is_known_session_config_kind_type(type_: &str) -> bool {
3627    matches!(type_, "select" | "boolean")
3628}
3629
3630#[cfg(feature = "schemars")]
3631fn other_session_config_kind_schema(schema: &mut Schema) {
3632    super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
3633}
3634
3635/// A session configuration option selector and its current state.
3636#[serde_as]
3637#[skip_serializing_none]
3638#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3639#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3640#[serde(rename_all = "camelCase")]
3641#[non_exhaustive]
3642pub struct SessionConfigOption {
3643    /// Unique identifier for the configuration option.
3644    pub config_id: SessionConfigId,
3645    /// Human-readable label for the option.
3646    pub name: String,
3647    /// Optional description for the Client to display to the user.
3648    ///
3649    /// Omitted and `null` are equivalent and mean no description is provided.
3650    #[serde_as(deserialize_as = "DefaultOnError")]
3651    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3652    #[serde(default)]
3653    pub description: Option<String>,
3654    /// Optional semantic category for this option (UX only).
3655    ///
3656    /// Omitted and `null` are equivalent and mean no category is provided.
3657    #[serde_as(deserialize_as = "DefaultOnError")]
3658    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3659    #[serde(default)]
3660    pub category: Option<SessionConfigOptionCategory>,
3661    /// Type-specific fields for this configuration option.
3662    #[serde(flatten)]
3663    pub kind: SessionConfigKind,
3664    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3665    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3666    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3667    ///
3668    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3669    #[serde_as(deserialize_as = "DefaultOnError")]
3670    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3671    #[serde(default)]
3672    #[serde(rename = "_meta")]
3673    pub meta: Option<Meta>,
3674}
3675
3676impl SessionConfigOption {
3677    /// Builds [`SessionConfigOption`] with the required fields set. Optional fields start unset or empty.
3678    #[must_use]
3679    pub fn new(
3680        config_id: impl Into<SessionConfigId>,
3681        name: impl Into<String>,
3682        kind: SessionConfigKind,
3683    ) -> Self {
3684        Self {
3685            config_id: config_id.into(),
3686            name: name.into(),
3687            description: None,
3688            category: None,
3689            kind,
3690            meta: None,
3691        }
3692    }
3693
3694    /// Builds a select-style session configuration option with its current value and choices.
3695    #[must_use]
3696    pub fn select(
3697        config_id: impl Into<SessionConfigId>,
3698        name: impl Into<String>,
3699        current_value: impl Into<SessionConfigValueId>,
3700        options: impl Into<SessionConfigSelectOptions>,
3701    ) -> Self {
3702        Self::new(
3703            config_id,
3704            name,
3705            SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
3706        )
3707    }
3708
3709    /// Builds a boolean-style session configuration option with its current value.
3710    #[must_use]
3711    pub fn boolean(
3712        config_id: impl Into<SessionConfigId>,
3713        name: impl Into<String>,
3714        current_value: bool,
3715    ) -> Self {
3716        Self::new(
3717            config_id,
3718            name,
3719            SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
3720        )
3721    }
3722
3723    /// Sets or clears the optional `description` field.
3724    #[must_use]
3725    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3726        self.description = description.into_option();
3727        self
3728    }
3729
3730    /// Sets or clears the optional `category` field.
3731    #[must_use]
3732    pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
3733        self.category = category.into_option();
3734        self
3735    }
3736
3737    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3738    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3739    /// do not recognize.
3740    ///
3741    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3742    #[must_use]
3743    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3744        self.meta = meta.into_option();
3745        self
3746    }
3747}
3748
3749/// The value to set for a session configuration option.
3750///
3751/// The `type` field acts as the discriminator in the serialized JSON form.
3752///
3753/// The `type` discriminator describes the *shape* of the value, not the option
3754/// kind. For example every option kind that picks from a list of ids
3755/// (`select`, `radio`, …) would use [`Id`](Self::Id), while a future freeform
3756/// text option would get its own variant.
3757#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3758#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3759#[serde(tag = "type", rename_all = "snake_case")]
3760#[non_exhaustive]
3761pub enum SessionConfigOptionValue {
3762    /// A `SessionConfigValueId` string value (`type: "id"`).
3763    Id {
3764        /// The value ID.
3765        value: SessionConfigValueId,
3766    },
3767    /// A boolean value (`type: "boolean"`).
3768    Boolean {
3769        /// The boolean value.
3770        value: bool,
3771    },
3772    /// Custom or future session configuration option value payload.
3773    ///
3774    /// Values beginning with `_` are reserved for implementation-specific
3775    /// extensions. Unknown values that do not begin with `_` are reserved for
3776    /// future ACP variants.
3777    ///
3778    /// Agents MUST reject a `session/set_config_option` request with a value type
3779    /// they do not support with `-32602`.
3780    #[serde(untagged)]
3781    Other(OtherSessionConfigOptionValue),
3782}
3783
3784/// Custom or future session configuration option value payload.
3785#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3786#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3787#[cfg_attr(feature = "schemars", schemars(inline))]
3788#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
3789#[serde(rename_all = "camelCase")]
3790#[non_exhaustive]
3791pub struct OtherSessionConfigOptionValue {
3792    /// Custom or future session configuration option value type.
3793    ///
3794    /// Values beginning with `_` are reserved for implementation-specific
3795    /// extensions. Unknown values that do not begin with `_` are reserved for
3796    /// future ACP variants.
3797    #[serde(rename = "type")]
3798    pub type_: String,
3799    /// Raw value payload for the custom or future value type.
3800    pub value: serde_json::Value,
3801    /// Additional fields from the unknown session configuration option value payload.
3802    #[serde(flatten)]
3803    pub fields: BTreeMap<String, serde_json::Value>,
3804}
3805
3806impl OtherSessionConfigOptionValue {
3807    /// Builds [`OtherSessionConfigOptionValue`] from an unknown discriminator and preserves the remaining extension fields.
3808    #[must_use]
3809    pub fn new(
3810        type_: impl Into<String>,
3811        value: serde_json::Value,
3812        mut fields: BTreeMap<String, serde_json::Value>,
3813    ) -> Self {
3814        fields.remove("type");
3815        fields.remove("value");
3816        fields.remove("_meta");
3817        Self {
3818            type_: type_.into(),
3819            value,
3820            fields,
3821        }
3822    }
3823}
3824
3825impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
3826    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3827    where
3828        D: serde::Deserializer<'de>,
3829    {
3830        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3831        let type_ = fields
3832            .remove("type")
3833            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3834        let serde_json::Value::String(type_) = type_ else {
3835            return Err(serde::de::Error::custom("`type` must be a string"));
3836        };
3837
3838        if is_known_session_config_option_value_type(&type_) {
3839            return Err(serde::de::Error::custom(format!(
3840                "known session configuration option value `{type_}` did not match its schema"
3841            )));
3842        }
3843
3844        let value = fields
3845            .remove("value")
3846            .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3847
3848        Ok(Self {
3849            type_,
3850            value,
3851            fields,
3852        })
3853    }
3854}
3855
3856impl<'de> Deserialize<'de> for SessionConfigOptionValue {
3857    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3858    where
3859        D: serde::Deserializer<'de>,
3860    {
3861        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3862        let type_ = fields.remove("type");
3863        let value = fields
3864            .remove("value")
3865            .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3866
3867        let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
3868
3869        let serde_json::Value::String(type_) = type_ else {
3870            return Err(serde::de::Error::custom("`type` must be a string"));
3871        };
3872
3873        match type_.as_str() {
3874            "id" => {
3875                let value = serde_json::from_value(value).map_err(|error| {
3876                    serde::de::Error::custom(format!(
3877                        "`value` must be a string for `type: id`: {error}"
3878                    ))
3879                })?;
3880                Ok(Self::Id { value })
3881            }
3882            "boolean" => {
3883                let value = serde_json::from_value(value).map_err(|error| {
3884                    serde::de::Error::custom(format!(
3885                        "`value` must be a boolean for `type: boolean`: {error}"
3886                    ))
3887                })?;
3888                Ok(Self::Boolean { value })
3889            }
3890            _ => Ok(Self::Other(OtherSessionConfigOptionValue {
3891                type_,
3892                value,
3893                fields,
3894            })),
3895        }
3896    }
3897}
3898
3899fn is_known_session_config_option_value_type(type_: &str) -> bool {
3900    matches!(type_, "id" | "boolean")
3901}
3902
3903#[cfg(feature = "schemars")]
3904fn other_session_config_option_value_schema(schema: &mut Schema) {
3905    super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
3906}
3907
3908impl SessionConfigOptionValue {
3909    /// Create an id option value (used by `select` and other id-based option types).
3910    #[must_use]
3911    pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
3912        Self::Id { value: id.into() }
3913    }
3914
3915    /// Create a boolean option value.
3916    #[must_use]
3917    pub fn boolean(val: bool) -> Self {
3918        Self::Boolean { value: val }
3919    }
3920
3921    /// Return the inner [`SessionConfigValueId`] if this is a
3922    /// [`Id`](Self::Id) value.
3923    #[must_use]
3924    pub fn as_id(&self) -> Option<&SessionConfigValueId> {
3925        match self {
3926            Self::Id { value } => Some(value),
3927            _ => None,
3928        }
3929    }
3930
3931    /// Return the inner [`bool`] if this is a [`Boolean`](Self::Boolean) value.
3932    #[must_use]
3933    pub fn as_bool(&self) -> Option<bool> {
3934        match self {
3935            Self::Boolean { value } => Some(*value),
3936            _ => None,
3937        }
3938    }
3939}
3940
3941impl From<SessionConfigValueId> for SessionConfigOptionValue {
3942    fn from(value: SessionConfigValueId) -> Self {
3943        Self::Id { value }
3944    }
3945}
3946
3947impl From<bool> for SessionConfigOptionValue {
3948    fn from(value: bool) -> Self {
3949        Self::Boolean { value }
3950    }
3951}
3952
3953impl From<&str> for SessionConfigOptionValue {
3954    fn from(value: &str) -> Self {
3955        Self::Id {
3956            value: SessionConfigValueId::new(value),
3957        }
3958    }
3959}
3960
3961/// Request parameters for setting a session configuration option.
3962///
3963/// Agents MUST support this method for any session where they advertise config options.
3964/// Agents MUST reject an unknown `configId` or invalid value with `-32602` and leave the
3965/// configuration unchanged.
3966///
3967/// See protocol docs: [Session Config Options](https://agentclientprotocol.com/protocol/v2/session-config-options#setting-a-config-option)
3968#[serde_as]
3969#[skip_serializing_none]
3970#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3971#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3972#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
3973#[serde(rename_all = "camelCase")]
3974#[non_exhaustive]
3975pub struct SetSessionConfigOptionRequest {
3976    /// The ID of the session to set the configuration option for.
3977    pub session_id: SessionId,
3978    /// The ID of the configuration option to set.
3979    pub config_id: SessionConfigId,
3980    /// The value to set, including a `type` discriminator and the raw `value`.
3981    ///
3982    /// Payloads must send `type: "id"` for id-based options.
3983    #[serde(flatten)]
3984    pub value: SessionConfigOptionValue,
3985    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3986    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3987    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3988    ///
3989    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3990    #[serde_as(deserialize_as = "DefaultOnError")]
3991    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3992    #[serde(default)]
3993    #[serde(rename = "_meta")]
3994    pub meta: Option<Meta>,
3995}
3996
3997impl SetSessionConfigOptionRequest {
3998    /// Builds [`SetSessionConfigOptionRequest`] with the required request fields set. Optional fields start unset or empty.
3999    #[must_use]
4000    pub fn new(
4001        session_id: impl Into<SessionId>,
4002        config_id: impl Into<SessionConfigId>,
4003        value: impl Into<SessionConfigOptionValue>,
4004    ) -> Self {
4005        Self {
4006            session_id: session_id.into(),
4007            config_id: config_id.into(),
4008            value: value.into(),
4009            meta: None,
4010        }
4011    }
4012
4013    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4014    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4015    /// do not recognize.
4016    ///
4017    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4018    #[must_use]
4019    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4020        self.meta = meta.into_option();
4021        self
4022    }
4023}
4024
4025/// Response to `session/set_config_option` method.
4026#[serde_as]
4027#[skip_serializing_none]
4028#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4029#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4030#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4031#[serde(rename_all = "camelCase")]
4032#[non_exhaustive]
4033pub struct SetSessionConfigOptionResponse {
4034    /// The full set of configuration options and their current values.
4035    #[serde_as(deserialize_as = "VecSkipError<_>")]
4036    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-skip-invalid-items" = true)))]
4037    pub config_options: Vec<SessionConfigOption>,
4038    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4039    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4040    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4041    ///
4042    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4043    #[serde_as(deserialize_as = "DefaultOnError")]
4044    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4045    #[serde(default)]
4046    #[serde(rename = "_meta")]
4047    pub meta: Option<Meta>,
4048}
4049
4050impl SetSessionConfigOptionResponse {
4051    /// Builds [`SetSessionConfigOptionResponse`] with the required response fields set. Optional fields start unset or empty.
4052    #[must_use]
4053    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
4054        Self {
4055            config_options,
4056            meta: None,
4057        }
4058    }
4059
4060    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4061    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4062    /// do not recognize.
4063    ///
4064    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4065    #[must_use]
4066    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4067        self.meta = meta.into_option();
4068        self
4069    }
4070}
4071
4072// MCP
4073
4074/// Configuration for connecting to an MCP (Model Context Protocol) server.
4075///
4076/// MCP servers provide tools and context that the agent can use when
4077/// processing prompts.
4078///
4079/// See protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/v2/session-setup#mcp-servers)
4080#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4081#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4082#[serde(tag = "type", rename_all = "snake_case")]
4083#[non_exhaustive]
4084pub enum McpServer {
4085    /// HTTP transport configuration
4086    ///
4087    /// Only available when the Agent capabilities include `session.mcp.http`.
4088    Http(McpServerHttp),
4089    /// **UNSTABLE**
4090    ///
4091    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4092    ///
4093    /// ACP transport configuration
4094    ///
4095    /// Only available when the Agent capabilities include `session.mcp.acp`.
4096    /// The MCP server is provided by an ACP component and communicates over the ACP channel.
4097    #[cfg(feature = "unstable_mcp_over_acp")]
4098    Acp(McpServerAcp),
4099    /// Stdio transport configuration
4100    ///
4101    /// Only available when the Agent capabilities include `session.mcp.stdio`.
4102    Stdio(McpServerStdio),
4103    /// Custom or future MCP server transport configuration.
4104    ///
4105    /// Values beginning with `_` are reserved for implementation-specific
4106    /// extensions. Unknown values that do not begin with `_` are reserved for
4107    /// future ACP variants.
4108    ///
4109    /// Receivers that do not understand this transport should preserve the raw
4110    /// payload when storing, replaying, proxying, or forwarding session setup
4111    /// data. Agents MUST reject a request with a transport they do not support
4112    /// with `-32602`.
4113    #[serde(untagged)]
4114    Other(OtherMcpServer),
4115}
4116
4117/// Custom or future MCP server transport payload.
4118#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4119#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
4120#[cfg_attr(feature = "schemars", schemars(inline))]
4121#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
4122#[serde(rename_all = "camelCase")]
4123#[non_exhaustive]
4124pub struct OtherMcpServer {
4125    /// Custom or future MCP server transport type.
4126    ///
4127    /// Values beginning with `_` are reserved for implementation-specific
4128    /// extensions. Unknown values that do not begin with `_` are reserved for
4129    /// future ACP variants.
4130    #[serde(rename = "type")]
4131    pub type_: String,
4132    /// Additional fields from the unknown MCP server transport payload.
4133    #[serde(flatten)]
4134    pub fields: BTreeMap<String, serde_json::Value>,
4135}
4136
4137impl OtherMcpServer {
4138    /// Builds [`OtherMcpServer`] from an unknown discriminator and preserves the remaining extension fields.
4139    #[must_use]
4140    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
4141        fields.remove("type");
4142        Self {
4143            type_: type_.into(),
4144            fields,
4145        }
4146    }
4147}
4148
4149impl<'de> Deserialize<'de> for OtherMcpServer {
4150    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4151    where
4152        D: serde::Deserializer<'de>,
4153    {
4154        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4155        let type_ = fields
4156            .remove("type")
4157            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
4158        let serde_json::Value::String(type_) = type_ else {
4159            return Err(serde::de::Error::custom("`type` must be a string"));
4160        };
4161
4162        if is_known_mcp_server_type(&type_) {
4163            return Err(serde::de::Error::custom(format!(
4164                "known MCP server transport `{type_}` did not match its schema"
4165            )));
4166        }
4167
4168        Ok(Self { type_, fields })
4169    }
4170}
4171
4172fn is_known_mcp_server_type(type_: &str) -> bool {
4173    match type_ {
4174        "http" | "stdio" => true,
4175        #[cfg(feature = "unstable_mcp_over_acp")]
4176        "acp" => true,
4177        _ => false,
4178    }
4179}
4180
4181#[cfg(feature = "schemars")]
4182fn other_mcp_server_schema(schema: &mut Schema) {
4183    super::schema_util::reject_known_string_discriminators(
4184        schema,
4185        "type",
4186        &[
4187            "http",
4188            "stdio",
4189            #[cfg(feature = "unstable_mcp_over_acp")]
4190            "acp",
4191        ],
4192    );
4193}
4194
4195/// HTTP transport configuration for MCP.
4196#[serde_as]
4197#[skip_serializing_none]
4198#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4199#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4200#[serde(rename_all = "camelCase")]
4201#[non_exhaustive]
4202pub struct McpServerHttp {
4203    /// Human-readable name identifying this MCP server.
4204    pub name: String,
4205    /// URL to the MCP server.
4206    #[cfg_attr(feature = "schemars", schemars(url))]
4207    pub url: String,
4208    /// HTTP headers to set when making requests to the MCP server. Headers the MCP transport
4209    /// defines are owned by the transport, and Agents use the transport's values for them.
4210    #[serde_as(deserialize_as = "DefaultOnNull")]
4211    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4212    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4213    pub headers: Vec<HttpHeader>,
4214    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4215    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4216    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4217    ///
4218    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4219    #[serde_as(deserialize_as = "DefaultOnError")]
4220    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4221    #[serde(default)]
4222    #[serde(rename = "_meta")]
4223    pub meta: Option<Meta>,
4224}
4225
4226impl McpServerHttp {
4227    /// Builds [`McpServerHttp`] with the required fields set. Optional fields start unset or empty.
4228    #[must_use]
4229    pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
4230        Self {
4231            name: name.into(),
4232            url: url.into(),
4233            headers: Vec::new(),
4234            meta: None,
4235        }
4236    }
4237
4238    /// HTTP headers to set when making requests to the MCP server. Headers the MCP transport
4239    /// defines are owned by the transport, and Agents use the transport's values for them.
4240    #[must_use]
4241    pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
4242        self.headers = headers;
4243        self
4244    }
4245
4246    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4247    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4248    /// do not recognize.
4249    ///
4250    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4251    #[must_use]
4252    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4253        self.meta = meta.into_option();
4254        self
4255    }
4256}
4257
4258/// **UNSTABLE**
4259///
4260/// This capability is not part of the spec yet, and may be removed or changed at any point.
4261///
4262/// Unique identifier for an MCP server using the ACP transport.
4263///
4264/// The value is opaque and generated by the ACP component providing the MCP server. It is
4265/// used by `mcp/message` to route requests to the component that declared the server.
4266#[cfg(feature = "unstable_mcp_over_acp")]
4267#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4268#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
4269#[serde(transparent)]
4270#[from(forward)]
4271#[non_exhaustive]
4272pub struct McpServerAcpId(pub Arc<str>);
4273
4274#[cfg(feature = "unstable_mcp_over_acp")]
4275impl McpServerAcpId {
4276    /// Wraps a protocol string as a typed [`McpServerAcpId`].
4277    #[must_use]
4278    pub fn new(id: impl Into<Self>) -> Self {
4279        id.into()
4280    }
4281}
4282
4283/// **UNSTABLE**
4284///
4285/// This capability is not part of the spec yet, and may be removed or changed at any point.
4286///
4287/// ACP transport configuration for MCP.
4288///
4289/// The MCP server is provided by an ACP component and communicates over the ACP channel
4290/// using `mcp/message`.
4291#[serde_as]
4292#[skip_serializing_none]
4293#[cfg(feature = "unstable_mcp_over_acp")]
4294#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4295#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4296#[serde(rename_all = "camelCase")]
4297#[non_exhaustive]
4298pub struct McpServerAcp {
4299    /// Human-readable name identifying this MCP server.
4300    pub name: String,
4301    /// Unique identifier for this MCP server, generated by the component providing it.
4302    ///
4303    /// Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible
4304    /// on the same ACP connection.
4305    pub server_id: McpServerAcpId,
4306    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4307    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4308    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4309    ///
4310    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4311    #[serde_as(deserialize_as = "DefaultOnError")]
4312    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4313    #[serde(default)]
4314    #[serde(rename = "_meta")]
4315    pub meta: Option<Meta>,
4316}
4317
4318#[cfg(feature = "unstable_mcp_over_acp")]
4319impl McpServerAcp {
4320    /// Builds [`McpServerAcp`] with the required fields set. Optional fields start unset or empty.
4321    #[must_use]
4322    pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
4323        Self {
4324            name: name.into(),
4325            server_id: server_id.into(),
4326            meta: None,
4327        }
4328    }
4329
4330    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4331    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4332    /// do not recognize.
4333    ///
4334    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4335    #[must_use]
4336    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4337        self.meta = meta.into_option();
4338        self
4339    }
4340}
4341
4342/// Stdio transport configuration for MCP.
4343#[serde_as]
4344#[skip_serializing_none]
4345#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4346#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4347#[serde(rename_all = "camelCase")]
4348#[non_exhaustive]
4349pub struct McpServerStdio {
4350    /// Human-readable name identifying this MCP server.
4351    pub name: String,
4352    /// Absolute path to the MCP server executable.
4353    pub command: AbsolutePath,
4354    /// Command-line arguments to pass to the MCP server.
4355    #[serde_as(deserialize_as = "DefaultOnNull")]
4356    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4357    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4358    pub args: Vec<String>,
4359    /// Environment variables to set when launching the MCP server. Names SHOULD be unique.
4360    /// Agents MAY reject duplicates with `-32602`, and otherwise the last entry wins.
4361    #[serde_as(deserialize_as = "DefaultOnNull")]
4362    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4363    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4364    pub env: Vec<EnvVariable>,
4365    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4366    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4367    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4368    ///
4369    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4370    #[serde_as(deserialize_as = "DefaultOnError")]
4371    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4372    #[serde(default)]
4373    #[serde(rename = "_meta")]
4374    pub meta: Option<Meta>,
4375}
4376
4377impl McpServerStdio {
4378    /// Builds [`McpServerStdio`] with the required fields set. Optional fields start unset or empty.
4379    #[must_use]
4380    pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
4381        Self {
4382            name: name.into(),
4383            command: command.into(),
4384            args: Vec::new(),
4385            env: Vec::new(),
4386            meta: None,
4387        }
4388    }
4389
4390    /// Command-line arguments to pass to the MCP server.
4391    #[must_use]
4392    pub fn args(mut self, args: Vec<String>) -> Self {
4393        self.args = args;
4394        self
4395    }
4396
4397    /// Environment variables to set when launching the MCP server. Names SHOULD be unique.
4398    /// Agents MAY reject duplicates with `-32602`, and otherwise the last entry wins.
4399    #[must_use]
4400    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
4401        self.env = env;
4402        self
4403    }
4404
4405    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4406    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4407    /// do not recognize.
4408    ///
4409    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4410    #[must_use]
4411    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4412        self.meta = meta.into_option();
4413        self
4414    }
4415}
4416
4417/// An environment variable to set when launching a process.
4418#[serde_as]
4419#[skip_serializing_none]
4420#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4421#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4422#[serde(rename_all = "camelCase")]
4423#[non_exhaustive]
4424pub struct EnvVariable {
4425    /// The name of the environment variable.
4426    pub name: String,
4427    /// The value to set for the environment variable.
4428    pub value: String,
4429    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4430    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4431    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4432    ///
4433    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4434    #[serde_as(deserialize_as = "DefaultOnError")]
4435    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4436    #[serde(default)]
4437    #[serde(rename = "_meta")]
4438    pub meta: Option<Meta>,
4439}
4440
4441impl EnvVariable {
4442    /// Builds [`EnvVariable`] with the required fields set. Optional fields start unset or empty.
4443    #[must_use]
4444    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4445        Self {
4446            name: name.into(),
4447            value: value.into(),
4448            meta: None,
4449        }
4450    }
4451
4452    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4453    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4454    /// do not recognize.
4455    ///
4456    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4457    #[must_use]
4458    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4459        self.meta = meta.into_option();
4460        self
4461    }
4462}
4463
4464/// An HTTP header to set when making requests to the MCP server.
4465#[serde_as]
4466#[skip_serializing_none]
4467#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4468#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4469#[serde(rename_all = "camelCase")]
4470#[non_exhaustive]
4471pub struct HttpHeader {
4472    /// The name of the HTTP header.
4473    pub name: String,
4474    /// The value to set for the HTTP header.
4475    pub value: String,
4476    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4477    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4478    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4479    ///
4480    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4481    #[serde_as(deserialize_as = "DefaultOnError")]
4482    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4483    #[serde(default)]
4484    #[serde(rename = "_meta")]
4485    pub meta: Option<Meta>,
4486}
4487
4488impl HttpHeader {
4489    /// Builds [`HttpHeader`] with the required fields set. Optional fields start unset or empty.
4490    #[must_use]
4491    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4492        Self {
4493            name: name.into(),
4494            value: value.into(),
4495            meta: None,
4496        }
4497    }
4498
4499    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4500    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4501    /// do not recognize.
4502    ///
4503    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4504    #[must_use]
4505    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4506        self.meta = meta.into_option();
4507        self
4508    }
4509}
4510
4511// System prompt
4512
4513/// Client-provided system-level instructions for a session.
4514///
4515/// Clients MUST only send this when the Agent advertises
4516/// `session.systemPrompt`. Agents that cannot apply it, for example because of
4517/// an unadvertised mode or an unsupported content block type, MUST return an
4518/// error instead of creating or resuming the session.
4519#[serde_as]
4520#[skip_serializing_none]
4521#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4522#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4523#[serde(rename_all = "camelCase")]
4524#[non_exhaustive]
4525pub struct SystemPrompt {
4526    /// How the Agent combines `content` with its own instructions.
4527    ///
4528    /// Required. Agents MUST return an error for a mode they do not support rather than
4529    /// treating it as `append` or `override`.
4530    pub mode: SystemPromptMode,
4531    /// The system prompt content.
4532    ///
4533    /// Agents advertising the capability MUST accept `text` content blocks.
4534    /// Clients MUST NOT send other content block types until a future
4535    /// capability allows them, and Agents MUST return an error for content
4536    /// blocks they do not support, including unknown types.
4537    ///
4538    /// Clients MUST NOT send an empty array. Agents MAY reject one with
4539    /// `-32602` without creating or resuming the session.
4540    pub content: Vec<ContentBlock>,
4541    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4542    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4543    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4544    ///
4545    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4546    #[serde_as(deserialize_as = "DefaultOnError")]
4547    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4548    #[serde(default)]
4549    #[serde(rename = "_meta")]
4550    pub meta: Option<Meta>,
4551}
4552
4553impl SystemPrompt {
4554    /// Builds [`SystemPrompt`] with the required fields set. Optional fields start unset or empty.
4555    ///
4556    /// `mode` starts as [`SystemPromptMode::Append`]. Use [`SystemPrompt::mode`]
4557    /// to request another mode.
4558    #[must_use]
4559    pub fn new(content: Vec<ContentBlock>) -> Self {
4560        Self {
4561            mode: SystemPromptMode::default(),
4562            content,
4563            meta: None,
4564        }
4565    }
4566
4567    /// How the Agent combines `content` with its own instructions.
4568    #[must_use]
4569    pub fn mode(mut self, mode: SystemPromptMode) -> Self {
4570        self.mode = mode;
4571        self
4572    }
4573
4574    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4575    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4576    /// do not recognize.
4577    ///
4578    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4579    #[must_use]
4580    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4581        self.meta = meta.into_option();
4582        self
4583    }
4584}
4585
4586/// How the Agent combines a client-provided system prompt with its own instructions.
4587///
4588/// Unknown modes are preserved so they can be stored,
4589/// proxied, or forwarded. An Agent MUST return an error for a mode it does not
4590/// support rather than treating it as `append` or `override`.
4591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4592#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
4593#[serde(rename_all = "snake_case")]
4594#[non_exhaustive]
4595pub enum SystemPromptMode {
4596    /// Add the content to the Agent's own instructions rather than replacing them.
4597    ///
4598    /// Every Agent that advertises `session.systemPrompt` supports this mode.
4599    /// Agents SHOULD give the content system-level precedence, for example in the
4600    /// model's system prompt or developer instructions rather than in user-turn
4601    /// content. Where it is placed and how it is formatted is implementation-defined.
4602    #[default]
4603    Append,
4604    /// Replace the Agent's built-in default instructions with the content.
4605    ///
4606    /// This does not change tool availability, MCP servers, permissions, or session
4607    /// configuration. Context the Agent supplies from the workspace or environment
4608    /// MAY still be included. Clients that use this mode take responsibility for any
4609    /// tool guidance or safety instructions the Agent's default instructions
4610    /// provided, and MUST only use it when the Agent advertises
4611    /// `session.systemPrompt.mode.override`.
4612    Override,
4613    /// Custom or future system prompt mode.
4614    ///
4615    /// Values beginning with `_` are reserved for implementation-specific
4616    /// extensions. Unknown values that do not begin with `_` are reserved for
4617    /// future ACP variants.
4618    #[serde(untagged)]
4619    Other(String),
4620}
4621
4622// Prompt
4623
4624/// Request parameters for sending a user prompt to the agent.
4625///
4626/// Contains the user's message and any additional context.
4627///
4628/// See protocol docs: [User Message](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#1-user-message)
4629#[serde_as]
4630#[skip_serializing_none]
4631#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4632#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4633#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4634#[serde(rename_all = "camelCase")]
4635#[non_exhaustive]
4636pub struct PromptRequest {
4637    /// The ID of the session to send this user message to
4638    pub session_id: SessionId,
4639    /// The blocks of content that compose the user's message.
4640    ///
4641    /// As a baseline, the Agent MUST support `text` and `resource_link` content blocks,
4642    /// while other variants are optionally enabled via `PromptCapabilities`.
4643    ///
4644    /// The Client MUST adapt its interface according to `PromptCapabilities`.
4645    ///
4646    /// The client MAY include referenced pieces of context as either
4647    /// `resource` or `resource_link` content blocks.
4648    ///
4649    /// When available, `resource` is preferred
4650    /// as it avoids extra round-trips and allows the message to include
4651    /// pieces of context from sources the agent may not have access to.
4652    ///
4653    /// Clients MUST NOT send an empty array. Agents MAY reject one with `-32602` without
4654    /// inserting it.
4655    #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
4656    pub prompt: Vec<ContentBlock>,
4657    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4658    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4659    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4660    ///
4661    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4662    #[serde_as(deserialize_as = "DefaultOnError")]
4663    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4664    #[serde(default)]
4665    #[serde(rename = "_meta")]
4666    pub meta: Option<Meta>,
4667}
4668
4669impl PromptRequest {
4670    /// Builds [`PromptRequest`] with the required request fields set. Optional fields start unset or empty.
4671    #[must_use]
4672    pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
4673        Self {
4674            session_id: session_id.into(),
4675            prompt,
4676            meta: None,
4677        }
4678    }
4679
4680    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4681    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4682    /// do not recognize.
4683    ///
4684    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4685    #[must_use]
4686    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4687        self.meta = meta.into_option();
4688        self
4689    }
4690}
4691
4692/// Response acknowledging that a user prompt was inserted into the ACP conversation.
4693///
4694/// This response does not indicate that the prompt was merely received or held, nor that the
4695/// agent has finished processing it.
4696/// Processing and completion are reported through `state_update` session updates.
4697///
4698/// Insertion is the logical addition of a user message with a message ID and a position
4699/// relative to other messages in the ACP conversation.
4700///
4701/// See protocol docs: [Prompt Accepted](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#2-prompt-accepted)
4702#[serde_as]
4703#[skip_serializing_none]
4704#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4705#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4706#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4707#[serde(rename_all = "camelCase")]
4708#[non_exhaustive]
4709pub struct PromptResponse {
4710    /// Identifies the user message inserted into the ACP conversation.
4711    ///
4712    /// Required and non-null. Omission and explicit `null` are both invalid.
4713    ///
4714    /// The corresponding user-message session update carries this same identifier and may arrive
4715    /// before or after this response. Agents must echo the message during the live session, but are
4716    /// not required to retain it. If retained and replayed, the message keeps this identifier.
4717    pub message_id: MessageId,
4718    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4719    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4720    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4721    ///
4722    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4723    #[serde_as(deserialize_as = "DefaultOnError")]
4724    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4725    #[serde(default)]
4726    #[serde(rename = "_meta")]
4727    pub meta: Option<Meta>,
4728}
4729
4730impl PromptResponse {
4731    /// Builds [`PromptResponse`] with the required response fields set. Optional fields start unset or empty.
4732    #[must_use]
4733    pub fn new(message_id: impl Into<MessageId>) -> Self {
4734        Self {
4735            message_id: message_id.into(),
4736            meta: None,
4737        }
4738    }
4739
4740    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4741    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4742    /// do not recognize.
4743    ///
4744    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4745    #[must_use]
4746    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4747        self.meta = meta.into_option();
4748        self
4749    }
4750}
4751
4752/// Reasons why an agent stops active session work.
4753///
4754/// See protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#stop-reasons)
4755#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4756#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
4757#[serde(tag = "stopReason", rename_all = "snake_case")]
4758#[non_exhaustive]
4759pub enum StopReason {
4760    /// The active work ended successfully.
4761    /// Includes locally handled commands that required no model call.
4762    EndTurn,
4763    /// The active work ended because the agent reached the maximum number of tokens.
4764    MaxTokens,
4765    /// The active work ended because the agent reached the maximum number of
4766    /// model requests for the foreground work.
4767    MaxTurnRequests,
4768    /// The active work ended because the agent refused to continue.
4769    Refusal,
4770    /// The user or client chose to stop the work: via `session/cancel`, by
4771    /// closing the session, or through an interaction the agent offers, such
4772    /// as a permission option that tells the agent to stop. Work the agent
4773    /// stops for its own reasons uses another reason, such as `error` for an
4774    /// internal timeout.
4775    ///
4776    /// Agents MUST report this stop reason on an idle `state_update` when
4777    /// cancellation ends active foreground work, even if aborting causes
4778    /// exceptions. Agents MUST catch these errors and report `cancelled`, not
4779    /// `error`.
4780    Cancelled,
4781    /// The active work ended because something failed.
4782    ///
4783    /// This includes pre-insertion foreground work. A prompt failure before
4784    /// insertion also requires an error response to `session/prompt`. After
4785    /// insertion, the prompt's response remains successful.
4786    Error(ErrorStopReason),
4787    /// Custom or future stop reason.
4788    ///
4789    /// Values beginning with `_` are reserved for implementation-specific
4790    /// extensions. Unknown values that do not begin with `_` are reserved for
4791    /// future ACP variants.
4792    #[serde(untagged)]
4793    Other(OtherStopReason),
4794}
4795
4796impl From<ErrorStopReason> for StopReason {
4797    fn from(value: ErrorStopReason) -> Self {
4798        Self::Error(value)
4799    }
4800}
4801
4802/// Details of a failure that ended active work.
4803#[serde_as]
4804#[skip_serializing_none]
4805#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4806#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4807#[serde(rename_all = "camelCase")]
4808#[non_exhaustive]
4809pub struct ErrorStopReason {
4810    /// The failure, as a JSON-RPC error object.
4811    ///
4812    /// Optional. Omitted and `null` are equivalent and mean the agent is not reporting failure details.
4813    /// Agents SHOULD include it.
4814    #[serde_as(deserialize_as = "DefaultOnError")]
4815    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4816    #[serde(default)]
4817    pub error: Option<Box<Error>>,
4818}
4819
4820impl ErrorStopReason {
4821    /// Builds [`ErrorStopReason`] with the required fields set. Optional fields start unset or empty.
4822    #[must_use]
4823    pub fn new() -> Self {
4824        Self::default()
4825    }
4826
4827    /// The failure, as a JSON-RPC error object.
4828    #[must_use]
4829    pub fn error(mut self, error: impl IntoOption<Error>) -> Self {
4830        self.error = error.into_option().map(Box::new);
4831        self
4832    }
4833}
4834
4835/// Custom or future stop reason payload.
4836#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4837#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
4838#[cfg_attr(feature = "schemars", schemars(inline))]
4839#[cfg_attr(feature = "schemars", schemars(transform = other_stop_reason_schema))]
4840#[serde(rename_all = "camelCase")]
4841#[non_exhaustive]
4842pub struct OtherStopReason {
4843    /// Custom or future stop reason.
4844    ///
4845    /// Values beginning with `_` are reserved for implementation-specific
4846    /// extensions. Unknown values that do not begin with `_` are reserved for
4847    /// future ACP variants.
4848    pub stop_reason: String,
4849    /// Additional fields from the unknown stop reason payload.
4850    #[serde(flatten)]
4851    pub fields: BTreeMap<String, serde_json::Value>,
4852}
4853
4854impl OtherStopReason {
4855    /// Builds [`OtherStopReason`] from an unknown discriminator and preserves the remaining extension fields.
4856    #[must_use]
4857    pub fn new(
4858        stop_reason: impl Into<String>,
4859        mut fields: BTreeMap<String, serde_json::Value>,
4860    ) -> Self {
4861        remove_idle_state_update_fields(&mut fields);
4862        Self {
4863            stop_reason: stop_reason.into(),
4864            fields,
4865        }
4866    }
4867}
4868
4869impl<'de> Deserialize<'de> for OtherStopReason {
4870    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4871    where
4872        D: serde::Deserializer<'de>,
4873    {
4874        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4875        let stop_reason = fields
4876            .remove("stopReason")
4877            .ok_or_else(|| serde::de::Error::missing_field("stopReason"))?;
4878        let serde_json::Value::String(stop_reason) = stop_reason else {
4879            return Err(serde::de::Error::custom("`stopReason` must be a string"));
4880        };
4881
4882        if KNOWN_STOP_REASONS.contains(&stop_reason.as_str()) {
4883            return Err(serde::de::Error::custom(format!(
4884                "known stop reason `{stop_reason}` did not match its schema"
4885            )));
4886        }
4887        remove_idle_state_update_fields(&mut fields);
4888
4889        Ok(Self {
4890            stop_reason,
4891            fields,
4892        })
4893    }
4894}
4895
4896const KNOWN_STOP_REASONS: &[&str] = &[
4897    "end_turn",
4898    "max_tokens",
4899    "max_turn_requests",
4900    "refusal",
4901    "cancelled",
4902    "error",
4903];
4904
4905/// Keeps the idle update's own fields out of an unknown stop reason's payload.
4906fn remove_idle_state_update_fields(fields: &mut BTreeMap<String, serde_json::Value>) {
4907    fields.remove("stopReason");
4908    fields.remove("usage");
4909    fields.remove("_meta");
4910}
4911
4912#[cfg(feature = "schemars")]
4913fn other_stop_reason_schema(schema: &mut Schema) {
4914    super::schema_util::reject_known_string_discriminators(
4915        schema,
4916        "stopReason",
4917        KNOWN_STOP_REASONS,
4918    );
4919}
4920
4921/// **UNSTABLE**
4922///
4923/// This capability is not part of the spec yet, and may be removed or changed at any point.
4924///
4925/// Token usage for the foreground work that just completed, not cumulative for the session.
4926#[cfg(feature = "unstable_end_turn_token_usage")]
4927#[serde_as]
4928#[skip_serializing_none]
4929#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4930#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4931#[serde(rename_all = "camelCase")]
4932#[non_exhaustive]
4933pub struct Usage {
4934    /// Sum of all token types for the completed work.
4935    pub total_tokens: u64,
4936    /// Input tokens for the completed work.
4937    pub input_tokens: u64,
4938    /// Output tokens for the completed work.
4939    pub output_tokens: u64,
4940    /// Thought/reasoning tokens for the completed work.
4941    ///
4942    /// Omitted and `null` are equivalent and mean the count is not reported.
4943    #[serde_as(deserialize_as = "DefaultOnError")]
4944    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4945    #[serde(default)]
4946    pub thought_tokens: Option<u64>,
4947    /// Cache read tokens for the completed work.
4948    ///
4949    /// Omitted and `null` are equivalent and mean the count is not reported.
4950    #[serde_as(deserialize_as = "DefaultOnError")]
4951    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4952    #[serde(default)]
4953    pub cached_read_tokens: Option<u64>,
4954    /// Cache write tokens for the completed work.
4955    ///
4956    /// Omitted and `null` are equivalent and mean the count is not reported.
4957    #[serde_as(deserialize_as = "DefaultOnError")]
4958    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4959    #[serde(default)]
4960    pub cached_write_tokens: Option<u64>,
4961    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4962    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4963    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4964    ///
4965    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4966    #[serde_as(deserialize_as = "DefaultOnError")]
4967    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4968    #[serde(default)]
4969    #[serde(rename = "_meta")]
4970    pub meta: Option<Meta>,
4971}
4972
4973#[cfg(feature = "unstable_end_turn_token_usage")]
4974impl Usage {
4975    /// Builds [`Usage`] with the required fields set. Optional fields start unset or empty.
4976    #[must_use]
4977    pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
4978        Self {
4979            total_tokens,
4980            input_tokens,
4981            output_tokens,
4982            thought_tokens: None,
4983            cached_read_tokens: None,
4984            cached_write_tokens: None,
4985            meta: None,
4986        }
4987    }
4988
4989    /// Thought/reasoning tokens for the completed work.
4990    #[must_use]
4991    pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
4992        self.thought_tokens = thought_tokens.into_option();
4993        self
4994    }
4995
4996    /// Cache read tokens for the completed work.
4997    #[must_use]
4998    pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
4999        self.cached_read_tokens = cached_read_tokens.into_option();
5000        self
5001    }
5002
5003    /// Cache write tokens for the completed work.
5004    #[must_use]
5005    pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
5006        self.cached_write_tokens = cached_write_tokens.into_option();
5007        self
5008    }
5009
5010    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5011    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5012    /// do not recognize.
5013    ///
5014    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5015    #[must_use]
5016    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5017        self.meta = meta.into_option();
5018        self
5019    }
5020}
5021
5022// Providers
5023
5024/// **UNSTABLE**
5025///
5026/// This capability is not part of the spec yet, and may be removed or changed at any point.
5027///
5028/// Well-known API protocol identifiers for LLM providers.
5029///
5030/// Agents and clients MUST handle unknown protocol identifiers gracefully.
5031///
5032/// Protocol names beginning with `_` are free for custom use, like other ACP extension methods.
5033/// Protocol names that do not begin with `_` are reserved for the ACP spec.
5034#[cfg(feature = "unstable_llm_providers")]
5035#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5036#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5037#[serde(rename_all = "snake_case")]
5038#[non_exhaustive]
5039#[expect(clippy::doc_markdown)]
5040pub enum LlmProtocol {
5041    /// Anthropic API protocol.
5042    Anthropic,
5043    /// OpenAI API protocol.
5044    #[serde(rename = "openai")]
5045    OpenAi,
5046    /// Azure OpenAI API protocol.
5047    Azure,
5048    /// Google Vertex AI API protocol.
5049    Vertex,
5050    /// AWS Bedrock API protocol.
5051    Bedrock,
5052    /// Custom or future protocol.
5053    ///
5054    /// Values beginning with `_` are reserved for implementation-specific
5055    /// extensions. Unknown values that do not begin with `_` are reserved for
5056    /// future ACP variants.
5057    #[serde(untagged)]
5058    Other(String),
5059}
5060
5061/// **UNSTABLE**
5062///
5063/// This capability is not part of the spec yet, and may be removed or changed at any point.
5064///
5065/// Current effective non-secret routing configuration for a provider.
5066#[cfg(feature = "unstable_llm_providers")]
5067#[serde_as]
5068#[skip_serializing_none]
5069#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5070#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5071#[serde(rename_all = "camelCase")]
5072#[non_exhaustive]
5073pub struct ProviderCurrentConfig {
5074    /// Protocol currently used by this provider.
5075    pub api_type: LlmProtocol,
5076    /// Base URL currently used by this provider.
5077    #[cfg_attr(feature = "schemars", schemars(url))]
5078    pub base_url: String,
5079    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5080    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5081    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5082    ///
5083    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5084    #[serde_as(deserialize_as = "DefaultOnError")]
5085    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5086    #[serde(default)]
5087    #[serde(rename = "_meta")]
5088    pub meta: Option<Meta>,
5089}
5090
5091#[cfg(feature = "unstable_llm_providers")]
5092impl ProviderCurrentConfig {
5093    /// Builds [`ProviderCurrentConfig`] with the required fields set. Optional fields start unset or empty.
5094    #[must_use]
5095    pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
5096        Self {
5097            api_type,
5098            base_url: base_url.into(),
5099            meta: None,
5100        }
5101    }
5102
5103    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5104    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5105    /// do not recognize.
5106    ///
5107    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5108    #[must_use]
5109    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5110        self.meta = meta.into_option();
5111        self
5112    }
5113}
5114
5115/// **UNSTABLE**
5116///
5117/// This capability is not part of the spec yet, and may be removed or changed at any point.
5118///
5119/// Unique identifier for a configurable LLM provider.
5120#[cfg(feature = "unstable_llm_providers")]
5121#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
5123#[serde(transparent)]
5124#[from(forward)]
5125#[non_exhaustive]
5126pub struct ProviderId(pub Arc<str>);
5127
5128#[cfg(feature = "unstable_llm_providers")]
5129impl ProviderId {
5130    /// Wraps a protocol string as a typed [`ProviderId`].
5131    #[must_use]
5132    pub fn new(id: impl Into<Self>) -> Self {
5133        id.into()
5134    }
5135}
5136
5137/// **UNSTABLE**
5138///
5139/// This capability is not part of the spec yet, and may be removed or changed at any point.
5140///
5141/// Information about a configurable LLM provider.
5142#[cfg(feature = "unstable_llm_providers")]
5143#[serde_as]
5144#[skip_serializing_none]
5145#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5146#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5147#[serde(rename_all = "camelCase")]
5148#[non_exhaustive]
5149pub struct ProviderInfo {
5150    /// Provider identifier, for example "main" or "openai".
5151    pub provider_id: ProviderId,
5152    /// Supported protocol types for this provider.
5153    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
5154    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
5155    pub supported: Vec<LlmProtocol>,
5156    /// Whether this provider is mandatory and cannot be disabled via `providers/disable`.
5157    /// If true, clients must not call `providers/disable` for this provider ID.
5158    pub required: bool,
5159    /// Current effective non-secret routing config.
5160    /// Null or omitted means provider is disabled.
5161    #[serde(default)]
5162    pub current: Option<ProviderCurrentConfig>,
5163    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5164    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5165    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5166    ///
5167    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5168    #[serde_as(deserialize_as = "DefaultOnError")]
5169    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5170    #[serde(default)]
5171    #[serde(rename = "_meta")]
5172    pub meta: Option<Meta>,
5173}
5174
5175#[cfg(feature = "unstable_llm_providers")]
5176impl ProviderInfo {
5177    /// Builds [`ProviderInfo`] with the required fields set. Optional fields start unset or empty.
5178    #[must_use]
5179    pub fn new(
5180        provider_id: impl Into<ProviderId>,
5181        supported: Vec<LlmProtocol>,
5182        required: bool,
5183        current: impl IntoOption<ProviderCurrentConfig>,
5184    ) -> Self {
5185        Self {
5186            provider_id: provider_id.into(),
5187            supported,
5188            required,
5189            current: current.into_option(),
5190            meta: None,
5191        }
5192    }
5193
5194    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5195    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5196    /// do not recognize.
5197    ///
5198    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5199    #[must_use]
5200    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5201        self.meta = meta.into_option();
5202        self
5203    }
5204}
5205
5206#[cfg(feature = "unstable_llm_providers")]
5207crate::serde_util::default_on_null! {
5208    /// **UNSTABLE**
5209    ///
5210    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5211    ///
5212    /// Request parameters for `providers/list`.
5213    #[serde_as]
5214    #[skip_serializing_none]
5215    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5216    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5217    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5218    #[serde(rename_all = "camelCase")]
5219    #[non_exhaustive]
5220    pub struct ListProvidersRequest {
5221        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5222        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5223        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5224        ///
5225        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5226        #[serde_as(deserialize_as = "DefaultOnError")]
5227        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5228        #[serde(default)]
5229        #[serde(rename = "_meta")]
5230        pub meta: Option<Meta>,
5231    }
5232}
5233
5234#[cfg(feature = "unstable_llm_providers")]
5235impl ListProvidersRequest {
5236    /// Builds [`ListProvidersRequest`] with the required request fields set. Optional fields start unset or empty.
5237    #[must_use]
5238    pub fn new() -> Self {
5239        Self::default()
5240    }
5241
5242    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5243    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5244    /// do not recognize.
5245    ///
5246    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5247    #[must_use]
5248    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5249        self.meta = meta.into_option();
5250        self
5251    }
5252}
5253
5254/// **UNSTABLE**
5255///
5256/// This capability is not part of the spec yet, and may be removed or changed at any point.
5257///
5258/// Response to `providers/list`.
5259#[cfg(feature = "unstable_llm_providers")]
5260#[serde_as]
5261#[skip_serializing_none]
5262#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5264#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5265#[serde(rename_all = "camelCase")]
5266#[non_exhaustive]
5267pub struct ListProvidersResponse {
5268    /// Configurable providers with current routing info suitable for UI display.
5269    pub providers: Vec<ProviderInfo>,
5270    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5271    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5272    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5273    ///
5274    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5275    #[serde_as(deserialize_as = "DefaultOnError")]
5276    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5277    #[serde(default)]
5278    #[serde(rename = "_meta")]
5279    pub meta: Option<Meta>,
5280}
5281
5282#[cfg(feature = "unstable_llm_providers")]
5283impl ListProvidersResponse {
5284    /// Builds [`ListProvidersResponse`] with the required response fields set. Optional fields start unset or empty.
5285    #[must_use]
5286    pub fn new(providers: Vec<ProviderInfo>) -> Self {
5287        Self {
5288            providers,
5289            meta: None,
5290        }
5291    }
5292
5293    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5294    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5295    /// do not recognize.
5296    ///
5297    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5298    #[must_use]
5299    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5300        self.meta = meta.into_option();
5301        self
5302    }
5303}
5304
5305/// **UNSTABLE**
5306///
5307/// This capability is not part of the spec yet, and may be removed or changed at any point.
5308///
5309/// Request parameters for `providers/set`.
5310///
5311/// Replaces the full configuration for one provider ID.
5312#[cfg(feature = "unstable_llm_providers")]
5313#[serde_as]
5314#[skip_serializing_none]
5315#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5316#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
5317#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5318#[serde(rename_all = "camelCase")]
5319#[non_exhaustive]
5320pub struct SetProviderRequest {
5321    /// Provider ID to configure.
5322    pub provider_id: ProviderId,
5323    /// Protocol type for this provider.
5324    pub api_type: LlmProtocol,
5325    /// Base URL for requests sent through this provider.
5326    #[cfg_attr(feature = "schemars", schemars(url))]
5327    pub base_url: String,
5328    /// Full headers map for this provider.
5329    /// May include authorization, routing, or other integration-specific headers.
5330    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
5331    pub headers: HashMap<String, String>,
5332    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5333    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5334    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5335    ///
5336    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5337    #[serde_as(deserialize_as = "DefaultOnError")]
5338    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5339    #[serde(default)]
5340    #[serde(rename = "_meta")]
5341    pub meta: Option<Meta>,
5342}
5343
5344#[cfg(feature = "unstable_llm_providers")]
5345impl SetProviderRequest {
5346    /// Builds [`SetProviderRequest`] with the required request fields set. Optional fields start unset or empty.
5347    #[must_use]
5348    pub fn new(
5349        provider_id: impl Into<ProviderId>,
5350        api_type: LlmProtocol,
5351        base_url: impl Into<String>,
5352    ) -> Self {
5353        Self {
5354            provider_id: provider_id.into(),
5355            api_type,
5356            base_url: base_url.into(),
5357            headers: HashMap::new(),
5358            meta: None,
5359        }
5360    }
5361
5362    /// Full headers map for this provider.
5363    /// May include authorization, routing, or other integration-specific headers.
5364    #[must_use]
5365    pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
5366        self.headers = headers;
5367        self
5368    }
5369
5370    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5371    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5372    /// do not recognize.
5373    ///
5374    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5375    #[must_use]
5376    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5377        self.meta = meta.into_option();
5378        self
5379    }
5380}
5381
5382#[cfg(feature = "unstable_llm_providers")]
5383crate::serde_util::default_on_null! {
5384    /// **UNSTABLE**
5385    ///
5386    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5387    ///
5388    /// Response to `providers/set`.
5389    #[serde_as]
5390    #[skip_serializing_none]
5391    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5392    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5393    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5394    #[serde(rename_all = "camelCase")]
5395    #[non_exhaustive]
5396    pub struct SetProviderResponse {
5397        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5398        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5399        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5400        ///
5401        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5402        #[serde_as(deserialize_as = "DefaultOnError")]
5403        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5404        #[serde(default)]
5405        #[serde(rename = "_meta")]
5406        pub meta: Option<Meta>,
5407    }
5408}
5409
5410#[cfg(feature = "unstable_llm_providers")]
5411impl SetProviderResponse {
5412    /// Builds [`SetProviderResponse`] with the required response fields set. Optional fields start unset or empty.
5413    #[must_use]
5414    pub fn new() -> Self {
5415        Self::default()
5416    }
5417
5418    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5419    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5420    /// do not recognize.
5421    ///
5422    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5423    #[must_use]
5424    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5425        self.meta = meta.into_option();
5426        self
5427    }
5428}
5429
5430/// **UNSTABLE**
5431///
5432/// This capability is not part of the spec yet, and may be removed or changed at any point.
5433///
5434/// Request parameters for `providers/disable`.
5435#[cfg(feature = "unstable_llm_providers")]
5436#[serde_as]
5437#[skip_serializing_none]
5438#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5440#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5441#[serde(rename_all = "camelCase")]
5442#[non_exhaustive]
5443pub struct DisableProviderRequest {
5444    /// Provider ID to disable.
5445    pub provider_id: ProviderId,
5446    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5447    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5448    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5449    ///
5450    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5451    #[serde_as(deserialize_as = "DefaultOnError")]
5452    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5453    #[serde(default)]
5454    #[serde(rename = "_meta")]
5455    pub meta: Option<Meta>,
5456}
5457
5458#[cfg(feature = "unstable_llm_providers")]
5459impl DisableProviderRequest {
5460    /// Builds [`DisableProviderRequest`] with the required request fields set. Optional fields start unset or empty.
5461    #[must_use]
5462    pub fn new(provider_id: impl Into<ProviderId>) -> Self {
5463        Self {
5464            provider_id: provider_id.into(),
5465            meta: None,
5466        }
5467    }
5468
5469    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5470    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5471    /// do not recognize.
5472    ///
5473    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5474    #[must_use]
5475    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5476        self.meta = meta.into_option();
5477        self
5478    }
5479}
5480
5481#[cfg(feature = "unstable_llm_providers")]
5482crate::serde_util::default_on_null! {
5483    /// **UNSTABLE**
5484    ///
5485    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5486    ///
5487    /// Response to `providers/disable`.
5488    #[serde_as]
5489    #[skip_serializing_none]
5490    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5491    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5492    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5493    #[serde(rename_all = "camelCase")]
5494    #[non_exhaustive]
5495    pub struct DisableProviderResponse {
5496        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5497        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5498        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5499        ///
5500        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5501        #[serde_as(deserialize_as = "DefaultOnError")]
5502        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5503        #[serde(default)]
5504        #[serde(rename = "_meta")]
5505        pub meta: Option<Meta>,
5506    }
5507}
5508
5509#[cfg(feature = "unstable_llm_providers")]
5510impl DisableProviderResponse {
5511    /// Builds [`DisableProviderResponse`] with the required response fields set. Optional fields start unset or empty.
5512    #[must_use]
5513    pub fn new() -> Self {
5514        Self::default()
5515    }
5516
5517    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5518    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5519    /// do not recognize.
5520    ///
5521    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5522    #[must_use]
5523    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5524        self.meta = meta.into_option();
5525        self
5526    }
5527}
5528
5529// Capabilities
5530
5531/// Capabilities supported by the agent.
5532///
5533/// Advertised during initialization to inform the client about
5534/// available features and content types.
5535///
5536/// See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#agent-capabilities)
5537#[serde_as]
5538#[skip_serializing_none]
5539#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5540#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5541#[serde(rename_all = "camelCase")]
5542#[non_exhaustive]
5543pub struct AgentCapabilities {
5544    /// Session capabilities supported by the agent.
5545    ///
5546    /// Optional. Omitted and `null` are equivalent and mean the agent does not support the
5547    /// `session/*` methods. Supplying `{}` means the agent supports the
5548    /// baseline session methods: `session/new`, `session/list`,
5549    /// `session/resume`, `session/close`, `session/prompt`, `session/cancel`,
5550    /// and `session/update`.
5551    #[serde_as(deserialize_as = "DefaultOnError")]
5552    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5553    #[serde(default)]
5554    pub session: Option<SessionCapabilities>,
5555    /// Authentication-related extension capabilities supported by the agent.
5556    ///
5557    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise any
5558    /// authentication-related extensions. This field does not advertise support
5559    /// for `auth/login` or `auth/logout`. Those methods are advertised by a
5560    /// non-empty `authMethods` list in the `initialize` response.
5561    #[serde_as(deserialize_as = "DefaultOnError")]
5562    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5563    #[serde(default)]
5564    pub auth: Option<AgentAuthCapabilities>,
5565    /// **UNSTABLE**
5566    ///
5567    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5568    ///
5569    /// Provider configuration capabilities supported by the agent.
5570    ///
5571    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5572    /// Supplying `{}` means the agent supports provider configuration methods.
5573    #[cfg(feature = "unstable_llm_providers")]
5574    #[serde_as(deserialize_as = "DefaultOnError")]
5575    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5576    #[serde(default)]
5577    pub providers: Option<ProvidersCapabilities>,
5578    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5579    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5580    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5581    ///
5582    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5583    #[serde_as(deserialize_as = "DefaultOnError")]
5584    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5585    #[serde(default)]
5586    #[serde(rename = "_meta")]
5587    pub meta: Option<Meta>,
5588}
5589
5590impl AgentCapabilities {
5591    /// Builds an empty [`AgentCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5592    #[must_use]
5593    pub fn new() -> Self {
5594        Self::default()
5595    }
5596
5597    /// Session capabilities supported by the agent.
5598    ///
5599    /// Omitted and `null` are equivalent and mean the agent does not support the `session/*`
5600    /// methods. Supplying `{}` means the agent supports the baseline
5601    /// session methods: `session/new`, `session/list`, `session/resume`,
5602    /// `session/close`, `session/prompt`, `session/cancel`, and
5603    /// `session/update`.
5604    #[must_use]
5605    pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
5606        self.session = session.into_option();
5607        self
5608    }
5609
5610    /// Authentication-related extension capabilities supported by the agent.
5611    ///
5612    /// This field does not advertise support for `auth/login` or `auth/logout`.
5613    #[must_use]
5614    pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
5615        self.auth = auth.into_option();
5616        self
5617    }
5618
5619    /// **UNSTABLE**
5620    ///
5621    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5622    ///
5623    /// Provider configuration capabilities supported by the agent.
5624    #[cfg(feature = "unstable_llm_providers")]
5625    #[must_use]
5626    pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
5627        self.providers = providers.into_option();
5628        self
5629    }
5630
5631    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5632    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5633    /// do not recognize.
5634    ///
5635    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5636    #[must_use]
5637    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5638        self.meta = meta.into_option();
5639        self
5640    }
5641}
5642
5643/// **UNSTABLE**
5644///
5645/// This capability is not part of the spec yet, and may be removed or changed at any point.
5646///
5647/// Provider configuration capabilities supported by the agent.
5648///
5649/// Supplying `{}` means the agent supports provider configuration methods.
5650#[cfg(feature = "unstable_llm_providers")]
5651#[serde_as]
5652#[skip_serializing_none]
5653#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5654#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5655#[non_exhaustive]
5656pub struct ProvidersCapabilities {
5657    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5658    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5659    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5660    ///
5661    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5662    #[serde_as(deserialize_as = "DefaultOnError")]
5663    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5664    #[serde(default)]
5665    #[serde(rename = "_meta")]
5666    pub meta: Option<Meta>,
5667}
5668
5669#[cfg(feature = "unstable_llm_providers")]
5670impl ProvidersCapabilities {
5671    /// Builds an empty [`ProvidersCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5672    #[must_use]
5673    pub fn new() -> Self {
5674        Self::default()
5675    }
5676
5677    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5678    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5679    /// do not recognize.
5680    ///
5681    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5682    #[must_use]
5683    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5684        self.meta = meta.into_option();
5685        self
5686    }
5687}
5688
5689/// Session capabilities supported by the agent.
5690///
5691/// Supplying `{}` means the agent supports the baseline session methods:
5692/// `session/new`, `session/list`, `session/resume`, `session/close`,
5693/// `session/prompt`, `session/cancel`, and `session/update`.
5694///
5695/// Agents that support sessions **MAY** support additional session methods,
5696/// prompt content types, and MCP transports by specifying additional
5697/// capabilities.
5698///
5699/// See protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#session-capabilities)
5700#[serde_as]
5701#[skip_serializing_none]
5702#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5703#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5704#[serde(rename_all = "camelCase")]
5705#[non_exhaustive]
5706pub struct SessionCapabilities {
5707    /// Prompt capabilities supported by the agent in `session/prompt` requests.
5708    ///
5709    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise any
5710    /// prompt extensions beyond the baseline text and resource-link content
5711    /// required by `session/prompt`.
5712    #[serde_as(deserialize_as = "DefaultOnError")]
5713    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5714    #[serde(default)]
5715    pub prompt: Option<PromptCapabilities>,
5716    /// MCP capabilities supported by the agent for session lifecycle requests.
5717    ///
5718    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise MCP
5719    /// server transport support for sessions.
5720    #[serde_as(deserialize_as = "DefaultOnError")]
5721    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5722    #[serde(default)]
5723    pub mcp: Option<McpCapabilities>,
5724    /// Whether the agent supports `session/delete`.
5725    ///
5726    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5727    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
5728    #[serde_as(deserialize_as = "DefaultOnError")]
5729    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5730    #[serde(default)]
5731    pub delete: Option<SessionDeleteCapabilities>,
5732    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
5733    ///
5734    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5735    /// Supplying `{}` means the agent supports `additionalDirectories` on
5736    /// supported session lifecycle requests.
5737    ///
5738    /// Agents may return `SessionInfo.additionalDirectories` to report the
5739    /// complete ordered additional-root list associated with a listed session.
5740    #[serde_as(deserialize_as = "DefaultOnError")]
5741    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5742    #[serde(default)]
5743    pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
5744    /// Whether the agent supports `systemPrompt` on session lifecycle requests.
5745    ///
5746    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5747    /// Supplying `{}` means the agent supports `systemPrompt` in `append` mode.
5748    #[serde_as(deserialize_as = "DefaultOnError")]
5749    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5750    #[serde(default)]
5751    pub system_prompt: Option<SessionSystemPromptCapabilities>,
5752    /// **UNSTABLE**
5753    ///
5754    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5755    ///
5756    /// Whether the agent supports `session/fork`.
5757    ///
5758    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5759    /// Supplying `{}` means the agent supports forking sessions.
5760    #[cfg(feature = "unstable_session_fork")]
5761    #[serde_as(deserialize_as = "DefaultOnError")]
5762    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5763    #[serde(default)]
5764    pub fork: Option<SessionForkCapabilities>,
5765    /// **UNSTABLE**
5766    ///
5767    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5768    ///
5769    /// Whether the agent supports `session/archive` and `session/unarchive`.
5770    ///
5771    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5772    /// Supplying `{}` means the agent supports both methods, the `archived` filter on
5773    /// `session/list`, and `SessionInfo.archived`.
5774    #[cfg(feature = "unstable_session_archive")]
5775    #[serde_as(deserialize_as = "DefaultOnError")]
5776    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5777    #[serde(default)]
5778    pub archive: Option<SessionArchiveCapabilities>,
5779    /// **UNSTABLE**
5780    ///
5781    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5782    ///
5783    /// Whether the agent supports `session/status`.
5784    ///
5785    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5786    /// Supplying `{}` means the agent supports checking session status.
5787    #[cfg(feature = "unstable_session_status")]
5788    #[serde_as(deserialize_as = "DefaultOnError")]
5789    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5790    #[serde(default)]
5791    pub status: Option<SessionStatusCapabilities>,
5792    /// **UNSTABLE**
5793    ///
5794    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5795    ///
5796    /// Whether the agent supports `session/set_title`.
5797    ///
5798    /// Agents report titles through `session/list` and `session_info_update` without this
5799    /// capability. It only advertises that Clients can set titles.
5800    ///
5801    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5802    /// Supplying `{}` means the agent supports setting session titles.
5803    #[cfg(feature = "unstable_session_set_title")]
5804    #[serde_as(deserialize_as = "DefaultOnError")]
5805    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5806    #[serde(default)]
5807    pub title: Option<SessionTitleCapabilities>,
5808    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5809    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5810    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5811    ///
5812    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5813    #[serde_as(deserialize_as = "DefaultOnError")]
5814    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5815    #[serde(default)]
5816    #[serde(rename = "_meta")]
5817    pub meta: Option<Meta>,
5818}
5819
5820impl SessionCapabilities {
5821    /// Builds an empty [`SessionCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5822    #[must_use]
5823    pub fn new() -> Self {
5824        Self::default()
5825    }
5826
5827    /// Prompt capabilities supported by the agent in `session/prompt` requests.
5828    ///
5829    /// Omitted and `null` are equivalent and mean the agent does not advertise any prompt
5830    /// extensions beyond the baseline text and resource-link content required by
5831    /// `session/prompt`.
5832    #[must_use]
5833    pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
5834        self.prompt = prompt.into_option();
5835        self
5836    }
5837
5838    /// MCP capabilities supported by the agent for session lifecycle requests.
5839    ///
5840    /// Omitted and `null` are equivalent and mean the agent does not advertise MCP server
5841    /// transport support for sessions.
5842    #[must_use]
5843    pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
5844        self.mcp = mcp.into_option();
5845        self
5846    }
5847
5848    /// Whether the agent supports `session/delete`.
5849    ///
5850    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5851    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
5852    #[must_use]
5853    pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
5854        self.delete = delete.into_option();
5855        self
5856    }
5857
5858    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
5859    ///
5860    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5861    /// Supplying `{}` means the agent supports `additionalDirectories` on
5862    /// supported session lifecycle requests.
5863    ///
5864    /// Agents may return `SessionInfo.additionalDirectories` to report the
5865    /// complete ordered additional-root list associated with a listed session.
5866    #[must_use]
5867    pub fn additional_directories(
5868        mut self,
5869        additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
5870    ) -> Self {
5871        self.additional_directories = additional_directories.into_option();
5872        self
5873    }
5874
5875    /// Whether the agent supports `systemPrompt` on session lifecycle requests.
5876    ///
5877    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5878    /// Supplying `{}` means the agent supports `systemPrompt` in `append` mode.
5879    #[must_use]
5880    pub fn system_prompt(
5881        mut self,
5882        system_prompt: impl IntoOption<SessionSystemPromptCapabilities>,
5883    ) -> Self {
5884        self.system_prompt = system_prompt.into_option();
5885        self
5886    }
5887
5888    #[cfg(feature = "unstable_session_fork")]
5889    /// Whether the agent supports `session/fork`.
5890    ///
5891    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5892    /// Supplying `{}` means the agent supports forking sessions.
5893    #[must_use]
5894    pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
5895        self.fork = fork.into_option();
5896        self
5897    }
5898
5899    /// **UNSTABLE**
5900    ///
5901    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5902    ///
5903    /// Whether the agent supports `session/archive` and `session/unarchive`.
5904    ///
5905    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5906    /// Supplying `{}` means the agent supports both methods, the `archived` filter on
5907    /// `session/list`, and `SessionInfo.archived`.
5908    #[cfg(feature = "unstable_session_archive")]
5909    #[must_use]
5910    pub fn archive(mut self, archive: impl IntoOption<SessionArchiveCapabilities>) -> Self {
5911        self.archive = archive.into_option();
5912        self
5913    }
5914
5915    /// **UNSTABLE**
5916    ///
5917    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5918    ///
5919    /// Whether the agent supports `session/status`.
5920    ///
5921    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5922    /// Supplying `{}` means the agent supports checking session status.
5923    #[cfg(feature = "unstable_session_status")]
5924    #[must_use]
5925    pub fn status(mut self, status: impl IntoOption<SessionStatusCapabilities>) -> Self {
5926        self.status = status.into_option();
5927        self
5928    }
5929
5930    /// **UNSTABLE**
5931    ///
5932    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5933    ///
5934    /// Whether the agent supports `session/set_title`.
5935    ///
5936    /// Agents report titles through `session/list` and `session_info_update` without this
5937    /// capability. It only advertises that Clients can set titles.
5938    ///
5939    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5940    /// Supplying `{}` means the agent supports setting session titles.
5941    #[cfg(feature = "unstable_session_set_title")]
5942    #[must_use]
5943    pub fn title(mut self, title: impl IntoOption<SessionTitleCapabilities>) -> Self {
5944        self.title = title.into_option();
5945        self
5946    }
5947
5948    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5949    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5950    /// do not recognize.
5951    ///
5952    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5953    #[must_use]
5954    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5955        self.meta = meta.into_option();
5956        self
5957    }
5958}
5959
5960/// Capabilities for the `session/delete` method.
5961///
5962/// Supplying `{}` means the agent supports deleting sessions from `session/list`.
5963#[serde_as]
5964#[skip_serializing_none]
5965#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5966#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5967#[non_exhaustive]
5968pub struct SessionDeleteCapabilities {
5969    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5970    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5971    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5972    ///
5973    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5974    #[serde_as(deserialize_as = "DefaultOnError")]
5975    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5976    #[serde(default)]
5977    #[serde(rename = "_meta")]
5978    pub meta: Option<Meta>,
5979}
5980
5981impl SessionDeleteCapabilities {
5982    /// Builds an empty [`SessionDeleteCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5983    #[must_use]
5984    pub fn new() -> Self {
5985        Self::default()
5986    }
5987
5988    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5989    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5990    /// do not recognize.
5991    ///
5992    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5993    #[must_use]
5994    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5995        self.meta = meta.into_option();
5996        self
5997    }
5998}
5999
6000/// Capabilities for additional session directories support.
6001///
6002/// Supplying `{}` means the agent supports the `additionalDirectories` field on
6003/// supported session lifecycle requests. Agents may return
6004/// `SessionInfo.additionalDirectories` in `session/list` to report the
6005/// complete ordered additional-root list associated with a listed session.
6006#[serde_as]
6007#[skip_serializing_none]
6008#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6009#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6010#[non_exhaustive]
6011pub struct SessionAdditionalDirectoriesCapabilities {
6012    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6013    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6014    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6015    ///
6016    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6017    #[serde_as(deserialize_as = "DefaultOnError")]
6018    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6019    #[serde(default)]
6020    #[serde(rename = "_meta")]
6021    pub meta: Option<Meta>,
6022}
6023
6024impl SessionAdditionalDirectoriesCapabilities {
6025    /// Builds an empty [`SessionAdditionalDirectoriesCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6026    #[must_use]
6027    pub fn new() -> Self {
6028        Self::default()
6029    }
6030
6031    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6032    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6033    /// do not recognize.
6034    ///
6035    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6036    #[must_use]
6037    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6038        self.meta = meta.into_option();
6039        self
6040    }
6041}
6042
6043/// Capabilities for client-provided system prompts.
6044///
6045/// Supplying `{}` means the agent supports `systemPrompt` on session lifecycle
6046/// requests in `append` mode.
6047#[serde_as]
6048#[skip_serializing_none]
6049#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6050#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6051#[serde(rename_all = "camelCase")]
6052#[non_exhaustive]
6053pub struct SessionSystemPromptCapabilities {
6054    /// Delivery modes the agent supports in addition to `append`.
6055    ///
6056    /// Optional. Omitted and `null` are equivalent and mean the agent supports only `append`.
6057    #[serde_as(deserialize_as = "DefaultOnError")]
6058    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6059    #[serde(default)]
6060    pub mode: Option<SessionSystemPromptModeCapabilities>,
6061    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6062    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6063    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6064    ///
6065    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6066    #[serde_as(deserialize_as = "DefaultOnError")]
6067    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6068    #[serde(default)]
6069    #[serde(rename = "_meta")]
6070    pub meta: Option<Meta>,
6071}
6072
6073impl SessionSystemPromptCapabilities {
6074    /// Builds an empty [`SessionSystemPromptCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6075    #[must_use]
6076    pub fn new() -> Self {
6077        Self::default()
6078    }
6079
6080    /// Delivery modes the agent supports in addition to `append`.
6081    ///
6082    /// Omitted and `null` are equivalent and mean the agent supports only `append`.
6083    #[must_use]
6084    pub fn mode(mut self, mode: impl IntoOption<SessionSystemPromptModeCapabilities>) -> Self {
6085        self.mode = mode.into_option();
6086        self
6087    }
6088
6089    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6090    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6091    /// do not recognize.
6092    ///
6093    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6094    #[must_use]
6095    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6096        self.meta = meta.into_option();
6097        self
6098    }
6099}
6100
6101/// System prompt delivery modes the agent supports in addition to `append`.
6102///
6103/// `append` has no entry because every agent advertising `session.systemPrompt`
6104/// supports it.
6105#[serde_as]
6106#[skip_serializing_none]
6107#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6108#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6109#[serde(rename_all = "camelCase")]
6110#[non_exhaustive]
6111pub struct SessionSystemPromptModeCapabilities {
6112    /// Whether the agent supports `override` mode.
6113    ///
6114    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6115    /// Supplying `{}` means the agent supports replacing its built-in default
6116    /// instructions with the client-provided system prompt.
6117    #[serde_as(deserialize_as = "DefaultOnError")]
6118    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6119    #[serde(default, rename = "override")]
6120    pub override_: Option<SessionSystemPromptOverrideCapabilities>,
6121    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6122    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6123    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6124    ///
6125    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6126    #[serde_as(deserialize_as = "DefaultOnError")]
6127    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6128    #[serde(default)]
6129    #[serde(rename = "_meta")]
6130    pub meta: Option<Meta>,
6131}
6132
6133impl SessionSystemPromptModeCapabilities {
6134    /// Builds an empty [`SessionSystemPromptModeCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6135    #[must_use]
6136    pub fn new() -> Self {
6137        Self::default()
6138    }
6139
6140    /// Whether the agent supports `override` mode.
6141    ///
6142    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6143    /// Supplying `{}` means the agent supports replacing its built-in default
6144    /// instructions with the client-provided system prompt.
6145    #[must_use]
6146    pub fn override_(
6147        mut self,
6148        override_: impl IntoOption<SessionSystemPromptOverrideCapabilities>,
6149    ) -> Self {
6150        self.override_ = override_.into_option();
6151        self
6152    }
6153
6154    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6155    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6156    /// do not recognize.
6157    ///
6158    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6159    #[must_use]
6160    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6161        self.meta = meta.into_option();
6162        self
6163    }
6164}
6165
6166/// Capabilities for the `override` system prompt mode.
6167///
6168/// Supplying `{}` means the agent supports replacing its built-in default
6169/// instructions with the client-provided system prompt.
6170#[serde_as]
6171#[skip_serializing_none]
6172#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6173#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6174#[non_exhaustive]
6175pub struct SessionSystemPromptOverrideCapabilities {
6176    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6177    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6178    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6179    ///
6180    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6181    #[serde_as(deserialize_as = "DefaultOnError")]
6182    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6183    #[serde(default)]
6184    #[serde(rename = "_meta")]
6185    pub meta: Option<Meta>,
6186}
6187
6188impl SessionSystemPromptOverrideCapabilities {
6189    /// Builds an empty [`SessionSystemPromptOverrideCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6190    #[must_use]
6191    pub fn new() -> Self {
6192        Self::default()
6193    }
6194
6195    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6196    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6197    /// do not recognize.
6198    ///
6199    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6200    #[must_use]
6201    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6202        self.meta = meta.into_option();
6203        self
6204    }
6205}
6206
6207/// **UNSTABLE**
6208///
6209/// This capability is not part of the spec yet, and may be removed or changed at any point.
6210///
6211/// Capabilities for the `session/fork` method.
6212///
6213/// Supplying `{}` means the agent supports forking sessions.
6214#[cfg(feature = "unstable_session_fork")]
6215#[serde_as]
6216#[skip_serializing_none]
6217#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6218#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6219#[non_exhaustive]
6220pub struct SessionForkCapabilities {
6221    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6222    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6223    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6224    ///
6225    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6226    #[serde_as(deserialize_as = "DefaultOnError")]
6227    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6228    #[serde(default)]
6229    #[serde(rename = "_meta")]
6230    pub meta: Option<Meta>,
6231}
6232
6233#[cfg(feature = "unstable_session_fork")]
6234impl SessionForkCapabilities {
6235    /// Builds an empty [`SessionForkCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6236    #[must_use]
6237    pub fn new() -> Self {
6238        Self::default()
6239    }
6240
6241    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6242    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6243    /// do not recognize.
6244    ///
6245    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6246    #[must_use]
6247    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6248        self.meta = meta.into_option();
6249        self
6250    }
6251}
6252
6253/// **UNSTABLE**
6254///
6255/// This capability is not part of the spec yet, and may be removed or changed at any point.
6256///
6257/// Capabilities for `session/archive` and `session/unarchive`.
6258///
6259/// Supplying `{}` means the agent supports both methods, the `archived` filter on
6260/// `session/list`, and `SessionInfo.archived`.
6261#[cfg(feature = "unstable_session_archive")]
6262#[serde_as]
6263#[skip_serializing_none]
6264#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6265#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6266#[non_exhaustive]
6267pub struct SessionArchiveCapabilities {
6268    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6269    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6270    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6271    ///
6272    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6273    #[serde_as(deserialize_as = "DefaultOnError")]
6274    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6275    #[serde(default)]
6276    #[serde(rename = "_meta")]
6277    pub meta: Option<Meta>,
6278}
6279
6280#[cfg(feature = "unstable_session_archive")]
6281impl SessionArchiveCapabilities {
6282    /// Builds an empty [`SessionArchiveCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6283    #[must_use]
6284    pub fn new() -> Self {
6285        Self::default()
6286    }
6287
6288    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6289    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6290    /// do not recognize.
6291    ///
6292    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6293    #[must_use]
6294    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6295        self.meta = meta.into_option();
6296        self
6297    }
6298}
6299
6300/// **UNSTABLE**
6301///
6302/// This capability is not part of the spec yet, and may be removed or changed at any point.
6303///
6304/// Capabilities for the `session/status` method.
6305///
6306/// Supplying `{}` means the agent supports checking session status.
6307#[cfg(feature = "unstable_session_status")]
6308#[serde_as]
6309#[skip_serializing_none]
6310#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6311#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6312#[non_exhaustive]
6313pub struct SessionStatusCapabilities {
6314    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6315    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6316    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6317    ///
6318    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6319    #[serde_as(deserialize_as = "DefaultOnError")]
6320    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6321    #[serde(default)]
6322    #[serde(rename = "_meta")]
6323    pub meta: Option<Meta>,
6324}
6325
6326#[cfg(feature = "unstable_session_status")]
6327impl SessionStatusCapabilities {
6328    /// Builds an empty [`SessionStatusCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6329    #[must_use]
6330    pub fn new() -> Self {
6331        Self::default()
6332    }
6333
6334    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6335    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6336    /// do not recognize.
6337    ///
6338    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6339    #[must_use]
6340    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6341        self.meta = meta.into_option();
6342        self
6343    }
6344}
6345
6346/// **UNSTABLE**
6347///
6348/// This capability is not part of the spec yet, and may be removed or changed at any point.
6349///
6350/// Capabilities for Client-set session titles.
6351///
6352/// Supplying `{}` means the agent supports `session/set_title`. Agents report titles
6353/// through `session/list` and `session_info_update` without this capability.
6354#[cfg(feature = "unstable_session_set_title")]
6355#[serde_as]
6356#[skip_serializing_none]
6357#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6358#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6359#[non_exhaustive]
6360pub struct SessionTitleCapabilities {
6361    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6362    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6363    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6364    ///
6365    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6366    #[serde_as(deserialize_as = "DefaultOnError")]
6367    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6368    #[serde(default)]
6369    #[serde(rename = "_meta")]
6370    pub meta: Option<Meta>,
6371}
6372
6373#[cfg(feature = "unstable_session_set_title")]
6374impl SessionTitleCapabilities {
6375    /// Builds an empty [`SessionTitleCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6376    #[must_use]
6377    pub fn new() -> Self {
6378        Self::default()
6379    }
6380
6381    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6382    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6383    /// do not recognize.
6384    ///
6385    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6386    #[must_use]
6387    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6388        self.meta = meta.into_option();
6389        self
6390    }
6391}
6392
6393/// Prompt capabilities supported by the agent in `session/prompt` requests.
6394///
6395/// Baseline agent functionality requires support for `text`
6396/// and `resource_link` content blocks in prompt requests.
6397///
6398/// Other variants must be explicitly opted in to.
6399/// Capabilities for different types of content in prompt requests.
6400///
6401/// Indicates which content types beyond the baseline (text and resource links)
6402/// the agent can process.
6403///
6404/// See protocol docs: [Session Prompt Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#session-prompt-capabilities)
6405#[serde_as]
6406#[skip_serializing_none]
6407#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6408#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6409#[serde(rename_all = "camelCase")]
6410#[non_exhaustive]
6411pub struct PromptCapabilities {
6412    /// Agent supports `image` content blocks.
6413    ///
6414    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6415    /// Supplying `{}` means the agent supports image content in prompts.
6416    #[serde_as(deserialize_as = "DefaultOnError")]
6417    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6418    #[serde(default)]
6419    pub image: Option<PromptImageCapabilities>,
6420    /// Agent supports `audio` content blocks.
6421    ///
6422    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6423    /// Supplying `{}` means the agent supports audio content in prompts.
6424    #[serde_as(deserialize_as = "DefaultOnError")]
6425    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6426    #[serde(default)]
6427    pub audio: Option<PromptAudioCapabilities>,
6428    /// Agent supports embedded context in `session/prompt` requests.
6429    ///
6430    /// When enabled, the Client is allowed to include `resource` content blocks
6431    /// in prompt requests for pieces of context that are referenced in the message.
6432    ///
6433    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6434    /// Supplying `{}` means the agent supports embedded context in prompts.
6435    #[serde_as(deserialize_as = "DefaultOnError")]
6436    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6437    #[serde(default)]
6438    pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
6439    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6440    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6441    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6442    ///
6443    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6444    #[serde_as(deserialize_as = "DefaultOnError")]
6445    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6446    #[serde(default)]
6447    #[serde(rename = "_meta")]
6448    pub meta: Option<Meta>,
6449}
6450
6451impl PromptCapabilities {
6452    /// Builds an empty [`PromptCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6453    #[must_use]
6454    pub fn new() -> Self {
6455        Self::default()
6456    }
6457
6458    /// Agent supports `image` content blocks.
6459    ///
6460    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6461    /// Supplying `{}` means the agent supports image content in prompts.
6462    #[must_use]
6463    pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
6464        self.image = image.into_option();
6465        self
6466    }
6467
6468    /// Agent supports `audio` content blocks.
6469    ///
6470    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6471    /// Supplying `{}` means the agent supports audio content in prompts.
6472    #[must_use]
6473    pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
6474        self.audio = audio.into_option();
6475        self
6476    }
6477
6478    /// Agent supports embedded context in `session/prompt` requests.
6479    ///
6480    /// When enabled, the Client is allowed to include `resource` content blocks
6481    /// in prompt requests for pieces of context that are referenced in the message.
6482    ///
6483    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6484    /// Supplying `{}` means the agent supports embedded context in prompts.
6485    #[must_use]
6486    pub fn embedded_context(
6487        mut self,
6488        embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
6489    ) -> Self {
6490        self.embedded_context = embedded_context.into_option();
6491        self
6492    }
6493
6494    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6495    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6496    /// do not recognize.
6497    ///
6498    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6499    #[must_use]
6500    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6501        self.meta = meta.into_option();
6502        self
6503    }
6504}
6505
6506/// Capabilities for image content in prompt requests.
6507///
6508/// Supplying `{}` means the agent supports image content in prompts.
6509#[serde_as]
6510#[skip_serializing_none]
6511#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6512#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6513#[non_exhaustive]
6514pub struct PromptImageCapabilities {
6515    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6516    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6517    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6518    ///
6519    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6520    #[serde_as(deserialize_as = "DefaultOnError")]
6521    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6522    #[serde(default)]
6523    #[serde(rename = "_meta")]
6524    pub meta: Option<Meta>,
6525}
6526
6527impl PromptImageCapabilities {
6528    /// Builds an empty [`PromptImageCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6529    #[must_use]
6530    pub fn new() -> Self {
6531        Self::default()
6532    }
6533
6534    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6535    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6536    /// do not recognize.
6537    ///
6538    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6539    #[must_use]
6540    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6541        self.meta = meta.into_option();
6542        self
6543    }
6544}
6545
6546/// Capabilities for audio content in prompt requests.
6547///
6548/// Supplying `{}` means the agent supports audio content in prompts.
6549#[serde_as]
6550#[skip_serializing_none]
6551#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6552#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6553#[non_exhaustive]
6554pub struct PromptAudioCapabilities {
6555    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6556    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6557    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6558    ///
6559    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6560    #[serde_as(deserialize_as = "DefaultOnError")]
6561    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6562    #[serde(default)]
6563    #[serde(rename = "_meta")]
6564    pub meta: Option<Meta>,
6565}
6566
6567impl PromptAudioCapabilities {
6568    /// Builds an empty [`PromptAudioCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6569    #[must_use]
6570    pub fn new() -> Self {
6571        Self::default()
6572    }
6573
6574    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6575    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6576    /// do not recognize.
6577    ///
6578    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6579    #[must_use]
6580    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6581        self.meta = meta.into_option();
6582        self
6583    }
6584}
6585
6586/// Capabilities for embedded context in prompt requests.
6587///
6588/// Supplying `{}` means the agent supports embedded context in prompts.
6589#[serde_as]
6590#[skip_serializing_none]
6591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6592#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6593#[non_exhaustive]
6594pub struct PromptEmbeddedContextCapabilities {
6595    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6596    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6597    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6598    ///
6599    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6600    #[serde_as(deserialize_as = "DefaultOnError")]
6601    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6602    #[serde(default)]
6603    #[serde(rename = "_meta")]
6604    pub meta: Option<Meta>,
6605}
6606
6607impl PromptEmbeddedContextCapabilities {
6608    /// Builds an empty [`PromptEmbeddedContextCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6609    #[must_use]
6610    pub fn new() -> Self {
6611        Self::default()
6612    }
6613
6614    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6615    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6616    /// do not recognize.
6617    ///
6618    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6619    #[must_use]
6620    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6621        self.meta = meta.into_option();
6622        self
6623    }
6624}
6625
6626/// MCP capabilities supported by the agent for session lifecycle requests.
6627#[serde_as]
6628#[skip_serializing_none]
6629#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6630#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6631#[serde(rename_all = "camelCase")]
6632#[non_exhaustive]
6633pub struct McpCapabilities {
6634    /// Agent supports `stdio` MCP servers.
6635    ///
6636    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6637    /// Supplying `{}` means the agent supports stdio MCP server transports.
6638    #[serde_as(deserialize_as = "DefaultOnError")]
6639    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6640    #[serde(default)]
6641    pub stdio: Option<McpStdioCapabilities>,
6642    /// Agent supports `http` MCP servers.
6643    ///
6644    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6645    /// Supplying `{}` means the agent supports HTTP MCP server transports.
6646    #[serde_as(deserialize_as = "DefaultOnError")]
6647    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6648    #[serde(default)]
6649    pub http: Option<McpHttpCapabilities>,
6650    /// **UNSTABLE**
6651    ///
6652    /// This capability is not part of the spec yet, and may be removed or changed at any point.
6653    ///
6654    /// Agent supports `acp` MCP servers.
6655    ///
6656    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6657    /// Supplying `{}` means the agent supports ACP MCP server transports.
6658    #[cfg(feature = "unstable_mcp_over_acp")]
6659    #[serde_as(deserialize_as = "DefaultOnError")]
6660    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6661    #[serde(default)]
6662    pub acp: Option<McpAcpCapabilities>,
6663    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6664    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6665    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6666    ///
6667    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6668    #[serde_as(deserialize_as = "DefaultOnError")]
6669    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6670    #[serde(default)]
6671    #[serde(rename = "_meta")]
6672    pub meta: Option<Meta>,
6673}
6674
6675impl McpCapabilities {
6676    /// Builds an empty [`McpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6677    #[must_use]
6678    pub fn new() -> Self {
6679        Self::default()
6680    }
6681
6682    /// Agent supports `stdio` MCP servers.
6683    ///
6684    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6685    /// Supplying `{}` means the agent supports stdio MCP server transports.
6686    #[must_use]
6687    pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
6688        self.stdio = stdio.into_option();
6689        self
6690    }
6691
6692    /// Agent supports `http` MCP servers.
6693    ///
6694    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6695    /// Supplying `{}` means the agent supports HTTP MCP server transports.
6696    #[must_use]
6697    pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
6698        self.http = http.into_option();
6699        self
6700    }
6701
6702    /// **UNSTABLE**
6703    ///
6704    /// This capability is not part of the spec yet, and may be removed or changed at any point.
6705    ///
6706    /// Agent supports `acp` MCP servers.
6707    #[cfg(feature = "unstable_mcp_over_acp")]
6708    ///
6709    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6710    /// Supplying `{}` means the agent supports ACP MCP server transports.
6711    #[must_use]
6712    pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
6713        self.acp = acp.into_option();
6714        self
6715    }
6716
6717    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6718    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6719    /// do not recognize.
6720    ///
6721    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6722    #[must_use]
6723    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6724        self.meta = meta.into_option();
6725        self
6726    }
6727}
6728
6729/// Capabilities for stdio MCP server transports.
6730///
6731/// Supplying `{}` means the agent supports stdio MCP server transports.
6732#[serde_as]
6733#[skip_serializing_none]
6734#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6735#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6736#[non_exhaustive]
6737pub struct McpStdioCapabilities {
6738    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6739    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6740    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6741    ///
6742    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6743    #[serde_as(deserialize_as = "DefaultOnError")]
6744    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6745    #[serde(default)]
6746    #[serde(rename = "_meta")]
6747    pub meta: Option<Meta>,
6748}
6749
6750impl McpStdioCapabilities {
6751    /// Builds an empty [`McpStdioCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6752    #[must_use]
6753    pub fn new() -> Self {
6754        Self::default()
6755    }
6756
6757    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6758    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6759    /// do not recognize.
6760    ///
6761    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6762    #[must_use]
6763    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6764        self.meta = meta.into_option();
6765        self
6766    }
6767}
6768
6769/// Capabilities for HTTP MCP server transports.
6770///
6771/// Supplying `{}` means the agent supports HTTP MCP server transports.
6772#[serde_as]
6773#[skip_serializing_none]
6774#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6775#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6776#[non_exhaustive]
6777pub struct McpHttpCapabilities {
6778    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6779    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6780    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6781    ///
6782    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6783    #[serde_as(deserialize_as = "DefaultOnError")]
6784    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6785    #[serde(default)]
6786    #[serde(rename = "_meta")]
6787    pub meta: Option<Meta>,
6788}
6789
6790impl McpHttpCapabilities {
6791    /// Builds an empty [`McpHttpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6792    #[must_use]
6793    pub fn new() -> Self {
6794        Self::default()
6795    }
6796
6797    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6798    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6799    /// do not recognize.
6800    ///
6801    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6802    #[must_use]
6803    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6804        self.meta = meta.into_option();
6805        self
6806    }
6807}
6808
6809/// **UNSTABLE**
6810///
6811/// This capability is not part of the spec yet, and may be removed or changed at any point.
6812///
6813/// Capabilities for ACP MCP server transports.
6814///
6815/// Supplying `{}` means the agent supports ACP MCP server transports.
6816#[cfg(feature = "unstable_mcp_over_acp")]
6817#[serde_as]
6818#[skip_serializing_none]
6819#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6820#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6821#[non_exhaustive]
6822pub struct McpAcpCapabilities {
6823    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6824    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6825    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6826    ///
6827    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6828    #[serde_as(deserialize_as = "DefaultOnError")]
6829    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6830    #[serde(default)]
6831    #[serde(rename = "_meta")]
6832    pub meta: Option<Meta>,
6833}
6834
6835#[cfg(feature = "unstable_mcp_over_acp")]
6836impl McpAcpCapabilities {
6837    /// Builds an empty [`McpAcpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6838    #[must_use]
6839    pub fn new() -> Self {
6840        Self::default()
6841    }
6842
6843    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6844    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6845    /// do not recognize.
6846    ///
6847    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6848    #[must_use]
6849    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6850        self.meta = meta.into_option();
6851        self
6852    }
6853}
6854
6855/// Notification to cancel a session's foreground work and its prompts not yet inserted.
6856///
6857/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#cancellation)
6858#[serde_as]
6859#[skip_serializing_none]
6860#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6861#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6862#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
6863#[serde(rename_all = "camelCase")]
6864#[non_exhaustive]
6865pub struct CancelSessionNotification {
6866    /// The ID of the session to cancel operations for.
6867    pub session_id: SessionId,
6868    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6869    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6870    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6871    ///
6872    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6873    #[serde_as(deserialize_as = "DefaultOnError")]
6874    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6875    #[serde(default)]
6876    #[serde(rename = "_meta")]
6877    pub meta: Option<Meta>,
6878}
6879
6880impl CancelSessionNotification {
6881    /// Builds [`CancelSessionNotification`] with the required notification fields set. Optional fields start unset or empty.
6882    #[must_use]
6883    pub fn new(session_id: impl Into<SessionId>) -> Self {
6884        Self {
6885            session_id: session_id.into(),
6886            meta: None,
6887        }
6888    }
6889
6890    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6891    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6892    /// do not recognize.
6893    ///
6894    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6895    #[must_use]
6896    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6897        self.meta = meta.into_option();
6898        self
6899    }
6900}
6901
6902// Method schema
6903
6904/// Names of all methods that agents handle.
6905///
6906/// Provides a centralized definition of method names used in the protocol.
6907#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6908#[non_exhaustive]
6909pub struct AgentMethodNames {
6910    /// Method for initializing the connection.
6911    pub initialize: &'static str,
6912    /// Method for authenticating with the agent.
6913    pub auth_login: &'static str,
6914    /// Method for listing configurable providers.
6915    #[cfg(feature = "unstable_llm_providers")]
6916    pub providers_list: &'static str,
6917    /// Method for setting provider configuration.
6918    #[cfg(feature = "unstable_llm_providers")]
6919    pub providers_set: &'static str,
6920    /// Method for disabling a provider.
6921    #[cfg(feature = "unstable_llm_providers")]
6922    pub providers_disable: &'static str,
6923    /// Method for creating a new session.
6924    pub session_new: &'static str,
6925    /// Method for setting a configuration option for a session.
6926    pub session_set_config_option: &'static str,
6927    /// Method for sending a prompt to the agent.
6928    pub session_prompt: &'static str,
6929    /// Notification for cancelling operations.
6930    pub session_cancel: &'static str,
6931    /// Method for exchanging MCP-over-ACP messages.
6932    #[cfg(feature = "unstable_mcp_over_acp")]
6933    pub mcp_message: &'static str,
6934    /// Method for listing existing sessions.
6935    pub session_list: &'static str,
6936    /// Method for deleting an existing session.
6937    pub session_delete: &'static str,
6938    /// Method for forking an existing session.
6939    #[cfg(feature = "unstable_session_fork")]
6940    pub session_fork: &'static str,
6941    /// Method for archiving a session.
6942    #[cfg(feature = "unstable_session_archive")]
6943    pub session_archive: &'static str,
6944    /// Method for unarchiving a session.
6945    #[cfg(feature = "unstable_session_archive")]
6946    pub session_unarchive: &'static str,
6947    /// Method for checking the status of a session.
6948    #[cfg(feature = "unstable_session_status")]
6949    pub session_status: &'static str,
6950    /// Method for setting the title of a session.
6951    #[cfg(feature = "unstable_session_set_title")]
6952    pub session_set_title: &'static str,
6953    /// Method for resuming an existing session.
6954    pub session_resume: &'static str,
6955    /// Method for closing an active session.
6956    pub session_close: &'static str,
6957    /// Method for cancelling one tool call.
6958    pub tool_call_cancel: &'static str,
6959    /// Method for cancelling one terminal's process.
6960    pub terminal_cancel: &'static str,
6961    /// Method for logging out of an authenticated session.
6962    pub auth_logout: &'static str,
6963    /// Method for querying the agent's authentication state.
6964    #[cfg(feature = "unstable_auth_status")]
6965    pub auth_status: &'static str,
6966}
6967
6968/// Constant containing all agent method names.
6969pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
6970    initialize: INITIALIZE_METHOD_NAME,
6971    auth_login: AUTH_LOGIN_METHOD_NAME,
6972    #[cfg(feature = "unstable_llm_providers")]
6973    providers_list: PROVIDERS_LIST_METHOD_NAME,
6974    #[cfg(feature = "unstable_llm_providers")]
6975    providers_set: PROVIDERS_SET_METHOD_NAME,
6976    #[cfg(feature = "unstable_llm_providers")]
6977    providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
6978    session_new: SESSION_NEW_METHOD_NAME,
6979    session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
6980    session_prompt: SESSION_PROMPT_METHOD_NAME,
6981    session_cancel: SESSION_CANCEL_METHOD_NAME,
6982    #[cfg(feature = "unstable_mcp_over_acp")]
6983    mcp_message: MCP_MESSAGE_METHOD_NAME,
6984    session_list: SESSION_LIST_METHOD_NAME,
6985    session_delete: SESSION_DELETE_METHOD_NAME,
6986    #[cfg(feature = "unstable_session_fork")]
6987    session_fork: SESSION_FORK_METHOD_NAME,
6988    #[cfg(feature = "unstable_session_archive")]
6989    session_archive: SESSION_ARCHIVE_METHOD_NAME,
6990    #[cfg(feature = "unstable_session_archive")]
6991    session_unarchive: SESSION_UNARCHIVE_METHOD_NAME,
6992    #[cfg(feature = "unstable_session_status")]
6993    session_status: SESSION_STATUS_METHOD_NAME,
6994    #[cfg(feature = "unstable_session_set_title")]
6995    session_set_title: SESSION_SET_TITLE_METHOD_NAME,
6996    session_resume: SESSION_RESUME_METHOD_NAME,
6997    session_close: SESSION_CLOSE_METHOD_NAME,
6998    tool_call_cancel: TOOL_CALL_CANCEL_METHOD_NAME,
6999    terminal_cancel: TERMINAL_CANCEL_METHOD_NAME,
7000    auth_logout: AUTH_LOGOUT_METHOD_NAME,
7001    #[cfg(feature = "unstable_auth_status")]
7002    auth_status: AUTH_STATUS_METHOD_NAME,
7003};
7004
7005/// Method name for the initialize request.
7006pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
7007/// Method name for the `auth/login` request.
7008pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
7009/// Method name for listing configurable providers.
7010#[cfg(feature = "unstable_llm_providers")]
7011pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
7012/// Method name for setting provider configuration.
7013#[cfg(feature = "unstable_llm_providers")]
7014pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
7015/// Method name for disabling a provider.
7016#[cfg(feature = "unstable_llm_providers")]
7017pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
7018/// Method name for creating a new session.
7019pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
7020/// Method name for setting a configuration option for a session.
7021pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
7022/// Method name for sending a prompt.
7023pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
7024/// Method name for the cancel notification.
7025pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
7026/// Method name for listing existing sessions.
7027pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
7028/// Method name for deleting an existing session.
7029pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
7030/// Method name for forking an existing session.
7031#[cfg(feature = "unstable_session_fork")]
7032pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
7033/// Method name for archiving a session.
7034#[cfg(feature = "unstable_session_archive")]
7035pub(crate) const SESSION_ARCHIVE_METHOD_NAME: &str = "session/archive";
7036/// Method name for unarchiving a session.
7037#[cfg(feature = "unstable_session_archive")]
7038pub(crate) const SESSION_UNARCHIVE_METHOD_NAME: &str = "session/unarchive";
7039/// Method name for checking the status of a session.
7040#[cfg(feature = "unstable_session_status")]
7041pub(crate) const SESSION_STATUS_METHOD_NAME: &str = "session/status";
7042/// Method name for setting the title of a session.
7043#[cfg(feature = "unstable_session_set_title")]
7044pub(crate) const SESSION_SET_TITLE_METHOD_NAME: &str = "session/set_title";
7045/// Method name for resuming an existing session.
7046pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
7047/// Method name for closing an active session.
7048pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
7049/// Method name for cancelling one tool call.
7050pub(crate) const TOOL_CALL_CANCEL_METHOD_NAME: &str = "tool_call/cancel";
7051/// Method name for cancelling one terminal's process.
7052pub(crate) const TERMINAL_CANCEL_METHOD_NAME: &str = "terminal/cancel";
7053/// Method name for the `auth/logout` request.
7054pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
7055/// Method name for the `auth/status` request.
7056#[cfg(feature = "unstable_auth_status")]
7057pub(crate) const AUTH_STATUS_METHOD_NAME: &str = "auth/status";
7058
7059/// All possible requests that a client can send to an agent.
7060///
7061/// This enum is used internally for routing RPC requests. You typically won't need
7062/// to use this directly.
7063///
7064/// This enum encompasses all method calls from client to agent.
7065#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7066#[derive(Clone, Debug, Serialize, Deserialize)]
7067#[serde(untagged)]
7068#[cfg_attr(feature = "schemars", schemars(inline))]
7069#[non_exhaustive]
7070pub enum ClientRequest {
7071    /// Establishes the connection with a client and negotiates protocol capabilities.
7072    ///
7073    /// This method is called at the beginning of the connection, before any other request, to:
7074    /// - Negotiate the protocol version to use
7075    /// - Exchange capability information between client and agent
7076    /// - Determine available authentication methods
7077    ///
7078    /// The Client may repeat it before its first other request. The latest successful
7079    /// `initialize` is authoritative.
7080    ///
7081    /// The agent should respond with its supported protocol version and capabilities.
7082    ///
7083    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
7084    InitializeRequest(Box<InitializeRequest>),
7085    /// Authenticates the client using the specified authentication method.
7086    ///
7087    /// Agents MUST support this method when their `initialize` response included
7088    /// a non-empty `authMethods` list. Clients MUST call this method only
7089    /// with a method whose type defines a protocol-driven login flow, and MUST NOT
7090    /// call it when `authMethods` was omitted or empty.
7091    ///
7092    /// Called when the agent requires authentication before allowing session creation.
7093    /// The client provides the authentication method ID that was advertised during initialization.
7094    ///
7095    /// After successful authentication, the client can proceed to create sessions with
7096    /// `session/new` without receiving an `auth_required` (`-32000`) error.
7097    ///
7098    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
7099    LoginAuthRequest(Box<LoginAuthRequest>),
7100    /// **UNSTABLE**
7101    ///
7102    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7103    ///
7104    /// Lists providers that can be configured by the client.
7105    #[cfg(feature = "unstable_llm_providers")]
7106    ListProvidersRequest(Box<ListProvidersRequest>),
7107    /// **UNSTABLE**
7108    ///
7109    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7110    ///
7111    /// Replaces the configuration for a provider.
7112    #[cfg(feature = "unstable_llm_providers")]
7113    SetProviderRequest(Box<SetProviderRequest>),
7114    /// **UNSTABLE**
7115    ///
7116    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7117    ///
7118    /// Disables a provider.
7119    #[cfg(feature = "unstable_llm_providers")]
7120    DisableProviderRequest(Box<DisableProviderRequest>),
7121    /// Logs out of the current authenticated state.
7122    ///
7123    /// Agents MUST support this method when their `initialize` response included
7124    /// a non-empty `authMethods` list. Clients MUST NOT call this method
7125    /// when `authMethods` was omitted or empty.
7126    ///
7127    /// After a successful logout, authentication-gated requests require the
7128    /// client to complete an advertised authentication flow again. There is no
7129    /// guarantee about the behavior of already running sessions.
7130    LogoutAuthRequest(Box<LogoutAuthRequest>),
7131    /// **UNSTABLE**
7132    ///
7133    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7134    ///
7135    /// Queries the agent's current authentication state.
7136    ///
7137    /// This method is only available if the agent advertises the `auth.status` capability.
7138    /// It MUST NOT change any agent state, and agents MUST NOT reject it with `auth_required`.
7139    #[cfg(feature = "unstable_auth_status")]
7140    AuthStatusRequest(Box<AuthStatusRequest>),
7141    /// Creates a new conversation session with the agent.
7142    ///
7143    /// Sessions represent independent conversation contexts with their own history and state.
7144    ///
7145    /// The agent should:
7146    /// - Create a new session context
7147    /// - Connect to any specified MCP servers
7148    /// - Return a unique session ID for future requests
7149    ///
7150    /// May return an `auth_required` (`-32000`) error if the agent requires authentication.
7151    ///
7152    /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/v2/session-setup)
7153    NewSessionRequest(Box<NewSessionRequest>),
7154    /// Lists existing sessions known to the agent.
7155    ///
7156    /// The agent should return metadata about sessions with optional filtering and pagination support.
7157    ListSessionsRequest(Box<ListSessionsRequest>),
7158    /// Deletes an existing session from `session/list`.
7159    ///
7160    /// This method is only available if the agent advertises the `session.delete` capability.
7161    DeleteSessionRequest(Box<DeleteSessionRequest>),
7162    #[cfg(feature = "unstable_session_fork")]
7163    /// **UNSTABLE**
7164    ///
7165    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7166    ///
7167    /// Forks an existing session to create a new independent session.
7168    ///
7169    /// This method is only available if the agent advertises the `session.fork` capability.
7170    ///
7171    /// The agent should create a new session with the same conversation context as the
7172    /// original, allowing operations like generating summaries without affecting the
7173    /// original session's history.
7174    ///
7175    /// As with `session/new`, the forked session is active once the agent responds,
7176    /// set up with the request's `cwd`, `additionalDirectories`, and `mcpServers`.
7177    /// The client can prompt it without calling `session/resume` first.
7178    ForkSessionRequest(Box<ForkSessionRequest>),
7179    #[cfg(feature = "unstable_session_archive")]
7180    /// **UNSTABLE**
7181    ///
7182    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7183    ///
7184    /// Archives a session, hiding it from default `session/list` results.
7185    ///
7186    /// This method is only available if the agent advertises the `session.archive` capability.
7187    ArchiveSessionRequest(Box<ArchiveSessionRequest>),
7188    #[cfg(feature = "unstable_session_archive")]
7189    /// **UNSTABLE**
7190    ///
7191    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7192    ///
7193    /// Unarchives a session, returning it to default `session/list` results.
7194    ///
7195    /// This method is only available if the agent advertises the `session.archive` capability.
7196    UnarchiveSessionRequest(Box<UnarchiveSessionRequest>),
7197    #[cfg(feature = "unstable_session_status")]
7198    /// **UNSTABLE**
7199    ///
7200    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7201    ///
7202    /// Checks whether the Agent has a session in memory, without changing it.
7203    ///
7204    /// This method is only available if the agent advertises the `session.status` capability.
7205    SessionStatusRequest(Box<SessionStatusRequest>),
7206    #[cfg(feature = "unstable_session_set_title")]
7207    /// **UNSTABLE**
7208    ///
7209    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7210    ///
7211    /// Sets the title of a session.
7212    ///
7213    /// This method is only available if the agent advertises the `session.title` capability.
7214    ///
7215    /// The session does not need to be active on the connection. The agent stores the
7216    /// title and returns the stored title, which may be normalized.
7217    SetSessionTitleRequest(Box<SetSessionTitleRequest>),
7218    /// Resumes an existing session.
7219    ///
7220    /// The agent should resume the session context, allowing the conversation
7221    /// to continue. If `replayFrom` is set, the agent should replay
7222    /// retained conversation history before responding.
7223    ResumeSessionRequest(Box<ResumeSessionRequest>),
7224    /// Closes an active session and frees up any resources associated with it.
7225    ///
7226    /// The Agent MUST cancel any ongoing work as if `session/cancel` was called, stop the
7227    /// session's background work, and then free the resources associated with the session.
7228    CloseSessionRequest(Box<CloseSessionRequest>),
7229    /// Cancels one tool call without cancelling the rest of the session's work.
7230    ///
7231    /// This method is only available while the tool call advertises `capabilities.cancel`.
7232    /// The response means the Agent accepted the cancellation. A final `tool_call_update`
7233    /// confirms it.
7234    CancelToolCallRequest(Box<CancelToolCallRequest>),
7235    /// Stops one terminal's process without cancelling the rest of the session's work.
7236    ///
7237    /// This method is only available while the terminal advertises `capabilities.cancel`.
7238    /// The response means the Agent accepted the cancellation. A `terminal_update` with
7239    /// `exitStatus` confirms it.
7240    CancelTerminalRequest(Box<CancelTerminalRequest>),
7241    /// Sets the current value for a session configuration option.
7242    SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
7243    /// Processes a user prompt within a session.
7244    ///
7245    /// Acceptance means adding a logical user message with an ID and a position
7246    /// relative to other messages in the ACP conversation, not receipt or queueing.
7247    /// The Agent MUST return the inserted message's ID without waiting for
7248    /// processing to finish.
7249    ///
7250    /// The Agent reports the user message with the same ID through `session/update`.
7251    /// This notification may arrive before or after the response, but neither may
7252    /// precede insertion. Processing state, output, tool calls, and completion are
7253    /// also reported through session updates.
7254    ///
7255    /// An error response means the user message was not inserted. Once it is
7256    /// inserted, the Agent MUST NOT answer with an error, even if the work fails
7257    /// before the response is sent. It ends that work with the `error` stop reason.
7258    ///
7259    /// A rejected submission produces no user-message updates. Foreground state
7260    /// follows its normal lifecycle independently of the request outcome:
7261    /// `running` may precede insertion, and rejection alone does not imply `idle`.
7262    ///
7263    /// See protocol docs: [Prompt Lifecycle](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle)
7264    PromptRequest(Box<PromptRequest>),
7265    /// Handles extension method requests from the client.
7266    ///
7267    /// Extension methods provide a way to add custom functionality while maintaining
7268    /// protocol compatibility.
7269    ///
7270    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
7271    ExtMethodRequest(Box<ExtRequest>),
7272}
7273
7274impl ClientRequest {
7275    /// Returns the corresponding method name of the request.
7276    #[must_use]
7277    pub fn method(&self) -> &str {
7278        match self {
7279            Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
7280            Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
7281            #[cfg(feature = "unstable_llm_providers")]
7282            Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
7283            #[cfg(feature = "unstable_llm_providers")]
7284            Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
7285            #[cfg(feature = "unstable_llm_providers")]
7286            Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
7287            Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
7288            #[cfg(feature = "unstable_auth_status")]
7289            Self::AuthStatusRequest(_) => AGENT_METHOD_NAMES.auth_status,
7290            Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
7291            Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
7292            Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
7293            #[cfg(feature = "unstable_session_fork")]
7294            Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
7295            #[cfg(feature = "unstable_session_archive")]
7296            Self::ArchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_archive,
7297            #[cfg(feature = "unstable_session_archive")]
7298            Self::UnarchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_unarchive,
7299            #[cfg(feature = "unstable_session_status")]
7300            Self::SessionStatusRequest(_) => AGENT_METHOD_NAMES.session_status,
7301            #[cfg(feature = "unstable_session_set_title")]
7302            Self::SetSessionTitleRequest(_) => AGENT_METHOD_NAMES.session_set_title,
7303            Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
7304            Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
7305            Self::CancelToolCallRequest(_) => AGENT_METHOD_NAMES.tool_call_cancel,
7306            Self::CancelTerminalRequest(_) => AGENT_METHOD_NAMES.terminal_cancel,
7307            Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
7308            Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
7309            Self::ExtMethodRequest(ext_request) => &ext_request.method,
7310        }
7311    }
7312}
7313
7314/// All possible responses that an agent can send to a client.
7315///
7316/// This enum is used internally for routing RPC responses. You typically won't need
7317/// to use this directly - the responses are handled automatically by the connection.
7318///
7319/// These are responses to the corresponding `ClientRequest` variants.
7320#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7321#[derive(Clone, Debug, Serialize, Deserialize)]
7322#[serde(untagged)]
7323#[cfg_attr(feature = "schemars", schemars(inline))]
7324#[non_exhaustive]
7325pub enum AgentResponse {
7326    /// Successful result returned for a `initialize` request.
7327    InitializeResponse(Box<InitializeResponse>),
7328    /// Successful result returned for an `auth/login` request.
7329    LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
7330    /// Successful result returned for a `providers/list` request.
7331    #[cfg(feature = "unstable_llm_providers")]
7332    ListProvidersResponse(Box<ListProvidersResponse>),
7333    /// Successful result returned for a `providers/set` request.
7334    #[cfg(feature = "unstable_llm_providers")]
7335    SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
7336    /// Successful result returned for a `providers/disable` request.
7337    #[cfg(feature = "unstable_llm_providers")]
7338    DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
7339    /// Successful result returned for an `auth/logout` request.
7340    LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
7341    /// Successful result returned for an `auth/status` request.
7342    #[cfg(feature = "unstable_auth_status")]
7343    AuthStatusResponse(Box<AuthStatusResponse>),
7344    /// Successful result returned for a `session/new` request.
7345    NewSessionResponse(Box<NewSessionResponse>),
7346    /// Successful result returned for a `session/list` request.
7347    ListSessionsResponse(Box<ListSessionsResponse>),
7348    /// Successful result returned for a `session/delete` request.
7349    DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
7350    /// Successful result returned for a `session/fork` request.
7351    #[cfg(feature = "unstable_session_fork")]
7352    ForkSessionResponse(Box<ForkSessionResponse>),
7353    /// Successful result returned for a `session/archive` request.
7354    #[cfg(feature = "unstable_session_archive")]
7355    ArchiveSessionResponse(#[serde(default)] Box<ArchiveSessionResponse>),
7356    /// Successful result returned for a `session/unarchive` request.
7357    #[cfg(feature = "unstable_session_archive")]
7358    UnarchiveSessionResponse(#[serde(default)] Box<UnarchiveSessionResponse>),
7359    /// Successful result returned for a `session/status` request.
7360    #[cfg(feature = "unstable_session_status")]
7361    SessionStatusResponse(Box<SessionStatusResponse>),
7362    /// Successful result returned for a `session/set_title` request.
7363    #[cfg(feature = "unstable_session_set_title")]
7364    SetSessionTitleResponse(Box<SetSessionTitleResponse>),
7365    /// Successful result returned for a `session/resume` request.
7366    ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
7367    /// Successful result returned for a `session/close` request.
7368    CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
7369    /// Successful result returned for a `tool_call/cancel` request.
7370    CancelToolCallResponse(#[serde(default)] Box<CancelToolCallResponse>),
7371    /// Successful result returned for a `terminal/cancel` request.
7372    CancelTerminalResponse(#[serde(default)] Box<CancelTerminalResponse>),
7373    /// Successful result returned for a `session/set_config_option` request.
7374    SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
7375    /// Successful result returned for a `session/prompt` request.
7376    PromptResponse(Box<PromptResponse>),
7377    /// Successful result returned by an extension method outside the core ACP method set.
7378    ExtMethodResponse(Box<ExtResponse>),
7379}
7380
7381/// All possible notifications that a client can send to an agent.
7382///
7383/// This enum is used internally for routing RPC notifications. You typically won't need
7384/// to use this directly.
7385///
7386/// Notifications do not expect a response.
7387#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7388#[derive(Clone, Debug, Serialize, Deserialize)]
7389#[serde(untagged)]
7390#[cfg_attr(feature = "schemars", schemars(inline))]
7391#[non_exhaustive]
7392pub enum ClientNotification {
7393    /// Cancels a session's foreground work and its prompts not yet inserted.
7394    ///
7395    /// Work in the latest `background` list is not covered and keeps running. The
7396    /// Agent MAY stop background activity it does not list.
7397    ///
7398    /// Upon receiving this notification, the Agent SHOULD stop all language model
7399    /// requests and all tool call invocations of the cancelled foreground work as soon
7400    /// as possible.
7401    ///
7402    /// The Agent MUST stop waiting for pending `session/request_permission` requests of
7403    /// the cancelled work and treat them as not approved. It SHOULD cancel each of them
7404    /// with `$/cancel_request`.
7405    ///
7406    /// After all ongoing operations have been successfully aborted and pending
7407    /// updates have been sent, the Agent MUST send an idle `state_update` session
7408    /// update with the `cancelled` stop reason if cancellation ended active
7409    /// foreground work. No additional completion update is required if no
7410    /// foreground work was active.
7411    ///
7412    /// After cancellation completes, the Agent SHOULD ignore late answers rather
7413    /// than use them to resume the cancelled work.
7414    ///
7415    /// Agents MUST catch errors caused by aborting cancelled work and report the
7416    /// `cancelled` stop reason, not `error`.
7417    ///
7418    /// The Agent MAY send content or tool call updates after receiving
7419    /// `session/cancel`, but MUST send them before the idle `state_update` that
7420    /// reports cancellation.
7421    ///
7422    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#cancellation)
7423    CancelSessionNotification(Box<CancelSessionNotification>),
7424    /// **UNSTABLE**
7425    ///
7426    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7427    ///
7428    /// Sends an MCP-over-ACP notification.
7429    #[cfg(feature = "unstable_mcp_over_acp")]
7430    MessageMcpNotification(Box<MessageMcpNotification>),
7431    /// Handles extension notifications from the client.
7432    ///
7433    /// Extension notifications provide a way to send one-way messages for custom functionality
7434    /// while maintaining protocol compatibility.
7435    ///
7436    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
7437    ExtNotification(Box<ExtNotification>),
7438}
7439
7440impl ClientNotification {
7441    /// Returns the corresponding method name of the notification.
7442    #[must_use]
7443    pub fn method(&self) -> &str {
7444        match self {
7445            Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
7446            #[cfg(feature = "unstable_mcp_over_acp")]
7447            Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
7448            Self::ExtNotification(ext_notification) => &ext_notification.method,
7449        }
7450    }
7451}
7452
7453#[cfg(test)]
7454mod test_serialization {
7455    use std::path::PathBuf;
7456
7457    use super::*;
7458    use serde_json::json;
7459
7460    fn test_meta() -> Meta {
7461        json!({ "source": "test" }).as_object().unwrap().clone()
7462    }
7463
7464    #[test]
7465    fn list_sessions_cursor_and_session_directories_are_strict() {
7466        for cursor in [json!(null), json!("next")] {
7467            let response = json!({ "sessions": [], "nextCursor": cursor });
7468            serde_json::from_value::<ListSessionsResponse>(response).unwrap();
7469        }
7470        let malformed = json!({ "sessions": [], "nextCursor": 42 });
7471        assert!(serde_json::from_value::<ListSessionsResponse>(malformed).is_err());
7472
7473        let session = |dirs: serde_json::Value| json!({ "sessionId": "s", "cwd": "/w", "additionalDirectories": dirs });
7474        let info: SessionInfo = serde_json::from_value(session(json!(null))).unwrap();
7475        assert_eq!(info.additional_directories, Vec::<AbsolutePath>::new());
7476        for bad in [json!(["/a", 42]), json!("/a")] {
7477            assert!(serde_json::from_value::<SessionInfo>(session(bad)).is_err());
7478        }
7479        // A session with malformed directories is skipped, not truncated.
7480        let response = json!({ "sessions": [session(json!(["/a", 42]))] });
7481        let response: ListSessionsResponse = serde_json::from_value(response).unwrap();
7482        assert_eq!(response.sessions, Vec::<SessionInfo>::new());
7483    }
7484
7485    #[test]
7486    fn other_fallbacks_preserve_optional_fields_verbatim() {
7487        let valid = json!({ "type": "_custom", "methodId": "m", "name": "M",
7488            "description": "d", "_meta": { "k": 1 } });
7489        let method: AuthMethod = serde_json::from_value(valid.clone()).unwrap();
7490        assert_eq!(method.description(), Some("d"));
7491        assert_eq!(
7492            method.meta(),
7493            Some(&json!({ "k": 1 }).as_object().unwrap().clone())
7494        );
7495        assert_eq!(serde_json::to_value(&method).unwrap(), valid);
7496
7497        // Malformed optional fields are kept as sent and read as absent.
7498        let malformed = json!({ "type": "_custom", "methodId": "m", "name": "M",
7499            "description": 42, "_meta": "bad", "extra": true });
7500        let method: AuthMethod = serde_json::from_value(malformed.clone()).unwrap();
7501        assert_eq!(method.description(), None);
7502        assert_eq!(method.meta(), None);
7503        assert_eq!(serde_json::to_value(&method).unwrap(), malformed);
7504
7505        let replay = json!({ "type": "_custom", "_meta": "bad", "cursor": 3 });
7506        let decoded: ReplayFrom = serde_json::from_value(replay.clone()).unwrap();
7507        assert_eq!(serde_json::to_value(&decoded).unwrap(), replay);
7508    }
7509
7510    fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
7511        serde_json::to_string(value)
7512            .unwrap()
7513            .matches("\"_meta\"")
7514            .count()
7515    }
7516
7517    #[test]
7518    fn prompt_response_without_metadata_round_trips() {
7519        let response = PromptResponse::new(MessageId::new("message-1"));
7520        let serialized = serde_json::to_value(&response).unwrap();
7521        assert_eq!(serialized, json!({ "messageId": "message-1" }));
7522        assert_eq!(
7523            serde_json::from_value::<PromptResponse>(serialized).unwrap(),
7524            response
7525        );
7526        assert_eq!(
7527            serde_json::from_value::<PromptResponse>(json!({
7528                "messageId": "message-1",
7529                "_meta": null
7530            }))
7531            .unwrap(),
7532            response
7533        );
7534    }
7535
7536    #[test]
7537    fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
7538        let response = PromptResponse::new("message-1").meta(test_meta());
7539        let serialized = serde_json::to_value(&response).unwrap();
7540        assert_eq!(
7541            serialized,
7542            json!({
7543                "messageId": "message-1",
7544                "_meta": { "source": "test" }
7545            })
7546        );
7547
7548        let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
7549        assert_eq!(deserialized.message_id, MessageId::new("message-1"));
7550        assert_eq!(deserialized.meta, Some(test_meta()));
7551
7552        let malformed_meta: PromptResponse = serde_json::from_value(json!({
7553            "messageId": "message-2",
7554            "_meta": false
7555        }))
7556        .unwrap();
7557        assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
7558        assert_eq!(malformed_meta.meta, None);
7559
7560        for invalid in [
7561            json!({}),
7562            json!({ "messageId": null }),
7563            json!({ "messageId": 1 }),
7564            json!({ "messageId": false }),
7565            json!({ "messageId": {} }),
7566            json!({ "messageId": [] }),
7567        ] {
7568            assert!(
7569                serde_json::from_value::<PromptResponse>(invalid).is_err(),
7570                "missing, null, and non-string message IDs must be rejected"
7571            );
7572        }
7573    }
7574
7575    #[cfg(feature = "schemars")]
7576    #[test]
7577    fn prompt_response_schema_requires_non_null_string_message_id_reference() {
7578        let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
7579
7580        assert_eq!(schema["required"], json!(["messageId"]));
7581        assert_eq!(
7582            schema["properties"]["messageId"]["$ref"],
7583            "#/$defs/MessageId"
7584        );
7585        assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
7586    }
7587
7588    #[test]
7589    fn test_initialize_capabilities_default_on_malformed_values() {
7590        let request: InitializeRequest = serde_json::from_value(json!({
7591            "protocolVersion": 2,
7592            "capabilities": false,
7593            "info": {
7594                "name": "client",
7595                "version": "1.0.0"
7596            }
7597        }))
7598        .unwrap();
7599        assert_eq!(request.capabilities, ClientCapabilities::default());
7600
7601        let response: InitializeResponse = serde_json::from_value(json!({
7602            "protocolVersion": 2,
7603            "capabilities": false,
7604            "info": {
7605                "name": "agent",
7606                "version": "1.0.0"
7607            }
7608        }))
7609        .unwrap();
7610        assert_eq!(response.capabilities, AgentCapabilities::default());
7611    }
7612
7613    #[test]
7614    fn test_agent_capabilities_default_on_malformed_values() {
7615        let capabilities: AgentCapabilities = serde_json::from_value(json!({
7616            "session": false,
7617            "auth": false
7618        }))
7619        .unwrap();
7620
7621        assert!(capabilities.session.is_none());
7622        assert_eq!(capabilities.auth, None);
7623    }
7624
7625    #[test]
7626    fn test_mcp_server_stdio_serialization() {
7627        let server = McpServer::Stdio(
7628            McpServerStdio::new("test-server", "/usr/bin/server")
7629                .args(vec!["--port".to_string(), "3000".to_string()])
7630                .env(vec![EnvVariable::new("API_KEY", "secret123")]),
7631        );
7632
7633        let json = serde_json::to_value(&server).unwrap();
7634        assert_eq!(
7635            json,
7636            json!({
7637                "type": "stdio",
7638                "name": "test-server",
7639                "command": "/usr/bin/server",
7640                "args": ["--port", "3000"],
7641                "env": [
7642                    {
7643                        "name": "API_KEY",
7644                        "value": "secret123"
7645                    }
7646                ]
7647            })
7648        );
7649
7650        let deserialized: McpServer = serde_json::from_value(json).unwrap();
7651        match deserialized {
7652            McpServer::Stdio(McpServerStdio {
7653                name,
7654                command,
7655                args,
7656                env,
7657                meta: _,
7658            }) => {
7659                assert_eq!(name, "test-server");
7660                assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
7661                assert_eq!(args, vec!["--port", "3000"]);
7662                assert_eq!(env.len(), 1);
7663                assert_eq!(env[0].name, "API_KEY");
7664                assert_eq!(env[0].value, "secret123");
7665            }
7666            _ => panic!("Expected Stdio variant"),
7667        }
7668    }
7669
7670    #[test]
7671    fn test_mcp_server_empty_arrays_are_optional() {
7672        let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
7673        assert_eq!(
7674            serde_json::to_value(&stdio).unwrap(),
7675            json!({
7676                "type": "stdio",
7677                "name": "test-server",
7678                "command": "/usr/bin/server"
7679            })
7680        );
7681
7682        let McpServer::Stdio(McpServerStdio { args, env, .. }) =
7683            serde_json::from_value::<McpServer>(json!({
7684                "type": "stdio",
7685                "name": "test-server",
7686                "command": "/usr/bin/server"
7687            }))
7688            .unwrap()
7689        else {
7690            panic!("Expected Stdio variant");
7691        };
7692        assert_eq!(args, Vec::<String>::new());
7693        assert_eq!(env, []);
7694
7695        let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
7696        assert_eq!(
7697            serde_json::to_value(&http).unwrap(),
7698            json!({
7699                "type": "http",
7700                "name": "http-server",
7701                "url": "https://api.example.com"
7702            })
7703        );
7704
7705        let McpServer::Http(McpServerHttp { headers, .. }) =
7706            serde_json::from_value::<McpServer>(json!({
7707                "type": "http",
7708                "name": "http-server",
7709                "url": "https://api.example.com"
7710            }))
7711            .unwrap()
7712        else {
7713            panic!("Expected Http variant");
7714        };
7715        assert_eq!(headers, []);
7716    }
7717
7718    #[test]
7719    fn test_mcp_server_unknown_transport_serialization() {
7720        let json = json!({
7721            "type": "websocket",
7722            "name": "future-server",
7723            "url": "wss://example.com/mcp",
7724            "protocolVersion": "2026-01-01"
7725        });
7726
7727        let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
7728        let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
7729            panic!("Expected Other variant");
7730        };
7731
7732        assert_eq!(type_, "websocket");
7733        assert_eq!(fields["name"], "future-server");
7734        assert_eq!(fields["url"], "wss://example.com/mcp");
7735        assert_eq!(fields["protocolVersion"], "2026-01-01");
7736        assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
7737    }
7738
7739    #[test]
7740    fn test_mcp_server_stdio_requires_type() {
7741        let result = serde_json::from_value::<McpServer>(json!({
7742            "name": "test-server",
7743            "command": "/usr/bin/server",
7744            "args": [],
7745            "env": []
7746        }));
7747
7748        assert!(result.is_err());
7749    }
7750
7751    #[test]
7752    fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
7753        let result = serde_json::from_value::<McpServer>(json!({
7754            "type": "stdio",
7755            "name": "test-server",
7756            "args": [],
7757            "env": []
7758        }));
7759
7760        assert!(result.is_err());
7761    }
7762
7763    #[test]
7764    fn test_mcp_server_http_serialization() {
7765        let server = McpServer::Http(
7766            McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
7767                HttpHeader::new("Authorization", "Bearer token123"),
7768                HttpHeader::new("Content-Type", "application/json"),
7769            ]),
7770        );
7771
7772        let json = serde_json::to_value(&server).unwrap();
7773        assert_eq!(
7774            json,
7775            json!({
7776                "type": "http",
7777                "name": "http-server",
7778                "url": "https://api.example.com",
7779                "headers": [
7780                    {
7781                        "name": "Authorization",
7782                        "value": "Bearer token123"
7783                    },
7784                    {
7785                        "name": "Content-Type",
7786                        "value": "application/json"
7787                    }
7788                ]
7789            })
7790        );
7791
7792        let deserialized: McpServer = serde_json::from_value(json).unwrap();
7793        match deserialized {
7794            McpServer::Http(McpServerHttp {
7795                name,
7796                url,
7797                headers,
7798                meta: _,
7799            }) => {
7800                assert_eq!(name, "http-server");
7801                assert_eq!(url, "https://api.example.com");
7802                assert_eq!(headers.len(), 2);
7803                assert_eq!(headers[0].name, "Authorization");
7804                assert_eq!(headers[0].value, "Bearer token123");
7805                assert_eq!(headers[1].name, "Content-Type");
7806                assert_eq!(headers[1].value, "application/json");
7807            }
7808            _ => panic!("Expected Http variant"),
7809        }
7810    }
7811
7812    #[cfg(feature = "schemars")]
7813    #[test]
7814    fn mcp_server_http_schema_marks_url_as_uri() {
7815        let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
7816
7817        assert_eq!(schema["properties"]["url"]["format"], "uri");
7818    }
7819
7820    #[cfg(feature = "unstable_mcp_over_acp")]
7821    #[test]
7822    fn test_client_mcp_message_method_names() {
7823        use serde_json::json;
7824
7825        assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
7826
7827        let notification =
7828            MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
7829        assert_eq!(
7830            ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
7831            "mcp/message"
7832        );
7833        assert_eq!(
7834            serde_json::to_value(notification).unwrap(),
7835            json!({
7836                "serverId": "server-1",
7837                "requestId": "req-1",
7838                "method": "notifications/progress"
7839            })
7840        );
7841        let notification: MessageMcpNotification = serde_json::from_value(json!({
7842            "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
7843            "params": null, "_meta": null
7844        }))
7845        .unwrap();
7846        assert_eq!(notification.params, None);
7847        assert_eq!(notification.meta, None);
7848        for key in ["serverId", "requestId", "method"] {
7849            let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
7850            value.as_object_mut().unwrap().remove(key);
7851            assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
7852        }
7853    }
7854
7855    #[test]
7856    fn test_auth_method_names() {
7857        assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
7858        assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
7859
7860        assert_eq!(
7861            ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
7862                .method(),
7863            "auth/login"
7864        );
7865        assert_eq!(
7866            ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
7867            "auth/logout"
7868        );
7869    }
7870
7871    #[test]
7872    fn test_session_config_option_category_known_variants() {
7873        // Test serialization of known variants
7874        assert_eq!(
7875            serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
7876            json!("mode")
7877        );
7878        assert_eq!(
7879            serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
7880            json!("model")
7881        );
7882        assert_eq!(
7883            serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
7884            json!("model_config")
7885        );
7886        assert_eq!(
7887            serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
7888            json!("thought_level")
7889        );
7890
7891        // Test deserialization of known variants
7892        assert_eq!(
7893            serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
7894            SessionConfigOptionCategory::Mode
7895        );
7896        assert_eq!(
7897            serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
7898            SessionConfigOptionCategory::Model
7899        );
7900        assert_eq!(
7901            serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
7902            SessionConfigOptionCategory::ModelConfig
7903        );
7904        assert_eq!(
7905            serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
7906            SessionConfigOptionCategory::ThoughtLevel
7907        );
7908    }
7909
7910    #[test]
7911    fn test_session_config_option_category_unknown_variants() {
7912        // Test that unknown strings are captured in Other variant
7913        let unknown: SessionConfigOptionCategory =
7914            serde_json::from_str("\"some_future_category\"").unwrap();
7915        assert_eq!(
7916            unknown,
7917            SessionConfigOptionCategory::Other("some_future_category".to_string())
7918        );
7919
7920        // Test round-trip of unknown category
7921        let json = serde_json::to_value(&unknown).unwrap();
7922        assert_eq!(json, json!("some_future_category"));
7923    }
7924
7925    #[test]
7926    fn test_session_config_option_category_custom_categories() {
7927        // Category names beginning with `_` are free for custom use
7928        let custom: SessionConfigOptionCategory =
7929            serde_json::from_str("\"_my_custom_category\"").unwrap();
7930        assert_eq!(
7931            custom,
7932            SessionConfigOptionCategory::Other("_my_custom_category".to_string())
7933        );
7934
7935        // Test round-trip preserves the custom category name
7936        let json = serde_json::to_value(&custom).unwrap();
7937        assert_eq!(json, json!("_my_custom_category"));
7938
7939        // Deserialize back and verify
7940        let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
7941        assert_eq!(
7942            deserialized,
7943            SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
7944        );
7945    }
7946
7947    fn test_config_option() -> SessionConfigOption {
7948        SessionConfigOption::select(
7949            "mode",
7950            "Mode",
7951            "ask",
7952            vec![SessionConfigSelectOption::new("ask", "Ask")],
7953        )
7954    }
7955
7956    #[test]
7957    fn test_session_response_initial_state_default_empty_and_skip_serializing() {
7958        assert_eq!(
7959            serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
7960            json!({ "sessionId": "sess" })
7961        );
7962        assert_eq!(
7963            serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
7964            json!({})
7965        );
7966        #[cfg(feature = "unstable_session_fork")]
7967        assert_eq!(
7968            serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
7969            json!({ "sessionId": "fork" })
7970        );
7971
7972        let json = serde_json::to_value(
7973            NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
7974        )
7975        .unwrap();
7976        assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
7977    }
7978
7979    #[test]
7980    fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
7981        let missing: NewSessionResponse =
7982            serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
7983        assert_eq!(missing.config_options, []);
7984
7985        let null: NewSessionResponse = serde_json::from_value(json!({
7986            "sessionId": "sess",
7987            "configOptions": null
7988        }))
7989        .unwrap();
7990        assert_eq!(null.config_options, []);
7991
7992        let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
7993            "sessionId": "sess",
7994            "configOptions": "oops"
7995        }))
7996        .unwrap();
7997        assert_eq!(wrong_shape.config_options, []);
7998
7999        let valid_option = serde_json::to_value(test_config_option()).unwrap();
8000        let mixed: NewSessionResponse = serde_json::from_value(json!({
8001            "sessionId": "sess",
8002            "configOptions": ["oops", valid_option]
8003        }))
8004        .unwrap();
8005        assert_eq!(mixed.config_options.len(), 1);
8006
8007        let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
8008        assert_eq!(resume.config_options, []);
8009        #[cfg(feature = "unstable_session_fork")]
8010        {
8011            let fork: ForkSessionResponse =
8012                serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
8013            assert_eq!(fork.config_options, []);
8014        }
8015    }
8016
8017    #[test]
8018    fn test_session_response_available_commands_roundtrip_with_config_options() {
8019        use super::super::{AvailableCommandInput, Command, TextCommandInput};
8020
8021        let commands = vec![
8022            AvailableCommand::Command(
8023                Command::new("web", "Search the web")
8024                    .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
8025            ),
8026            AvailableCommand::Command(Command::new("test", "Run project tests")),
8027        ];
8028        let mut expected = json!({
8029            "sessionId": "sess",
8030            "configOptions": [test_config_option()],
8031            "availableCommands": [
8032                {
8033                    "type": "command",
8034                    "name": "web",
8035                    "description": "Search the web",
8036                    "input": { "type": "text", "hint": "Query" }
8037                },
8038                { "type": "command", "name": "test", "description": "Run project tests" }
8039            ]
8040        });
8041
8042        let new = NewSessionResponse::new("sess")
8043            .config_options(vec![test_config_option()])
8044            .available_commands(commands.clone());
8045        assert_eq!(serde_json::to_value(&new).unwrap(), expected);
8046        assert_eq!(
8047            serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
8048            new
8049        );
8050
8051        expected.as_object_mut().unwrap().remove("sessionId");
8052        let resume = ResumeSessionResponse::new()
8053            .config_options(vec![test_config_option()])
8054            .available_commands(commands.clone());
8055        assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
8056        assert_eq!(
8057            serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
8058            resume
8059        );
8060
8061        #[cfg(feature = "unstable_session_fork")]
8062        {
8063            expected["sessionId"] = json!("fork");
8064            let fork = ForkSessionResponse::new("fork")
8065                .config_options(vec![test_config_option()])
8066                .available_commands(commands);
8067            assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
8068            assert_eq!(
8069                serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
8070                fork
8071            );
8072        }
8073    }
8074
8075    #[test]
8076    fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
8077        let command =
8078            AvailableCommand::Command(super::super::Command::new("test", "Run project tests"));
8079        let config_options = vec![test_config_option()];
8080
8081        for (commands, expected) in [
8082            (None, vec![]),
8083            (Some(json!(null)), vec![]),
8084            (Some(json!([])), vec![]),
8085            (Some(json!("oops")), vec![]),
8086            (Some(json!({})), vec![]),
8087            (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
8088            (
8089                Some(json!([{ "name": "test", "description": "Run project tests" }])),
8090                vec![],
8091            ),
8092            (
8093                Some(json!([null, command, { "name": "invalid" }])),
8094                vec![command.clone()],
8095            ),
8096        ] {
8097            let mut value = json!({
8098                "sessionId": "sess",
8099                "configOptions": config_options
8100            });
8101            if let Some(commands) = commands {
8102                value["availableCommands"] = commands;
8103            }
8104
8105            let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
8106            assert_eq!(new.available_commands, expected);
8107            assert_eq!(new.config_options, config_options);
8108
8109            #[cfg(feature = "unstable_session_fork")]
8110            {
8111                let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
8112                assert_eq!(fork.available_commands, expected);
8113                assert_eq!(fork.config_options, config_options);
8114            }
8115
8116            value.as_object_mut().unwrap().remove("sessionId");
8117            let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
8118            assert_eq!(resume.available_commands, expected);
8119            assert_eq!(resume.config_options, config_options);
8120        }
8121
8122        let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
8123        assert_eq!(resume.available_commands, []);
8124    }
8125
8126    #[test]
8127    fn test_resume_session_replay_from_serialization() {
8128        assert_eq!(
8129            serde_json::to_value(ResumeSessionRequest::new(
8130                "sess_abc123",
8131                "/home/user/project"
8132            ))
8133            .unwrap(),
8134            json!({
8135                "sessionId": "sess_abc123",
8136                "cwd": "/home/user/project"
8137            })
8138        );
8139        assert_eq!(
8140            serde_json::to_value(
8141                ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8142                    .replay_from(ReplayFrom::from(ReplayFromStart::new()))
8143            )
8144            .unwrap(),
8145            json!({
8146                "sessionId": "sess_abc123",
8147                "cwd": "/home/user/project",
8148                "replayFrom": {
8149                    "type": "start"
8150                }
8151            })
8152        );
8153
8154        let replay: ResumeSessionRequest = serde_json::from_value(json!({
8155            "sessionId": "sess_abc123",
8156            "cwd": "/home/user/project",
8157            "replayFrom": {
8158                "type": "start"
8159            }
8160        }))
8161        .unwrap();
8162        assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
8163
8164        let none: ResumeSessionRequest = serde_json::from_value(json!({
8165            "sessionId": "sess_abc123",
8166            "cwd": "/home/user/project",
8167            "replayFrom": null
8168        }))
8169        .unwrap();
8170        assert!(none.replay_from.is_none());
8171    }
8172
8173    #[test]
8174    fn test_auth_method_agent_serialization() {
8175        let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
8176
8177        let json = serde_json::to_value(&method).unwrap();
8178        assert_eq!(
8179            json,
8180            json!({
8181                "methodId": "default-auth",
8182                "name": "Default Auth",
8183                "type": "agent"
8184            })
8185        );
8186        // description should be omitted when None
8187        assert!(!json.as_object().unwrap().contains_key("description"));
8188
8189        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8190        match deserialized {
8191            AuthMethod::Agent(AuthMethodAgent {
8192                method_id, name, ..
8193            }) => {
8194                assert_eq!(method_id.0.as_ref(), "default-auth");
8195                assert_eq!(name, "Default Auth");
8196            }
8197            _ => panic!("Expected Agent variant"),
8198        }
8199    }
8200
8201    #[test]
8202    fn test_auth_method_agent_deserialization() {
8203        let json = json!({
8204            "methodId": "agent-auth",
8205            "name": "Agent Auth",
8206            "type": "agent"
8207        });
8208
8209        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8210        assert!(matches!(deserialized, AuthMethod::Agent(_)));
8211    }
8212
8213    #[test]
8214    fn test_auth_method_agent_requires_type() {
8215        assert!(
8216            serde_json::from_value::<AuthMethod>(json!({
8217                "methodId": "agent-auth",
8218                "name": "Agent Auth"
8219            }))
8220            .is_err()
8221        );
8222    }
8223
8224    #[test]
8225    fn test_auth_method_agent_rejects_null_type() {
8226        assert!(
8227            serde_json::from_value::<AuthMethod>(json!({
8228                "methodId": "agent-auth",
8229                "name": "Agent Auth",
8230                "type": null
8231            }))
8232            .is_err()
8233        );
8234    }
8235
8236    #[test]
8237    fn test_auth_method_unknown_does_not_hide_malformed_agent() {
8238        assert!(
8239            serde_json::from_value::<AuthMethod>(json!({
8240                "methodId": "agent-auth",
8241                "type": "agent"
8242            }))
8243            .is_err()
8244        );
8245        assert!(
8246            serde_json::from_value::<AuthMethod>(json!({
8247                "methodId": "api-key",
8248                "type": "env_var",
8249                "vars": [{"name": "API_KEY"}]
8250            }))
8251            .is_err()
8252        );
8253    }
8254
8255    #[test]
8256    fn test_auth_method_unknown_variant_roundtrip() {
8257        let method: AuthMethod = serde_json::from_value(json!({
8258            "methodId": "oauth",
8259            "name": "OAuth",
8260            "type": "_oauth",
8261            "authorizationUrl": "https://example.com/auth"
8262        }))
8263        .unwrap();
8264
8265        assert_eq!(method.method_id().0.as_ref(), "oauth");
8266        assert_eq!(method.name(), "OAuth");
8267        let AuthMethod::Other(unknown) = method else {
8268            panic!("expected unknown auth method");
8269        };
8270        assert_eq!(unknown.type_, "_oauth");
8271        assert_eq!(
8272            unknown.fields.get("authorizationUrl"),
8273            Some(&json!("https://example.com/auth"))
8274        );
8275
8276        assert_eq!(
8277            serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
8278            json!({
8279                "methodId": "oauth",
8280                "name": "OAuth",
8281                "type": "_oauth",
8282                "authorizationUrl": "https://example.com/auth"
8283            })
8284        );
8285    }
8286
8287    #[test]
8288    fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
8289        assert!(
8290            serde_json::from_value::<AuthMethod>(json!({
8291                "methodId": "terminal-auth",
8292                "type": "terminal"
8293            }))
8294            .is_err()
8295        );
8296    }
8297
8298    #[test]
8299    fn test_tool_call_and_terminal_cancel_serialization() {
8300        assert_eq!(AGENT_METHOD_NAMES.tool_call_cancel, "tool_call/cancel");
8301        assert_eq!(AGENT_METHOD_NAMES.terminal_cancel, "terminal/cancel");
8302        assert_eq!(
8303            ClientRequest::CancelToolCallRequest(Box::new(CancelToolCallRequest::new(
8304                "sess_abc123",
8305                "call_012"
8306            )))
8307            .method(),
8308            "tool_call/cancel"
8309        );
8310        assert_eq!(
8311            ClientRequest::CancelTerminalRequest(Box::new(CancelTerminalRequest::new(
8312                "sess_abc123",
8313                "term_003"
8314            )))
8315            .method(),
8316            "terminal/cancel"
8317        );
8318        assert_eq!(
8319            serde_json::to_value(CancelToolCallRequest::new("sess_abc123", "call_012")).unwrap(),
8320            json!({"sessionId": "sess_abc123", "toolCallId": "call_012"})
8321        );
8322        assert_eq!(
8323            serde_json::to_value(CancelTerminalRequest::new("sess_abc123", "term_003")).unwrap(),
8324            json!({"sessionId": "sess_abc123", "terminalId": "term_003"})
8325        );
8326        assert!(
8327            serde_json::from_value::<CancelToolCallRequest>(json!({"sessionId": "sess_abc123"}))
8328                .is_err()
8329        );
8330        assert_eq!(
8331            serde_json::to_value(CancelTerminalResponse::new()).unwrap(),
8332            json!({})
8333        );
8334    }
8335
8336    #[test]
8337    fn test_session_delete_serialization() {
8338        assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
8339        assert_eq!(
8340            ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
8341                .method(),
8342            "session/delete"
8343        );
8344        assert_eq!(
8345            serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
8346            json!({
8347                "sessionId": "sess_abc123"
8348            })
8349        );
8350        assert_eq!(
8351            serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
8352            json!({})
8353        );
8354        assert_eq!(
8355            serde_json::to_value(
8356                SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
8357            )
8358            .unwrap(),
8359            json!({
8360                "delete": {}
8361            })
8362        );
8363    }
8364    #[cfg(feature = "unstable_session_set_title")]
8365    #[test]
8366    fn test_session_set_title_serialization() {
8367        assert_eq!(AGENT_METHOD_NAMES.session_set_title, "session/set_title");
8368        assert_eq!(
8369            ClientRequest::SetSessionTitleRequest(Box::new(SetSessionTitleRequest::new(
8370                "sess_abc123",
8371                "Investigate OAuth timeout"
8372            )))
8373            .method(),
8374            "session/set_title"
8375        );
8376        assert_eq!(
8377            serde_json::to_value(SetSessionTitleRequest::new(
8378                "sess_abc123",
8379                "Investigate OAuth timeout"
8380            ))
8381            .unwrap(),
8382            json!({
8383                "sessionId": "sess_abc123",
8384                "title": "Investigate OAuth timeout"
8385            })
8386        );
8387        assert_eq!(
8388            serde_json::to_value(SetSessionTitleResponse::new("Investigate OAuth timeout"))
8389                .unwrap(),
8390            json!({
8391                "title": "Investigate OAuth timeout"
8392            })
8393        );
8394        assert_eq!(
8395            serde_json::to_value(SessionCapabilities::new().title(SessionTitleCapabilities::new()))
8396                .unwrap(),
8397            json!({
8398                "title": {}
8399            })
8400        );
8401    }
8402
8403    #[cfg(feature = "unstable_session_set_title")]
8404    #[test]
8405    fn test_session_set_title_requires_title() {
8406        for params in [
8407            json!({"sessionId": "sess_abc123"}),
8408            json!({"sessionId": "sess_abc123", "title": null}),
8409            json!({"sessionId": "sess_abc123", "title": 42}),
8410        ] {
8411            assert!(serde_json::from_value::<SetSessionTitleRequest>(params).is_err());
8412        }
8413        for result in [json!({}), json!({"title": null})] {
8414            assert!(serde_json::from_value::<SetSessionTitleResponse>(result).is_err());
8415        }
8416    }
8417
8418    #[cfg(feature = "unstable_session_status")]
8419    #[test]
8420    fn test_session_status_serialization() {
8421        assert_eq!(AGENT_METHOD_NAMES.session_status, "session/status");
8422        assert_eq!(
8423            ClientRequest::SessionStatusRequest(Box::new(SessionStatusRequest::new("sess_abc123")))
8424                .method(),
8425            "session/status"
8426        );
8427        assert_eq!(
8428            serde_json::to_value(SessionStatusRequest::new("sess_abc123")).unwrap(),
8429            json!({"sessionId": "sess_abc123"})
8430        );
8431        assert_eq!(
8432            serde_json::to_value(SessionStatusResponse::new(SessionStatus::Saved)).unwrap(),
8433            json!({"status": "saved"})
8434        );
8435        assert_eq!(
8436            serde_json::to_value(
8437                SessionCapabilities::new().status(SessionStatusCapabilities::new())
8438            )
8439            .unwrap(),
8440            json!({"status": {}})
8441        );
8442        assert_eq!(
8443            serde_json::to_value(SessionStatusResponse::new(SessionStatus::Open).state(
8444                StateUpdate::RequiresAction(crate::v2::RequiresActionStateUpdate::new())
8445            ))
8446            .unwrap(),
8447            json!({
8448                "status": "open",
8449                "state": {"state": "requires_action"}
8450            })
8451        );
8452        for state in [json!(null), json!("running"), json!({"state": 7})] {
8453            let response = serde_json::from_value::<SessionStatusResponse>(
8454                json!({"status": "open", "state": state}),
8455            )
8456            .unwrap();
8457            assert_eq!(response.state, None);
8458        }
8459    }
8460
8461    #[cfg(feature = "unstable_session_status")]
8462    #[test]
8463    fn test_session_status_is_strict_and_open() {
8464        let custom =
8465            serde_json::from_value::<SessionStatusResponse>(json!({"status": "_suspended"}))
8466                .unwrap();
8467        assert_eq!(
8468            custom.status,
8469            SessionStatus::Other("_suspended".to_string())
8470        );
8471        assert_eq!(
8472            serde_json::to_value(&custom).unwrap(),
8473            json!({"status": "_suspended"})
8474        );
8475        for result in [json!({}), json!({"status": null}), json!({"status": 1})] {
8476            assert!(serde_json::from_value::<SessionStatusResponse>(result).is_err());
8477        }
8478        for params in [json!({}), json!({"sessionId": null})] {
8479            assert!(serde_json::from_value::<SessionStatusRequest>(params).is_err());
8480        }
8481    }
8482
8483    #[cfg(feature = "unstable_session_archive")]
8484    #[test]
8485    fn test_session_archive_serialization() {
8486        assert_eq!(AGENT_METHOD_NAMES.session_archive, "session/archive");
8487        assert_eq!(AGENT_METHOD_NAMES.session_unarchive, "session/unarchive");
8488        assert_eq!(
8489            ClientRequest::ArchiveSessionRequest(Box::new(ArchiveSessionRequest::new(
8490                "sess_abc123"
8491            )))
8492            .method(),
8493            "session/archive"
8494        );
8495        assert_eq!(
8496            ClientRequest::UnarchiveSessionRequest(Box::new(UnarchiveSessionRequest::new(
8497                "sess_abc123"
8498            )))
8499            .method(),
8500            "session/unarchive"
8501        );
8502        assert_eq!(
8503            serde_json::to_value(ArchiveSessionRequest::new("sess_abc123")).unwrap(),
8504            json!({"sessionId": "sess_abc123"})
8505        );
8506        assert_eq!(
8507            serde_json::to_value(UnarchiveSessionResponse::new()).unwrap(),
8508            json!({})
8509        );
8510        assert_eq!(
8511            serde_json::to_value(
8512                SessionCapabilities::new().archive(SessionArchiveCapabilities::new())
8513            )
8514            .unwrap(),
8515            json!({"archive": {}})
8516        );
8517        assert_eq!(
8518            serde_json::to_value(
8519                ListSessionsRequest::new()
8520                    .cwd("/home/user/project")
8521                    .archived(SessionArchiveFilter::Only)
8522            )
8523            .unwrap(),
8524            json!({"cwd": "/home/user/project", "archived": "only"})
8525        );
8526        assert_eq!(
8527            serde_json::to_value(
8528                SessionInfo::new("sess_abc123", "/home/user/project").archived(true)
8529            )
8530            .unwrap(),
8531            json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": true})
8532        );
8533    }
8534
8535    #[cfg(feature = "unstable_session_archive")]
8536    #[test]
8537    fn test_session_archive_filter_is_strict_and_open() {
8538        for (value, expected) in [
8539            (json!(null), None),
8540            (json!("include"), Some(SessionArchiveFilter::Include)),
8541            (
8542                json!("_recent"),
8543                Some(SessionArchiveFilter::Other("_recent".to_string())),
8544            ),
8545        ] {
8546            let request =
8547                serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).unwrap();
8548            assert_eq!(request.archived, expected);
8549        }
8550        for value in [json!(true), json!(1), json!({})] {
8551            assert!(
8552                serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).is_err()
8553            );
8554        }
8555        let info = serde_json::from_value::<SessionInfo>(
8556            json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": "yes"}),
8557        )
8558        .unwrap();
8559        assert_eq!(info.archived, None);
8560    }
8561
8562    #[test]
8563    fn test_session_additional_directories_serialization() {
8564        assert_eq!(
8565            serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
8566            json!({
8567                "cwd": "/home/user/project",
8568            })
8569        );
8570        assert_eq!(
8571            serde_json::to_value(
8572                NewSessionRequest::new("/home/user/project").additional_directories(vec![
8573                    PathBuf::from("/home/user/shared-lib"),
8574                    PathBuf::from("/home/user/product-docs"),
8575                ])
8576            )
8577            .unwrap(),
8578            json!({
8579                "cwd": "/home/user/project",
8580                "additionalDirectories": [
8581                    "/home/user/shared-lib",
8582                    "/home/user/product-docs"
8583                ],
8584            })
8585        );
8586        assert_eq!(
8587            serde_json::to_value(ResumeSessionRequest::new(
8588                "sess_abc123",
8589                "/home/user/project"
8590            ))
8591            .unwrap(),
8592            json!({
8593                "sessionId": "sess_abc123",
8594                "cwd": "/home/user/project",
8595            })
8596        );
8597        assert_eq!(
8598            serde_json::from_value::<ResumeSessionRequest>(json!({
8599                "sessionId": "sess_abc123",
8600                "cwd": "/home/user/project"
8601            }))
8602            .unwrap()
8603            .mcp_servers,
8604            Vec::<McpServer>::new()
8605        );
8606        assert_eq!(
8607            serde_json::from_value::<ResumeSessionRequest>(json!({
8608                "sessionId": "sess_abc123",
8609                "cwd": "/home/user/project",
8610                "mcpServers": null
8611            }))
8612            .unwrap()
8613            .mcp_servers,
8614            Vec::<McpServer>::new()
8615        );
8616        assert_eq!(
8617            serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
8618            json!({
8619                "sessionId": "sess_abc123",
8620                "cwd": "/home/user/project"
8621            })
8622        );
8623        assert_eq!(
8624            serde_json::to_value(
8625                SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
8626                    PathBuf::from("/home/user/shared-lib"),
8627                    PathBuf::from("/home/user/product-docs"),
8628                ])
8629            )
8630            .unwrap(),
8631            json!({
8632                "sessionId": "sess_abc123",
8633                "cwd": "/home/user/project",
8634                "additionalDirectories": [
8635                    "/home/user/shared-lib",
8636                    "/home/user/product-docs"
8637                ]
8638            })
8639        );
8640        assert_eq!(
8641            serde_json::from_value::<SessionInfo>(json!({
8642                "sessionId": "sess_abc123",
8643                "cwd": "/home/user/project"
8644            }))
8645            .unwrap()
8646            .additional_directories,
8647            Vec::<AbsolutePath>::new()
8648        );
8649    }
8650    #[test]
8651    fn test_session_additional_directories_capabilities_serialization() {
8652        assert_eq!(
8653            serde_json::to_value(
8654                SessionCapabilities::new()
8655                    .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
8656            )
8657            .unwrap(),
8658            json!({
8659                "additionalDirectories": {}
8660            })
8661        );
8662    }
8663
8664    #[test]
8665    fn test_session_system_prompt_capabilities_serialization() {
8666        assert_eq!(
8667            serde_json::to_value(
8668                SessionCapabilities::new().system_prompt(SessionSystemPromptCapabilities::new())
8669            )
8670            .unwrap(),
8671            json!({ "systemPrompt": {} })
8672        );
8673        assert_eq!(
8674            serde_json::to_value(
8675                SessionCapabilities::new().system_prompt(
8676                    SessionSystemPromptCapabilities::new().mode(
8677                        SessionSystemPromptModeCapabilities::new()
8678                            .override_(SessionSystemPromptOverrideCapabilities::new())
8679                    )
8680                )
8681            )
8682            .unwrap(),
8683            json!({ "systemPrompt": { "mode": { "override": {} } } })
8684        );
8685        assert_eq!(
8686            serde_json::from_value::<SessionCapabilities>(
8687                json!({ "systemPrompt": { "mode": { "override": {} } } })
8688            )
8689            .unwrap()
8690            .system_prompt
8691            .and_then(|capabilities| capabilities.mode)
8692            .and_then(|mode| mode.override_),
8693            Some(SessionSystemPromptOverrideCapabilities::new())
8694        );
8695
8696        // Capabilities fall back to "not advertised" when malformed.
8697        assert_eq!(
8698            serde_json::from_value::<SessionCapabilities>(json!({ "systemPrompt": "yes" }))
8699                .unwrap()
8700                .system_prompt,
8701            None
8702        );
8703        assert_eq!(
8704            serde_json::from_value::<SessionCapabilities>(
8705                json!({ "systemPrompt": { "mode": { "override": true } } })
8706            )
8707            .unwrap()
8708            .system_prompt,
8709            Some(
8710                SessionSystemPromptCapabilities::new()
8711                    .mode(SessionSystemPromptModeCapabilities::new())
8712            )
8713        );
8714        assert_eq!(
8715            serde_json::from_value::<SessionCapabilities>(
8716                json!({ "systemPrompt": { "mode": "override" } })
8717            )
8718            .unwrap()
8719            .system_prompt,
8720            Some(SessionSystemPromptCapabilities::new())
8721        );
8722    }
8723
8724    #[test]
8725    fn test_system_prompt_lifecycle_request_serialization() {
8726        let system_prompt = SystemPrompt::new(vec![ContentBlock::from(
8727            "You are a code review specialist.",
8728        )]);
8729        let system_prompt_json = json!({
8730            "mode": "append",
8731            "content": [{ "type": "text", "text": "You are a code review specialist." }]
8732        });
8733
8734        let request =
8735            NewSessionRequest::new("/home/user/project").system_prompt(system_prompt.clone());
8736        let request_json = json!({
8737            "cwd": "/home/user/project",
8738            "systemPrompt": system_prompt_json
8739        });
8740        assert_eq!(serde_json::to_value(&request).unwrap(), request_json);
8741        assert_eq!(
8742            serde_json::from_value::<NewSessionRequest>(request_json).unwrap(),
8743            request
8744        );
8745
8746        assert_eq!(
8747            serde_json::to_value(
8748                ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8749                    .system_prompt(system_prompt.clone())
8750            )
8751            .unwrap()["systemPrompt"],
8752            system_prompt_json
8753        );
8754        #[cfg(feature = "unstable_session_fork")]
8755        assert_eq!(
8756            serde_json::to_value(
8757                ForkSessionRequest::new("sess_abc123", "/home/user/project")
8758                    .system_prompt(system_prompt.clone())
8759            )
8760            .unwrap()["systemPrompt"],
8761            system_prompt_json
8762        );
8763
8764        let override_prompt =
8765            SystemPrompt::new(vec![ContentBlock::from("...")]).mode(SystemPromptMode::Override);
8766        let override_json = json!({
8767            "mode": "override",
8768            "content": [{ "type": "text", "text": "..." }]
8769        });
8770        assert_eq!(
8771            serde_json::to_value(&override_prompt).unwrap(),
8772            override_json
8773        );
8774        assert_eq!(
8775            serde_json::from_value::<SystemPrompt>(override_json).unwrap(),
8776            override_prompt
8777        );
8778    }
8779
8780    /// `SystemPrompt::new` defaults to `append` in Rust, but `mode` is required on
8781    /// the wire, so it is always serialized.
8782    #[test]
8783    fn test_system_prompt_new_defaults_to_append() {
8784        let system_prompt = SystemPrompt::new(vec![ContentBlock::from("...")]);
8785        assert_eq!(system_prompt.mode, SystemPromptMode::Append);
8786        assert_eq!(
8787            serde_json::to_value(&system_prompt).unwrap(),
8788            json!({ "mode": "append", "content": [{ "type": "text", "text": "..." }] })
8789        );
8790    }
8791
8792    /// Unknown modes follow the enum variant extension rule: they parse as
8793    /// `SystemPromptMode::Other` so they are not dropped, and an agent that does
8794    /// not support them must reject the request.
8795    #[test]
8796    fn test_system_prompt_preserves_unknown_modes() {
8797        for mode in ["_vendor_mode", "prepend"] {
8798            let system_prompt_json =
8799                json!({ "mode": mode, "content": [{ "type": "text", "text": "..." }] });
8800            let system_prompt: SystemPrompt =
8801                serde_json::from_value(system_prompt_json.clone()).unwrap();
8802            assert_eq!(
8803                system_prompt.mode,
8804                SystemPromptMode::Other(mode.to_string())
8805            );
8806            assert_eq!(
8807                serde_json::to_value(&system_prompt).unwrap(),
8808                system_prompt_json
8809            );
8810        }
8811    }
8812
8813    /// The receiver acts on `systemPrompt`, so malformed values fail the request
8814    /// (`-32602`) instead of creating a session without the prompt, and `null`
8815    /// is equivalent to omitting the field.
8816    fn assert_system_prompt_is_strict<T>(base: &serde_json::Value)
8817    where
8818        T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
8819    {
8820        let name = std::any::type_name::<T>();
8821        let with = |system_prompt: serde_json::Value| {
8822            let mut request = base.clone();
8823            request["systemPrompt"] = system_prompt;
8824            serde_json::from_value::<T>(request)
8825        };
8826
8827        for system_prompt in [
8828            json!("You are a code review specialist."),
8829            json!({ "content": [{ "type": "text", "text": "..." }] }),
8830            json!({ "mode": null, "content": [{ "type": "text", "text": "..." }] }),
8831            json!({ "mode": 1, "content": [{ "type": "text", "text": "..." }] }),
8832            json!({ "mode": "append" }),
8833            json!({ "mode": "append", "content": "..." }),
8834            json!({ "mode": "append", "content": [{ "type": "text" }] }),
8835        ] {
8836            assert!(
8837                with(system_prompt.clone()).is_err(),
8838                "{name}.systemPrompt: {system_prompt}"
8839            );
8840        }
8841
8842        assert_eq!(
8843            with(serde_json::Value::Null).unwrap(),
8844            serde_json::from_value::<T>(base.clone()).unwrap(),
8845            "{name}.systemPrompt"
8846        );
8847    }
8848
8849    #[test]
8850    fn test_session_setup_requests_reject_malformed_system_prompts() {
8851        assert!(
8852            serde_json::to_value(NewSessionRequest::new("/repo"))
8853                .unwrap()
8854                .get("systemPrompt")
8855                .is_none()
8856        );
8857        assert_system_prompt_is_strict::<NewSessionRequest>(&json!({ "cwd": "/repo" }));
8858        assert_system_prompt_is_strict::<ResumeSessionRequest>(&json!({
8859            "sessionId": "sess-1",
8860            "cwd": "/repo"
8861        }));
8862        #[cfg(feature = "unstable_session_fork")]
8863        assert_system_prompt_is_strict::<ForkSessionRequest>(&json!({
8864            "sessionId": "sess-1",
8865            "cwd": "/repo"
8866        }));
8867    }
8868
8869    /// Unknown content block types parse as `ContentBlock::Other` so they are not
8870    /// dropped. An agent that does not support them must reject the request.
8871    #[test]
8872    fn test_system_prompt_preserves_unknown_content_blocks() {
8873        let request: NewSessionRequest = serde_json::from_value(json!({
8874            "cwd": "/home/user/project",
8875            "systemPrompt": {
8876                "mode": "append",
8877                "content": [{ "type": "_vendor_video", "uri": "https://example.com/intro.mp4" }]
8878            }
8879        }))
8880        .unwrap();
8881        let content = request.system_prompt.unwrap().content;
8882        assert_eq!(content.len(), 1);
8883        assert!(matches!(content[0], ContentBlock::Other(_)));
8884    }
8885
8886    #[test]
8887    fn test_auth_method_terminal_serialization() {
8888        let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
8889
8890        let json = serde_json::to_value(&method).unwrap();
8891        assert_eq!(
8892            json,
8893            json!({
8894                "methodId": "tui-auth",
8895                "name": "Terminal Auth",
8896                "type": "terminal"
8897            })
8898        );
8899        // args and env should be omitted when empty
8900        assert!(!json.as_object().unwrap().contains_key("args"));
8901        assert!(!json.as_object().unwrap().contains_key("env"));
8902
8903        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8904        match deserialized {
8905            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
8906                assert_eq!(args, Vec::<String>::new());
8907                assert_eq!(env, []);
8908            }
8909            _ => panic!("Expected Terminal variant"),
8910        }
8911    }
8912
8913    #[test]
8914    fn test_auth_method_terminal_with_args_and_env_serialization() {
8915        let method = AuthMethod::Terminal(
8916            AuthMethodTerminal::new("tui-auth", "Terminal Auth")
8917                .args(vec!["--interactive".to_string(), "--color".to_string()])
8918                .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
8919        );
8920
8921        let json = serde_json::to_value(&method).unwrap();
8922        assert_eq!(
8923            json,
8924            json!({
8925                "methodId": "tui-auth",
8926                "name": "Terminal Auth",
8927                "type": "terminal",
8928                "args": ["--interactive", "--color"],
8929                "env": [
8930                    {
8931                        "name": "TERM",
8932                        "value": "xterm-256color"
8933                    }
8934                ]
8935            })
8936        );
8937
8938        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8939        match deserialized {
8940            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
8941                assert_eq!(args, vec!["--interactive", "--color"]);
8942                assert_eq!(env.len(), 1);
8943                assert_eq!(env[0].name, "TERM");
8944                assert_eq!(env[0].value, "xterm-256color");
8945            }
8946            _ => panic!("Expected Terminal variant"),
8947        }
8948    }
8949
8950    #[test]
8951    fn test_session_config_option_id_serialize() {
8952        let val = SessionConfigOptionValue::id("model-1");
8953        let json = serde_json::to_value(&val).unwrap();
8954        assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
8955    }
8956
8957    #[test]
8958    fn test_session_config_option_value_boolean_serialize() {
8959        let val = SessionConfigOptionValue::boolean(true);
8960        let json = serde_json::to_value(&val).unwrap();
8961        assert_eq!(json, json!({ "type": "boolean", "value": true }));
8962    }
8963
8964    #[test]
8965    fn test_session_config_option_value_deserialize_id() {
8966        let json = json!({ "type": "id", "value": "model-1" });
8967        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
8968        assert_eq!(val, SessionConfigOptionValue::id("model-1"));
8969        assert_eq!(val.as_id().unwrap().to_string(), "model-1");
8970    }
8971
8972    #[test]
8973    fn test_session_config_option_value_deserialize_requires_type() {
8974        let json = json!({ "value": "model-1" });
8975        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
8976        assert!(result.is_err());
8977    }
8978
8979    #[test]
8980    fn test_session_config_option_value_deserialize_boolean() {
8981        let json = json!({ "type": "boolean", "value": true });
8982        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
8983        assert_eq!(val, SessionConfigOptionValue::boolean(true));
8984        assert_eq!(val.as_bool(), Some(true));
8985    }
8986
8987    #[test]
8988    fn test_session_config_option_value_deserialize_boolean_false() {
8989        let json = json!({ "type": "boolean", "value": false });
8990        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
8991        assert_eq!(val, SessionConfigOptionValue::boolean(false));
8992        assert_eq!(val.as_bool(), Some(false));
8993    }
8994
8995    #[test]
8996    fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
8997        let json = json!({
8998            "type": "text",
8999            "value": "freeform input",
9000            "maxLength": 200
9001        });
9002        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9003        let SessionConfigOptionValue::Other(unknown) = val else {
9004            panic!("Expected Other variant");
9005        };
9006        assert_eq!(unknown.type_, "text");
9007        assert_eq!(unknown.value, json!("freeform input"));
9008        assert_eq!(unknown.fields["maxLength"], json!(200));
9009    }
9010
9011    #[test]
9012    fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
9013        let json = json!({
9014            "type": "range",
9015            "value": { "min": 1, "max": 5 }
9016        });
9017        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9018        let SessionConfigOptionValue::Other(unknown) = val else {
9019            panic!("Expected Other variant");
9020        };
9021        assert_eq!(unknown.type_, "range");
9022        assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
9023    }
9024
9025    #[test]
9026    fn test_session_config_option_value_roundtrip_id() {
9027        let original = SessionConfigOptionValue::id("option-a");
9028        let json = serde_json::to_value(&original).unwrap();
9029        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9030        assert_eq!(original, roundtripped);
9031    }
9032
9033    #[test]
9034    fn test_session_config_option_value_roundtrip_boolean() {
9035        let original = SessionConfigOptionValue::boolean(false);
9036        let json = serde_json::to_value(&original).unwrap();
9037        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9038        assert_eq!(original, roundtripped);
9039    }
9040
9041    #[test]
9042    fn test_session_config_option_value_roundtrip_other() {
9043        let mut fields = BTreeMap::new();
9044        fields.insert("maxLength".to_string(), json!(200));
9045        let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
9046            "text",
9047            json!("freeform input"),
9048            fields,
9049        ));
9050        let json = serde_json::to_value(&original).unwrap();
9051        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9052        assert_eq!(original, roundtripped);
9053    }
9054
9055    #[test]
9056    fn test_session_config_option_value_type_mismatch_boolean_with_string() {
9057        let json = json!({ "type": "boolean", "value": "not a bool" });
9058        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9059        assert!(result.is_err());
9060    }
9061
9062    #[test]
9063    fn test_session_config_option_value_from_impls() {
9064        let from_str: SessionConfigOptionValue = "model-1".into();
9065        assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
9066
9067        let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
9068        assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
9069
9070        let from_bool: SessionConfigOptionValue = true.into();
9071        assert_eq!(from_bool.as_bool(), Some(true));
9072    }
9073
9074    #[test]
9075    fn test_set_session_config_option_request_id() {
9076        let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
9077        let json = serde_json::to_value(&req).unwrap();
9078        assert_eq!(
9079            json,
9080            json!({
9081                "sessionId": "sess_1",
9082                "configId": "model",
9083                "type": "id",
9084                "value": "model-1"
9085            })
9086        );
9087    }
9088
9089    #[test]
9090    fn test_set_session_config_option_request_boolean() {
9091        let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
9092        let json = serde_json::to_value(&req).unwrap();
9093        assert_eq!(
9094            json,
9095            json!({
9096                "sessionId": "sess_1",
9097                "configId": "brave_mode",
9098                "type": "boolean",
9099                "value": true
9100            })
9101        );
9102    }
9103
9104    #[test]
9105    fn test_set_session_config_option_request_deserialize_requires_type() {
9106        let json = json!({
9107            "sessionId": "sess_1",
9108            "configId": "model",
9109            "value": "model-1"
9110        });
9111        let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
9112        assert!(result.is_err());
9113    }
9114
9115    #[test]
9116    fn test_set_session_config_option_request_deserialize_boolean() {
9117        let json = json!({
9118            "sessionId": "sess_1",
9119            "configId": "brave_mode",
9120            "type": "boolean",
9121            "value": true
9122        });
9123        let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9124        assert_eq!(req.value.as_bool(), Some(true));
9125    }
9126
9127    #[test]
9128    fn test_set_session_config_option_request_roundtrip_id() {
9129        let original = SetSessionConfigOptionRequest::new("s", "c", "v");
9130        let json = serde_json::to_value(&original).unwrap();
9131        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9132        assert_eq!(original, roundtripped);
9133    }
9134
9135    #[test]
9136    fn test_set_session_config_option_request_roundtrip_boolean() {
9137        let original = SetSessionConfigOptionRequest::new("s", "c", false);
9138        let json = serde_json::to_value(&original).unwrap();
9139        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9140        assert_eq!(original, roundtripped);
9141    }
9142
9143    #[test]
9144    fn test_session_config_boolean_serialization() {
9145        let cfg = SessionConfigBoolean::new(true);
9146        let json = serde_json::to_value(&cfg).unwrap();
9147        assert_eq!(json, json!({ "currentValue": true }));
9148
9149        let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
9150        assert!(deserialized.current_value);
9151    }
9152
9153    #[test]
9154    fn test_session_config_option_boolean_variant() {
9155        let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
9156            .description("Skip confirmation prompts")
9157            .meta(test_meta());
9158        assert_eq!(serialized_meta_key_count(&opt), 1);
9159
9160        let json = serde_json::to_value(&opt).unwrap();
9161        assert_eq!(
9162            json,
9163            json!({
9164                "configId": "brave_mode",
9165                "name": "Brave Mode",
9166                "description": "Skip confirmation prompts",
9167                "type": "boolean",
9168                "currentValue": false,
9169                "_meta": {
9170                    "source": "test"
9171                }
9172            })
9173        );
9174
9175        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9176        assert_eq!(deserialized.config_id.to_string(), "brave_mode");
9177        assert_eq!(deserialized.name, "Brave Mode");
9178        match deserialized.kind {
9179            SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
9180            _ => panic!("Expected Boolean kind"),
9181        }
9182    }
9183
9184    #[test]
9185    fn test_session_config_option_select_still_works() {
9186        // Make sure existing select options are unaffected
9187        let opt = SessionConfigOption::select(
9188            "model",
9189            "Model",
9190            "model-1",
9191            vec![
9192                SessionConfigSelectOption::new("model-1", "Model 1"),
9193                SessionConfigSelectOption::new("model-2", "Model 2"),
9194            ],
9195        )
9196        .meta(test_meta());
9197        assert_eq!(serialized_meta_key_count(&opt), 1);
9198
9199        let json = serde_json::to_value(&opt).unwrap();
9200        assert_eq!(json["type"], "select");
9201        assert_eq!(json["currentValue"], "model-1");
9202        assert_eq!(json["options"].as_array().unwrap().len(), 2);
9203        assert_eq!(json["_meta"]["source"], "test");
9204
9205        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9206        match deserialized.kind {
9207            SessionConfigKind::Select(ref s) => {
9208                assert_eq!(s.current_value.to_string(), "model-1");
9209            }
9210            _ => panic!("Expected Select kind"),
9211        }
9212    }
9213
9214    #[test]
9215    fn test_session_config_option_unknown_kind_roundtrip() {
9216        let option: SessionConfigOption = serde_json::from_value(json!({
9217            "configId": "verbosity",
9218            "name": "Verbosity",
9219            "type": "_slider",
9220            "currentValue": 3,
9221            "min": 0,
9222            "max": 5,
9223            "_meta": {
9224                "source": "test"
9225            }
9226        }))
9227        .unwrap();
9228
9229        assert_eq!(option.config_id.to_string(), "verbosity");
9230        assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
9231        let SessionConfigKind::Other(unknown) = &option.kind else {
9232            panic!("expected unknown config kind");
9233        };
9234        assert_eq!(unknown.type_, "_slider");
9235        assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
9236        assert!(!unknown.fields.contains_key("_meta"));
9237        assert_eq!(serialized_meta_key_count(&option), 1);
9238
9239        let json = serde_json::to_value(&option).unwrap();
9240        assert_eq!(json["type"], "_slider");
9241        assert_eq!(json["currentValue"], 3);
9242        assert_eq!(json["min"], 0);
9243        assert_eq!(json["max"], 5);
9244        assert_eq!(json["_meta"]["source"], "test");
9245    }
9246
9247    #[test]
9248    fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
9249        let mut fields = std::collections::BTreeMap::new();
9250        fields.insert("currentValue".to_string(), json!(3));
9251        fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
9252
9253        let option = SessionConfigOption::new(
9254            "verbosity",
9255            "Verbosity",
9256            SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
9257        )
9258        .meta(test_meta());
9259
9260        let SessionConfigKind::Other(unknown) = &option.kind else {
9261            panic!("expected unknown config kind");
9262        };
9263        assert!(!unknown.fields.contains_key("_meta"));
9264        assert_eq!(serialized_meta_key_count(&option), 1);
9265
9266        let json = serde_json::to_value(&option).unwrap();
9267        assert_eq!(json["type"], "_slider");
9268        assert_eq!(json["currentValue"], 3);
9269        assert_eq!(json["_meta"]["source"], "test");
9270    }
9271
9272    #[test]
9273    fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
9274        assert!(
9275            serde_json::from_value::<SessionConfigOption>(json!({
9276                "configId": "model",
9277                "name": "Model",
9278                "type": "select"
9279            }))
9280            .is_err()
9281        );
9282    }
9283
9284    #[cfg(feature = "unstable_llm_providers")]
9285    #[test]
9286    fn test_llm_protocol_known_variants() {
9287        assert_eq!(
9288            serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
9289            json!("anthropic")
9290        );
9291        assert_eq!(
9292            serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
9293            json!("openai")
9294        );
9295        assert_eq!(
9296            serde_json::to_value(&LlmProtocol::Azure).unwrap(),
9297            json!("azure")
9298        );
9299        assert_eq!(
9300            serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
9301            json!("vertex")
9302        );
9303        assert_eq!(
9304            serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
9305            json!("bedrock")
9306        );
9307
9308        assert_eq!(
9309            serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
9310            LlmProtocol::Anthropic
9311        );
9312        assert_eq!(
9313            serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
9314            LlmProtocol::OpenAi
9315        );
9316        assert_eq!(
9317            serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
9318            LlmProtocol::Azure
9319        );
9320        assert_eq!(
9321            serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
9322            LlmProtocol::Vertex
9323        );
9324        assert_eq!(
9325            serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
9326            LlmProtocol::Bedrock
9327        );
9328    }
9329
9330    #[cfg(feature = "unstable_llm_providers")]
9331    #[test]
9332    fn test_llm_protocol_unknown_variant() {
9333        let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
9334        assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
9335
9336        let json = serde_json::to_value(&unknown).unwrap();
9337        assert_eq!(json, json!("cohere"));
9338    }
9339
9340    #[cfg(feature = "unstable_llm_providers")]
9341    #[test]
9342    fn test_provider_current_config_serialization() {
9343        let config =
9344            ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
9345
9346        let json = serde_json::to_value(&config).unwrap();
9347        assert_eq!(
9348            json,
9349            json!({
9350                "apiType": "anthropic",
9351                "baseUrl": "https://api.anthropic.com"
9352            })
9353        );
9354
9355        let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
9356        assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
9357        assert_eq!(deserialized.base_url, "https://api.anthropic.com");
9358    }
9359
9360    #[cfg(feature = "unstable_llm_providers")]
9361    #[test]
9362    fn test_provider_info_with_current_config() {
9363        let info = ProviderInfo::new(
9364            "main",
9365            vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
9366            true,
9367            Some(ProviderCurrentConfig::new(
9368                LlmProtocol::Anthropic,
9369                "https://api.anthropic.com",
9370            )),
9371        );
9372
9373        let json = serde_json::to_value(&info).unwrap();
9374        assert_eq!(
9375            json,
9376            json!({
9377                "providerId": "main",
9378                "supported": ["anthropic", "openai"],
9379                "required": true,
9380                "current": {
9381                    "apiType": "anthropic",
9382                    "baseUrl": "https://api.anthropic.com"
9383                }
9384            })
9385        );
9386
9387        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9388        assert_eq!(deserialized.provider_id.to_string(), "main");
9389        assert_eq!(deserialized.supported.len(), 2);
9390        assert!(deserialized.required);
9391        assert!(deserialized.current.is_some());
9392        assert_eq!(
9393            deserialized.current.as_ref().unwrap().api_type,
9394            LlmProtocol::Anthropic
9395        );
9396    }
9397
9398    #[cfg(feature = "unstable_llm_providers")]
9399    #[test]
9400    fn test_provider_info_disabled() {
9401        let info = ProviderInfo::new(
9402            "secondary",
9403            vec![LlmProtocol::OpenAi],
9404            false,
9405            None::<ProviderCurrentConfig>,
9406        );
9407
9408        let json = serde_json::to_value(&info).unwrap();
9409        assert_eq!(
9410            json,
9411            json!({
9412                "providerId": "secondary",
9413                "supported": ["openai"],
9414                "required": false
9415            })
9416        );
9417
9418        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9419        assert_eq!(deserialized.provider_id.to_string(), "secondary");
9420        assert!(!deserialized.required);
9421        assert!(deserialized.current.is_none());
9422    }
9423
9424    #[cfg(feature = "unstable_llm_providers")]
9425    #[test]
9426    fn test_provider_info_missing_current_defaults_to_none() {
9427        // current is optional; omitting it should decode as None
9428        let json = json!({
9429            "providerId": "main",
9430            "supported": ["anthropic"],
9431            "required": true
9432        });
9433        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9434        assert!(deserialized.current.is_none());
9435    }
9436
9437    #[cfg(feature = "unstable_llm_providers")]
9438    #[test]
9439    fn test_provider_info_explicit_null_current_decodes_to_none() {
9440        // current: null and an omitted current are equivalent on the wire;
9441        // both must deserialize into None so the disabled state is preserved
9442        // regardless of which form the peer chose to send.
9443        let json = json!({
9444            "providerId": "main",
9445            "supported": ["anthropic"],
9446            "required": true,
9447            "current": null
9448        });
9449        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9450        assert!(deserialized.current.is_none());
9451    }
9452
9453    #[cfg(feature = "unstable_llm_providers")]
9454    #[test]
9455    fn test_list_providers_response_serialization() {
9456        let response = ListProvidersResponse::new(vec![ProviderInfo::new(
9457            "main",
9458            vec![LlmProtocol::Anthropic],
9459            true,
9460            Some(ProviderCurrentConfig::new(
9461                LlmProtocol::Anthropic,
9462                "https://api.anthropic.com",
9463            )),
9464        )]);
9465
9466        let json = serde_json::to_value(&response).unwrap();
9467        assert_eq!(json["providers"].as_array().unwrap().len(), 1);
9468        assert_eq!(json["providers"][0]["providerId"], "main");
9469
9470        let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
9471        assert_eq!(deserialized.providers.len(), 1);
9472    }
9473
9474    #[cfg(feature = "unstable_llm_providers")]
9475    #[test]
9476    fn test_set_provider_request_serialization() {
9477        use std::collections::HashMap;
9478
9479        let mut headers = HashMap::new();
9480        headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
9481
9482        let request =
9483            SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
9484                .headers(headers);
9485
9486        let json = serde_json::to_value(&request).unwrap();
9487        assert_eq!(
9488            json,
9489            json!({
9490                "providerId": "main",
9491                "apiType": "openai",
9492                "baseUrl": "https://api.openai.com/v1",
9493                "headers": {
9494                    "Authorization": "Bearer sk-test"
9495                }
9496            })
9497        );
9498
9499        let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
9500        assert_eq!(deserialized.provider_id.to_string(), "main");
9501        assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
9502        assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
9503        assert_eq!(deserialized.headers.len(), 1);
9504        assert_eq!(
9505            deserialized.headers.get("Authorization").unwrap(),
9506            "Bearer sk-test"
9507        );
9508    }
9509
9510    #[cfg(feature = "unstable_llm_providers")]
9511    #[test]
9512    fn test_set_provider_request_omits_empty_headers() {
9513        let request =
9514            SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
9515
9516        let json = serde_json::to_value(&request).unwrap();
9517        // headers should be omitted when empty
9518        assert!(!json.as_object().unwrap().contains_key("headers"));
9519    }
9520
9521    #[cfg(feature = "unstable_llm_providers")]
9522    #[test]
9523    fn test_disable_provider_request_serialization() {
9524        let request = DisableProviderRequest::new("secondary");
9525
9526        let json = serde_json::to_value(&request).unwrap();
9527        assert_eq!(json, json!({ "providerId": "secondary" }));
9528
9529        let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
9530        assert_eq!(deserialized.provider_id.to_string(), "secondary");
9531    }
9532
9533    #[cfg(feature = "unstable_llm_providers")]
9534    #[test]
9535    fn test_providers_capabilities_serialization() {
9536        let caps = ProvidersCapabilities::new();
9537
9538        let json = serde_json::to_value(&caps).unwrap();
9539        assert_eq!(json, json!({}));
9540
9541        let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
9542        assert!(deserialized.meta.is_none());
9543    }
9544
9545    #[cfg(feature = "unstable_llm_providers")]
9546    #[test]
9547    fn test_agent_capabilities_with_providers() {
9548        let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
9549
9550        let json = serde_json::to_value(&caps).unwrap();
9551        assert_eq!(json["providers"], json!({}));
9552
9553        let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
9554        assert!(deserialized.providers.is_some());
9555    }
9556
9557    #[test]
9558    fn test_agent_capabilities_session_is_explicit() {
9559        let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
9560        assert!(json.get("session").is_none());
9561
9562        let caps = AgentCapabilities::new().session(
9563            SessionCapabilities::new()
9564                .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
9565                .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
9566        );
9567
9568        assert_eq!(
9569            serde_json::to_value(&caps).unwrap(),
9570            json!({
9571                "session": {
9572                    "prompt": {
9573                        "image": {}
9574                    },
9575                    "mcp": {
9576                        "stdio": {}
9577                    }
9578                }
9579            })
9580        );
9581
9582        let deserialized: AgentCapabilities = serde_json::from_value(json!({
9583            "session": false
9584        }))
9585        .unwrap();
9586        assert!(deserialized.session.is_none());
9587    }
9588
9589    #[test]
9590    fn test_prompt_capabilities_serialize_supported_content_as_objects() {
9591        let caps = PromptCapabilities::new()
9592            .image(PromptImageCapabilities::new())
9593            .audio(PromptAudioCapabilities::new())
9594            .embedded_context(PromptEmbeddedContextCapabilities::new());
9595
9596        assert_eq!(
9597            serde_json::to_value(&caps).unwrap(),
9598            json!({
9599                "image": {},
9600                "audio": {},
9601                "embeddedContext": {}
9602            })
9603        );
9604
9605        let deserialized: PromptCapabilities = serde_json::from_value(json!({
9606            "image": null,
9607            "audio": false,
9608            "embeddedContext": {}
9609        }))
9610        .unwrap();
9611        assert!(deserialized.image.is_none());
9612        assert!(deserialized.audio.is_none());
9613        assert!(deserialized.embedded_context.is_some());
9614    }
9615
9616    #[test]
9617    fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
9618        let caps = McpCapabilities::new()
9619            .stdio(McpStdioCapabilities::new())
9620            .http(McpHttpCapabilities::new());
9621
9622        assert_eq!(
9623            serde_json::to_value(&caps).unwrap(),
9624            json!({
9625                "stdio": {},
9626                "http": {}
9627            })
9628        );
9629
9630        let deserialized: McpCapabilities = serde_json::from_value(json!({
9631            "stdio": null,
9632            "http": false
9633        }))
9634        .unwrap();
9635        assert!(deserialized.stdio.is_none());
9636        assert!(deserialized.http.is_none());
9637    }
9638
9639    #[cfg(feature = "unstable_mcp_over_acp")]
9640    #[test]
9641    fn test_mcp_capabilities_serialize_acp_support_as_object() {
9642        let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
9643
9644        assert_eq!(
9645            serde_json::to_value(&caps).unwrap(),
9646            json!({
9647                "acp": {}
9648            })
9649        );
9650    }
9651
9652    #[test]
9653    fn prompt_request_rejects_malformed_content_block() {
9654        use serde_json::json;
9655
9656        assert!(
9657            serde_json::from_value::<PromptRequest>(json!({
9658                "sessionId": "sess-1",
9659                "prompt": [{"type": "text"}]
9660            }))
9661            .is_err()
9662        );
9663    }
9664
9665    #[test]
9666    fn prompt_request_rejects_non_array_prompt() {
9667        use serde_json::json;
9668
9669        assert!(
9670            serde_json::from_value::<PromptRequest>(json!({
9671                "sessionId": "sess-1",
9672                "prompt": "hello"
9673            }))
9674            .is_err()
9675        );
9676    }
9677
9678    fn assert_session_setup_lists_are_strict<T>(base: &serde_json::Value)
9679    where
9680        T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
9681    {
9682        let name = std::any::type_name::<T>();
9683        let with = |field: &str, value: serde_json::Value| {
9684            let mut request = base.clone();
9685            request[field] = value;
9686            serde_json::from_value::<T>(request)
9687        };
9688
9689        for (field, value) in [
9690            // Dropping the malformed server would start the session without it.
9691            (
9692                "mcpServers",
9693                json!([
9694                    {"type": "stdio", "name": "github", "command": "/usr/bin/gh-mcp", "env": [{"name": "GITHUB_TOKEN", "value": null}]},
9695                    {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp"}
9696                ]),
9697            ),
9698            ("mcpServers", json!({"type": "stdio", "name": "fs"})),
9699            ("additionalDirectories", json!(["/repo/lib", 42])),
9700            ("additionalDirectories", json!("/repo/lib")),
9701        ] {
9702            assert!(
9703                with(field, value.clone()).is_err(),
9704                "{name}.{field}: {value}"
9705            );
9706        }
9707
9708        let empty = serde_json::from_value::<T>(base.clone()).unwrap();
9709        for field in ["mcpServers", "additionalDirectories"] {
9710            assert_eq!(
9711                with(field, serde_json::Value::Null).unwrap(),
9712                empty,
9713                "{name}.{field}"
9714            );
9715        }
9716    }
9717
9718    #[test]
9719    fn session_setup_requests_reject_malformed_mcp_servers_and_directories() {
9720        assert_session_setup_lists_are_strict::<NewSessionRequest>(&json!({"cwd": "/repo"}));
9721        assert_session_setup_lists_are_strict::<ResumeSessionRequest>(&json!({
9722            "sessionId": "sess-1",
9723            "cwd": "/repo"
9724        }));
9725        #[cfg(feature = "unstable_session_fork")]
9726        assert_session_setup_lists_are_strict::<ForkSessionRequest>(&json!({
9727            "sessionId": "sess-1",
9728            "cwd": "/repo"
9729        }));
9730    }
9731
9732    #[test]
9733    fn resume_session_request_rejects_malformed_replay_from() {
9734        let request = |replay_from: serde_json::Value| {
9735            serde_json::from_value::<ResumeSessionRequest>(json!({
9736                "sessionId": "sess-1",
9737                "cwd": "/repo",
9738                "replayFrom": replay_from
9739            }))
9740        };
9741
9742        // Treating this as omitted would reconnect without the requested replay.
9743        assert!(request(json!("start")).is_err());
9744        assert_eq!(request(serde_json::Value::Null).unwrap().replay_from, None);
9745        assert!(matches!(
9746            request(json!({"type": "start"})).unwrap().replay_from,
9747            Some(ReplayFrom::Start(_))
9748        ));
9749    }
9750
9751    #[test]
9752    fn mcp_server_lists_treat_null_as_empty() {
9753        let servers: Vec<McpServer> = serde_json::from_value(json!([
9754            {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp", "args": null, "env": null},
9755            {"type": "http", "name": "api", "url": "https://example.com/mcp", "headers": null}
9756        ]))
9757        .unwrap();
9758
9759        assert_eq!(
9760            servers,
9761            vec![
9762                McpServer::Stdio(McpServerStdio::new("fs", "/usr/bin/fs-mcp")),
9763                McpServer::Http(McpServerHttp::new("api", "https://example.com/mcp")),
9764            ]
9765        );
9766    }
9767
9768    #[test]
9769    fn terminal_auth_methods_reject_malformed_launch_config() {
9770        // The client spawns a process from `args` and `env`, so a malformed
9771        // method is dropped from `authMethods` instead of launched with
9772        // altered arguments or preserved as an unknown method.
9773        let response: InitializeResponse = serde_json::from_value(json!({
9774            "protocolVersion": 2,
9775            "info": {"name": "agent", "version": "1.0.0"},
9776            "authMethods": [
9777                {"type": "terminal", "methodId": "bad-args", "name": "Log in", "args": ["auth", "--port", 8123]},
9778                {"type": "terminal", "methodId": "bad-env", "name": "Log in", "env": [{"name": "DEBUG", "value": 1}]},
9779                {"type": "terminal", "methodId": "null-config", "name": "Log in", "args": null, "env": null},
9780                {"type": "agent", "methodId": "agent", "name": "Agent login"}
9781            ]
9782        }))
9783        .unwrap();
9784
9785        assert_eq!(
9786            response.auth_methods,
9787            vec![
9788                AuthMethod::Terminal(AuthMethodTerminal::new("null-config", "Log in")),
9789                AuthMethod::Agent(AuthMethodAgent::new("agent", "Agent login")),
9790            ]
9791        );
9792    }
9793
9794    #[cfg(feature = "unstable_auth_status")]
9795    #[test]
9796    fn auth_status_response_round_trips() {
9797        let response = AuthStatusResponse::new(AuthState::Authenticated).accounts(vec![
9798            AuthAccount::new("Anthropic API key").active(true),
9799            AuthAccount::new("Claude Max").email("user@example.com".to_string()),
9800        ]);
9801        let json = serde_json::to_value(&response).unwrap();
9802        assert_eq!(
9803            json,
9804            json!({
9805                "state": "authenticated",
9806                "accounts": [
9807                    {
9808                        "label": "Anthropic API key",
9809                        "active": true
9810                    },
9811                    {
9812                        "label": "Claude Max",
9813                        "email": "user@example.com"
9814                    }
9815                ]
9816            })
9817        );
9818        assert_eq!(
9819            serde_json::from_value::<AuthStatusResponse>(json).unwrap(),
9820            response
9821        );
9822        assert_eq!(
9823            serde_json::to_value(AuthStatusResponse::new(AuthState::NotRequired)).unwrap(),
9824            json!({"state": "not_required"})
9825        );
9826    }
9827
9828    #[cfg(feature = "unstable_auth_status")]
9829    #[test]
9830    fn auth_status_state_is_strict_and_accounts_are_lenient() {
9831        assert!(serde_json::from_value::<AuthStatusResponse>(json!({})).is_err());
9832        assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": null})).is_err());
9833        assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": 1})).is_err());
9834
9835        let response: AuthStatusResponse = serde_json::from_value(json!({
9836            "state": "_vendor_state",
9837            "accounts": [
9838                {"active": true},
9839                {
9840                    "label": "Vertex AI",
9841                    "active": "yes",
9842                    "email": ["not", "a", "string"]
9843                }
9844            ]
9845        }))
9846        .unwrap();
9847        assert_eq!(
9848            response,
9849            AuthStatusResponse::new(AuthState::Other("_vendor_state".to_string()))
9850                .accounts(vec![AuthAccount::new("Vertex AI")])
9851        );
9852        assert_eq!(
9853            serde_json::from_value::<AuthStatusResponse>(
9854                json!({"state": "unknown", "accounts": null})
9855            )
9856            .unwrap(),
9857            AuthStatusResponse::new(AuthState::Unknown)
9858        );
9859    }
9860
9861    #[cfg(feature = "unstable_auth_status")]
9862    #[test]
9863    fn auth_status_update_notification_routes() {
9864        use crate::v2::{AgentNotification, CLIENT_METHOD_NAMES};
9865
9866        assert_eq!(AGENT_METHOD_NAMES.auth_status, "auth/status");
9867        assert_eq!(CLIENT_METHOD_NAMES.auth_status_update, "auth/status_update");
9868        let notification = AgentNotification::AuthStatusUpdateNotification(Box::new(
9869            AuthStatusUpdateNotification::new(AuthState::Unauthenticated),
9870        ));
9871        assert_eq!(notification.method(), "auth/status_update");
9872        assert_eq!(
9873            serde_json::to_value(&notification).unwrap(),
9874            json!({"state": "unauthenticated"})
9875        );
9876    }
9877}