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, so it should be readable by people.
202    ///
203    /// If not provided, the name should be used for display.
204    ///
205    /// Omitted and `null` are equivalent and mean the `name` is used for display.
206    #[serde_as(deserialize_as = "DefaultOnError")]
207    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
208    #[serde(default)]
209    pub title: Option<String>,
210    /// Version of the implementation. Can be displayed to the user or used
211    /// for debugging or metrics purposes. (e.g. "1.0.0").
212    pub version: String,
213    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
214    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
215    /// Implementations MUST NOT assign meaning to keys they do not recognize.
216    ///
217    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
218    #[serde_as(deserialize_as = "DefaultOnError")]
219    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
220    #[serde(default)]
221    #[serde(rename = "_meta")]
222    pub meta: Option<Meta>,
223}
224
225impl Implementation {
226    /// Builds [`Implementation`] with the required fields set. Optional fields start unset or empty.
227    #[must_use]
228    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
229        Self {
230            name: name.into(),
231            title: None,
232            version: version.into(),
233            meta: None,
234        }
235    }
236
237    /// Intended for UI and end-user contexts, so it should be readable by people.
238    ///
239    /// If not provided, the name should be used for display.
240    #[must_use]
241    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
242        self.title = title.into_option();
243        self
244    }
245
246    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
247    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
248    /// do not recognize.
249    ///
250    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
251    #[must_use]
252    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
253        self.meta = meta.into_option();
254        self
255    }
256}
257
258// Authentication
259
260/// Request parameters for the `auth/login` method.
261///
262/// Specifies which authentication method to use.
263///
264/// Agents MUST support this method when their `initialize` response included
265/// a non-empty `authMethods` list. Clients MUST NOT call this method
266/// when `authMethods` was omitted or empty.
267#[serde_as]
268#[skip_serializing_none]
269#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
270#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
271#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
272#[serde(rename_all = "camelCase")]
273#[non_exhaustive]
274pub struct LoginAuthRequest {
275    /// The ID of the authentication method to use.
276    /// Must be one of the methods advertised in the initialize response.
277    pub method_id: AuthMethodId,
278    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
279    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
280    /// Implementations MUST NOT assign meaning to keys they do not recognize.
281    ///
282    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
283    #[serde_as(deserialize_as = "DefaultOnError")]
284    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
285    #[serde(default)]
286    #[serde(rename = "_meta")]
287    pub meta: Option<Meta>,
288}
289
290impl LoginAuthRequest {
291    /// Builds [`LoginAuthRequest`] with the required request fields set. Optional fields start unset or empty.
292    #[must_use]
293    pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
294        Self {
295            method_id: method_id.into(),
296            meta: None,
297        }
298    }
299
300    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
301    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
302    /// do not recognize.
303    ///
304    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
305    #[must_use]
306    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
307        self.meta = meta.into_option();
308        self
309    }
310}
311
312crate::serde_util::default_on_null! {
313    /// Response to the `auth/login` method.
314    #[serde_as]
315    #[skip_serializing_none]
316    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
317    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
318    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
319    #[serde(rename_all = "camelCase")]
320    #[non_exhaustive]
321    pub struct LoginAuthResponse {
322        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
323        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
324        /// Implementations MUST NOT assign meaning to keys they do not recognize.
325        ///
326        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
327        #[serde_as(deserialize_as = "DefaultOnError")]
328        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
329        #[serde(default)]
330        #[serde(rename = "_meta")]
331        pub meta: Option<Meta>,
332    }
333}
334
335impl LoginAuthResponse {
336    /// Builds [`LoginAuthResponse`] with the required response fields set. Optional fields start unset or empty.
337    #[must_use]
338    pub fn new() -> Self {
339        Self::default()
340    }
341
342    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
343    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
344    /// do not recognize.
345    ///
346    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
347    #[must_use]
348    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
349        self.meta = meta.into_option();
350        self
351    }
352}
353
354// Logout
355
356crate::serde_util::default_on_null! {
357    /// Request parameters for the `auth/logout` method.
358    ///
359    /// Terminates the current authenticated session.
360    ///
361    /// Agents MUST support this method when their `initialize` response included
362    /// a non-empty `authMethods` list. Clients MUST NOT call this method
363    /// when `authMethods` was omitted or empty.
364    #[serde_as]
365    #[skip_serializing_none]
366    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
367    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
368    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
369    #[serde(rename_all = "camelCase")]
370    #[non_exhaustive]
371    pub struct LogoutAuthRequest {
372        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
373        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
374        /// Implementations MUST NOT assign meaning to keys they do not recognize.
375        ///
376        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
377        #[serde_as(deserialize_as = "DefaultOnError")]
378        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
379        #[serde(default)]
380        #[serde(rename = "_meta")]
381        pub meta: Option<Meta>,
382    }
383}
384
385impl LogoutAuthRequest {
386    /// Builds [`LogoutAuthRequest`] with the required request fields set. Optional fields start unset or empty.
387    #[must_use]
388    pub fn new() -> Self {
389        Self::default()
390    }
391
392    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
393    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
394    /// do not recognize.
395    ///
396    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
397    #[must_use]
398    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
399        self.meta = meta.into_option();
400        self
401    }
402}
403
404crate::serde_util::default_on_null! {
405    /// Response to the `auth/logout` method.
406    #[serde_as]
407    #[skip_serializing_none]
408    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
409    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
410    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
411    #[serde(rename_all = "camelCase")]
412    #[non_exhaustive]
413    pub struct LogoutAuthResponse {
414        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
415        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
416        /// Implementations MUST NOT assign meaning to keys they do not recognize.
417        ///
418        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
419        #[serde_as(deserialize_as = "DefaultOnError")]
420        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
421        #[serde(default)]
422        #[serde(rename = "_meta")]
423        pub meta: Option<Meta>,
424    }
425}
426
427impl LogoutAuthResponse {
428    /// Builds [`LogoutAuthResponse`] with the required response fields set. Optional fields start unset or empty.
429    #[must_use]
430    pub fn new() -> Self {
431        Self::default()
432    }
433
434    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
435    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
436    /// do not recognize.
437    ///
438    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
439    #[must_use]
440    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
441        self.meta = meta.into_option();
442        self
443    }
444}
445
446// Auth status
447
448#[cfg(feature = "unstable_auth_status")]
449crate::serde_util::default_on_null! {
450    /// **UNSTABLE**
451    ///
452    /// This capability is not part of the spec yet, and may be removed or changed at any point.
453    ///
454    /// Request parameters for the `auth/status` method.
455    ///
456    /// Queries the agent's current authentication state without changing it.
457    #[serde_as]
458    #[skip_serializing_none]
459    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
460    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
461    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
462    #[serde(rename_all = "camelCase")]
463    #[non_exhaustive]
464    pub struct AuthStatusRequest {
465        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
466        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
467        /// Implementations MUST NOT assign meaning to keys they do not recognize.
468        ///
469        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
470        #[serde_as(deserialize_as = "DefaultOnError")]
471        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
472        #[serde(default)]
473        #[serde(rename = "_meta")]
474        pub meta: Option<Meta>,
475    }
476}
477
478#[cfg(feature = "unstable_auth_status")]
479impl AuthStatusRequest {
480    /// Builds [`AuthStatusRequest`] with the required request fields set. Optional fields start unset or empty.
481    #[must_use]
482    pub fn new() -> Self {
483        Self::default()
484    }
485
486    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
487    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
488    /// do not recognize.
489    ///
490    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
491    #[must_use]
492    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
493        self.meta = meta.into_option();
494        self
495    }
496}
497
498/// **UNSTABLE**
499///
500/// This capability is not part of the spec yet, and may be removed or changed at any point.
501///
502/// Response to the `auth/status` method.
503#[cfg(feature = "unstable_auth_status")]
504#[serde_as]
505#[skip_serializing_none]
506#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
507#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
508#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
509#[serde(rename_all = "camelCase")]
510#[non_exhaustive]
511pub struct AuthStatusResponse {
512    /// Whether the agent can start sessions without the user signing in first.
513    ///
514    /// This is the only field clients should act on. Clients MUST treat values they
515    /// do not recognize as `unknown`.
516    pub state: AuthState,
517    /// The accounts the agent has configured, for display.
518    ///
519    /// Optional. Omitted and empty are equivalent and mean no accounts are reported.
520    /// Agents that hold credentials for several providers or accounts SHOULD list each
521    /// of them and mark the ones in effect with `active`.
522    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
523    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
524    #[serde(default, skip_serializing_if = "Vec::is_empty")]
525    pub accounts: Vec<AuthAccount>,
526    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
527    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
528    /// Implementations MUST NOT assign meaning to keys they do not recognize.
529    ///
530    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
531    #[serde_as(deserialize_as = "DefaultOnError")]
532    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
533    #[serde(default)]
534    #[serde(rename = "_meta")]
535    pub meta: Option<Meta>,
536}
537
538#[cfg(feature = "unstable_auth_status")]
539impl AuthStatusResponse {
540    /// Builds [`AuthStatusResponse`] with the required fields set. Optional fields start unset or empty.
541    #[must_use]
542    pub fn new(state: AuthState) -> Self {
543        Self {
544            state,
545            accounts: vec![],
546            meta: None,
547        }
548    }
549
550    /// The accounts the agent has configured, for display.
551    #[must_use]
552    pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
553        self.accounts = accounts;
554        self
555    }
556
557    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
558    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
559    /// do not recognize.
560    ///
561    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
562    #[must_use]
563    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
564        self.meta = meta.into_option();
565        self
566    }
567}
568
569/// **UNSTABLE**
570///
571/// This capability is not part of the spec yet, and may be removed or changed at any point.
572///
573/// Notification sent by the agent when its authentication status changes.
574///
575/// Carries the same fields as the `auth/status` response. Agents that advertise the
576/// `auth.status` capability MAY send it at any time after `initialize`, and SHOULD only
577/// send it when the reported status differs from the last one sent on the connection.
578#[cfg(feature = "unstable_auth_status")]
579#[serde_as]
580#[skip_serializing_none]
581#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
583#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = AUTH_STATUS_UPDATE_NOTIFICATION)))]
584#[serde(rename_all = "camelCase")]
585#[non_exhaustive]
586pub struct AuthStatusUpdateNotification {
587    /// Whether the agent can start sessions without the user signing in first.
588    ///
589    /// This is the only field clients should act on. Clients MUST treat values they
590    /// do not recognize as `unknown`.
591    pub state: AuthState,
592    /// The accounts the agent has configured, for display.
593    ///
594    /// Optional. Omitted and empty are equivalent and mean no accounts are reported.
595    /// Agents that hold credentials for several providers or accounts SHOULD list each
596    /// of them and mark the ones in effect with `active`.
597    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
598    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
599    #[serde(default, skip_serializing_if = "Vec::is_empty")]
600    pub accounts: Vec<AuthAccount>,
601    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
602    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
603    /// Implementations MUST NOT assign meaning to keys they do not recognize.
604    ///
605    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
606    #[serde_as(deserialize_as = "DefaultOnError")]
607    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
608    #[serde(default)]
609    #[serde(rename = "_meta")]
610    pub meta: Option<Meta>,
611}
612
613#[cfg(feature = "unstable_auth_status")]
614impl AuthStatusUpdateNotification {
615    /// Builds [`AuthStatusUpdateNotification`] with the required fields set. Optional fields start unset or empty.
616    #[must_use]
617    pub fn new(state: AuthState) -> Self {
618        Self {
619            state,
620            accounts: vec![],
621            meta: None,
622        }
623    }
624
625    /// The accounts the agent has configured, for display.
626    #[must_use]
627    pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
628        self.accounts = accounts;
629        self
630    }
631
632    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
633    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
634    /// do not recognize.
635    ///
636    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
637    #[must_use]
638    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
639        self.meta = meta.into_option();
640        self
641    }
642}
643
644/// **UNSTABLE**
645///
646/// This capability is not part of the spec yet, and may be removed or changed at any point.
647///
648/// The agent's overall authentication state.
649#[cfg(feature = "unstable_auth_status")]
650#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
651#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
652#[serde(rename_all = "snake_case")]
653#[non_exhaustive]
654pub enum AuthState {
655    /// Credentials are configured. This does not guarantee that they are valid.
656    Authenticated,
657    /// The user needs to sign in. This includes credentials that are missing,
658    /// expired, or known to be invalid.
659    Unauthenticated,
660    /// No authentication is needed, for example because the agent uses a local model.
661    NotRequired,
662    /// The agent cannot currently determine the state.
663    Unknown,
664    /// Custom or future authentication state.
665    ///
666    /// Values beginning with `_` are reserved for implementation-specific
667    /// extensions. Unknown values that do not begin with `_` are reserved for
668    /// future ACP variants.
669    #[serde(untagged)]
670    Other(String),
671}
672
673/// **UNSTABLE**
674///
675/// This capability is not part of the spec yet, and may be removed or changed at any point.
676///
677/// An account or credential the agent has configured, such as a signed-in user
678/// account or an API key.
679///
680/// All fields are display information only. Clients MUST NOT use them as stable
681/// account identifiers, and SHOULD NOT log them or send them elsewhere, such as to
682/// telemetry.
683#[cfg(feature = "unstable_auth_status")]
684#[serde_as]
685#[skip_serializing_none]
686#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
687#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
688#[serde(rename_all = "camelCase")]
689#[non_exhaustive]
690pub struct AuthAccount {
691    /// Human-readable name for the account, suitable for display on its own,
692    /// for example `Claude Max`, `OpenAI API key`, or `Vertex AI`.
693    pub label: String,
694    /// Whether the agent currently uses this account by default.
695    ///
696    /// Several accounts MAY be active, for example one per provider.
697    ///
698    /// Optional. Omitted and `null` are equivalent and mean the agent does not indicate whether this account is in effect.
699    #[serde_as(deserialize_as = "DefaultOnError")]
700    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
701    #[serde(default)]
702    pub active: Option<bool>,
703    /// Display name of the user, such as a username or full name.
704    ///
705    /// Optional. Omitted and `null` are equivalent and mean no name is reported.
706    #[serde_as(deserialize_as = "DefaultOnError")]
707    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
708    #[serde(default)]
709    pub name: Option<String>,
710    /// Email address of the user.
711    ///
712    /// Optional. Omitted and `null` are equivalent and mean no email is reported.
713    #[serde_as(deserialize_as = "DefaultOnError")]
714    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
715    #[serde(default)]
716    pub email: Option<String>,
717    /// Display name of the organization, team, or workspace the account is using.
718    ///
719    /// Optional. Omitted and `null` are equivalent and mean no organization is reported.
720    #[serde_as(deserialize_as = "DefaultOnError")]
721    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
722    #[serde(default)]
723    pub organization: Option<String>,
724    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
725    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
726    /// Implementations MUST NOT assign meaning to keys they do not recognize.
727    ///
728    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
729    #[serde_as(deserialize_as = "DefaultOnError")]
730    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
731    #[serde(default)]
732    #[serde(rename = "_meta")]
733    pub meta: Option<Meta>,
734}
735
736#[cfg(feature = "unstable_auth_status")]
737impl AuthAccount {
738    /// Builds [`AuthAccount`] with the required fields set. Optional fields start unset or empty.
739    #[must_use]
740    pub fn new(label: impl Into<String>) -> Self {
741        Self {
742            label: label.into(),
743            active: None,
744            name: None,
745            email: None,
746            organization: None,
747            meta: None,
748        }
749    }
750
751    /// Whether the agent currently uses this account by default.
752    #[must_use]
753    pub fn active(mut self, active: impl IntoOption<bool>) -> Self {
754        self.active = active.into_option();
755        self
756    }
757
758    /// Display name of the user, such as a username or full name.
759    #[must_use]
760    pub fn name(mut self, name: impl IntoOption<String>) -> Self {
761        self.name = name.into_option();
762        self
763    }
764
765    /// Email address of the user.
766    #[must_use]
767    pub fn email(mut self, email: impl IntoOption<String>) -> Self {
768        self.email = email.into_option();
769        self
770    }
771
772    /// Display name of the organization, team, or workspace the account is using.
773    #[must_use]
774    pub fn organization(mut self, organization: impl IntoOption<String>) -> Self {
775        self.organization = organization.into_option();
776        self
777    }
778
779    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
780    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
781    /// do not recognize.
782    ///
783    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
784    #[must_use]
785    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
786        self.meta = meta.into_option();
787        self
788    }
789}
790
791/// **UNSTABLE**
792///
793/// This capability is not part of the spec yet, and may be removed or changed at any point.
794///
795/// Auth status capabilities supported by the agent.
796///
797/// Supplying `{}` means the agent supports the `auth/status` method.
798#[cfg(feature = "unstable_auth_status")]
799#[serde_as]
800#[skip_serializing_none]
801#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
802#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
803#[non_exhaustive]
804pub struct AuthStatusCapabilities {
805    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
806    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
807    /// Implementations MUST NOT assign meaning to keys they do not recognize.
808    ///
809    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
810    #[serde_as(deserialize_as = "DefaultOnError")]
811    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
812    #[serde(default)]
813    #[serde(rename = "_meta")]
814    pub meta: Option<Meta>,
815}
816
817#[cfg(feature = "unstable_auth_status")]
818impl AuthStatusCapabilities {
819    /// Builds an empty [`AuthStatusCapabilities`]. Use builder methods to advertise supported sub-capabilities.
820    #[must_use]
821    pub fn new() -> Self {
822        Self::default()
823    }
824
825    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
826    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
827    /// do not recognize.
828    ///
829    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
830    #[must_use]
831    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
832        self.meta = meta.into_option();
833        self
834    }
835}
836
837/// Authentication-related extension capabilities supported by the agent.
838///
839/// This object does not advertise support for `auth/login` or `auth/logout`.
840/// Those methods are advertised by a non-empty `authMethods` list in the
841/// `initialize` response.
842#[serde_as]
843#[skip_serializing_none]
844#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
845#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
846#[serde(rename_all = "camelCase")]
847#[non_exhaustive]
848pub struct AgentAuthCapabilities {
849    /// **UNSTABLE**
850    ///
851    /// This capability is not part of the spec yet, and may be removed or changed at any point.
852    ///
853    /// Whether the agent supports the `auth/status` method.
854    ///
855    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
856    /// Supplying `{}` means the agent supports the `auth/status` method.
857    #[cfg(feature = "unstable_auth_status")]
858    #[serde_as(deserialize_as = "DefaultOnError")]
859    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
860    #[serde(default)]
861    pub status: Option<AuthStatusCapabilities>,
862    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
863    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
864    /// Implementations MUST NOT assign meaning to keys they do not recognize.
865    ///
866    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
867    #[serde_as(deserialize_as = "DefaultOnError")]
868    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
869    #[serde(default)]
870    #[serde(rename = "_meta")]
871    pub meta: Option<Meta>,
872}
873
874impl AgentAuthCapabilities {
875    /// Builds an empty [`AgentAuthCapabilities`].
876    #[must_use]
877    pub fn new() -> Self {
878        Self::default()
879    }
880
881    /// **UNSTABLE**
882    ///
883    /// This capability is not part of the spec yet, and may be removed or changed at any point.
884    ///
885    /// Whether the agent supports the `auth/status` method.
886    #[cfg(feature = "unstable_auth_status")]
887    #[must_use]
888    pub fn status(mut self, status: impl IntoOption<AuthStatusCapabilities>) -> Self {
889        self.status = status.into_option();
890        self
891    }
892
893    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
894    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
895    /// do not recognize.
896    ///
897    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
898    #[must_use]
899    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
900        self.meta = meta.into_option();
901        self
902    }
903}
904
905/// Typed identifier used for auth method values on the wire.
906#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
907#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
908#[serde(transparent)]
909#[from(forward)]
910#[non_exhaustive]
911pub struct AuthMethodId(pub Arc<str>);
912
913impl AuthMethodId {
914    /// Wraps a protocol string as a typed [`AuthMethodId`].
915    #[must_use]
916    pub fn new(id: impl Into<Self>) -> Self {
917        id.into()
918    }
919}
920
921/// Describes an available authentication method.
922///
923/// The `type` field acts as the discriminator in the serialized JSON form.
924#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
925#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
926#[serde(tag = "type", rename_all = "snake_case")]
927#[non_exhaustive]
928pub enum AuthMethod {
929    /// Client runs the configured agent program as a separate interactive
930    /// process, without passing this method to `auth/login`.
931    Terminal(AuthMethodTerminal),
932    /// Agent handles authentication itself through `auth/login`.
933    ///
934    /// The `type` discriminator value is `agent`.
935    Agent(AuthMethodAgent),
936    /// Custom or future authentication method.
937    ///
938    /// Values beginning with `_` are reserved for implementation-specific
939    /// extensions. Unknown values that do not begin with `_` are reserved for
940    /// future ACP variants.
941    ///
942    /// Clients that do not understand this method type should preserve the raw
943    /// payload when storing, replaying, proxying, or forwarding initialization
944    /// data, and otherwise ignore the method or display it generically.
945    #[serde(untagged)]
946    Other(OtherAuthMethod),
947}
948
949impl AuthMethod {
950    /// The unique identifier for this authentication method.
951    #[must_use]
952    pub fn method_id(&self) -> &AuthMethodId {
953        match self {
954            Self::Agent(a) => &a.method_id,
955            Self::Other(a) => &a.method_id,
956            Self::Terminal(t) => &t.method_id,
957        }
958    }
959
960    /// The human-readable name of this authentication method.
961    #[must_use]
962    pub fn name(&self) -> &str {
963        match self {
964            Self::Agent(a) => &a.name,
965            Self::Other(a) => &a.name,
966            Self::Terminal(t) => &t.name,
967        }
968    }
969
970    /// Optional human-readable description of this authentication method.
971    #[must_use]
972    pub fn description(&self) -> Option<&str> {
973        match self {
974            Self::Agent(a) => a.description.as_deref(),
975            Self::Other(a) => a
976                .fields
977                .get("description")
978                .and_then(serde_json::Value::as_str),
979            Self::Terminal(t) => t.description.as_deref(),
980        }
981    }
982
983    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
984    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
985    /// do not recognize.
986    ///
987    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
988    #[must_use]
989    pub fn meta(&self) -> Option<&Meta> {
990        match self {
991            Self::Agent(a) => a.meta.as_ref(),
992            Self::Other(a) => a.fields.get("_meta").and_then(serde_json::Value::as_object),
993            Self::Terminal(t) => t.meta.as_ref(),
994        }
995    }
996}
997
998/// Custom or future authentication method payload.
999#[serde_as]
1000#[skip_serializing_none]
1001#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1002#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1003#[cfg_attr(feature = "schemars", schemars(inline))]
1004#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
1005#[serde(rename_all = "camelCase")]
1006#[non_exhaustive]
1007pub struct OtherAuthMethod {
1008    /// Custom or future authentication method type.
1009    ///
1010    /// Values beginning with `_` are reserved for implementation-specific
1011    /// extensions. Unknown values that do not begin with `_` are reserved for
1012    /// future ACP variants.
1013    #[serde(rename = "type")]
1014    pub type_: String,
1015    /// Unique identifier for this authentication method.
1016    pub method_id: AuthMethodId,
1017    /// Human-readable name of the authentication method.
1018    pub name: String,
1019    /// The remaining fields of the unknown authentication method payload, preserved verbatim,
1020    /// including `description` and `_meta`.
1021    #[serde(flatten)]
1022    pub fields: BTreeMap<String, serde_json::Value>,
1023}
1024
1025impl OtherAuthMethod {
1026    /// Builds [`OtherAuthMethod`] from an unknown discriminator and preserves the remaining extension fields.
1027    #[must_use]
1028    pub fn new(
1029        type_: impl Into<String>,
1030        method_id: impl Into<AuthMethodId>,
1031        name: impl Into<String>,
1032        mut fields: BTreeMap<String, serde_json::Value>,
1033    ) -> Self {
1034        fields.remove("type");
1035        fields.remove("methodId");
1036        fields.remove("name");
1037        Self {
1038            type_: type_.into(),
1039            method_id: method_id.into(),
1040            name: name.into(),
1041            fields,
1042        }
1043    }
1044}
1045
1046impl<'de> Deserialize<'de> for OtherAuthMethod {
1047    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1048    where
1049        D: serde::Deserializer<'de>,
1050    {
1051        #[derive(Deserialize)]
1052        #[serde(rename_all = "camelCase")]
1053        struct RawOtherAuthMethod {
1054            #[serde(rename = "type")]
1055            type_: String,
1056            method_id: AuthMethodId,
1057            name: String,
1058            #[serde(flatten)]
1059            fields: BTreeMap<String, serde_json::Value>,
1060        }
1061
1062        let raw = RawOtherAuthMethod::deserialize(deserializer)?;
1063        if is_known_auth_method_type(&raw.type_) {
1064            return Err(serde::de::Error::custom(format!(
1065                "known authentication method `{}` did not match its schema",
1066                raw.type_
1067            )));
1068        }
1069
1070        Ok(Self {
1071            type_: raw.type_,
1072            method_id: raw.method_id,
1073            name: raw.name,
1074            fields: raw.fields,
1075        })
1076    }
1077}
1078
1079fn is_known_auth_method_type(type_: &str) -> bool {
1080    matches!(type_, "agent" | "terminal")
1081}
1082
1083#[cfg(feature = "schemars")]
1084fn other_auth_method_schema(schema: &mut Schema) {
1085    super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
1086}
1087
1088/// Agent handles authentication itself through `auth/login`.
1089///
1090/// The `type` discriminator value is `agent`.
1091#[serde_as]
1092#[skip_serializing_none]
1093#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1094#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1095#[serde(rename_all = "camelCase")]
1096#[non_exhaustive]
1097pub struct AuthMethodAgent {
1098    /// Unique identifier for this authentication method.
1099    pub method_id: AuthMethodId,
1100    /// Human-readable name of the authentication method.
1101    pub name: String,
1102    /// Optional human-readable description of this authentication method.
1103    ///
1104    /// Omitted and `null` are equivalent and mean no description is provided.
1105    #[serde_as(deserialize_as = "DefaultOnError")]
1106    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1107    #[serde(default)]
1108    pub description: Option<String>,
1109    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1110    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1111    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1112    ///
1113    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1114    #[serde_as(deserialize_as = "DefaultOnError")]
1115    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1116    #[serde(default)]
1117    #[serde(rename = "_meta")]
1118    pub meta: Option<Meta>,
1119}
1120
1121impl AuthMethodAgent {
1122    /// Builds [`AuthMethodAgent`] with the required fields set. Optional fields start unset or empty.
1123    #[must_use]
1124    pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1125        Self {
1126            method_id: method_id.into(),
1127            name: name.into(),
1128            description: None,
1129            meta: None,
1130        }
1131    }
1132
1133    /// Optional human-readable description of this authentication method.
1134    #[must_use]
1135    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1136        self.description = description.into_option();
1137        self
1138    }
1139
1140    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1141    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1142    /// do not recognize.
1143    ///
1144    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1145    #[must_use]
1146    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1147        self.meta = meta.into_option();
1148        self
1149    }
1150}
1151
1152/// Terminal-based authentication method.
1153///
1154/// The client runs the configured agent program as a separate interactive
1155/// process for the user to authenticate via a TUI. Agents MUST advertise this
1156/// method only when the client enabled its terminal authentication capability.
1157/// A zero exit status signals success. Any other termination signals failure.
1158/// The client MUST NOT pass this method to `auth/login`.
1159#[serde_as]
1160#[skip_serializing_none]
1161#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1163#[serde(rename_all = "camelCase")]
1164#[non_exhaustive]
1165pub struct AuthMethodTerminal {
1166    /// Unique identifier for this authentication method.
1167    pub method_id: AuthMethodId,
1168    /// Human-readable name of the authentication method.
1169    pub name: String,
1170    /// Optional human-readable description of this authentication method.
1171    ///
1172    /// Omitted and `null` are equivalent and mean no description is provided.
1173    #[serde_as(deserialize_as = "DefaultOnError")]
1174    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1175    #[serde(default)]
1176    pub description: Option<String>,
1177    /// Additional arguments to append to the configured agent invocation for terminal auth.
1178    #[serde_as(deserialize_as = "DefaultOnNull")]
1179    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1180    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1181    pub args: Vec<String>,
1182    /// Additional environment variables to set on the configured agent invocation for terminal auth.
1183    /// Names MUST be unique. These values override same-named variables in the
1184    /// base launch configuration.
1185    #[serde_as(deserialize_as = "DefaultOnNull")]
1186    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1187    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1188    pub env: Vec<EnvVariable>,
1189    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1190    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1191    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1192    ///
1193    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1194    #[serde_as(deserialize_as = "DefaultOnError")]
1195    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1196    #[serde(default)]
1197    #[serde(rename = "_meta")]
1198    pub meta: Option<Meta>,
1199}
1200
1201impl AuthMethodTerminal {
1202    /// Builds [`AuthMethodTerminal`] with the required fields set. Optional fields start unset or empty.
1203    #[must_use]
1204    pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1205        Self {
1206            method_id: method_id.into(),
1207            name: name.into(),
1208            description: None,
1209            args: Vec::new(),
1210            env: Vec::new(),
1211            meta: None,
1212        }
1213    }
1214
1215    /// Additional arguments to append to the configured agent invocation for terminal auth.
1216    #[must_use]
1217    pub fn args(mut self, args: Vec<String>) -> Self {
1218        self.args = args;
1219        self
1220    }
1221
1222    /// Additional environment variables to set on the configured agent invocation for terminal auth.
1223    /// Names MUST be unique. These values override same-named variables in the
1224    /// base launch configuration.
1225    #[must_use]
1226    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1227        self.env = env;
1228        self
1229    }
1230
1231    /// Optional human-readable description of this authentication method.
1232    #[must_use]
1233    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1234        self.description = description.into_option();
1235        self
1236    }
1237
1238    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1239    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1240    /// do not recognize.
1241    ///
1242    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1243    #[must_use]
1244    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1245        self.meta = meta.into_option();
1246        self
1247    }
1248}
1249
1250// New session
1251
1252/// Request parameters for creating a new session.
1253///
1254/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/v2/session-setup#creating-a-session)
1255#[serde_as]
1256#[skip_serializing_none]
1257#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1259#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1260#[serde(rename_all = "camelCase")]
1261#[non_exhaustive]
1262pub struct NewSessionRequest {
1263    /// The working directory for this session. Must be an absolute path.
1264    pub cwd: AbsolutePath,
1265    /// Additional workspace roots for this session. Each path must be absolute.
1266    ///
1267    /// These expand the session's workspace scope without changing `cwd`, which
1268    /// remains the base for relative paths. When omitted or empty, no
1269    /// additional roots are activated for the new session.
1270    #[serde_as(deserialize_as = "DefaultOnNull")]
1271    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1272    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1273    pub additional_directories: Vec<AbsolutePath>,
1274    /// List of MCP (Model Context Protocol) servers the agent should connect to.
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 mcp_servers: Vec<McpServer>,
1279    /// **UNSTABLE**
1280    ///
1281    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1282    ///
1283    /// System-level instructions for the new session.
1284    ///
1285    /// Optional. Omitted and `null` are equivalent and mean the Agent uses its
1286    /// own default instructions. Clients MUST only send this when the Agent
1287    /// advertises `session.systemPrompt`.
1288    #[cfg(feature = "unstable_session_system_prompt")]
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            #[cfg(feature = "unstable_session_system_prompt")]
1312            system_prompt: None,
1313            meta: None,
1314        }
1315    }
1316
1317    /// Additional workspace roots for this session. Each path must be absolute.
1318    #[must_use]
1319    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1320    where
1321        I: IntoIterator<Item = P>,
1322        P: Into<AbsolutePath>,
1323    {
1324        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1325        self
1326    }
1327
1328    /// List of MCP (Model Context Protocol) servers the agent should connect to.
1329    #[must_use]
1330    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1331        self.mcp_servers = mcp_servers;
1332        self
1333    }
1334
1335    /// **UNSTABLE**
1336    ///
1337    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1338    ///
1339    /// System-level instructions for the new session.
1340    #[cfg(feature = "unstable_session_system_prompt")]
1341    #[must_use]
1342    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1343        self.system_prompt = system_prompt.into_option();
1344        self
1345    }
1346
1347    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1348    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1349    /// do not recognize.
1350    ///
1351    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1352    #[must_use]
1353    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1354        self.meta = meta.into_option();
1355        self
1356    }
1357}
1358
1359/// Response from creating a new session.
1360///
1361/// New sessions default to `idle` until a state update reports otherwise.
1362/// Updates may arrive before or after this response. Clients MUST NOT overwrite
1363/// an already-reported state when the response arrives.
1364///
1365/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/v2/session-setup#creating-a-session)
1366#[serde_as]
1367#[skip_serializing_none]
1368#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1369#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1370#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1371#[serde(rename_all = "camelCase")]
1372#[non_exhaustive]
1373pub struct NewSessionResponse {
1374    /// Unique identifier for the created session.
1375    ///
1376    /// Used in all subsequent requests for this conversation.
1377    pub session_id: SessionId,
1378    /// Initial session configuration options.
1379    ///
1380    /// Optional. Omitted or empty means no config options are advertised.
1381    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1382    /// Later `config_option_update` notifications replace this list.
1383    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1384    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1385    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1386    pub config_options: Vec<SessionConfigOption>,
1387    /// Initial commands the agent can execute in this session.
1388    ///
1389    /// Optional. Omitted or empty means no initial commands are advertised.
1390    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1391    /// Later `available_commands_update` notifications replace this list.
1392    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1393    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1394    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1395    pub available_commands: Vec<AvailableCommand>,
1396    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1397    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1398    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1399    ///
1400    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1401    #[serde_as(deserialize_as = "DefaultOnError")]
1402    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1403    #[serde(default)]
1404    #[serde(rename = "_meta")]
1405    pub meta: Option<Meta>,
1406}
1407
1408impl NewSessionResponse {
1409    /// Builds [`NewSessionResponse`] with the required response fields set. Optional fields start unset or empty.
1410    #[must_use]
1411    pub fn new(session_id: impl Into<SessionId>) -> Self {
1412        Self {
1413            session_id: session_id.into(),
1414            config_options: Vec::new(),
1415            available_commands: Vec::new(),
1416            meta: None,
1417        }
1418    }
1419
1420    /// Initial session configuration options.
1421    ///
1422    /// Optional. Omitted or empty means no config options are advertised.
1423    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1424    /// Later `config_option_update` notifications replace this list.
1425    #[must_use]
1426    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1427        self.config_options = config_options;
1428        self
1429    }
1430
1431    /// Initial commands the agent can execute in this session.
1432    #[must_use]
1433    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1434        self.available_commands = available_commands;
1435        self
1436    }
1437
1438    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1439    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1440    /// do not recognize.
1441    ///
1442    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1443    #[must_use]
1444    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1445        self.meta = meta.into_option();
1446        self
1447    }
1448}
1449
1450// Fork session
1451
1452/// **UNSTABLE**
1453///
1454/// This capability is not part of the spec yet, and may be removed or changed at any point.
1455///
1456/// Request parameters for forking an existing session.
1457///
1458/// Creates a new session based on the context of an existing one, allowing
1459/// operations like generating summaries without affecting the original session's history.
1460///
1461/// Only available if the Agent supports the `session.fork` capability.
1462#[cfg(feature = "unstable_session_fork")]
1463#[serde_as]
1464#[skip_serializing_none]
1465#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1466#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1467#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1468#[serde(rename_all = "camelCase")]
1469#[non_exhaustive]
1470pub struct ForkSessionRequest {
1471    /// The ID of the session to fork.
1472    pub session_id: SessionId,
1473    /// The working directory for this session. Must be an absolute path.
1474    pub cwd: AbsolutePath,
1475    /// Additional workspace roots to activate for this session. Each path must be absolute.
1476    ///
1477    /// When omitted or empty, no additional roots are activated. When non-empty,
1478    /// this is the complete resulting additional-root list for the forked
1479    /// session.
1480    #[serde_as(deserialize_as = "DefaultOnNull")]
1481    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1482    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1483    pub additional_directories: Vec<AbsolutePath>,
1484    /// List of MCP servers to connect to for this session.
1485    #[serde_as(deserialize_as = "DefaultOnNull")]
1486    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1487    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1488    pub mcp_servers: Vec<McpServer>,
1489    /// **UNSTABLE**
1490    ///
1491    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1492    ///
1493    /// System-level instructions for the forked session.
1494    ///
1495    /// Optional. Omitted and `null` are equivalent. The Agent may then keep a
1496    /// system prompt it stored with the source session or use its default
1497    /// instructions. Clients MUST only send this when the Agent advertises
1498    /// `session.systemPrompt`, and SHOULD send the same value they used for the
1499    /// source session.
1500    #[cfg(feature = "unstable_session_system_prompt")]
1501    #[serde(default)]
1502    pub system_prompt: Option<SystemPrompt>,
1503    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1504    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1505    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1506    ///
1507    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1508    #[serde_as(deserialize_as = "DefaultOnError")]
1509    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1510    #[serde(default)]
1511    #[serde(rename = "_meta")]
1512    pub meta: Option<Meta>,
1513}
1514
1515#[cfg(feature = "unstable_session_fork")]
1516impl ForkSessionRequest {
1517    /// Builds [`ForkSessionRequest`] with the required request fields set. Optional fields start unset or empty.
1518    #[must_use]
1519    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1520        Self {
1521            session_id: session_id.into(),
1522            cwd: cwd.into(),
1523            additional_directories: vec![],
1524            mcp_servers: vec![],
1525            #[cfg(feature = "unstable_session_system_prompt")]
1526            system_prompt: None,
1527            meta: None,
1528        }
1529    }
1530
1531    /// Additional workspace roots to activate for this session. Each path must be absolute.
1532    #[must_use]
1533    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1534    where
1535        I: IntoIterator<Item = P>,
1536        P: Into<AbsolutePath>,
1537    {
1538        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1539        self
1540    }
1541
1542    /// List of MCP servers to connect to for this session.
1543    #[must_use]
1544    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1545        self.mcp_servers = mcp_servers;
1546        self
1547    }
1548
1549    /// **UNSTABLE**
1550    ///
1551    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1552    ///
1553    /// System-level instructions for the forked session.
1554    #[cfg(feature = "unstable_session_system_prompt")]
1555    #[must_use]
1556    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1557        self.system_prompt = system_prompt.into_option();
1558        self
1559    }
1560
1561    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1562    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1563    /// do not recognize.
1564    ///
1565    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1566    #[must_use]
1567    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1568        self.meta = meta.into_option();
1569        self
1570    }
1571}
1572
1573/// **UNSTABLE**
1574///
1575/// This capability is not part of the spec yet, and may be removed or changed at any point.
1576///
1577/// Response from forking an existing session.
1578#[cfg(feature = "unstable_session_fork")]
1579#[serde_as]
1580#[skip_serializing_none]
1581#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1583#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1584#[serde(rename_all = "camelCase")]
1585#[non_exhaustive]
1586pub struct ForkSessionResponse {
1587    /// Unique identifier for the newly created forked session.
1588    pub session_id: SessionId,
1589    /// Initial session configuration options.
1590    ///
1591    /// Optional. Omitted or empty means no config options are advertised.
1592    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1593    /// Later `config_option_update` notifications replace this list.
1594    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1595    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1596    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1597    pub config_options: Vec<SessionConfigOption>,
1598    /// Initial commands the agent can execute in this session.
1599    ///
1600    /// Optional. Omitted or empty means no initial commands are advertised.
1601    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1602    /// Later `available_commands_update` notifications replace this list.
1603    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1604    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1605    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1606    pub available_commands: Vec<AvailableCommand>,
1607    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1608    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1609    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1610    ///
1611    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1612    #[serde_as(deserialize_as = "DefaultOnError")]
1613    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1614    #[serde(default)]
1615    #[serde(rename = "_meta")]
1616    pub meta: Option<Meta>,
1617}
1618
1619#[cfg(feature = "unstable_session_fork")]
1620impl ForkSessionResponse {
1621    /// Builds [`ForkSessionResponse`] with the required response fields set. Optional fields start unset or empty.
1622    #[must_use]
1623    pub fn new(session_id: impl Into<SessionId>) -> Self {
1624        Self {
1625            session_id: session_id.into(),
1626            config_options: Vec::new(),
1627            available_commands: Vec::new(),
1628            meta: None,
1629        }
1630    }
1631
1632    /// Initial session configuration options.
1633    ///
1634    /// Optional. Omitted or empty means no config options are advertised.
1635    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1636    /// Later `config_option_update` notifications replace this list.
1637    #[must_use]
1638    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1639        self.config_options = config_options;
1640        self
1641    }
1642
1643    /// Initial commands the agent can execute in this session.
1644    #[must_use]
1645    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1646        self.available_commands = available_commands;
1647        self
1648    }
1649
1650    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1651    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1652    /// do not recognize.
1653    ///
1654    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1655    #[must_use]
1656    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1657        self.meta = meta.into_option();
1658        self
1659    }
1660}
1661
1662// Resume session
1663
1664/// Request parameters for resuming an existing session.
1665///
1666/// Resumes an existing session and optionally replays retained conversation
1667/// history according to `replayFrom`.
1668#[serde_as]
1669#[skip_serializing_none]
1670#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1671#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1672#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1673#[serde(rename_all = "camelCase")]
1674#[non_exhaustive]
1675pub struct ResumeSessionRequest {
1676    /// The ID of the session to resume.
1677    pub session_id: SessionId,
1678    /// The working directory for this session. Must be an absolute path.
1679    ///
1680    /// Clients MUST send the session's working directory. Agents MAY reject a different directory
1681    /// with `-32602`, and MAY normalize paths when comparing.
1682    pub cwd: AbsolutePath,
1683    /// Additional workspace roots to activate for this session. Each path must be absolute.
1684    ///
1685    /// When omitted or empty, no additional roots are activated. When non-empty,
1686    /// this is the complete resulting additional-root list for the resumed
1687    /// session. It may differ from any previously used or reported list as long as
1688    /// the request `cwd` matches the session's `cwd`.
1689    #[serde_as(deserialize_as = "DefaultOnNull")]
1690    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1691    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1692    pub additional_directories: Vec<AbsolutePath>,
1693    /// List of MCP servers to connect to for this session.
1694    ///
1695    /// The complete set of Client-provided servers. Omitted and `[]` mean none. Agents SHOULD
1696    /// NOT restore servers from earlier requests.
1697    #[serde_as(deserialize_as = "DefaultOnNull")]
1698    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1699    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1700    pub mcp_servers: Vec<McpServer>,
1701    /// Inclusive cursor describing where conversation replay should begin.
1702    ///
1703    /// Optional. Omitted and `null` are equivalent and mean the Agent should resume without
1704    /// replaying previous conversation history. Replay cursors are inclusive:
1705    /// replay includes the position identified by the cursor. Supplying
1706    /// `{ "type": "start" }` means the Agent should replay all retained
1707    /// conversation history before responding.
1708    ///
1709    /// Agents MUST reject unsupported cursor types or malformed cursor values
1710    /// with `-32602` (Invalid params), rather than substituting another cursor
1711    /// or resuming without replay.
1712    #[serde(default)]
1713    pub replay_from: Option<ReplayFrom>,
1714    /// **UNSTABLE**
1715    ///
1716    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1717    ///
1718    /// System-level instructions for the resumed session.
1719    ///
1720    /// Optional. Omitted and `null` are equivalent. The Agent may then keep a
1721    /// system prompt it stored with the session or use its default instructions.
1722    /// Clients MUST only send this when the Agent advertises
1723    /// `session.systemPrompt`, and SHOULD send the same value they used when the
1724    /// session was created.
1725    #[cfg(feature = "unstable_session_system_prompt")]
1726    #[serde(default)]
1727    pub system_prompt: Option<SystemPrompt>,
1728    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1729    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1730    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1731    ///
1732    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1733    #[serde_as(deserialize_as = "DefaultOnError")]
1734    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1735    #[serde(default)]
1736    #[serde(rename = "_meta")]
1737    pub meta: Option<Meta>,
1738}
1739
1740impl ResumeSessionRequest {
1741    /// Builds [`ResumeSessionRequest`] with the required request fields set. Optional fields start unset or empty.
1742    #[must_use]
1743    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1744        Self {
1745            session_id: session_id.into(),
1746            cwd: cwd.into(),
1747            additional_directories: vec![],
1748            mcp_servers: vec![],
1749            replay_from: None,
1750            #[cfg(feature = "unstable_session_system_prompt")]
1751            system_prompt: None,
1752            meta: None,
1753        }
1754    }
1755
1756    /// Additional workspace roots to activate for this session. Each path must be absolute.
1757    #[must_use]
1758    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1759    where
1760        I: IntoIterator<Item = P>,
1761        P: Into<AbsolutePath>,
1762    {
1763        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1764        self
1765    }
1766
1767    /// List of MCP servers to connect to for this session.
1768    #[must_use]
1769    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1770        self.mcp_servers = mcp_servers;
1771        self
1772    }
1773
1774    /// Inclusive cursor describing where conversation replay should begin.
1775    ///
1776    /// Omitted and `null` are equivalent and mean the Agent should resume without replaying
1777    /// previous conversation history. Replay cursors are inclusive: replay
1778    /// includes the position identified by the cursor. Supplying
1779    /// `{ "type": "start" }` means the Agent should replay all retained
1780    /// conversation history before responding.
1781    #[must_use]
1782    pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1783        self.replay_from = replay_from.into_option();
1784        self
1785    }
1786
1787    /// **UNSTABLE**
1788    ///
1789    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1790    ///
1791    /// System-level instructions for the resumed session.
1792    #[cfg(feature = "unstable_session_system_prompt")]
1793    #[must_use]
1794    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1795        self.system_prompt = system_prompt.into_option();
1796        self
1797    }
1798
1799    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1800    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1801    /// do not recognize.
1802    ///
1803    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1804    #[must_use]
1805    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1806        self.meta = meta.into_option();
1807        self
1808    }
1809}
1810
1811/// Inclusive cursor describing where replayed session history should begin.
1812///
1813/// Replay includes the position identified by the cursor.
1814#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1815#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1816#[serde(tag = "type", rename_all = "snake_case")]
1817#[non_exhaustive]
1818pub enum ReplayFrom {
1819    /// Replay all retained conversation history from its first replayable entry.
1820    Start(ReplayFromStart),
1821    /// Custom or future replay cursor.
1822    ///
1823    /// Values beginning with `_` are reserved for implementation-specific
1824    /// extensions. Unknown values that do not begin with `_` are reserved for
1825    /// future ACP variants.
1826    ///
1827    /// Intermediaries SHOULD preserve the raw payload when storing, replaying,
1828    /// proxying, or forwarding requests. Agents that do not support this cursor
1829    /// MUST reject the request with `-32602` (Invalid params), rather than
1830    /// substituting another cursor or resuming without replay.
1831    #[serde(untagged)]
1832    Other(OtherReplayFrom),
1833}
1834
1835impl From<ReplayFromStart> for ReplayFrom {
1836    fn from(replay_from: ReplayFromStart) -> Self {
1837        Self::Start(replay_from)
1838    }
1839}
1840
1841/// Inclusive replay cursor requesting replay from the start of retained conversation history.
1842#[serde_as]
1843#[skip_serializing_none]
1844#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1845#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1846#[serde(rename_all = "camelCase")]
1847#[non_exhaustive]
1848pub struct ReplayFromStart {
1849    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1850    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1851    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1852    ///
1853    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1854    #[serde_as(deserialize_as = "DefaultOnError")]
1855    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1856    #[serde(default)]
1857    #[serde(rename = "_meta")]
1858    pub meta: Option<Meta>,
1859}
1860
1861impl ReplayFromStart {
1862    /// Builds [`ReplayFromStart`].
1863    #[must_use]
1864    pub fn new() -> Self {
1865        Self::default()
1866    }
1867
1868    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1869    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1870    /// do not recognize.
1871    ///
1872    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1873    #[must_use]
1874    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1875        self.meta = meta.into_option();
1876        self
1877    }
1878}
1879
1880/// Custom or future replay cursor payload.
1881#[serde_as]
1882#[skip_serializing_none]
1883#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1884#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1885#[cfg_attr(feature = "schemars", schemars(inline))]
1886#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1887#[serde(rename_all = "camelCase")]
1888#[non_exhaustive]
1889pub struct OtherReplayFrom {
1890    /// Custom or future replay cursor type.
1891    ///
1892    /// Values beginning with `_` are reserved for implementation-specific
1893    /// extensions. Unknown values that do not begin with `_` are reserved for
1894    /// future ACP variants.
1895    #[serde(rename = "type")]
1896    pub type_: String,
1897    /// The remaining fields of the unknown replay cursor payload, preserved verbatim,
1898    /// including `_meta`.
1899    #[serde(flatten)]
1900    pub fields: BTreeMap<String, serde_json::Value>,
1901}
1902
1903impl OtherReplayFrom {
1904    /// Builds [`OtherReplayFrom`] from an unknown discriminator and preserves the remaining extension fields.
1905    #[must_use]
1906    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1907        fields.remove("type");
1908        Self {
1909            type_: type_.into(),
1910            fields,
1911        }
1912    }
1913}
1914
1915impl<'de> Deserialize<'de> for OtherReplayFrom {
1916    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1917    where
1918        D: serde::Deserializer<'de>,
1919    {
1920        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1921        let type_ = fields
1922            .remove("type")
1923            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1924        let serde_json::Value::String(type_) = type_ else {
1925            return Err(serde::de::Error::custom("`type` must be a string"));
1926        };
1927
1928        if is_known_replay_from_type(&type_) {
1929            return Err(serde::de::Error::custom(format!(
1930                "known replay cursor `{type_}` did not match its schema"
1931            )));
1932        }
1933
1934        Ok(Self { type_, fields })
1935    }
1936}
1937
1938fn is_known_replay_from_type(type_: &str) -> bool {
1939    matches!(type_, "start")
1940}
1941
1942#[cfg(feature = "schemars")]
1943fn other_replay_from_schema(schema: &mut Schema) {
1944    super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1945}
1946
1947crate::serde_util::default_on_null! {
1948    /// Response from resuming an existing session.
1949    ///
1950    /// The agent MUST send a current `state_update` after any requested history
1951    /// replay and before this response, even without replay. The update reports
1952    /// foreground state after resuming. The response alone does not imply `idle`.
1953    ///
1954    /// See protocol docs: [Current-State Synchronization](https://agentclientprotocol.com/protocol/v2/session-setup#current-state-synchronization)
1955    #[serde_as]
1956    #[skip_serializing_none]
1957    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1958    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1959    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1960    #[serde(rename_all = "camelCase")]
1961    #[non_exhaustive]
1962    pub struct ResumeSessionResponse {
1963        /// Initial session configuration options.
1964        ///
1965        /// Optional. Omitted or empty means no config options are advertised.
1966        /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1967        /// Later `config_option_update` notifications replace this list.
1968        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1969        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1970        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1971        pub config_options: Vec<SessionConfigOption>,
1972        /// Initial commands the agent can execute in this session.
1973        ///
1974        /// Optional. Omitted or empty means no initial commands are advertised.
1975        /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1976        /// Later `available_commands_update` notifications replace this list.
1977        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1978        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1979        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1980        pub available_commands: Vec<AvailableCommand>,
1981        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1982        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1983        /// Implementations MUST NOT assign meaning to keys they do not recognize.
1984        ///
1985        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1986        #[serde_as(deserialize_as = "DefaultOnError")]
1987        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1988        #[serde(default)]
1989        #[serde(rename = "_meta")]
1990        pub meta: Option<Meta>,
1991    }
1992}
1993
1994impl ResumeSessionResponse {
1995    /// Builds [`ResumeSessionResponse`] with the required response fields set. Optional fields start unset or empty.
1996    #[must_use]
1997    pub fn new() -> Self {
1998        Self::default()
1999    }
2000
2001    /// Initial session configuration options.
2002    ///
2003    /// Optional. Omitted or empty means no config options are advertised.
2004    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
2005    /// Later `config_option_update` notifications replace this list.
2006    #[must_use]
2007    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
2008        self.config_options = config_options;
2009        self
2010    }
2011
2012    /// Initial commands the agent can execute in this session.
2013    #[must_use]
2014    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
2015        self.available_commands = available_commands;
2016        self
2017    }
2018
2019    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2020    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2021    /// do not recognize.
2022    ///
2023    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2024    #[must_use]
2025    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2026        self.meta = meta.into_option();
2027        self
2028    }
2029}
2030
2031// Close session
2032
2033/// Request parameters for closing an active session.
2034///
2035/// The Agent MUST cancel any ongoing work related to the session as if `session/cancel`
2036/// was called, then free the resources associated with the session. Unlike `session/cancel`,
2037/// closing also stops the session's background work. After the close response, the Agent
2038/// sends no other messages for the session on that connection.
2039///
2040/// Closing a session that does not exist or is not active SHOULD succeed, and Clients SHOULD
2041/// treat a `-32002` response as a successful close.
2042///
2043/// When closing cancels active foreground work, the Agent MUST send the final
2044/// updates for that work and the idle `state_update` with `stopReason: "cancelled"`
2045/// before responding successfully to `session/close`. Closing an idle session
2046/// does not itself require another idle update.
2047///
2048/// Closing also cancels pending `session/prompt` requests whose messages have not
2049/// been inserted, including held prompts. If closing prevents a prompt's user message from being inserted, the
2050/// Agent MUST answer that request with `-32800` (Request Cancelled). If the message
2051/// has already been inserted, the Agent MUST respond successfully with its
2052/// `messageId`. Prompt responses need not precede the close response.
2053#[serde_as]
2054#[skip_serializing_none]
2055#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2056#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2057#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2058#[serde(rename_all = "camelCase")]
2059#[non_exhaustive]
2060pub struct CloseSessionRequest {
2061    /// The ID of the session to close.
2062    pub session_id: SessionId,
2063    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2064    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2065    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2066    ///
2067    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2068    #[serde_as(deserialize_as = "DefaultOnError")]
2069    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2070    #[serde(default)]
2071    #[serde(rename = "_meta")]
2072    pub meta: Option<Meta>,
2073}
2074
2075impl CloseSessionRequest {
2076    /// Builds [`CloseSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2077    #[must_use]
2078    pub fn new(session_id: impl Into<SessionId>) -> Self {
2079        Self {
2080            session_id: session_id.into(),
2081            meta: None,
2082        }
2083    }
2084
2085    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2086    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2087    /// do not recognize.
2088    ///
2089    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2090    #[must_use]
2091    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2092        self.meta = meta.into_option();
2093        self
2094    }
2095}
2096
2097crate::serde_util::default_on_null! {
2098    /// Response from closing a session.
2099    #[serde_as]
2100    #[skip_serializing_none]
2101    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2102    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2103    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2104    #[serde(rename_all = "camelCase")]
2105    #[non_exhaustive]
2106    pub struct CloseSessionResponse {
2107        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2108        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2109        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2110        ///
2111        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2112        #[serde_as(deserialize_as = "DefaultOnError")]
2113        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2114        #[serde(default)]
2115        #[serde(rename = "_meta")]
2116        pub meta: Option<Meta>,
2117    }
2118}
2119
2120impl CloseSessionResponse {
2121    /// Builds [`CloseSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2122    #[must_use]
2123    pub fn new() -> Self {
2124        Self::default()
2125    }
2126
2127    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2128    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2129    /// do not recognize.
2130    ///
2131    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2132    #[must_use]
2133    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2134        self.meta = meta.into_option();
2135        self
2136    }
2137}
2138
2139// Session status
2140
2141/// **UNSTABLE**
2142///
2143/// This capability is not part of the spec yet, and may be removed or changed at any point.
2144///
2145/// Request parameters for checking the status of a session.
2146///
2147/// Only available if the Agent supports the `session.status` capability.
2148///
2149/// `session/status` MUST NOT have side effects. The Agent does not load, resume, create,
2150/// close, or change the session, and does not attach the requesting connection to it.
2151/// A session the Agent does not know gets `-32002` with the requested `sessionId` in `data`.
2152#[cfg(feature = "unstable_session_status")]
2153#[serde_as]
2154#[skip_serializing_none]
2155#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2156#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2157#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2158#[serde(rename_all = "camelCase")]
2159#[non_exhaustive]
2160pub struct SessionStatusRequest {
2161    /// The ID of the session to check.
2162    pub session_id: SessionId,
2163    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2164    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2165    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2166    ///
2167    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2168    #[serde_as(deserialize_as = "DefaultOnError")]
2169    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2170    #[serde(default)]
2171    #[serde(rename = "_meta")]
2172    pub meta: Option<Meta>,
2173}
2174
2175#[cfg(feature = "unstable_session_status")]
2176impl SessionStatusRequest {
2177    /// Builds [`SessionStatusRequest`] with the required request fields set. Optional fields start unset or empty.
2178    #[must_use]
2179    pub fn new(session_id: impl Into<SessionId>) -> Self {
2180        Self {
2181            session_id: session_id.into(),
2182            meta: None,
2183        }
2184    }
2185
2186    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2187    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2188    /// do not recognize.
2189    ///
2190    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2191    #[must_use]
2192    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2193        self.meta = meta.into_option();
2194        self
2195    }
2196}
2197
2198/// **UNSTABLE**
2199///
2200/// This capability is not part of the spec yet, and may be removed or changed at any point.
2201///
2202/// Response from checking the status of a session.
2203///
2204/// The result is a snapshot. A session reported `open` may be closed or evicted before
2205/// the Client's next request.
2206#[cfg(feature = "unstable_session_status")]
2207#[serde_as]
2208#[skip_serializing_none]
2209#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2211#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2212#[serde(rename_all = "camelCase")]
2213#[non_exhaustive]
2214pub struct SessionStatusResponse {
2215    /// Whether the Agent has the session in memory.
2216    ///
2217    /// Required. Clients MUST treat a value they do not recognize as `saved`.
2218    pub status: SessionStatus,
2219    /// The session's current foreground state, as the same snapshot the Agent sends in
2220    /// `state_update` notifications.
2221    ///
2222    /// Optional. Agents SHOULD include it when `status` is `open`. Omitted, `null`, and
2223    /// malformed values mean the Agent is not reporting foreground state, and Clients
2224    /// MUST NOT read that as `idle`. This is a snapshot, not a new transition. Clients
2225    /// MUST NOT count an `idle` snapshot as a new completion.
2226    #[serde_as(deserialize_as = "DefaultOnError")]
2227    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2228    #[serde(default)]
2229    pub state: Option<StateUpdate>,
2230    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2231    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2232    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2233    ///
2234    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2235    #[serde_as(deserialize_as = "DefaultOnError")]
2236    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2237    #[serde(default)]
2238    #[serde(rename = "_meta")]
2239    pub meta: Option<Meta>,
2240}
2241
2242#[cfg(feature = "unstable_session_status")]
2243impl SessionStatusResponse {
2244    /// Builds [`SessionStatusResponse`] with the required response fields set. Optional fields start unset or empty.
2245    #[must_use]
2246    pub fn new(status: SessionStatus) -> Self {
2247        Self {
2248            status,
2249            state: None,
2250            meta: None,
2251        }
2252    }
2253
2254    /// The session's current foreground state, as the same snapshot the Agent sends in
2255    /// `state_update` notifications.
2256    #[must_use]
2257    pub fn state(mut self, state: impl IntoOption<StateUpdate>) -> Self {
2258        self.state = state.into_option();
2259        self
2260    }
2261
2262    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2263    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2264    /// do not recognize.
2265    ///
2266    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2267    #[must_use]
2268    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2269        self.meta = meta.into_option();
2270        self
2271    }
2272}
2273
2274/// **UNSTABLE**
2275///
2276/// This capability is not part of the spec yet, and may be removed or changed at any point.
2277///
2278/// Whether the Agent has a session in memory, as reported by `session/status`.
2279#[cfg(feature = "unstable_session_status")]
2280#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2281#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2282#[serde(rename_all = "snake_case")]
2283#[non_exhaustive]
2284pub enum SessionStatus {
2285    /// The Agent has the session in memory, for any connection.
2286    ///
2287    /// This does not mean the requesting connection is attached to it. A Client that did
2288    /// not create or resume the session on its own connection MUST still call
2289    /// `session/resume` before sending `session/prompt` or expecting
2290    /// `session/update` notifications.
2291    Open,
2292    /// The Agent knows the session and can resume it, but does not have it in memory.
2293    ///
2294    /// The Client can call `session/resume` to start it.
2295    Saved,
2296    /// Custom or future session status.
2297    ///
2298    /// Values beginning with `_` are reserved for implementation-specific
2299    /// extensions. Unknown values that do not begin with `_` are reserved for
2300    /// future ACP variants. Clients MUST treat a value they do not recognize as
2301    /// `saved`.
2302    #[serde(untagged)]
2303    Other(String),
2304}
2305
2306// Cancel tool call
2307
2308/// Request parameters for cancelling one tool call.
2309///
2310/// Clients MUST NOT send it unless the tool call currently advertises `capabilities.cancel`.
2311///
2312/// The Agent responds once it has accepted the cancellation. The response does not mean
2313/// the work has stopped. The Agent confirms it with a `tool_call_update` to a final
2314/// status: normally `cancelled`, or `completed` or `failed` if the work finished first.
2315///
2316/// The Agent responds with `-32602` if the tool call is unknown, already final, or not
2317/// cancellable. Cancelling a tool call does not change the foreground state by itself.
2318///
2319/// See protocol docs: [Cancelling Tool Calls](https://agentclientprotocol.com/protocol/v2/tool-calls#cancelling-tool-calls)
2320#[serde_as]
2321#[skip_serializing_none]
2322#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2323#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2324#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2325#[serde(rename_all = "camelCase")]
2326#[non_exhaustive]
2327pub struct CancelToolCallRequest {
2328    /// The ID of the session that owns the tool call.
2329    pub session_id: SessionId,
2330    /// The ID of the tool call to cancel.
2331    pub tool_call_id: ToolCallId,
2332    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2333    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2334    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2335    ///
2336    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2337    #[serde_as(deserialize_as = "DefaultOnError")]
2338    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2339    #[serde(default)]
2340    #[serde(rename = "_meta")]
2341    pub meta: Option<Meta>,
2342}
2343
2344impl CancelToolCallRequest {
2345    /// Builds [`CancelToolCallRequest`] with the required request fields set. Optional fields start unset or empty.
2346    #[must_use]
2347    pub fn new(session_id: impl Into<SessionId>, tool_call_id: impl Into<ToolCallId>) -> Self {
2348        Self {
2349            session_id: session_id.into(),
2350            tool_call_id: tool_call_id.into(),
2351            meta: None,
2352        }
2353    }
2354
2355    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2356    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2357    /// do not recognize.
2358    ///
2359    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2360    #[must_use]
2361    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2362        self.meta = meta.into_option();
2363        self
2364    }
2365}
2366
2367crate::serde_util::default_on_null! {
2368    /// Response from cancelling a tool call.
2369    #[serde_as]
2370    #[skip_serializing_none]
2371    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2372    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2373    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2374    #[serde(rename_all = "camelCase")]
2375    #[non_exhaustive]
2376    pub struct CancelToolCallResponse {
2377        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2378        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2379        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2380        ///
2381        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2382        #[serde_as(deserialize_as = "DefaultOnError")]
2383        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2384        #[serde(default)]
2385        #[serde(rename = "_meta")]
2386        pub meta: Option<Meta>,
2387    }
2388}
2389
2390impl CancelToolCallResponse {
2391    /// Builds [`CancelToolCallResponse`] with the required response fields set. Optional fields start unset or empty.
2392    #[must_use]
2393    pub fn new() -> Self {
2394        Self::default()
2395    }
2396
2397    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2398    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2399    /// do not recognize.
2400    ///
2401    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2402    #[must_use]
2403    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2404        self.meta = meta.into_option();
2405        self
2406    }
2407}
2408
2409// Cancel terminal
2410
2411/// Request parameters for cancelling one terminal.
2412///
2413/// Clients MUST NOT send it unless the terminal currently advertises `capabilities.cancel`.
2414///
2415/// The Agent responds once it has accepted the cancellation. The response does not mean
2416/// the work has stopped. The Agent confirms it with a `terminal_update` that sets
2417/// `exitStatus`. Cancelling stops the process but does not release the terminal, and does
2418/// not change the status of a tool call that references it.
2419///
2420/// The Agent responds with `-32602` if the terminal is unknown, has already exited, or is not
2421/// cancellable. Cancelling a terminal does not change the foreground state by itself.
2422///
2423/// See protocol docs: [Cancelling Terminals](https://agentclientprotocol.com/protocol/v2/tool-calls#cancelling-terminals)
2424#[serde_as]
2425#[skip_serializing_none]
2426#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2427#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2428#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2429#[serde(rename_all = "camelCase")]
2430#[non_exhaustive]
2431pub struct CancelTerminalRequest {
2432    /// The ID of the session that owns the terminal.
2433    pub session_id: SessionId,
2434    /// The ID of the terminal to cancel.
2435    pub terminal_id: TerminalId,
2436    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2437    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2438    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2439    ///
2440    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2441    #[serde_as(deserialize_as = "DefaultOnError")]
2442    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2443    #[serde(default)]
2444    #[serde(rename = "_meta")]
2445    pub meta: Option<Meta>,
2446}
2447
2448impl CancelTerminalRequest {
2449    /// Builds [`CancelTerminalRequest`] with the required request fields set. Optional fields start unset or empty.
2450    #[must_use]
2451    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
2452        Self {
2453            session_id: session_id.into(),
2454            terminal_id: terminal_id.into(),
2455            meta: None,
2456        }
2457    }
2458
2459    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2460    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2461    /// do not recognize.
2462    ///
2463    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2464    #[must_use]
2465    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2466        self.meta = meta.into_option();
2467        self
2468    }
2469}
2470
2471crate::serde_util::default_on_null! {
2472    /// Response from cancelling a terminal.
2473    #[serde_as]
2474    #[skip_serializing_none]
2475    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2476    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2477    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2478    #[serde(rename_all = "camelCase")]
2479    #[non_exhaustive]
2480    pub struct CancelTerminalResponse {
2481        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2482        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2483        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2484        ///
2485        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2486        #[serde_as(deserialize_as = "DefaultOnError")]
2487        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2488        #[serde(default)]
2489        #[serde(rename = "_meta")]
2490        pub meta: Option<Meta>,
2491    }
2492}
2493
2494impl CancelTerminalResponse {
2495    /// Builds [`CancelTerminalResponse`] with the required response fields set. Optional fields start unset or empty.
2496    #[must_use]
2497    pub fn new() -> Self {
2498        Self::default()
2499    }
2500
2501    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2502    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2503    /// do not recognize.
2504    ///
2505    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2506    #[must_use]
2507    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2508        self.meta = meta.into_option();
2509        self
2510    }
2511}
2512
2513// List sessions
2514
2515/// An opaque cursor used to paginate `session/list` results.
2516#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2517#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2518#[serde(transparent)]
2519#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
2520#[non_exhaustive]
2521pub struct SessionListCursor(pub Arc<str>);
2522
2523impl SessionListCursor {
2524    /// Wraps a protocol string as a typed [`SessionListCursor`].
2525    #[must_use]
2526    pub fn new(cursor: impl Into<Self>) -> Self {
2527        cursor.into()
2528    }
2529}
2530
2531impl AsRef<str> for SessionListCursor {
2532    fn as_ref(&self) -> &str {
2533        &self.0
2534    }
2535}
2536
2537impl From<&String> for SessionListCursor {
2538    fn from(cursor: &String) -> Self {
2539        Self(cursor.as_str().into())
2540    }
2541}
2542
2543macro_rules! impl_session_list_cursor_option_conversion {
2544    ($source:ty) => {
2545        impl IntoOption<SessionListCursor> for $source {
2546            fn into_option(self) -> Option<SessionListCursor> {
2547                Some(self.into())
2548            }
2549        }
2550    };
2551}
2552
2553impl_session_list_cursor_option_conversion!(Arc<str>);
2554impl_session_list_cursor_option_conversion!(String);
2555impl_session_list_cursor_option_conversion!(&str);
2556impl_session_list_cursor_option_conversion!(&mut str);
2557impl_session_list_cursor_option_conversion!(&String);
2558impl_session_list_cursor_option_conversion!(Box<str>);
2559impl_session_list_cursor_option_conversion!(Cow<'_, str>);
2560
2561crate::serde_util::default_on_null! {
2562    /// Request parameters for listing existing sessions.
2563    #[serde_as]
2564    #[skip_serializing_none]
2565    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2566    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2567    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2568    #[serde(rename_all = "camelCase")]
2569    #[non_exhaustive]
2570    pub struct ListSessionsRequest {
2571        /// Filter sessions by working directory. Must be an absolute path.
2572        ///
2573        /// Only sessions whose `cwd` is this same directory match. Subdirectories and
2574        /// `additionalDirectories` don't. Agents MAY normalize paths when comparing.
2575        ///
2576        /// Omitted and `null` are equivalent and mean sessions are not filtered by working directory.
2577        #[serde(default)]
2578        pub cwd: Option<AbsolutePath>,
2579        /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
2580        ///
2581        /// Omitted and `null` are equivalent and mean the first page is requested.
2582        #[serde(default)]
2583        pub cursor: Option<SessionListCursor>,
2584        /// **UNSTABLE**
2585        ///
2586        /// This capability is not part of the spec yet, and may be removed or changed at any point.
2587        ///
2588        /// Filter sessions by archive state.
2589        ///
2590        /// Optional. Omitted and `null` are equivalent and mean `exclude`. Only send this when
2591        /// the Agent advertises the `session.archive` capability. The filter combines with `cwd` and
2592        /// applies before pagination. Clients keep the same value when following `nextCursor`.
2593        #[cfg(feature = "unstable_session_archive")]
2594        #[serde(default)]
2595        pub archived: Option<SessionArchiveFilter>,
2596        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2597        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2598        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2599        ///
2600        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2601        #[serde_as(deserialize_as = "DefaultOnError")]
2602        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2603        #[serde(default)]
2604        #[serde(rename = "_meta")]
2605        pub meta: Option<Meta>,
2606    }
2607}
2608
2609impl ListSessionsRequest {
2610    /// Builds [`ListSessionsRequest`] with the required request fields set. Optional fields start unset or empty.
2611    #[must_use]
2612    pub fn new() -> Self {
2613        Self::default()
2614    }
2615
2616    /// Filter sessions by working directory. Must be an absolute path.
2617    #[must_use]
2618    pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
2619        self.cwd = cwd.into_option();
2620        self
2621    }
2622
2623    /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
2624    #[must_use]
2625    pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
2626        self.cursor = cursor.into_option();
2627        self
2628    }
2629
2630    /// **UNSTABLE**
2631    ///
2632    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2633    ///
2634    /// Filter sessions by archive state.
2635    #[cfg(feature = "unstable_session_archive")]
2636    #[must_use]
2637    pub fn archived(mut self, archived: impl IntoOption<SessionArchiveFilter>) -> Self {
2638        self.archived = archived.into_option();
2639        self
2640    }
2641
2642    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2643    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2644    /// do not recognize.
2645    ///
2646    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2647    #[must_use]
2648    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2649        self.meta = meta.into_option();
2650        self
2651    }
2652}
2653
2654/// Response from listing sessions.
2655#[serde_as]
2656#[skip_serializing_none]
2657#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2658#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2659#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2660#[serde(rename_all = "camelCase")]
2661#[non_exhaustive]
2662pub struct ListSessionsResponse {
2663    /// Array of session information objects.
2664    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2665    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2666    pub sessions: Vec<SessionInfo>,
2667    /// Opaque cursor token. If present, pass this in the next request's cursor parameter
2668    /// to fetch the next page. Omitted and `null` are equivalent and mean there are no more
2669    /// results. An empty string is a valid cursor, not the end of results.
2670    #[serde(default)]
2671    pub next_cursor: Option<SessionListCursor>,
2672    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2673    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2674    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2675    ///
2676    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2677    #[serde_as(deserialize_as = "DefaultOnError")]
2678    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2679    #[serde(default)]
2680    #[serde(rename = "_meta")]
2681    pub meta: Option<Meta>,
2682}
2683
2684impl ListSessionsResponse {
2685    /// Builds [`ListSessionsResponse`] with the required response fields set. Optional fields start unset or empty.
2686    #[must_use]
2687    pub fn new(sessions: Vec<SessionInfo>) -> Self {
2688        Self {
2689            sessions,
2690            next_cursor: None,
2691            meta: None,
2692        }
2693    }
2694
2695    /// Sets or clears the optional `nextCursor` field.
2696    #[must_use]
2697    pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
2698        self.next_cursor = next_cursor.into_option();
2699        self
2700    }
2701
2702    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2703    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2704    /// do not recognize.
2705    ///
2706    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2707    #[must_use]
2708    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2709        self.meta = meta.into_option();
2710        self
2711    }
2712}
2713
2714// Delete session
2715
2716/// Request parameters for deleting an existing session from `session/list`.
2717///
2718/// Only available if the Agent supports the `session.delete` capability.
2719#[serde_as]
2720#[skip_serializing_none]
2721#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2722#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2723#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2724#[serde(rename_all = "camelCase")]
2725#[non_exhaustive]
2726pub struct DeleteSessionRequest {
2727    /// The ID of the session to delete.
2728    pub session_id: SessionId,
2729    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2730    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2731    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2732    ///
2733    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2734    #[serde_as(deserialize_as = "DefaultOnError")]
2735    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2736    #[serde(default)]
2737    #[serde(rename = "_meta")]
2738    pub meta: Option<Meta>,
2739}
2740
2741impl DeleteSessionRequest {
2742    /// Builds [`DeleteSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2743    #[must_use]
2744    pub fn new(session_id: impl Into<SessionId>) -> Self {
2745        Self {
2746            session_id: session_id.into(),
2747            meta: None,
2748        }
2749    }
2750
2751    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2752    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2753    /// do not recognize.
2754    ///
2755    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2756    #[must_use]
2757    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2758        self.meta = meta.into_option();
2759        self
2760    }
2761}
2762
2763crate::serde_util::default_on_null! {
2764    /// Response from deleting a session.
2765    #[serde_as]
2766    #[skip_serializing_none]
2767    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2768    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2769    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2770    #[serde(rename_all = "camelCase")]
2771    #[non_exhaustive]
2772    pub struct DeleteSessionResponse {
2773        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2774        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2775        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2776        ///
2777        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2778        #[serde_as(deserialize_as = "DefaultOnError")]
2779        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2780        #[serde(default)]
2781        #[serde(rename = "_meta")]
2782        pub meta: Option<Meta>,
2783    }
2784}
2785
2786impl DeleteSessionResponse {
2787    /// Builds [`DeleteSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2788    #[must_use]
2789    pub fn new() -> Self {
2790        Self::default()
2791    }
2792
2793    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2794    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2795    /// do not recognize.
2796    ///
2797    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2798    #[must_use]
2799    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2800        self.meta = meta.into_option();
2801        self
2802    }
2803}
2804
2805// Archive session
2806
2807/// **UNSTABLE**
2808///
2809/// This capability is not part of the spec yet, and may be removed or changed at any point.
2810///
2811/// Request parameters for archiving a session.
2812///
2813/// Only available if the Agent supports the `session.archive` capability.
2814///
2815/// Archiving hides the session from default `session/list` results. It keeps the session ID
2816/// and saved history. If the session is active, the Agent MUST cancel any ongoing work and
2817/// free the session's resources as if `session/close` had been called, then archive it.
2818/// Archiving an archived session succeeds. A session that does not exist gets `-32002`.
2819///
2820#[cfg(feature = "unstable_session_archive")]
2821#[serde_as]
2822#[skip_serializing_none]
2823#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2824#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2825#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2826#[serde(rename_all = "camelCase")]
2827#[non_exhaustive]
2828pub struct ArchiveSessionRequest {
2829    /// The ID of the session to archive.
2830    pub session_id: SessionId,
2831    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2832    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2833    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2834    ///
2835    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2836    #[serde_as(deserialize_as = "DefaultOnError")]
2837    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2838    #[serde(default)]
2839    #[serde(rename = "_meta")]
2840    pub meta: Option<Meta>,
2841}
2842
2843#[cfg(feature = "unstable_session_archive")]
2844impl ArchiveSessionRequest {
2845    /// Builds [`ArchiveSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2846    #[must_use]
2847    pub fn new(session_id: impl Into<SessionId>) -> Self {
2848        Self {
2849            session_id: session_id.into(),
2850            meta: None,
2851        }
2852    }
2853
2854    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2855    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2856    /// do not recognize.
2857    ///
2858    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2859    #[must_use]
2860    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2861        self.meta = meta.into_option();
2862        self
2863    }
2864}
2865
2866#[cfg(feature = "unstable_session_archive")]
2867crate::serde_util::default_on_null! {
2868    /// **UNSTABLE**
2869    ///
2870    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2871    ///
2872    /// Response from archiving a session.
2873    #[serde_as]
2874    #[skip_serializing_none]
2875    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2876    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2877    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2878    #[serde(rename_all = "camelCase")]
2879    #[non_exhaustive]
2880    pub struct ArchiveSessionResponse {
2881        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2882        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2883        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2884        ///
2885        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2886        #[serde_as(deserialize_as = "DefaultOnError")]
2887        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2888        #[serde(default)]
2889        #[serde(rename = "_meta")]
2890        pub meta: Option<Meta>,
2891    }
2892}
2893
2894#[cfg(feature = "unstable_session_archive")]
2895impl ArchiveSessionResponse {
2896    /// Builds [`ArchiveSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2897    #[must_use]
2898    pub fn new() -> Self {
2899        Self::default()
2900    }
2901
2902    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2903    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2904    /// do not recognize.
2905    ///
2906    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2907    #[must_use]
2908    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2909        self.meta = meta.into_option();
2910        self
2911    }
2912}
2913
2914/// **UNSTABLE**
2915///
2916/// This capability is not part of the spec yet, and may be removed or changed at any point.
2917///
2918/// Request parameters for unarchiving a session.
2919///
2920/// Only available if the Agent supports the `session.archive` capability.
2921///
2922/// Unarchiving returns the session to default `session/list` results with the same ID and
2923/// saved history. It never loads or resumes the session. Clients that want to continue an
2924/// archived session SHOULD unarchive it before calling `session/resume`.
2925/// Unarchiving a session that is not archived succeeds. A session that does not exist
2926/// gets `-32002`.
2927///
2928#[cfg(feature = "unstable_session_archive")]
2929#[serde_as]
2930#[skip_serializing_none]
2931#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2932#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2933#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2934#[serde(rename_all = "camelCase")]
2935#[non_exhaustive]
2936pub struct UnarchiveSessionRequest {
2937    /// The ID of the session to unarchive.
2938    pub session_id: SessionId,
2939    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2940    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2941    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2942    ///
2943    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2944    #[serde_as(deserialize_as = "DefaultOnError")]
2945    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2946    #[serde(default)]
2947    #[serde(rename = "_meta")]
2948    pub meta: Option<Meta>,
2949}
2950
2951#[cfg(feature = "unstable_session_archive")]
2952impl UnarchiveSessionRequest {
2953    /// Builds [`UnarchiveSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2954    #[must_use]
2955    pub fn new(session_id: impl Into<SessionId>) -> Self {
2956        Self {
2957            session_id: session_id.into(),
2958            meta: None,
2959        }
2960    }
2961
2962    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2963    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2964    /// do not recognize.
2965    ///
2966    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2967    #[must_use]
2968    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2969        self.meta = meta.into_option();
2970        self
2971    }
2972}
2973
2974#[cfg(feature = "unstable_session_archive")]
2975crate::serde_util::default_on_null! {
2976    /// **UNSTABLE**
2977    ///
2978    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2979    ///
2980    /// Response from unarchiving a session.
2981    #[serde_as]
2982    #[skip_serializing_none]
2983    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2984    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2985    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2986    #[serde(rename_all = "camelCase")]
2987    #[non_exhaustive]
2988    pub struct UnarchiveSessionResponse {
2989        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2990        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2991        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2992        ///
2993        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2994        #[serde_as(deserialize_as = "DefaultOnError")]
2995        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2996        #[serde(default)]
2997        #[serde(rename = "_meta")]
2998        pub meta: Option<Meta>,
2999    }
3000}
3001
3002#[cfg(feature = "unstable_session_archive")]
3003impl UnarchiveSessionResponse {
3004    /// Builds [`UnarchiveSessionResponse`] with the required response fields set. Optional fields start unset or empty.
3005    #[must_use]
3006    pub fn new() -> Self {
3007        Self::default()
3008    }
3009
3010    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3011    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3012    /// do not recognize.
3013    ///
3014    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3015    #[must_use]
3016    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3017        self.meta = meta.into_option();
3018        self
3019    }
3020}
3021
3022/// **UNSTABLE**
3023///
3024/// This capability is not part of the spec yet, and may be removed or changed at any point.
3025///
3026/// Which sessions `session/list` returns based on their archive state.
3027///
3028/// Unknown values are preserved so they can be stored, proxied, or forwarded. An Agent MUST
3029/// return `-32602` for a value it does not support rather than treating it as `exclude`.
3030#[cfg(feature = "unstable_session_archive")]
3031#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3032#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
3033#[serde(rename_all = "snake_case")]
3034#[non_exhaustive]
3035pub enum SessionArchiveFilter {
3036    /// Only sessions that are not archived. This is the default.
3037    #[default]
3038    Exclude,
3039    /// Both archived sessions and sessions that are not archived.
3040    Include,
3041    /// Only archived sessions.
3042    Only,
3043    /// Custom or future archive filter.
3044    ///
3045    /// Values beginning with `_` are reserved for implementation-specific
3046    /// extensions. Unknown values that do not begin with `_` are reserved for
3047    /// future ACP variants.
3048    #[serde(untagged)]
3049    Other(String),
3050}
3051
3052/// Information about a session returned by session/list
3053#[serde_as]
3054#[skip_serializing_none]
3055#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3056#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3057#[serde(rename_all = "camelCase")]
3058#[non_exhaustive]
3059pub struct SessionInfo {
3060    /// Unique identifier for the session
3061    pub session_id: SessionId,
3062    /// The working directory for this session. Must be an absolute path.
3063    pub cwd: AbsolutePath,
3064    /// Additional workspace roots reported for this session. Each path must be absolute.
3065    ///
3066    /// When present, this is the complete ordered additional-root list reported
3067    /// by the Agent. Omitted and empty values are equivalent: the response
3068    /// reports no additional roots.
3069    #[serde_as(deserialize_as = "DefaultOnNull")]
3070    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
3071    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3072    pub additional_directories: Vec<AbsolutePath>,
3073
3074    /// Human-readable title for the session
3075    ///
3076    /// Omitted and `null` are equivalent and mean no title is provided.
3077    #[serde_as(deserialize_as = "DefaultOnError")]
3078    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3079    #[serde(default)]
3080    pub title: Option<String>,
3081    /// RFC 3339 timestamp of last activity.
3082    ///
3083    /// Omitted and `null` are equivalent and mean the last activity time is not reported.
3084    #[serde_as(deserialize_as = "DefaultOnError")]
3085    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
3086    #[serde(default)]
3087    pub updated_at: Option<String>,
3088    /// **UNSTABLE**
3089    ///
3090    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3091    ///
3092    /// Whether the session is archived.
3093    ///
3094    /// Optional. Omitted and `null` are equivalent and mean the archive state is not reported.
3095    /// Agents that advertise the `session.archive` capability MUST include it on every listed session.
3096    #[cfg(feature = "unstable_session_archive")]
3097    #[serde_as(deserialize_as = "DefaultOnError")]
3098    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3099    #[serde(default)]
3100    pub archived: Option<bool>,
3101    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3102    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3103    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3104    ///
3105    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3106    #[serde_as(deserialize_as = "DefaultOnError")]
3107    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3108    #[serde(default)]
3109    #[serde(rename = "_meta")]
3110    pub meta: Option<Meta>,
3111}
3112
3113impl SessionInfo {
3114    /// Builds [`SessionInfo`] with the required fields set. Optional fields start unset or empty.
3115    #[must_use]
3116    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
3117        Self {
3118            session_id: session_id.into(),
3119            cwd: cwd.into(),
3120            additional_directories: vec![],
3121            title: None,
3122            updated_at: None,
3123            #[cfg(feature = "unstable_session_archive")]
3124            archived: None,
3125            meta: None,
3126        }
3127    }
3128
3129    /// Additional workspace roots reported for this session. Each path must be absolute.
3130    #[must_use]
3131    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
3132    where
3133        I: IntoIterator<Item = P>,
3134        P: Into<AbsolutePath>,
3135    {
3136        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
3137        self
3138    }
3139
3140    /// Human-readable title for the session
3141    #[must_use]
3142    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
3143        self.title = title.into_option();
3144        self
3145    }
3146
3147    /// RFC 3339 timestamp of last activity.
3148    #[must_use]
3149    pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
3150        self.updated_at = updated_at.into_option();
3151        self
3152    }
3153
3154    /// **UNSTABLE**
3155    ///
3156    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3157    ///
3158    /// Whether the session is archived.
3159    #[cfg(feature = "unstable_session_archive")]
3160    #[must_use]
3161    pub fn archived(mut self, archived: impl IntoOption<bool>) -> Self {
3162        self.archived = archived.into_option();
3163        self
3164    }
3165
3166    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3167    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3168    /// do not recognize.
3169    ///
3170    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3171    #[must_use]
3172    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3173        self.meta = meta.into_option();
3174        self
3175    }
3176}
3177
3178// Set session title
3179
3180/// **UNSTABLE**
3181///
3182/// This capability is not part of the spec yet, and may be removed or changed at any point.
3183///
3184/// Request parameters for setting the title of a session.
3185///
3186/// Only available if the Agent supports the `session.title` capability.
3187///
3188/// The session does not need to be active on the connection. Any session the Agent would
3189/// return from `session/list` can be retitled. A session that does not exist gets `-32002`.
3190///
3191/// The Agent responds only after it has stored the title. After a successful response, the
3192/// Agent SHOULD NOT replace the title with one it generates automatically.
3193#[cfg(feature = "unstable_session_set_title")]
3194#[serde_as]
3195#[skip_serializing_none]
3196#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3197#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3198#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3199#[serde(rename_all = "camelCase")]
3200#[non_exhaustive]
3201pub struct SetSessionTitleRequest {
3202    /// The ID of the session to retitle.
3203    pub session_id: SessionId,
3204    /// The new title for the session.
3205    ///
3206    /// Required and non-null. Omission and explicit `null` are both invalid. The Agent MAY
3207    /// normalize the title before storing it, for example by trimming whitespace or
3208    /// truncating it. The Agent rejects a title that fails its validation, including one
3209    /// that is empty after normalization, with `-32602`.
3210    pub title: String,
3211    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3212    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3213    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3214    ///
3215    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3216    #[serde_as(deserialize_as = "DefaultOnError")]
3217    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3218    #[serde(default)]
3219    #[serde(rename = "_meta")]
3220    pub meta: Option<Meta>,
3221}
3222
3223#[cfg(feature = "unstable_session_set_title")]
3224impl SetSessionTitleRequest {
3225    /// Builds [`SetSessionTitleRequest`] with the required request fields set. Optional fields start unset or empty.
3226    #[must_use]
3227    pub fn new(session_id: impl Into<SessionId>, title: impl Into<String>) -> Self {
3228        Self {
3229            session_id: session_id.into(),
3230            title: title.into(),
3231            meta: None,
3232        }
3233    }
3234
3235    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3236    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3237    /// do not recognize.
3238    ///
3239    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3240    #[must_use]
3241    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3242        self.meta = meta.into_option();
3243        self
3244    }
3245}
3246
3247/// **UNSTABLE**
3248///
3249/// This capability is not part of the spec yet, and may be removed or changed at any point.
3250///
3251/// Response from setting the title of a session.
3252#[cfg(feature = "unstable_session_set_title")]
3253#[serde_as]
3254#[skip_serializing_none]
3255#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3256#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3257#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3258#[serde(rename_all = "camelCase")]
3259#[non_exhaustive]
3260pub struct SetSessionTitleResponse {
3261    /// The title the Agent stored.
3262    ///
3263    /// This differs from the requested title if the Agent normalized it. Clients SHOULD
3264    /// display this value rather than the one they sent.
3265    pub title: String,
3266    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3267    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3268    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3269    ///
3270    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3271    #[serde_as(deserialize_as = "DefaultOnError")]
3272    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3273    #[serde(default)]
3274    #[serde(rename = "_meta")]
3275    pub meta: Option<Meta>,
3276}
3277
3278#[cfg(feature = "unstable_session_set_title")]
3279impl SetSessionTitleResponse {
3280    /// Builds [`SetSessionTitleResponse`] with the required response fields set. Optional fields start unset or empty.
3281    #[must_use]
3282    pub fn new(title: impl Into<String>) -> Self {
3283        Self {
3284            title: title.into(),
3285            meta: None,
3286        }
3287    }
3288
3289    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3290    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3291    /// do not recognize.
3292    ///
3293    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3294    #[must_use]
3295    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3296        self.meta = meta.into_option();
3297        self
3298    }
3299}
3300
3301// Session config options
3302
3303/// Unique identifier for a session configuration option.
3304#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3305#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3306#[serde(transparent)]
3307#[from(forward)]
3308#[non_exhaustive]
3309pub struct SessionConfigId(pub Arc<str>);
3310
3311impl SessionConfigId {
3312    /// Wraps a protocol string as a typed [`SessionConfigId`].
3313    #[must_use]
3314    pub fn new(id: impl Into<Self>) -> Self {
3315        id.into()
3316    }
3317}
3318
3319/// Unique identifier for a session configuration option value.
3320#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3321#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3322#[serde(transparent)]
3323#[from(forward)]
3324#[non_exhaustive]
3325pub struct SessionConfigValueId(pub Arc<str>);
3326
3327impl SessionConfigValueId {
3328    /// Wraps a protocol string as a typed [`SessionConfigValueId`].
3329    #[must_use]
3330    pub fn new(id: impl Into<Self>) -> Self {
3331        id.into()
3332    }
3333}
3334
3335/// Unique identifier for a session configuration option value group.
3336#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3338#[serde(transparent)]
3339#[from(forward)]
3340#[non_exhaustive]
3341pub struct SessionConfigGroupId(pub Arc<str>);
3342
3343impl SessionConfigGroupId {
3344    /// Wraps a protocol string as a typed [`SessionConfigGroupId`].
3345    #[must_use]
3346    pub fn new(id: impl Into<Self>) -> Self {
3347        id.into()
3348    }
3349}
3350
3351/// A possible value for a session configuration option.
3352#[serde_as]
3353#[skip_serializing_none]
3354#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3355#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3356#[serde(rename_all = "camelCase")]
3357#[non_exhaustive]
3358pub struct SessionConfigSelectOption {
3359    /// Unique identifier for this option value.
3360    pub value: SessionConfigValueId,
3361    /// Human-readable label for this option value.
3362    pub name: String,
3363    /// Optional description for this option value.
3364    ///
3365    /// Omitted and `null` are equivalent and mean no description is provided.
3366    #[serde_as(deserialize_as = "DefaultOnError")]
3367    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3368    #[serde(default)]
3369    pub description: Option<String>,
3370    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3371    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3372    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3373    ///
3374    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3375    #[serde_as(deserialize_as = "DefaultOnError")]
3376    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3377    #[serde(default)]
3378    #[serde(rename = "_meta")]
3379    pub meta: Option<Meta>,
3380}
3381
3382impl SessionConfigSelectOption {
3383    /// Builds [`SessionConfigSelectOption`] with the required fields set. Optional fields start unset or empty.
3384    #[must_use]
3385    pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
3386        Self {
3387            value: value.into(),
3388            name: name.into(),
3389            description: None,
3390            meta: None,
3391        }
3392    }
3393
3394    /// Sets or clears the optional `description` field.
3395    #[must_use]
3396    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3397        self.description = description.into_option();
3398        self
3399    }
3400
3401    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3402    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3403    /// do not recognize.
3404    ///
3405    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3406    #[must_use]
3407    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3408        self.meta = meta.into_option();
3409        self
3410    }
3411}
3412
3413/// A group of possible values for a session configuration option.
3414#[serde_as]
3415#[skip_serializing_none]
3416#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3417#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3418#[serde(rename_all = "camelCase")]
3419#[non_exhaustive]
3420pub struct SessionConfigSelectGroup {
3421    /// Unique identifier for this group.
3422    pub group_id: SessionConfigGroupId,
3423    /// Human-readable label for this group.
3424    pub name: String,
3425    /// The set of option values in this group. MUST contain at least one value.
3426    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3427    #[cfg_attr(feature = "schemars", schemars(length(min = 1), extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3428    pub options: Vec<SessionConfigSelectOption>,
3429    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3430    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3431    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3432    ///
3433    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3434    #[serde_as(deserialize_as = "DefaultOnError")]
3435    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3436    #[serde(default)]
3437    #[serde(rename = "_meta")]
3438    pub meta: Option<Meta>,
3439}
3440
3441impl SessionConfigSelectGroup {
3442    /// Builds [`SessionConfigSelectGroup`] with the required fields set. Optional fields start unset or empty.
3443    #[must_use]
3444    pub fn new(
3445        group_id: impl Into<SessionConfigGroupId>,
3446        name: impl Into<String>,
3447        options: Vec<SessionConfigSelectOption>,
3448    ) -> Self {
3449        Self {
3450            group_id: group_id.into(),
3451            name: name.into(),
3452            options,
3453            meta: None,
3454        }
3455    }
3456
3457    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3458    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3459    /// do not recognize.
3460    ///
3461    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3462    #[must_use]
3463    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3464        self.meta = meta.into_option();
3465        self
3466    }
3467}
3468
3469/// Possible values for a session configuration option.
3470#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3471#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3472#[serde(untagged)]
3473#[non_exhaustive]
3474pub enum SessionConfigSelectOptions {
3475    /// A flat list of options with no grouping.
3476    Ungrouped(
3477        #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectOption>,
3478    ),
3479    /// A list of options grouped under headers.
3480    Grouped(
3481        #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectGroup>,
3482    ),
3483}
3484
3485impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
3486    fn from(options: Vec<SessionConfigSelectOption>) -> Self {
3487        SessionConfigSelectOptions::Ungrouped(options)
3488    }
3489}
3490
3491impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
3492    fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
3493        SessionConfigSelectOptions::Grouped(groups)
3494    }
3495}
3496
3497/// A single-value selector (dropdown) 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 SessionConfigSelect {
3504    /// The currently selected value. MUST be one of the `value`s in `options`.
3505    pub current_value: SessionConfigValueId,
3506    /// The set of selectable options. MUST contain at least one value, and each `value` MUST be
3507    /// unique within the option, including across groups.
3508    pub options: SessionConfigSelectOptions,
3509}
3510
3511impl SessionConfigSelect {
3512    /// Builds [`SessionConfigSelect`] with the required fields set. Optional fields start unset or empty.
3513    #[must_use]
3514    pub fn new(
3515        current_value: impl Into<SessionConfigValueId>,
3516        options: impl Into<SessionConfigSelectOptions>,
3517    ) -> Self {
3518        Self {
3519            current_value: current_value.into(),
3520            options: options.into(),
3521        }
3522    }
3523}
3524
3525/// A boolean on/off toggle session configuration option payload.
3526#[skip_serializing_none]
3527#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3528#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3529#[serde(rename_all = "camelCase")]
3530#[non_exhaustive]
3531pub struct SessionConfigBoolean {
3532    /// The current value of the boolean option.
3533    pub current_value: bool,
3534}
3535
3536impl SessionConfigBoolean {
3537    /// Builds [`SessionConfigBoolean`] with the required fields set. Optional fields start unset or empty.
3538    #[must_use]
3539    pub fn new(current_value: bool) -> Self {
3540        Self { current_value }
3541    }
3542}
3543
3544/// Semantic category for a session configuration option.
3545///
3546/// This is intended to help Clients distinguish broadly common selectors (e.g. model selector vs
3547/// session mode selector vs thought/reasoning level) for UX purposes (keyboard shortcuts, icons,
3548/// placement). It MUST NOT be required for correctness. Clients MUST handle missing or unknown
3549/// categories gracefully.
3550///
3551/// Category names beginning with `_` are free for custom use, like other ACP extension methods.
3552/// Category names that do not begin with `_` are reserved for the ACP spec.
3553#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3554#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3555#[serde(rename_all = "snake_case")]
3556#[non_exhaustive]
3557pub enum SessionConfigOptionCategory {
3558    /// Session mode selector.
3559    Mode,
3560    /// Model selector.
3561    Model,
3562    /// Model-related configuration parameter.
3563    ModelConfig,
3564    /// Thought/reasoning level selector.
3565    ThoughtLevel,
3566    /// Custom or future category.
3567    ///
3568    /// Values beginning with `_` are reserved for implementation-specific
3569    /// extensions. Unknown values that do not begin with `_` are reserved for
3570    /// future ACP variants.
3571    #[serde(untagged)]
3572    Other(String),
3573}
3574
3575/// Type-specific session configuration option payload.
3576#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3577#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3578#[serde(tag = "type", rename_all = "snake_case")]
3579#[non_exhaustive]
3580pub enum SessionConfigKind {
3581    /// Single-value selector (dropdown).
3582    Select(SessionConfigSelect),
3583    /// Boolean on/off toggle.
3584    Boolean(SessionConfigBoolean),
3585    /// Custom or future session configuration option payload.
3586    ///
3587    /// Values beginning with `_` are reserved for implementation-specific
3588    /// extensions. Unknown values that do not begin with `_` are reserved for
3589    /// future ACP variants.
3590    ///
3591    /// Clients that do not understand this option type should preserve the raw
3592    /// payload when storing, replaying, proxying, or forwarding configuration
3593    /// data, and otherwise ignore the option. The Agent keeps using its `currentValue`.
3594    #[serde(untagged)]
3595    Other(OtherSessionConfigKind),
3596}
3597
3598/// Custom or future session configuration option payload.
3599#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3600#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3601#[cfg_attr(feature = "schemars", schemars(inline))]
3602#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
3603#[serde(rename_all = "camelCase")]
3604#[non_exhaustive]
3605pub struct OtherSessionConfigKind {
3606    /// Custom or future session configuration option type.
3607    ///
3608    /// Values beginning with `_` are reserved for implementation-specific
3609    /// extensions. Unknown values that do not begin with `_` are reserved for
3610    /// future ACP variants.
3611    #[serde(rename = "type")]
3612    pub type_: String,
3613    /// Additional fields from the unknown session configuration option payload.
3614    #[serde(flatten)]
3615    pub fields: BTreeMap<String, serde_json::Value>,
3616}
3617
3618impl OtherSessionConfigKind {
3619    /// Builds [`OtherSessionConfigKind`] from an unknown discriminator and preserves the remaining extension fields.
3620    #[must_use]
3621    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
3622        fields.remove("type");
3623        fields.remove("_meta");
3624        Self {
3625            type_: type_.into(),
3626            fields,
3627        }
3628    }
3629}
3630
3631impl<'de> Deserialize<'de> for OtherSessionConfigKind {
3632    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3633    where
3634        D: serde::Deserializer<'de>,
3635    {
3636        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3637        let type_ = fields
3638            .remove("type")
3639            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3640        let serde_json::Value::String(type_) = type_ else {
3641            return Err(serde::de::Error::custom("`type` must be a string"));
3642        };
3643
3644        if is_known_session_config_kind_type(&type_) {
3645            return Err(serde::de::Error::custom(format!(
3646                "known session configuration option `{type_}` did not match its schema"
3647            )));
3648        }
3649
3650        Ok(Self { type_, fields })
3651    }
3652}
3653
3654fn is_known_session_config_kind_type(type_: &str) -> bool {
3655    matches!(type_, "select" | "boolean")
3656}
3657
3658#[cfg(feature = "schemars")]
3659fn other_session_config_kind_schema(schema: &mut Schema) {
3660    super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
3661}
3662
3663/// A session configuration option selector and its current state.
3664#[serde_as]
3665#[skip_serializing_none]
3666#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3667#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3668#[serde(rename_all = "camelCase")]
3669#[non_exhaustive]
3670pub struct SessionConfigOption {
3671    /// Unique identifier for the configuration option.
3672    pub config_id: SessionConfigId,
3673    /// Human-readable label for the option.
3674    pub name: String,
3675    /// Optional description for the Client to display to the user.
3676    ///
3677    /// Omitted and `null` are equivalent and mean no description is provided.
3678    #[serde_as(deserialize_as = "DefaultOnError")]
3679    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3680    #[serde(default)]
3681    pub description: Option<String>,
3682    /// Optional semantic category for this option (UX only).
3683    ///
3684    /// Omitted and `null` are equivalent and mean no category is provided.
3685    #[serde_as(deserialize_as = "DefaultOnError")]
3686    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3687    #[serde(default)]
3688    pub category: Option<SessionConfigOptionCategory>,
3689    /// Type-specific fields for this configuration option.
3690    #[serde(flatten)]
3691    pub kind: SessionConfigKind,
3692    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3693    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3694    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3695    ///
3696    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3697    #[serde_as(deserialize_as = "DefaultOnError")]
3698    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3699    #[serde(default)]
3700    #[serde(rename = "_meta")]
3701    pub meta: Option<Meta>,
3702}
3703
3704impl SessionConfigOption {
3705    /// Builds [`SessionConfigOption`] with the required fields set. Optional fields start unset or empty.
3706    #[must_use]
3707    pub fn new(
3708        config_id: impl Into<SessionConfigId>,
3709        name: impl Into<String>,
3710        kind: SessionConfigKind,
3711    ) -> Self {
3712        Self {
3713            config_id: config_id.into(),
3714            name: name.into(),
3715            description: None,
3716            category: None,
3717            kind,
3718            meta: None,
3719        }
3720    }
3721
3722    /// Builds a select-style session configuration option with its current value and choices.
3723    #[must_use]
3724    pub fn select(
3725        config_id: impl Into<SessionConfigId>,
3726        name: impl Into<String>,
3727        current_value: impl Into<SessionConfigValueId>,
3728        options: impl Into<SessionConfigSelectOptions>,
3729    ) -> Self {
3730        Self::new(
3731            config_id,
3732            name,
3733            SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
3734        )
3735    }
3736
3737    /// Builds a boolean-style session configuration option with its current value.
3738    #[must_use]
3739    pub fn boolean(
3740        config_id: impl Into<SessionConfigId>,
3741        name: impl Into<String>,
3742        current_value: bool,
3743    ) -> Self {
3744        Self::new(
3745            config_id,
3746            name,
3747            SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
3748        )
3749    }
3750
3751    /// Sets or clears the optional `description` field.
3752    #[must_use]
3753    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3754        self.description = description.into_option();
3755        self
3756    }
3757
3758    /// Sets or clears the optional `category` field.
3759    #[must_use]
3760    pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
3761        self.category = category.into_option();
3762        self
3763    }
3764
3765    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3766    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3767    /// do not recognize.
3768    ///
3769    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3770    #[must_use]
3771    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3772        self.meta = meta.into_option();
3773        self
3774    }
3775}
3776
3777/// The value to set for a session configuration option.
3778///
3779/// The `type` field acts as the discriminator in the serialized JSON form.
3780///
3781/// The `type` discriminator describes the *shape* of the value, not the option
3782/// kind. For example every option kind that picks from a list of ids
3783/// (`select`, `radio`, …) would use [`Id`](Self::Id), while a future freeform
3784/// text option would get its own variant.
3785#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3786#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3787#[serde(tag = "type", rename_all = "snake_case")]
3788#[non_exhaustive]
3789pub enum SessionConfigOptionValue {
3790    /// A `SessionConfigValueId` string value (`type: "id"`).
3791    Id {
3792        /// The value ID.
3793        value: SessionConfigValueId,
3794    },
3795    /// A boolean value (`type: "boolean"`).
3796    Boolean {
3797        /// The boolean value.
3798        value: bool,
3799    },
3800    /// Custom or future session configuration option value payload.
3801    ///
3802    /// Values beginning with `_` are reserved for implementation-specific
3803    /// extensions. Unknown values that do not begin with `_` are reserved for
3804    /// future ACP variants.
3805    ///
3806    /// Agents MUST reject a `session/set_config_option` request with a value type
3807    /// they do not support with `-32602`.
3808    #[serde(untagged)]
3809    Other(OtherSessionConfigOptionValue),
3810}
3811
3812/// Custom or future session configuration option value payload.
3813#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3814#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3815#[cfg_attr(feature = "schemars", schemars(inline))]
3816#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
3817#[serde(rename_all = "camelCase")]
3818#[non_exhaustive]
3819pub struct OtherSessionConfigOptionValue {
3820    /// Custom or future session configuration option value type.
3821    ///
3822    /// Values beginning with `_` are reserved for implementation-specific
3823    /// extensions. Unknown values that do not begin with `_` are reserved for
3824    /// future ACP variants.
3825    #[serde(rename = "type")]
3826    pub type_: String,
3827    /// Raw value payload for the custom or future value type.
3828    pub value: serde_json::Value,
3829    /// Additional fields from the unknown session configuration option value payload.
3830    #[serde(flatten)]
3831    pub fields: BTreeMap<String, serde_json::Value>,
3832}
3833
3834impl OtherSessionConfigOptionValue {
3835    /// Builds [`OtherSessionConfigOptionValue`] from an unknown discriminator and preserves the remaining extension fields.
3836    #[must_use]
3837    pub fn new(
3838        type_: impl Into<String>,
3839        value: serde_json::Value,
3840        mut fields: BTreeMap<String, serde_json::Value>,
3841    ) -> Self {
3842        fields.remove("type");
3843        fields.remove("value");
3844        fields.remove("_meta");
3845        Self {
3846            type_: type_.into(),
3847            value,
3848            fields,
3849        }
3850    }
3851}
3852
3853impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
3854    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3855    where
3856        D: serde::Deserializer<'de>,
3857    {
3858        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3859        let type_ = fields
3860            .remove("type")
3861            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3862        let serde_json::Value::String(type_) = type_ else {
3863            return Err(serde::de::Error::custom("`type` must be a string"));
3864        };
3865
3866        if is_known_session_config_option_value_type(&type_) {
3867            return Err(serde::de::Error::custom(format!(
3868                "known session configuration option value `{type_}` did not match its schema"
3869            )));
3870        }
3871
3872        let value = fields
3873            .remove("value")
3874            .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3875
3876        Ok(Self {
3877            type_,
3878            value,
3879            fields,
3880        })
3881    }
3882}
3883
3884impl<'de> Deserialize<'de> for SessionConfigOptionValue {
3885    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3886    where
3887        D: serde::Deserializer<'de>,
3888    {
3889        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3890        let type_ = fields.remove("type");
3891        let value = fields
3892            .remove("value")
3893            .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3894
3895        let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
3896
3897        let serde_json::Value::String(type_) = type_ else {
3898            return Err(serde::de::Error::custom("`type` must be a string"));
3899        };
3900
3901        match type_.as_str() {
3902            "id" => {
3903                let value = serde_json::from_value(value).map_err(|error| {
3904                    serde::de::Error::custom(format!(
3905                        "`value` must be a string for `type: id`: {error}"
3906                    ))
3907                })?;
3908                Ok(Self::Id { value })
3909            }
3910            "boolean" => {
3911                let value = serde_json::from_value(value).map_err(|error| {
3912                    serde::de::Error::custom(format!(
3913                        "`value` must be a boolean for `type: boolean`: {error}"
3914                    ))
3915                })?;
3916                Ok(Self::Boolean { value })
3917            }
3918            _ => Ok(Self::Other(OtherSessionConfigOptionValue {
3919                type_,
3920                value,
3921                fields,
3922            })),
3923        }
3924    }
3925}
3926
3927fn is_known_session_config_option_value_type(type_: &str) -> bool {
3928    matches!(type_, "id" | "boolean")
3929}
3930
3931#[cfg(feature = "schemars")]
3932fn other_session_config_option_value_schema(schema: &mut Schema) {
3933    super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
3934}
3935
3936impl SessionConfigOptionValue {
3937    /// Create an id option value (used by `select` and other id-based option types).
3938    #[must_use]
3939    pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
3940        Self::Id { value: id.into() }
3941    }
3942
3943    /// Create a boolean option value.
3944    #[must_use]
3945    pub fn boolean(val: bool) -> Self {
3946        Self::Boolean { value: val }
3947    }
3948
3949    /// Return the inner [`SessionConfigValueId`] if this is a
3950    /// [`Id`](Self::Id) value.
3951    #[must_use]
3952    pub fn as_id(&self) -> Option<&SessionConfigValueId> {
3953        match self {
3954            Self::Id { value } => Some(value),
3955            _ => None,
3956        }
3957    }
3958
3959    /// Return the inner [`bool`] if this is a [`Boolean`](Self::Boolean) value.
3960    #[must_use]
3961    pub fn as_bool(&self) -> Option<bool> {
3962        match self {
3963            Self::Boolean { value } => Some(*value),
3964            _ => None,
3965        }
3966    }
3967}
3968
3969impl From<SessionConfigValueId> for SessionConfigOptionValue {
3970    fn from(value: SessionConfigValueId) -> Self {
3971        Self::Id { value }
3972    }
3973}
3974
3975impl From<bool> for SessionConfigOptionValue {
3976    fn from(value: bool) -> Self {
3977        Self::Boolean { value }
3978    }
3979}
3980
3981impl From<&str> for SessionConfigOptionValue {
3982    fn from(value: &str) -> Self {
3983        Self::Id {
3984            value: SessionConfigValueId::new(value),
3985        }
3986    }
3987}
3988
3989/// Request parameters for setting a session configuration option.
3990///
3991/// Agents MUST support this method for any session where they advertise config options.
3992/// Agents MUST reject an unknown `configId` or invalid value with `-32602` and leave the
3993/// configuration unchanged.
3994///
3995/// See protocol docs: [Session Config Options](https://agentclientprotocol.com/protocol/v2/session-config-options#setting-a-config-option)
3996#[serde_as]
3997#[skip_serializing_none]
3998#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3999#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4000#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4001#[serde(rename_all = "camelCase")]
4002#[non_exhaustive]
4003pub struct SetSessionConfigOptionRequest {
4004    /// The ID of the session to set the configuration option for.
4005    pub session_id: SessionId,
4006    /// The ID of the configuration option to set.
4007    pub config_id: SessionConfigId,
4008    /// The value to set, including a `type` discriminator and the raw `value`.
4009    ///
4010    /// Payloads must send `type: "id"` for id-based options.
4011    #[serde(flatten)]
4012    pub value: SessionConfigOptionValue,
4013    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4014    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4015    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4016    ///
4017    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4018    #[serde_as(deserialize_as = "DefaultOnError")]
4019    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4020    #[serde(default)]
4021    #[serde(rename = "_meta")]
4022    pub meta: Option<Meta>,
4023}
4024
4025impl SetSessionConfigOptionRequest {
4026    /// Builds [`SetSessionConfigOptionRequest`] with the required request fields set. Optional fields start unset or empty.
4027    #[must_use]
4028    pub fn new(
4029        session_id: impl Into<SessionId>,
4030        config_id: impl Into<SessionConfigId>,
4031        value: impl Into<SessionConfigOptionValue>,
4032    ) -> Self {
4033        Self {
4034            session_id: session_id.into(),
4035            config_id: config_id.into(),
4036            value: value.into(),
4037            meta: None,
4038        }
4039    }
4040
4041    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4042    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4043    /// do not recognize.
4044    ///
4045    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4046    #[must_use]
4047    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4048        self.meta = meta.into_option();
4049        self
4050    }
4051}
4052
4053/// Response to `session/set_config_option` method.
4054#[serde_as]
4055#[skip_serializing_none]
4056#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4057#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4058#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4059#[serde(rename_all = "camelCase")]
4060#[non_exhaustive]
4061pub struct SetSessionConfigOptionResponse {
4062    /// The full set of configuration options and their current values.
4063    #[serde_as(deserialize_as = "VecSkipError<_>")]
4064    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-skip-invalid-items" = true)))]
4065    pub config_options: Vec<SessionConfigOption>,
4066    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4067    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4068    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4069    ///
4070    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4071    #[serde_as(deserialize_as = "DefaultOnError")]
4072    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4073    #[serde(default)]
4074    #[serde(rename = "_meta")]
4075    pub meta: Option<Meta>,
4076}
4077
4078impl SetSessionConfigOptionResponse {
4079    /// Builds [`SetSessionConfigOptionResponse`] with the required response fields set. Optional fields start unset or empty.
4080    #[must_use]
4081    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
4082        Self {
4083            config_options,
4084            meta: None,
4085        }
4086    }
4087
4088    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4089    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4090    /// do not recognize.
4091    ///
4092    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4093    #[must_use]
4094    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4095        self.meta = meta.into_option();
4096        self
4097    }
4098}
4099
4100// MCP
4101
4102/// Configuration for connecting to an MCP (Model Context Protocol) server.
4103///
4104/// MCP servers provide tools and context that the agent can use when
4105/// processing prompts.
4106///
4107/// See protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/v2/session-setup#mcp-servers)
4108#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4110#[serde(tag = "type", rename_all = "snake_case")]
4111#[non_exhaustive]
4112pub enum McpServer {
4113    /// HTTP transport configuration
4114    ///
4115    /// Only available when the Agent capabilities include `session.mcp.http`.
4116    Http(McpServerHttp),
4117    /// **UNSTABLE**
4118    ///
4119    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4120    ///
4121    /// ACP transport configuration
4122    ///
4123    /// Only available when the Agent capabilities include `session.mcp.acp`.
4124    /// The MCP server is provided by an ACP component and communicates over the ACP channel.
4125    #[cfg(feature = "unstable_mcp_over_acp")]
4126    Acp(McpServerAcp),
4127    /// Stdio transport configuration
4128    ///
4129    /// Only available when the Agent capabilities include `session.mcp.stdio`.
4130    Stdio(McpServerStdio),
4131    /// Custom or future MCP server transport configuration.
4132    ///
4133    /// Values beginning with `_` are reserved for implementation-specific
4134    /// extensions. Unknown values that do not begin with `_` are reserved for
4135    /// future ACP variants.
4136    ///
4137    /// Receivers that do not understand this transport should preserve the raw
4138    /// payload when storing, replaying, proxying, or forwarding session setup
4139    /// data. Agents MUST reject a request with a transport they do not support
4140    /// with `-32602`.
4141    #[serde(untagged)]
4142    Other(OtherMcpServer),
4143}
4144
4145/// Custom or future MCP server transport payload.
4146#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4147#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
4148#[cfg_attr(feature = "schemars", schemars(inline))]
4149#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
4150#[serde(rename_all = "camelCase")]
4151#[non_exhaustive]
4152pub struct OtherMcpServer {
4153    /// Custom or future MCP server transport type.
4154    ///
4155    /// Values beginning with `_` are reserved for implementation-specific
4156    /// extensions. Unknown values that do not begin with `_` are reserved for
4157    /// future ACP variants.
4158    #[serde(rename = "type")]
4159    pub type_: String,
4160    /// Additional fields from the unknown MCP server transport payload.
4161    #[serde(flatten)]
4162    pub fields: BTreeMap<String, serde_json::Value>,
4163}
4164
4165impl OtherMcpServer {
4166    /// Builds [`OtherMcpServer`] from an unknown discriminator and preserves the remaining extension fields.
4167    #[must_use]
4168    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
4169        fields.remove("type");
4170        Self {
4171            type_: type_.into(),
4172            fields,
4173        }
4174    }
4175}
4176
4177impl<'de> Deserialize<'de> for OtherMcpServer {
4178    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4179    where
4180        D: serde::Deserializer<'de>,
4181    {
4182        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4183        let type_ = fields
4184            .remove("type")
4185            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
4186        let serde_json::Value::String(type_) = type_ else {
4187            return Err(serde::de::Error::custom("`type` must be a string"));
4188        };
4189
4190        if is_known_mcp_server_type(&type_) {
4191            return Err(serde::de::Error::custom(format!(
4192                "known MCP server transport `{type_}` did not match its schema"
4193            )));
4194        }
4195
4196        Ok(Self { type_, fields })
4197    }
4198}
4199
4200fn is_known_mcp_server_type(type_: &str) -> bool {
4201    match type_ {
4202        "http" | "stdio" => true,
4203        #[cfg(feature = "unstable_mcp_over_acp")]
4204        "acp" => true,
4205        _ => false,
4206    }
4207}
4208
4209#[cfg(feature = "schemars")]
4210fn other_mcp_server_schema(schema: &mut Schema) {
4211    super::schema_util::reject_known_string_discriminators(
4212        schema,
4213        "type",
4214        &[
4215            "http",
4216            "stdio",
4217            #[cfg(feature = "unstable_mcp_over_acp")]
4218            "acp",
4219        ],
4220    );
4221}
4222
4223/// HTTP transport configuration for MCP.
4224#[serde_as]
4225#[skip_serializing_none]
4226#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4227#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4228#[serde(rename_all = "camelCase")]
4229#[non_exhaustive]
4230pub struct McpServerHttp {
4231    /// Human-readable name identifying this MCP server.
4232    pub name: String,
4233    /// URL to the MCP server.
4234    #[cfg_attr(feature = "schemars", schemars(url))]
4235    pub url: String,
4236    /// HTTP headers to set when making requests to the MCP server. Headers the MCP transport
4237    /// defines are owned by the transport, and Agents use the transport's values for them.
4238    #[serde_as(deserialize_as = "DefaultOnNull")]
4239    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4240    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4241    pub headers: Vec<HttpHeader>,
4242    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4243    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4244    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4245    ///
4246    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4247    #[serde_as(deserialize_as = "DefaultOnError")]
4248    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4249    #[serde(default)]
4250    #[serde(rename = "_meta")]
4251    pub meta: Option<Meta>,
4252}
4253
4254impl McpServerHttp {
4255    /// Builds [`McpServerHttp`] with the required fields set. Optional fields start unset or empty.
4256    #[must_use]
4257    pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
4258        Self {
4259            name: name.into(),
4260            url: url.into(),
4261            headers: Vec::new(),
4262            meta: None,
4263        }
4264    }
4265
4266    /// HTTP headers to set when making requests to the MCP server. Headers the MCP transport
4267    /// defines are owned by the transport, and Agents use the transport's values for them.
4268    #[must_use]
4269    pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
4270        self.headers = headers;
4271        self
4272    }
4273
4274    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4275    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4276    /// do not recognize.
4277    ///
4278    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4279    #[must_use]
4280    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4281        self.meta = meta.into_option();
4282        self
4283    }
4284}
4285
4286/// **UNSTABLE**
4287///
4288/// This capability is not part of the spec yet, and may be removed or changed at any point.
4289///
4290/// Unique identifier for an MCP server using the ACP transport.
4291///
4292/// The value is opaque and generated by the ACP component providing the MCP server. It is
4293/// used by `mcp/message` to route requests to the component that declared the server.
4294#[cfg(feature = "unstable_mcp_over_acp")]
4295#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
4297#[serde(transparent)]
4298#[from(forward)]
4299#[non_exhaustive]
4300pub struct McpServerAcpId(pub Arc<str>);
4301
4302#[cfg(feature = "unstable_mcp_over_acp")]
4303impl McpServerAcpId {
4304    /// Wraps a protocol string as a typed [`McpServerAcpId`].
4305    #[must_use]
4306    pub fn new(id: impl Into<Self>) -> Self {
4307        id.into()
4308    }
4309}
4310
4311/// **UNSTABLE**
4312///
4313/// This capability is not part of the spec yet, and may be removed or changed at any point.
4314///
4315/// ACP transport configuration for MCP.
4316///
4317/// The MCP server is provided by an ACP component and communicates over the ACP channel
4318/// using `mcp/message`.
4319#[serde_as]
4320#[skip_serializing_none]
4321#[cfg(feature = "unstable_mcp_over_acp")]
4322#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4323#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4324#[serde(rename_all = "camelCase")]
4325#[non_exhaustive]
4326pub struct McpServerAcp {
4327    /// Human-readable name identifying this MCP server.
4328    pub name: String,
4329    /// Unique identifier for this MCP server, generated by the component providing it.
4330    ///
4331    /// Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible
4332    /// on the same ACP connection.
4333    pub server_id: McpServerAcpId,
4334    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4335    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4336    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4337    ///
4338    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4339    #[serde_as(deserialize_as = "DefaultOnError")]
4340    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4341    #[serde(default)]
4342    #[serde(rename = "_meta")]
4343    pub meta: Option<Meta>,
4344}
4345
4346#[cfg(feature = "unstable_mcp_over_acp")]
4347impl McpServerAcp {
4348    /// Builds [`McpServerAcp`] with the required fields set. Optional fields start unset or empty.
4349    #[must_use]
4350    pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
4351        Self {
4352            name: name.into(),
4353            server_id: server_id.into(),
4354            meta: None,
4355        }
4356    }
4357
4358    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4359    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4360    /// do not recognize.
4361    ///
4362    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4363    #[must_use]
4364    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4365        self.meta = meta.into_option();
4366        self
4367    }
4368}
4369
4370/// Stdio transport configuration for MCP.
4371#[serde_as]
4372#[skip_serializing_none]
4373#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4374#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4375#[serde(rename_all = "camelCase")]
4376#[non_exhaustive]
4377pub struct McpServerStdio {
4378    /// Human-readable name identifying this MCP server.
4379    pub name: String,
4380    /// Absolute path to the MCP server executable.
4381    pub command: AbsolutePath,
4382    /// Command-line arguments to pass to the MCP server.
4383    #[serde_as(deserialize_as = "DefaultOnNull")]
4384    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4385    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4386    pub args: Vec<String>,
4387    /// Environment variables to set when launching the MCP server. Names SHOULD be unique.
4388    /// Agents MAY reject duplicates with `-32602`, and otherwise the last entry wins.
4389    #[serde_as(deserialize_as = "DefaultOnNull")]
4390    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4391    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4392    pub env: Vec<EnvVariable>,
4393    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4394    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4395    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4396    ///
4397    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4398    #[serde_as(deserialize_as = "DefaultOnError")]
4399    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4400    #[serde(default)]
4401    #[serde(rename = "_meta")]
4402    pub meta: Option<Meta>,
4403}
4404
4405impl McpServerStdio {
4406    /// Builds [`McpServerStdio`] with the required fields set. Optional fields start unset or empty.
4407    #[must_use]
4408    pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
4409        Self {
4410            name: name.into(),
4411            command: command.into(),
4412            args: Vec::new(),
4413            env: Vec::new(),
4414            meta: None,
4415        }
4416    }
4417
4418    /// Command-line arguments to pass to the MCP server.
4419    #[must_use]
4420    pub fn args(mut self, args: Vec<String>) -> Self {
4421        self.args = args;
4422        self
4423    }
4424
4425    /// Environment variables to set when launching the MCP server. Names SHOULD be unique.
4426    /// Agents MAY reject duplicates with `-32602`, and otherwise the last entry wins.
4427    #[must_use]
4428    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
4429        self.env = env;
4430        self
4431    }
4432
4433    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4434    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4435    /// do not recognize.
4436    ///
4437    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4438    #[must_use]
4439    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4440        self.meta = meta.into_option();
4441        self
4442    }
4443}
4444
4445/// An environment variable to set when launching a process.
4446#[serde_as]
4447#[skip_serializing_none]
4448#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4449#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4450#[serde(rename_all = "camelCase")]
4451#[non_exhaustive]
4452pub struct EnvVariable {
4453    /// The name of the environment variable.
4454    pub name: String,
4455    /// The value to set for the environment variable.
4456    pub value: String,
4457    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4458    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4459    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4460    ///
4461    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4462    #[serde_as(deserialize_as = "DefaultOnError")]
4463    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4464    #[serde(default)]
4465    #[serde(rename = "_meta")]
4466    pub meta: Option<Meta>,
4467}
4468
4469impl EnvVariable {
4470    /// Builds [`EnvVariable`] with the required fields set. Optional fields start unset or empty.
4471    #[must_use]
4472    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4473        Self {
4474            name: name.into(),
4475            value: value.into(),
4476            meta: None,
4477        }
4478    }
4479
4480    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4481    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4482    /// do not recognize.
4483    ///
4484    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4485    #[must_use]
4486    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4487        self.meta = meta.into_option();
4488        self
4489    }
4490}
4491
4492/// An HTTP header to set when making requests to the MCP server.
4493#[serde_as]
4494#[skip_serializing_none]
4495#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4496#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4497#[serde(rename_all = "camelCase")]
4498#[non_exhaustive]
4499pub struct HttpHeader {
4500    /// The name of the HTTP header.
4501    pub name: String,
4502    /// The value to set for the HTTP header.
4503    pub value: String,
4504    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4505    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4506    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4507    ///
4508    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4509    #[serde_as(deserialize_as = "DefaultOnError")]
4510    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4511    #[serde(default)]
4512    #[serde(rename = "_meta")]
4513    pub meta: Option<Meta>,
4514}
4515
4516impl HttpHeader {
4517    /// Builds [`HttpHeader`] with the required fields set. Optional fields start unset or empty.
4518    #[must_use]
4519    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4520        Self {
4521            name: name.into(),
4522            value: value.into(),
4523            meta: None,
4524        }
4525    }
4526
4527    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4528    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4529    /// do not recognize.
4530    ///
4531    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4532    #[must_use]
4533    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4534        self.meta = meta.into_option();
4535        self
4536    }
4537}
4538
4539// System prompt
4540
4541/// **UNSTABLE**
4542///
4543/// This capability is not part of the spec yet, and may be removed or changed at any point.
4544///
4545/// Client-provided system-level instructions for a session.
4546///
4547/// Clients MUST only send this when the Agent advertises
4548/// `session.systemPrompt`. Agents that cannot apply it, for example because of
4549/// an unadvertised mode or an unsupported content block type, MUST return an
4550/// error instead of creating, resuming, or forking the session.
4551#[cfg(feature = "unstable_session_system_prompt")]
4552#[serde_as]
4553#[skip_serializing_none]
4554#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4555#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4556#[serde(rename_all = "camelCase")]
4557#[non_exhaustive]
4558pub struct SystemPrompt {
4559    /// How the Agent combines `content` with its own instructions.
4560    ///
4561    /// Required. Agents MUST return an error for a mode they do not support rather than
4562    /// treating it as `append` or `override`.
4563    pub mode: SystemPromptMode,
4564    /// The system prompt content.
4565    ///
4566    /// Agents advertising the capability MUST accept `text` content blocks.
4567    /// Clients MUST NOT send other content block types until a future
4568    /// capability allows them, and Agents MUST return an error for content
4569    /// blocks they do not support, including unknown types.
4570    ///
4571    /// Clients MUST NOT send an empty array. Agents MAY reject one with
4572    /// `-32602` without creating, resuming, or forking the session.
4573    pub content: Vec<ContentBlock>,
4574    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4575    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4576    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4577    ///
4578    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4579    #[serde_as(deserialize_as = "DefaultOnError")]
4580    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4581    #[serde(default)]
4582    #[serde(rename = "_meta")]
4583    pub meta: Option<Meta>,
4584}
4585
4586#[cfg(feature = "unstable_session_system_prompt")]
4587impl SystemPrompt {
4588    /// Builds [`SystemPrompt`] with the required fields set. Optional fields start unset or empty.
4589    ///
4590    /// `mode` starts as [`SystemPromptMode::Append`]. Use [`SystemPrompt::mode`]
4591    /// to request another mode.
4592    #[must_use]
4593    pub fn new(content: Vec<ContentBlock>) -> Self {
4594        Self {
4595            mode: SystemPromptMode::default(),
4596            content,
4597            meta: None,
4598        }
4599    }
4600
4601    /// How the Agent combines `content` with its own instructions.
4602    #[must_use]
4603    pub fn mode(mut self, mode: SystemPromptMode) -> Self {
4604        self.mode = mode;
4605        self
4606    }
4607
4608    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4609    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4610    /// do not recognize.
4611    ///
4612    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4613    #[must_use]
4614    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4615        self.meta = meta.into_option();
4616        self
4617    }
4618}
4619
4620/// **UNSTABLE**
4621///
4622/// This capability is not part of the spec yet, and may be removed or changed at any point.
4623///
4624/// How the Agent combines a client-provided system prompt with its own instructions.
4625///
4626/// Unknown modes are preserved so they can be stored,
4627/// proxied, or forwarded. An Agent MUST return an error for a mode it does not
4628/// support rather than treating it as `append` or `override`.
4629#[cfg(feature = "unstable_session_system_prompt")]
4630#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4631#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
4632#[serde(rename_all = "snake_case")]
4633#[non_exhaustive]
4634pub enum SystemPromptMode {
4635    /// Add the content to the Agent's own instructions rather than replacing them.
4636    ///
4637    /// Every Agent that advertises `session.systemPrompt` supports this mode.
4638    /// Agents SHOULD give the content system-level precedence, for example in the
4639    /// model's system prompt or developer instructions rather than in user-turn
4640    /// content. Where it is placed and how it is formatted is implementation-defined.
4641    #[default]
4642    Append,
4643    /// Replace the Agent's built-in default instructions with the content.
4644    ///
4645    /// This does not change tool availability, MCP servers, permissions, or session
4646    /// configuration. Context the Agent supplies from the workspace or environment
4647    /// MAY still be included. Clients that use this mode take responsibility for any
4648    /// tool guidance or safety instructions the Agent's default instructions
4649    /// provided, and MUST only use it when the Agent advertises
4650    /// `session.systemPrompt.mode.override`.
4651    Override,
4652    /// Custom or future system prompt mode.
4653    ///
4654    /// Values beginning with `_` are reserved for implementation-specific
4655    /// extensions. Unknown values that do not begin with `_` are reserved for
4656    /// future ACP variants.
4657    #[serde(untagged)]
4658    Other(String),
4659}
4660
4661// Prompt
4662
4663/// Request parameters for sending a user prompt to the agent.
4664///
4665/// Contains the user's message and any additional context.
4666///
4667/// See protocol docs: [User Message](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#1-user-message)
4668#[serde_as]
4669#[skip_serializing_none]
4670#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4671#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4672#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4673#[serde(rename_all = "camelCase")]
4674#[non_exhaustive]
4675pub struct PromptRequest {
4676    /// The ID of the session to send this user message to
4677    pub session_id: SessionId,
4678    /// The blocks of content that compose the user's message.
4679    ///
4680    /// As a baseline, the Agent MUST support `text` and `resource_link` content blocks,
4681    /// while other variants are optionally enabled via `PromptCapabilities`.
4682    ///
4683    /// The Client MUST adapt its interface according to `PromptCapabilities`.
4684    ///
4685    /// The client MAY include referenced pieces of context as either
4686    /// `resource` or `resource_link` content blocks.
4687    ///
4688    /// When available, `resource` is preferred
4689    /// as it avoids extra round-trips and allows the message to include
4690    /// pieces of context from sources the agent may not have access to.
4691    ///
4692    /// Clients MUST NOT send an empty array. Agents MAY reject one with `-32602` without
4693    /// inserting it.
4694    #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
4695    pub prompt: Vec<ContentBlock>,
4696    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4697    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4698    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4699    ///
4700    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4701    #[serde_as(deserialize_as = "DefaultOnError")]
4702    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4703    #[serde(default)]
4704    #[serde(rename = "_meta")]
4705    pub meta: Option<Meta>,
4706}
4707
4708impl PromptRequest {
4709    /// Builds [`PromptRequest`] with the required request fields set. Optional fields start unset or empty.
4710    #[must_use]
4711    pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
4712        Self {
4713            session_id: session_id.into(),
4714            prompt,
4715            meta: None,
4716        }
4717    }
4718
4719    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4720    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4721    /// do not recognize.
4722    ///
4723    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4724    #[must_use]
4725    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4726        self.meta = meta.into_option();
4727        self
4728    }
4729}
4730
4731/// Response acknowledging that a user prompt was inserted into the ACP conversation.
4732///
4733/// This response does not indicate that the prompt was merely received or held, nor that the
4734/// agent has finished processing it.
4735/// Processing and completion are reported through `state_update` session updates.
4736///
4737/// Insertion is the logical addition of a user message with a message ID and a position
4738/// relative to other messages in the ACP conversation.
4739///
4740/// See protocol docs: [Prompt Accepted](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#2-prompt-accepted)
4741#[serde_as]
4742#[skip_serializing_none]
4743#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4744#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4745#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4746#[serde(rename_all = "camelCase")]
4747#[non_exhaustive]
4748pub struct PromptResponse {
4749    /// Identifies the user message inserted into the ACP conversation.
4750    ///
4751    /// Required and non-null. Omission and explicit `null` are both invalid.
4752    ///
4753    /// The corresponding user-message session update carries this same identifier and may arrive
4754    /// before or after this response. Agents must echo the message during the live session, but are
4755    /// not required to retain it. If retained and replayed, the message keeps this identifier.
4756    pub message_id: MessageId,
4757    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4758    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4759    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4760    ///
4761    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4762    #[serde_as(deserialize_as = "DefaultOnError")]
4763    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4764    #[serde(default)]
4765    #[serde(rename = "_meta")]
4766    pub meta: Option<Meta>,
4767}
4768
4769impl PromptResponse {
4770    /// Builds [`PromptResponse`] with the required response fields set. Optional fields start unset or empty.
4771    #[must_use]
4772    pub fn new(message_id: impl Into<MessageId>) -> Self {
4773        Self {
4774            message_id: message_id.into(),
4775            meta: None,
4776        }
4777    }
4778
4779    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4780    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4781    /// do not recognize.
4782    ///
4783    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4784    #[must_use]
4785    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4786        self.meta = meta.into_option();
4787        self
4788    }
4789}
4790
4791/// Reasons why an agent stops active session work.
4792///
4793/// See protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#stop-reasons)
4794#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4795#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
4796#[serde(tag = "stopReason", rename_all = "snake_case")]
4797#[non_exhaustive]
4798pub enum StopReason {
4799    /// The active work ended successfully.
4800    /// Includes locally handled commands that required no model call.
4801    EndTurn,
4802    /// The active work ended because the agent reached the maximum number of tokens.
4803    MaxTokens,
4804    /// The active work ended because the agent reached the maximum number of
4805    /// model requests for the foreground work.
4806    MaxTurnRequests,
4807    /// The active work ended because the agent refused to continue.
4808    Refusal,
4809    /// The user or client chose to stop the work: via `session/cancel`, by
4810    /// closing the session, or through an interaction the agent offers, such
4811    /// as a permission option that tells the agent to stop. Work the agent
4812    /// stops for its own reasons uses another reason, such as `error` for an
4813    /// internal timeout.
4814    ///
4815    /// Agents MUST report this stop reason on an idle `state_update` when
4816    /// cancellation ends active foreground work, even if aborting causes
4817    /// exceptions. Agents MUST catch these errors and report `cancelled`, not
4818    /// `error`.
4819    Cancelled,
4820    /// The active work ended because something failed.
4821    ///
4822    /// This includes pre-insertion foreground work. A prompt failure before
4823    /// insertion also requires an error response to `session/prompt`. After
4824    /// insertion, the prompt's response remains successful.
4825    Error(ErrorStopReason),
4826    /// Custom or future stop reason.
4827    ///
4828    /// Values beginning with `_` are reserved for implementation-specific
4829    /// extensions. Unknown values that do not begin with `_` are reserved for
4830    /// future ACP variants.
4831    #[serde(untagged)]
4832    Other(OtherStopReason),
4833}
4834
4835impl From<ErrorStopReason> for StopReason {
4836    fn from(value: ErrorStopReason) -> Self {
4837        Self::Error(value)
4838    }
4839}
4840
4841/// Details of a failure that ended active work.
4842#[serde_as]
4843#[skip_serializing_none]
4844#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4845#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4846#[serde(rename_all = "camelCase")]
4847#[non_exhaustive]
4848pub struct ErrorStopReason {
4849    /// The failure, as a JSON-RPC error object.
4850    ///
4851    /// Optional. Omitted and `null` are equivalent and mean the agent is not reporting failure details.
4852    /// Agents SHOULD include it.
4853    #[serde_as(deserialize_as = "DefaultOnError")]
4854    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4855    #[serde(default)]
4856    pub error: Option<Box<Error>>,
4857}
4858
4859impl ErrorStopReason {
4860    /// Builds [`ErrorStopReason`] with the required fields set. Optional fields start unset or empty.
4861    #[must_use]
4862    pub fn new() -> Self {
4863        Self::default()
4864    }
4865
4866    /// The failure, as a JSON-RPC error object.
4867    #[must_use]
4868    pub fn error(mut self, error: impl IntoOption<Error>) -> Self {
4869        self.error = error.into_option().map(Box::new);
4870        self
4871    }
4872}
4873
4874/// Custom or future stop reason payload.
4875#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4876#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
4877#[cfg_attr(feature = "schemars", schemars(inline))]
4878#[cfg_attr(feature = "schemars", schemars(transform = other_stop_reason_schema))]
4879#[serde(rename_all = "camelCase")]
4880#[non_exhaustive]
4881pub struct OtherStopReason {
4882    /// Custom or future stop reason.
4883    ///
4884    /// Values beginning with `_` are reserved for implementation-specific
4885    /// extensions. Unknown values that do not begin with `_` are reserved for
4886    /// future ACP variants.
4887    pub stop_reason: String,
4888    /// Additional fields from the unknown stop reason payload.
4889    #[serde(flatten)]
4890    pub fields: BTreeMap<String, serde_json::Value>,
4891}
4892
4893impl OtherStopReason {
4894    /// Builds [`OtherStopReason`] from an unknown discriminator and preserves the remaining extension fields.
4895    #[must_use]
4896    pub fn new(
4897        stop_reason: impl Into<String>,
4898        mut fields: BTreeMap<String, serde_json::Value>,
4899    ) -> Self {
4900        remove_idle_state_update_fields(&mut fields);
4901        Self {
4902            stop_reason: stop_reason.into(),
4903            fields,
4904        }
4905    }
4906}
4907
4908impl<'de> Deserialize<'de> for OtherStopReason {
4909    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4910    where
4911        D: serde::Deserializer<'de>,
4912    {
4913        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4914        let stop_reason = fields
4915            .remove("stopReason")
4916            .ok_or_else(|| serde::de::Error::missing_field("stopReason"))?;
4917        let serde_json::Value::String(stop_reason) = stop_reason else {
4918            return Err(serde::de::Error::custom("`stopReason` must be a string"));
4919        };
4920
4921        if KNOWN_STOP_REASONS.contains(&stop_reason.as_str()) {
4922            return Err(serde::de::Error::custom(format!(
4923                "known stop reason `{stop_reason}` did not match its schema"
4924            )));
4925        }
4926        remove_idle_state_update_fields(&mut fields);
4927
4928        Ok(Self {
4929            stop_reason,
4930            fields,
4931        })
4932    }
4933}
4934
4935const KNOWN_STOP_REASONS: &[&str] = &[
4936    "end_turn",
4937    "max_tokens",
4938    "max_turn_requests",
4939    "refusal",
4940    "cancelled",
4941    "error",
4942];
4943
4944/// Keeps the idle update's own fields out of an unknown stop reason's payload.
4945fn remove_idle_state_update_fields(fields: &mut BTreeMap<String, serde_json::Value>) {
4946    fields.remove("stopReason");
4947    fields.remove("usage");
4948    fields.remove("_meta");
4949}
4950
4951#[cfg(feature = "schemars")]
4952fn other_stop_reason_schema(schema: &mut Schema) {
4953    super::schema_util::reject_known_string_discriminators(
4954        schema,
4955        "stopReason",
4956        KNOWN_STOP_REASONS,
4957    );
4958}
4959
4960/// **UNSTABLE**
4961///
4962/// This capability is not part of the spec yet, and may be removed or changed at any point.
4963///
4964/// Token usage for the foreground work that just completed, not cumulative for the session.
4965#[cfg(feature = "unstable_end_turn_token_usage")]
4966#[serde_as]
4967#[skip_serializing_none]
4968#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4969#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4970#[serde(rename_all = "camelCase")]
4971#[non_exhaustive]
4972pub struct Usage {
4973    /// Sum of all token types for the completed work.
4974    pub total_tokens: u64,
4975    /// Input tokens for the completed work.
4976    pub input_tokens: u64,
4977    /// Output tokens for the completed work.
4978    pub output_tokens: u64,
4979    /// Thought/reasoning tokens for the completed work.
4980    ///
4981    /// Omitted and `null` are equivalent and mean the count is not reported.
4982    #[serde_as(deserialize_as = "DefaultOnError")]
4983    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4984    #[serde(default)]
4985    pub thought_tokens: Option<u64>,
4986    /// Cache read tokens for the completed work.
4987    ///
4988    /// Omitted and `null` are equivalent and mean the count is not reported.
4989    #[serde_as(deserialize_as = "DefaultOnError")]
4990    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4991    #[serde(default)]
4992    pub cached_read_tokens: Option<u64>,
4993    /// Cache write tokens for the completed work.
4994    ///
4995    /// Omitted and `null` are equivalent and mean the count is not reported.
4996    #[serde_as(deserialize_as = "DefaultOnError")]
4997    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4998    #[serde(default)]
4999    pub cached_write_tokens: Option<u64>,
5000    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5001    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5002    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5003    ///
5004    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5005    #[serde_as(deserialize_as = "DefaultOnError")]
5006    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5007    #[serde(default)]
5008    #[serde(rename = "_meta")]
5009    pub meta: Option<Meta>,
5010}
5011
5012#[cfg(feature = "unstable_end_turn_token_usage")]
5013impl Usage {
5014    /// Builds [`Usage`] with the required fields set. Optional fields start unset or empty.
5015    #[must_use]
5016    pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
5017        Self {
5018            total_tokens,
5019            input_tokens,
5020            output_tokens,
5021            thought_tokens: None,
5022            cached_read_tokens: None,
5023            cached_write_tokens: None,
5024            meta: None,
5025        }
5026    }
5027
5028    /// Thought/reasoning tokens for the completed work.
5029    #[must_use]
5030    pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
5031        self.thought_tokens = thought_tokens.into_option();
5032        self
5033    }
5034
5035    /// Cache read tokens for the completed work.
5036    #[must_use]
5037    pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
5038        self.cached_read_tokens = cached_read_tokens.into_option();
5039        self
5040    }
5041
5042    /// Cache write tokens for the completed work.
5043    #[must_use]
5044    pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
5045        self.cached_write_tokens = cached_write_tokens.into_option();
5046        self
5047    }
5048
5049    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5050    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5051    /// do not recognize.
5052    ///
5053    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5054    #[must_use]
5055    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5056        self.meta = meta.into_option();
5057        self
5058    }
5059}
5060
5061// Providers
5062
5063/// **UNSTABLE**
5064///
5065/// This capability is not part of the spec yet, and may be removed or changed at any point.
5066///
5067/// Well-known API protocol identifiers for LLM providers.
5068///
5069/// Agents and clients MUST handle unknown protocol identifiers gracefully.
5070///
5071/// Protocol names beginning with `_` are free for custom use, like other ACP extension methods.
5072/// Protocol names that do not begin with `_` are reserved for the ACP spec.
5073#[cfg(feature = "unstable_llm_providers")]
5074#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5075#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5076#[serde(rename_all = "snake_case")]
5077#[non_exhaustive]
5078#[expect(clippy::doc_markdown)]
5079pub enum LlmProtocol {
5080    /// Anthropic API protocol.
5081    Anthropic,
5082    /// OpenAI API protocol.
5083    #[serde(rename = "openai")]
5084    OpenAi,
5085    /// Azure OpenAI API protocol.
5086    Azure,
5087    /// Google Vertex AI API protocol.
5088    Vertex,
5089    /// AWS Bedrock API protocol.
5090    Bedrock,
5091    /// Custom or future protocol.
5092    ///
5093    /// Values beginning with `_` are reserved for implementation-specific
5094    /// extensions. Unknown values that do not begin with `_` are reserved for
5095    /// future ACP variants.
5096    #[serde(untagged)]
5097    Other(String),
5098}
5099
5100/// **UNSTABLE**
5101///
5102/// This capability is not part of the spec yet, and may be removed or changed at any point.
5103///
5104/// Current effective non-secret routing configuration for a provider.
5105#[cfg(feature = "unstable_llm_providers")]
5106#[serde_as]
5107#[skip_serializing_none]
5108#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5110#[serde(rename_all = "camelCase")]
5111#[non_exhaustive]
5112pub struct ProviderCurrentConfig {
5113    /// Protocol currently used by this provider.
5114    pub api_type: LlmProtocol,
5115    /// Base URL currently used by this provider.
5116    #[cfg_attr(feature = "schemars", schemars(url))]
5117    pub base_url: String,
5118    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5119    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5120    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5121    ///
5122    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5123    #[serde_as(deserialize_as = "DefaultOnError")]
5124    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5125    #[serde(default)]
5126    #[serde(rename = "_meta")]
5127    pub meta: Option<Meta>,
5128}
5129
5130#[cfg(feature = "unstable_llm_providers")]
5131impl ProviderCurrentConfig {
5132    /// Builds [`ProviderCurrentConfig`] with the required fields set. Optional fields start unset or empty.
5133    #[must_use]
5134    pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
5135        Self {
5136            api_type,
5137            base_url: base_url.into(),
5138            meta: None,
5139        }
5140    }
5141
5142    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5143    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5144    /// do not recognize.
5145    ///
5146    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5147    #[must_use]
5148    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5149        self.meta = meta.into_option();
5150        self
5151    }
5152}
5153
5154/// **UNSTABLE**
5155///
5156/// This capability is not part of the spec yet, and may be removed or changed at any point.
5157///
5158/// Unique identifier for a configurable LLM provider.
5159#[cfg(feature = "unstable_llm_providers")]
5160#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
5162#[serde(transparent)]
5163#[from(forward)]
5164#[non_exhaustive]
5165pub struct ProviderId(pub Arc<str>);
5166
5167#[cfg(feature = "unstable_llm_providers")]
5168impl ProviderId {
5169    /// Wraps a protocol string as a typed [`ProviderId`].
5170    #[must_use]
5171    pub fn new(id: impl Into<Self>) -> Self {
5172        id.into()
5173    }
5174}
5175
5176/// **UNSTABLE**
5177///
5178/// This capability is not part of the spec yet, and may be removed or changed at any point.
5179///
5180/// Information about a configurable LLM provider.
5181#[cfg(feature = "unstable_llm_providers")]
5182#[serde_as]
5183#[skip_serializing_none]
5184#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5186#[serde(rename_all = "camelCase")]
5187#[non_exhaustive]
5188pub struct ProviderInfo {
5189    /// Provider identifier, for example "main" or "openai".
5190    pub provider_id: ProviderId,
5191    /// Supported protocol types for this provider.
5192    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
5193    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
5194    pub supported: Vec<LlmProtocol>,
5195    /// Whether this provider is mandatory and cannot be disabled via `providers/disable`.
5196    /// If true, clients must not call `providers/disable` for this provider ID.
5197    pub required: bool,
5198    /// Current effective non-secret routing config.
5199    /// Null or omitted means provider is disabled.
5200    #[serde(default)]
5201    pub current: Option<ProviderCurrentConfig>,
5202    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5203    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5204    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5205    ///
5206    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5207    #[serde_as(deserialize_as = "DefaultOnError")]
5208    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5209    #[serde(default)]
5210    #[serde(rename = "_meta")]
5211    pub meta: Option<Meta>,
5212}
5213
5214#[cfg(feature = "unstable_llm_providers")]
5215impl ProviderInfo {
5216    /// Builds [`ProviderInfo`] with the required fields set. Optional fields start unset or empty.
5217    #[must_use]
5218    pub fn new(
5219        provider_id: impl Into<ProviderId>,
5220        supported: Vec<LlmProtocol>,
5221        required: bool,
5222        current: impl IntoOption<ProviderCurrentConfig>,
5223    ) -> Self {
5224        Self {
5225            provider_id: provider_id.into(),
5226            supported,
5227            required,
5228            current: current.into_option(),
5229            meta: None,
5230        }
5231    }
5232
5233    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5234    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5235    /// do not recognize.
5236    ///
5237    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5238    #[must_use]
5239    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5240        self.meta = meta.into_option();
5241        self
5242    }
5243}
5244
5245#[cfg(feature = "unstable_llm_providers")]
5246crate::serde_util::default_on_null! {
5247    /// **UNSTABLE**
5248    ///
5249    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5250    ///
5251    /// Request parameters for `providers/list`.
5252    #[serde_as]
5253    #[skip_serializing_none]
5254    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5255    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5256    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5257    #[serde(rename_all = "camelCase")]
5258    #[non_exhaustive]
5259    pub struct ListProvidersRequest {
5260        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5261        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5262        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5263        ///
5264        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5265        #[serde_as(deserialize_as = "DefaultOnError")]
5266        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5267        #[serde(default)]
5268        #[serde(rename = "_meta")]
5269        pub meta: Option<Meta>,
5270    }
5271}
5272
5273#[cfg(feature = "unstable_llm_providers")]
5274impl ListProvidersRequest {
5275    /// Builds [`ListProvidersRequest`] with the required request fields set. Optional fields start unset or empty.
5276    #[must_use]
5277    pub fn new() -> Self {
5278        Self::default()
5279    }
5280
5281    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5282    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5283    /// do not recognize.
5284    ///
5285    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5286    #[must_use]
5287    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5288        self.meta = meta.into_option();
5289        self
5290    }
5291}
5292
5293/// **UNSTABLE**
5294///
5295/// This capability is not part of the spec yet, and may be removed or changed at any point.
5296///
5297/// Response to `providers/list`.
5298#[cfg(feature = "unstable_llm_providers")]
5299#[serde_as]
5300#[skip_serializing_none]
5301#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5302#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5303#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5304#[serde(rename_all = "camelCase")]
5305#[non_exhaustive]
5306pub struct ListProvidersResponse {
5307    /// Configurable providers with current routing info suitable for UI display.
5308    pub providers: Vec<ProviderInfo>,
5309    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5310    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5311    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5312    ///
5313    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5314    #[serde_as(deserialize_as = "DefaultOnError")]
5315    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5316    #[serde(default)]
5317    #[serde(rename = "_meta")]
5318    pub meta: Option<Meta>,
5319}
5320
5321#[cfg(feature = "unstable_llm_providers")]
5322impl ListProvidersResponse {
5323    /// Builds [`ListProvidersResponse`] with the required response fields set. Optional fields start unset or empty.
5324    #[must_use]
5325    pub fn new(providers: Vec<ProviderInfo>) -> Self {
5326        Self {
5327            providers,
5328            meta: None,
5329        }
5330    }
5331
5332    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5333    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5334    /// do not recognize.
5335    ///
5336    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5337    #[must_use]
5338    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5339        self.meta = meta.into_option();
5340        self
5341    }
5342}
5343
5344/// **UNSTABLE**
5345///
5346/// This capability is not part of the spec yet, and may be removed or changed at any point.
5347///
5348/// Request parameters for `providers/set`.
5349///
5350/// Replaces the full configuration for one provider ID.
5351#[cfg(feature = "unstable_llm_providers")]
5352#[serde_as]
5353#[skip_serializing_none]
5354#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5355#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
5356#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5357#[serde(rename_all = "camelCase")]
5358#[non_exhaustive]
5359pub struct SetProviderRequest {
5360    /// Provider ID to configure.
5361    pub provider_id: ProviderId,
5362    /// Protocol type for this provider.
5363    pub api_type: LlmProtocol,
5364    /// Base URL for requests sent through this provider.
5365    #[cfg_attr(feature = "schemars", schemars(url))]
5366    pub base_url: String,
5367    /// Full headers map for this provider.
5368    /// May include authorization, routing, or other integration-specific headers.
5369    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
5370    pub headers: HashMap<String, String>,
5371    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5372    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5373    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5374    ///
5375    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5376    #[serde_as(deserialize_as = "DefaultOnError")]
5377    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5378    #[serde(default)]
5379    #[serde(rename = "_meta")]
5380    pub meta: Option<Meta>,
5381}
5382
5383#[cfg(feature = "unstable_llm_providers")]
5384impl SetProviderRequest {
5385    /// Builds [`SetProviderRequest`] with the required request fields set. Optional fields start unset or empty.
5386    #[must_use]
5387    pub fn new(
5388        provider_id: impl Into<ProviderId>,
5389        api_type: LlmProtocol,
5390        base_url: impl Into<String>,
5391    ) -> Self {
5392        Self {
5393            provider_id: provider_id.into(),
5394            api_type,
5395            base_url: base_url.into(),
5396            headers: HashMap::new(),
5397            meta: None,
5398        }
5399    }
5400
5401    /// Full headers map for this provider.
5402    /// May include authorization, routing, or other integration-specific headers.
5403    #[must_use]
5404    pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
5405        self.headers = headers;
5406        self
5407    }
5408
5409    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5410    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5411    /// do not recognize.
5412    ///
5413    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5414    #[must_use]
5415    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5416        self.meta = meta.into_option();
5417        self
5418    }
5419}
5420
5421#[cfg(feature = "unstable_llm_providers")]
5422crate::serde_util::default_on_null! {
5423    /// **UNSTABLE**
5424    ///
5425    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5426    ///
5427    /// Response to `providers/set`.
5428    #[serde_as]
5429    #[skip_serializing_none]
5430    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5431    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5432    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5433    #[serde(rename_all = "camelCase")]
5434    #[non_exhaustive]
5435    pub struct SetProviderResponse {
5436        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5437        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5438        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5439        ///
5440        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5441        #[serde_as(deserialize_as = "DefaultOnError")]
5442        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5443        #[serde(default)]
5444        #[serde(rename = "_meta")]
5445        pub meta: Option<Meta>,
5446    }
5447}
5448
5449#[cfg(feature = "unstable_llm_providers")]
5450impl SetProviderResponse {
5451    /// Builds [`SetProviderResponse`] with the required response fields set. Optional fields start unset or empty.
5452    #[must_use]
5453    pub fn new() -> Self {
5454        Self::default()
5455    }
5456
5457    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5458    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5459    /// do not recognize.
5460    ///
5461    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5462    #[must_use]
5463    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5464        self.meta = meta.into_option();
5465        self
5466    }
5467}
5468
5469/// **UNSTABLE**
5470///
5471/// This capability is not part of the spec yet, and may be removed or changed at any point.
5472///
5473/// Request parameters for `providers/disable`.
5474#[cfg(feature = "unstable_llm_providers")]
5475#[serde_as]
5476#[skip_serializing_none]
5477#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5478#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5479#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5480#[serde(rename_all = "camelCase")]
5481#[non_exhaustive]
5482pub struct DisableProviderRequest {
5483    /// Provider ID to disable.
5484    pub provider_id: ProviderId,
5485    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5486    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5487    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5488    ///
5489    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5490    #[serde_as(deserialize_as = "DefaultOnError")]
5491    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5492    #[serde(default)]
5493    #[serde(rename = "_meta")]
5494    pub meta: Option<Meta>,
5495}
5496
5497#[cfg(feature = "unstable_llm_providers")]
5498impl DisableProviderRequest {
5499    /// Builds [`DisableProviderRequest`] with the required request fields set. Optional fields start unset or empty.
5500    #[must_use]
5501    pub fn new(provider_id: impl Into<ProviderId>) -> Self {
5502        Self {
5503            provider_id: provider_id.into(),
5504            meta: None,
5505        }
5506    }
5507
5508    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5509    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5510    /// do not recognize.
5511    ///
5512    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5513    #[must_use]
5514    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5515        self.meta = meta.into_option();
5516        self
5517    }
5518}
5519
5520#[cfg(feature = "unstable_llm_providers")]
5521crate::serde_util::default_on_null! {
5522    /// **UNSTABLE**
5523    ///
5524    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5525    ///
5526    /// Response to `providers/disable`.
5527    #[serde_as]
5528    #[skip_serializing_none]
5529    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5530    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5531    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5532    #[serde(rename_all = "camelCase")]
5533    #[non_exhaustive]
5534    pub struct DisableProviderResponse {
5535        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5536        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5537        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5538        ///
5539        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5540        #[serde_as(deserialize_as = "DefaultOnError")]
5541        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5542        #[serde(default)]
5543        #[serde(rename = "_meta")]
5544        pub meta: Option<Meta>,
5545    }
5546}
5547
5548#[cfg(feature = "unstable_llm_providers")]
5549impl DisableProviderResponse {
5550    /// Builds [`DisableProviderResponse`] with the required response fields set. Optional fields start unset or empty.
5551    #[must_use]
5552    pub fn new() -> Self {
5553        Self::default()
5554    }
5555
5556    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5557    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5558    /// do not recognize.
5559    ///
5560    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5561    #[must_use]
5562    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5563        self.meta = meta.into_option();
5564        self
5565    }
5566}
5567
5568// Capabilities
5569
5570/// Capabilities supported by the agent.
5571///
5572/// Advertised during initialization to inform the client about
5573/// available features and content types.
5574///
5575/// See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#agent-capabilities)
5576#[serde_as]
5577#[skip_serializing_none]
5578#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5579#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5580#[serde(rename_all = "camelCase")]
5581#[non_exhaustive]
5582pub struct AgentCapabilities {
5583    /// Session capabilities supported by the agent.
5584    ///
5585    /// Optional. Omitted and `null` are equivalent and mean the agent does not support the
5586    /// `session/*` methods. Supplying `{}` means the agent supports the
5587    /// baseline session methods: `session/new`, `session/list`,
5588    /// `session/resume`, `session/close`, `session/prompt`, `session/cancel`,
5589    /// and `session/update`.
5590    #[serde_as(deserialize_as = "DefaultOnError")]
5591    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5592    #[serde(default)]
5593    pub session: Option<SessionCapabilities>,
5594    /// Authentication-related extension capabilities supported by the agent.
5595    ///
5596    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise any
5597    /// authentication-related extensions. This field does not advertise support
5598    /// for `auth/login` or `auth/logout`. Those methods are advertised by a
5599    /// non-empty `authMethods` list in the `initialize` response.
5600    #[serde_as(deserialize_as = "DefaultOnError")]
5601    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5602    #[serde(default)]
5603    pub auth: Option<AgentAuthCapabilities>,
5604    /// **UNSTABLE**
5605    ///
5606    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5607    ///
5608    /// Provider configuration capabilities supported by the agent.
5609    ///
5610    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5611    /// Supplying `{}` means the agent supports provider configuration methods.
5612    #[cfg(feature = "unstable_llm_providers")]
5613    #[serde_as(deserialize_as = "DefaultOnError")]
5614    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5615    #[serde(default)]
5616    pub providers: Option<ProvidersCapabilities>,
5617    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5618    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5619    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5620    ///
5621    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5622    #[serde_as(deserialize_as = "DefaultOnError")]
5623    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5624    #[serde(default)]
5625    #[serde(rename = "_meta")]
5626    pub meta: Option<Meta>,
5627}
5628
5629impl AgentCapabilities {
5630    /// Builds an empty [`AgentCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5631    #[must_use]
5632    pub fn new() -> Self {
5633        Self::default()
5634    }
5635
5636    /// Session capabilities supported by the agent.
5637    ///
5638    /// Omitted and `null` are equivalent and mean the agent does not support the `session/*`
5639    /// methods. Supplying `{}` means the agent supports the baseline
5640    /// session methods: `session/new`, `session/list`, `session/resume`,
5641    /// `session/close`, `session/prompt`, `session/cancel`, and
5642    /// `session/update`.
5643    #[must_use]
5644    pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
5645        self.session = session.into_option();
5646        self
5647    }
5648
5649    /// Authentication-related extension capabilities supported by the agent.
5650    ///
5651    /// This field does not advertise support for `auth/login` or `auth/logout`.
5652    #[must_use]
5653    pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
5654        self.auth = auth.into_option();
5655        self
5656    }
5657
5658    /// **UNSTABLE**
5659    ///
5660    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5661    ///
5662    /// Provider configuration capabilities supported by the agent.
5663    #[cfg(feature = "unstable_llm_providers")]
5664    #[must_use]
5665    pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
5666        self.providers = providers.into_option();
5667        self
5668    }
5669
5670    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5671    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5672    /// do not recognize.
5673    ///
5674    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5675    #[must_use]
5676    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5677        self.meta = meta.into_option();
5678        self
5679    }
5680}
5681
5682/// **UNSTABLE**
5683///
5684/// This capability is not part of the spec yet, and may be removed or changed at any point.
5685///
5686/// Provider configuration capabilities supported by the agent.
5687///
5688/// Supplying `{}` means the agent supports provider configuration methods.
5689#[cfg(feature = "unstable_llm_providers")]
5690#[serde_as]
5691#[skip_serializing_none]
5692#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5693#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5694#[non_exhaustive]
5695pub struct ProvidersCapabilities {
5696    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5697    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5698    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5699    ///
5700    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5701    #[serde_as(deserialize_as = "DefaultOnError")]
5702    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5703    #[serde(default)]
5704    #[serde(rename = "_meta")]
5705    pub meta: Option<Meta>,
5706}
5707
5708#[cfg(feature = "unstable_llm_providers")]
5709impl ProvidersCapabilities {
5710    /// Builds an empty [`ProvidersCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5711    #[must_use]
5712    pub fn new() -> Self {
5713        Self::default()
5714    }
5715
5716    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5717    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5718    /// do not recognize.
5719    ///
5720    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5721    #[must_use]
5722    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5723        self.meta = meta.into_option();
5724        self
5725    }
5726}
5727
5728/// Session capabilities supported by the agent.
5729///
5730/// Supplying `{}` means the agent supports the baseline session methods:
5731/// `session/new`, `session/list`, `session/resume`, `session/close`,
5732/// `session/prompt`, `session/cancel`, and `session/update`.
5733///
5734/// Agents that support sessions **MAY** support additional session methods,
5735/// prompt content types, and MCP transports by specifying additional
5736/// capabilities.
5737///
5738/// See protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#session-capabilities)
5739#[serde_as]
5740#[skip_serializing_none]
5741#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5742#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5743#[serde(rename_all = "camelCase")]
5744#[non_exhaustive]
5745pub struct SessionCapabilities {
5746    /// Prompt capabilities supported by the agent in `session/prompt` requests.
5747    ///
5748    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise any
5749    /// prompt extensions beyond the baseline text and resource-link content
5750    /// required by `session/prompt`.
5751    #[serde_as(deserialize_as = "DefaultOnError")]
5752    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5753    #[serde(default)]
5754    pub prompt: Option<PromptCapabilities>,
5755    /// MCP capabilities supported by the agent for session lifecycle requests.
5756    ///
5757    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise MCP
5758    /// server transport support for sessions.
5759    #[serde_as(deserialize_as = "DefaultOnError")]
5760    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5761    #[serde(default)]
5762    pub mcp: Option<McpCapabilities>,
5763    /// Whether the agent supports `session/delete`.
5764    ///
5765    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5766    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
5767    #[serde_as(deserialize_as = "DefaultOnError")]
5768    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5769    #[serde(default)]
5770    pub delete: Option<SessionDeleteCapabilities>,
5771    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
5772    ///
5773    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5774    /// Supplying `{}` means the agent supports `additionalDirectories` on
5775    /// supported session lifecycle requests.
5776    ///
5777    /// Agents may return `SessionInfo.additionalDirectories` to report the
5778    /// complete ordered additional-root list associated with a listed session.
5779    #[serde_as(deserialize_as = "DefaultOnError")]
5780    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5781    #[serde(default)]
5782    pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
5783    /// **UNSTABLE**
5784    ///
5785    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5786    ///
5787    /// Whether the agent supports `systemPrompt` on session lifecycle requests.
5788    ///
5789    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5790    /// Supplying `{}` means the agent supports `systemPrompt` in `append` mode.
5791    #[cfg(feature = "unstable_session_system_prompt")]
5792    #[serde_as(deserialize_as = "DefaultOnError")]
5793    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5794    #[serde(default)]
5795    pub system_prompt: Option<SessionSystemPromptCapabilities>,
5796    /// **UNSTABLE**
5797    ///
5798    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5799    ///
5800    /// Whether the agent supports `session/fork`.
5801    ///
5802    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5803    /// Supplying `{}` means the agent supports forking sessions.
5804    #[cfg(feature = "unstable_session_fork")]
5805    #[serde_as(deserialize_as = "DefaultOnError")]
5806    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5807    #[serde(default)]
5808    pub fork: Option<SessionForkCapabilities>,
5809    /// **UNSTABLE**
5810    ///
5811    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5812    ///
5813    /// Whether the agent supports `session/archive` and `session/unarchive`.
5814    ///
5815    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5816    /// Supplying `{}` means the agent supports both methods, the `archived` filter on
5817    /// `session/list`, and `SessionInfo.archived`.
5818    #[cfg(feature = "unstable_session_archive")]
5819    #[serde_as(deserialize_as = "DefaultOnError")]
5820    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5821    #[serde(default)]
5822    pub archive: Option<SessionArchiveCapabilities>,
5823    /// **UNSTABLE**
5824    ///
5825    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5826    ///
5827    /// Whether the agent supports `session/status`.
5828    ///
5829    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5830    /// Supplying `{}` means the agent supports checking session status.
5831    #[cfg(feature = "unstable_session_status")]
5832    #[serde_as(deserialize_as = "DefaultOnError")]
5833    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5834    #[serde(default)]
5835    pub status: Option<SessionStatusCapabilities>,
5836    /// **UNSTABLE**
5837    ///
5838    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5839    ///
5840    /// Whether the agent supports `session/set_title`.
5841    ///
5842    /// Agents report titles through `session/list` and `session_info_update` without this
5843    /// capability. It only advertises that Clients can set titles.
5844    ///
5845    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5846    /// Supplying `{}` means the agent supports setting session titles.
5847    #[cfg(feature = "unstable_session_set_title")]
5848    #[serde_as(deserialize_as = "DefaultOnError")]
5849    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5850    #[serde(default)]
5851    pub title: Option<SessionTitleCapabilities>,
5852    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5853    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5854    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5855    ///
5856    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5857    #[serde_as(deserialize_as = "DefaultOnError")]
5858    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5859    #[serde(default)]
5860    #[serde(rename = "_meta")]
5861    pub meta: Option<Meta>,
5862}
5863
5864impl SessionCapabilities {
5865    /// Builds an empty [`SessionCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5866    #[must_use]
5867    pub fn new() -> Self {
5868        Self::default()
5869    }
5870
5871    /// Prompt capabilities supported by the agent in `session/prompt` requests.
5872    ///
5873    /// Omitted and `null` are equivalent and mean the agent does not advertise any prompt
5874    /// extensions beyond the baseline text and resource-link content required by
5875    /// `session/prompt`.
5876    #[must_use]
5877    pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
5878        self.prompt = prompt.into_option();
5879        self
5880    }
5881
5882    /// MCP capabilities supported by the agent for session lifecycle requests.
5883    ///
5884    /// Omitted and `null` are equivalent and mean the agent does not advertise MCP server
5885    /// transport support for sessions.
5886    #[must_use]
5887    pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
5888        self.mcp = mcp.into_option();
5889        self
5890    }
5891
5892    /// Whether the agent supports `session/delete`.
5893    ///
5894    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5895    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
5896    #[must_use]
5897    pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
5898        self.delete = delete.into_option();
5899        self
5900    }
5901
5902    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
5903    ///
5904    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5905    /// Supplying `{}` means the agent supports `additionalDirectories` on
5906    /// supported session lifecycle requests.
5907    ///
5908    /// Agents may return `SessionInfo.additionalDirectories` to report the
5909    /// complete ordered additional-root list associated with a listed session.
5910    #[must_use]
5911    pub fn additional_directories(
5912        mut self,
5913        additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
5914    ) -> Self {
5915        self.additional_directories = additional_directories.into_option();
5916        self
5917    }
5918
5919    /// **UNSTABLE**
5920    ///
5921    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5922    ///
5923    /// Whether the agent supports `systemPrompt` on session lifecycle requests.
5924    ///
5925    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5926    /// Supplying `{}` means the agent supports `systemPrompt` in `append` mode.
5927    #[cfg(feature = "unstable_session_system_prompt")]
5928    #[must_use]
5929    pub fn system_prompt(
5930        mut self,
5931        system_prompt: impl IntoOption<SessionSystemPromptCapabilities>,
5932    ) -> Self {
5933        self.system_prompt = system_prompt.into_option();
5934        self
5935    }
5936
5937    #[cfg(feature = "unstable_session_fork")]
5938    /// Whether the agent supports `session/fork`.
5939    ///
5940    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5941    /// Supplying `{}` means the agent supports forking sessions.
5942    #[must_use]
5943    pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
5944        self.fork = fork.into_option();
5945        self
5946    }
5947
5948    /// **UNSTABLE**
5949    ///
5950    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5951    ///
5952    /// Whether the agent supports `session/archive` and `session/unarchive`.
5953    ///
5954    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5955    /// Supplying `{}` means the agent supports both methods, the `archived` filter on
5956    /// `session/list`, and `SessionInfo.archived`.
5957    #[cfg(feature = "unstable_session_archive")]
5958    #[must_use]
5959    pub fn archive(mut self, archive: impl IntoOption<SessionArchiveCapabilities>) -> Self {
5960        self.archive = archive.into_option();
5961        self
5962    }
5963
5964    /// **UNSTABLE**
5965    ///
5966    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5967    ///
5968    /// Whether the agent supports `session/status`.
5969    ///
5970    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5971    /// Supplying `{}` means the agent supports checking session status.
5972    #[cfg(feature = "unstable_session_status")]
5973    #[must_use]
5974    pub fn status(mut self, status: impl IntoOption<SessionStatusCapabilities>) -> Self {
5975        self.status = status.into_option();
5976        self
5977    }
5978
5979    /// **UNSTABLE**
5980    ///
5981    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5982    ///
5983    /// Whether the agent supports `session/set_title`.
5984    ///
5985    /// Agents report titles through `session/list` and `session_info_update` without this
5986    /// capability. It only advertises that Clients can set titles.
5987    ///
5988    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5989    /// Supplying `{}` means the agent supports setting session titles.
5990    #[cfg(feature = "unstable_session_set_title")]
5991    #[must_use]
5992    pub fn title(mut self, title: impl IntoOption<SessionTitleCapabilities>) -> Self {
5993        self.title = title.into_option();
5994        self
5995    }
5996
5997    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5998    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5999    /// do not recognize.
6000    ///
6001    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6002    #[must_use]
6003    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6004        self.meta = meta.into_option();
6005        self
6006    }
6007}
6008
6009/// Capabilities for the `session/delete` method.
6010///
6011/// Supplying `{}` means the agent supports deleting sessions from `session/list`.
6012#[serde_as]
6013#[skip_serializing_none]
6014#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6015#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6016#[non_exhaustive]
6017pub struct SessionDeleteCapabilities {
6018    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6019    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6020    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6021    ///
6022    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6023    #[serde_as(deserialize_as = "DefaultOnError")]
6024    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6025    #[serde(default)]
6026    #[serde(rename = "_meta")]
6027    pub meta: Option<Meta>,
6028}
6029
6030impl SessionDeleteCapabilities {
6031    /// Builds an empty [`SessionDeleteCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6032    #[must_use]
6033    pub fn new() -> Self {
6034        Self::default()
6035    }
6036
6037    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6038    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6039    /// do not recognize.
6040    ///
6041    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6042    #[must_use]
6043    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6044        self.meta = meta.into_option();
6045        self
6046    }
6047}
6048
6049/// Capabilities for additional session directories support.
6050///
6051/// Supplying `{}` means the agent supports the `additionalDirectories` field on
6052/// supported session lifecycle requests. Agents may return
6053/// `SessionInfo.additionalDirectories` in `session/list` to report the
6054/// complete ordered additional-root list associated with a listed session.
6055#[serde_as]
6056#[skip_serializing_none]
6057#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6058#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6059#[non_exhaustive]
6060pub struct SessionAdditionalDirectoriesCapabilities {
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 SessionAdditionalDirectoriesCapabilities {
6074    /// Builds an empty [`SessionAdditionalDirectoriesCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6075    #[must_use]
6076    pub fn new() -> Self {
6077        Self::default()
6078    }
6079
6080    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6081    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6082    /// do not recognize.
6083    ///
6084    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6085    #[must_use]
6086    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6087        self.meta = meta.into_option();
6088        self
6089    }
6090}
6091
6092/// **UNSTABLE**
6093///
6094/// This capability is not part of the spec yet, and may be removed or changed at any point.
6095///
6096/// Capabilities for client-provided system prompts.
6097///
6098/// Supplying `{}` means the agent supports `systemPrompt` on session lifecycle
6099/// requests in `append` mode.
6100#[cfg(feature = "unstable_session_system_prompt")]
6101#[serde_as]
6102#[skip_serializing_none]
6103#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6104#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6105#[serde(rename_all = "camelCase")]
6106#[non_exhaustive]
6107pub struct SessionSystemPromptCapabilities {
6108    /// Delivery modes the agent supports in addition to `append`.
6109    ///
6110    /// Optional. Omitted and `null` are equivalent and mean the agent supports only `append`.
6111    #[serde_as(deserialize_as = "DefaultOnError")]
6112    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6113    #[serde(default)]
6114    pub mode: Option<SessionSystemPromptModeCapabilities>,
6115    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6116    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6117    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6118    ///
6119    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6120    #[serde_as(deserialize_as = "DefaultOnError")]
6121    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6122    #[serde(default)]
6123    #[serde(rename = "_meta")]
6124    pub meta: Option<Meta>,
6125}
6126
6127#[cfg(feature = "unstable_session_system_prompt")]
6128impl SessionSystemPromptCapabilities {
6129    /// Builds an empty [`SessionSystemPromptCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6130    #[must_use]
6131    pub fn new() -> Self {
6132        Self::default()
6133    }
6134
6135    /// Delivery modes the agent supports in addition to `append`.
6136    ///
6137    /// Omitted and `null` are equivalent and mean the agent supports only `append`.
6138    #[must_use]
6139    pub fn mode(mut self, mode: impl IntoOption<SessionSystemPromptModeCapabilities>) -> Self {
6140        self.mode = mode.into_option();
6141        self
6142    }
6143
6144    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6145    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6146    /// do not recognize.
6147    ///
6148    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6149    #[must_use]
6150    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6151        self.meta = meta.into_option();
6152        self
6153    }
6154}
6155
6156/// **UNSTABLE**
6157///
6158/// This capability is not part of the spec yet, and may be removed or changed at any point.
6159///
6160/// System prompt delivery modes the agent supports in addition to `append`.
6161///
6162/// `append` has no entry because every agent advertising `session.systemPrompt`
6163/// supports it.
6164#[cfg(feature = "unstable_session_system_prompt")]
6165#[serde_as]
6166#[skip_serializing_none]
6167#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6168#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6169#[serde(rename_all = "camelCase")]
6170#[non_exhaustive]
6171pub struct SessionSystemPromptModeCapabilities {
6172    /// Whether the agent supports `override` mode.
6173    ///
6174    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6175    /// Supplying `{}` means the agent supports replacing its built-in default
6176    /// instructions with the client-provided system prompt.
6177    #[serde_as(deserialize_as = "DefaultOnError")]
6178    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6179    #[serde(default, rename = "override")]
6180    pub override_: Option<SessionSystemPromptOverrideCapabilities>,
6181    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6182    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6183    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6184    ///
6185    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6186    #[serde_as(deserialize_as = "DefaultOnError")]
6187    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6188    #[serde(default)]
6189    #[serde(rename = "_meta")]
6190    pub meta: Option<Meta>,
6191}
6192
6193#[cfg(feature = "unstable_session_system_prompt")]
6194impl SessionSystemPromptModeCapabilities {
6195    /// Builds an empty [`SessionSystemPromptModeCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6196    #[must_use]
6197    pub fn new() -> Self {
6198        Self::default()
6199    }
6200
6201    /// Whether the agent supports `override` mode.
6202    ///
6203    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6204    /// Supplying `{}` means the agent supports replacing its built-in default
6205    /// instructions with the client-provided system prompt.
6206    #[must_use]
6207    pub fn override_(
6208        mut self,
6209        override_: impl IntoOption<SessionSystemPromptOverrideCapabilities>,
6210    ) -> Self {
6211        self.override_ = override_.into_option();
6212        self
6213    }
6214
6215    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6216    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6217    /// do not recognize.
6218    ///
6219    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6220    #[must_use]
6221    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6222        self.meta = meta.into_option();
6223        self
6224    }
6225}
6226
6227/// **UNSTABLE**
6228///
6229/// This capability is not part of the spec yet, and may be removed or changed at any point.
6230///
6231/// Capabilities for the `override` system prompt mode.
6232///
6233/// Supplying `{}` means the agent supports replacing its built-in default
6234/// instructions with the client-provided system prompt.
6235#[cfg(feature = "unstable_session_system_prompt")]
6236#[serde_as]
6237#[skip_serializing_none]
6238#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6239#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6240#[non_exhaustive]
6241pub struct SessionSystemPromptOverrideCapabilities {
6242    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6243    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6244    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6245    ///
6246    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6247    #[serde_as(deserialize_as = "DefaultOnError")]
6248    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6249    #[serde(default)]
6250    #[serde(rename = "_meta")]
6251    pub meta: Option<Meta>,
6252}
6253
6254#[cfg(feature = "unstable_session_system_prompt")]
6255impl SessionSystemPromptOverrideCapabilities {
6256    /// Builds an empty [`SessionSystemPromptOverrideCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6257    #[must_use]
6258    pub fn new() -> Self {
6259        Self::default()
6260    }
6261
6262    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6263    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6264    /// do not recognize.
6265    ///
6266    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6267    #[must_use]
6268    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6269        self.meta = meta.into_option();
6270        self
6271    }
6272}
6273
6274/// **UNSTABLE**
6275///
6276/// This capability is not part of the spec yet, and may be removed or changed at any point.
6277///
6278/// Capabilities for the `session/fork` method.
6279///
6280/// Supplying `{}` means the agent supports forking sessions.
6281#[cfg(feature = "unstable_session_fork")]
6282#[serde_as]
6283#[skip_serializing_none]
6284#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6285#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6286#[non_exhaustive]
6287pub struct SessionForkCapabilities {
6288    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6289    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6290    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6291    ///
6292    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6293    #[serde_as(deserialize_as = "DefaultOnError")]
6294    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6295    #[serde(default)]
6296    #[serde(rename = "_meta")]
6297    pub meta: Option<Meta>,
6298}
6299
6300#[cfg(feature = "unstable_session_fork")]
6301impl SessionForkCapabilities {
6302    /// Builds an empty [`SessionForkCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6303    #[must_use]
6304    pub fn new() -> Self {
6305        Self::default()
6306    }
6307
6308    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6309    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6310    /// do not recognize.
6311    ///
6312    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6313    #[must_use]
6314    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6315        self.meta = meta.into_option();
6316        self
6317    }
6318}
6319
6320/// **UNSTABLE**
6321///
6322/// This capability is not part of the spec yet, and may be removed or changed at any point.
6323///
6324/// Capabilities for `session/archive` and `session/unarchive`.
6325///
6326/// Supplying `{}` means the agent supports both methods, the `archived` filter on
6327/// `session/list`, and `SessionInfo.archived`.
6328#[cfg(feature = "unstable_session_archive")]
6329#[serde_as]
6330#[skip_serializing_none]
6331#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6332#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6333#[non_exhaustive]
6334pub struct SessionArchiveCapabilities {
6335    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6336    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6337    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6338    ///
6339    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6340    #[serde_as(deserialize_as = "DefaultOnError")]
6341    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6342    #[serde(default)]
6343    #[serde(rename = "_meta")]
6344    pub meta: Option<Meta>,
6345}
6346
6347#[cfg(feature = "unstable_session_archive")]
6348impl SessionArchiveCapabilities {
6349    /// Builds an empty [`SessionArchiveCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6350    #[must_use]
6351    pub fn new() -> Self {
6352        Self::default()
6353    }
6354
6355    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6356    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6357    /// do not recognize.
6358    ///
6359    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6360    #[must_use]
6361    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6362        self.meta = meta.into_option();
6363        self
6364    }
6365}
6366
6367/// **UNSTABLE**
6368///
6369/// This capability is not part of the spec yet, and may be removed or changed at any point.
6370///
6371/// Capabilities for the `session/status` method.
6372///
6373/// Supplying `{}` means the agent supports checking session status.
6374#[cfg(feature = "unstable_session_status")]
6375#[serde_as]
6376#[skip_serializing_none]
6377#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6378#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6379#[non_exhaustive]
6380pub struct SessionStatusCapabilities {
6381    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6382    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6383    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6384    ///
6385    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6386    #[serde_as(deserialize_as = "DefaultOnError")]
6387    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6388    #[serde(default)]
6389    #[serde(rename = "_meta")]
6390    pub meta: Option<Meta>,
6391}
6392
6393#[cfg(feature = "unstable_session_status")]
6394impl SessionStatusCapabilities {
6395    /// Builds an empty [`SessionStatusCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6396    #[must_use]
6397    pub fn new() -> Self {
6398        Self::default()
6399    }
6400
6401    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6402    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6403    /// do not recognize.
6404    ///
6405    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6406    #[must_use]
6407    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6408        self.meta = meta.into_option();
6409        self
6410    }
6411}
6412
6413/// **UNSTABLE**
6414///
6415/// This capability is not part of the spec yet, and may be removed or changed at any point.
6416///
6417/// Capabilities for Client-set session titles.
6418///
6419/// Supplying `{}` means the agent supports `session/set_title`. Agents report titles
6420/// through `session/list` and `session_info_update` without this capability.
6421#[cfg(feature = "unstable_session_set_title")]
6422#[serde_as]
6423#[skip_serializing_none]
6424#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6425#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6426#[non_exhaustive]
6427pub struct SessionTitleCapabilities {
6428    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6429    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6430    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6431    ///
6432    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6433    #[serde_as(deserialize_as = "DefaultOnError")]
6434    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6435    #[serde(default)]
6436    #[serde(rename = "_meta")]
6437    pub meta: Option<Meta>,
6438}
6439
6440#[cfg(feature = "unstable_session_set_title")]
6441impl SessionTitleCapabilities {
6442    /// Builds an empty [`SessionTitleCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6443    #[must_use]
6444    pub fn new() -> Self {
6445        Self::default()
6446    }
6447
6448    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6449    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6450    /// do not recognize.
6451    ///
6452    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6453    #[must_use]
6454    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6455        self.meta = meta.into_option();
6456        self
6457    }
6458}
6459
6460/// Prompt capabilities supported by the agent in `session/prompt` requests.
6461///
6462/// Baseline agent functionality requires support for `text`
6463/// and `resource_link` content blocks in prompt requests.
6464///
6465/// Other variants must be explicitly opted in to.
6466/// Capabilities for different types of content in prompt requests.
6467///
6468/// Indicates which content types beyond the baseline (text and resource links)
6469/// the agent can process.
6470///
6471/// See protocol docs: [Session Prompt Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#session-prompt-capabilities)
6472#[serde_as]
6473#[skip_serializing_none]
6474#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6475#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6476#[serde(rename_all = "camelCase")]
6477#[non_exhaustive]
6478pub struct PromptCapabilities {
6479    /// Agent supports `image` content blocks.
6480    ///
6481    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6482    /// Supplying `{}` means the agent supports image content in prompts.
6483    #[serde_as(deserialize_as = "DefaultOnError")]
6484    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6485    #[serde(default)]
6486    pub image: Option<PromptImageCapabilities>,
6487    /// Agent supports `audio` content blocks.
6488    ///
6489    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6490    /// Supplying `{}` means the agent supports audio content in prompts.
6491    #[serde_as(deserialize_as = "DefaultOnError")]
6492    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6493    #[serde(default)]
6494    pub audio: Option<PromptAudioCapabilities>,
6495    /// Agent supports embedded context in `session/prompt` requests.
6496    ///
6497    /// When enabled, the Client is allowed to include `resource` content blocks
6498    /// in prompt requests for pieces of context that are referenced in the message.
6499    ///
6500    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6501    /// Supplying `{}` means the agent supports embedded context in prompts.
6502    #[serde_as(deserialize_as = "DefaultOnError")]
6503    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6504    #[serde(default)]
6505    pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
6506    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6507    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6508    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6509    ///
6510    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6511    #[serde_as(deserialize_as = "DefaultOnError")]
6512    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6513    #[serde(default)]
6514    #[serde(rename = "_meta")]
6515    pub meta: Option<Meta>,
6516}
6517
6518impl PromptCapabilities {
6519    /// Builds an empty [`PromptCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6520    #[must_use]
6521    pub fn new() -> Self {
6522        Self::default()
6523    }
6524
6525    /// Agent supports `image` content blocks.
6526    ///
6527    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6528    /// Supplying `{}` means the agent supports image content in prompts.
6529    #[must_use]
6530    pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
6531        self.image = image.into_option();
6532        self
6533    }
6534
6535    /// Agent supports `audio` content blocks.
6536    ///
6537    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6538    /// Supplying `{}` means the agent supports audio content in prompts.
6539    #[must_use]
6540    pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
6541        self.audio = audio.into_option();
6542        self
6543    }
6544
6545    /// Agent supports embedded context in `session/prompt` requests.
6546    ///
6547    /// When enabled, the Client is allowed to include `resource` content blocks
6548    /// in prompt requests for pieces of context that are referenced in the message.
6549    ///
6550    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6551    /// Supplying `{}` means the agent supports embedded context in prompts.
6552    #[must_use]
6553    pub fn embedded_context(
6554        mut self,
6555        embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
6556    ) -> Self {
6557        self.embedded_context = embedded_context.into_option();
6558        self
6559    }
6560
6561    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6562    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6563    /// do not recognize.
6564    ///
6565    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6566    #[must_use]
6567    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6568        self.meta = meta.into_option();
6569        self
6570    }
6571}
6572
6573/// Capabilities for image content in prompt requests.
6574///
6575/// Supplying `{}` means the agent supports image content in prompts.
6576#[serde_as]
6577#[skip_serializing_none]
6578#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6579#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6580#[non_exhaustive]
6581pub struct PromptImageCapabilities {
6582    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6583    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6584    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6585    ///
6586    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6587    #[serde_as(deserialize_as = "DefaultOnError")]
6588    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6589    #[serde(default)]
6590    #[serde(rename = "_meta")]
6591    pub meta: Option<Meta>,
6592}
6593
6594impl PromptImageCapabilities {
6595    /// Builds an empty [`PromptImageCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6596    #[must_use]
6597    pub fn new() -> Self {
6598        Self::default()
6599    }
6600
6601    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6602    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6603    /// do not recognize.
6604    ///
6605    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6606    #[must_use]
6607    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6608        self.meta = meta.into_option();
6609        self
6610    }
6611}
6612
6613/// Capabilities for audio content in prompt requests.
6614///
6615/// Supplying `{}` means the agent supports audio content in prompts.
6616#[serde_as]
6617#[skip_serializing_none]
6618#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6619#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6620#[non_exhaustive]
6621pub struct PromptAudioCapabilities {
6622    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6623    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6624    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6625    ///
6626    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6627    #[serde_as(deserialize_as = "DefaultOnError")]
6628    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6629    #[serde(default)]
6630    #[serde(rename = "_meta")]
6631    pub meta: Option<Meta>,
6632}
6633
6634impl PromptAudioCapabilities {
6635    /// Builds an empty [`PromptAudioCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6636    #[must_use]
6637    pub fn new() -> Self {
6638        Self::default()
6639    }
6640
6641    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6642    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6643    /// do not recognize.
6644    ///
6645    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6646    #[must_use]
6647    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6648        self.meta = meta.into_option();
6649        self
6650    }
6651}
6652
6653/// Capabilities for embedded context in prompt requests.
6654///
6655/// Supplying `{}` means the agent supports embedded context in prompts.
6656#[serde_as]
6657#[skip_serializing_none]
6658#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6659#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6660#[non_exhaustive]
6661pub struct PromptEmbeddedContextCapabilities {
6662    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6663    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6664    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6665    ///
6666    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6667    #[serde_as(deserialize_as = "DefaultOnError")]
6668    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6669    #[serde(default)]
6670    #[serde(rename = "_meta")]
6671    pub meta: Option<Meta>,
6672}
6673
6674impl PromptEmbeddedContextCapabilities {
6675    /// Builds an empty [`PromptEmbeddedContextCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6676    #[must_use]
6677    pub fn new() -> Self {
6678        Self::default()
6679    }
6680
6681    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6682    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6683    /// do not recognize.
6684    ///
6685    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6686    #[must_use]
6687    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6688        self.meta = meta.into_option();
6689        self
6690    }
6691}
6692
6693/// MCP capabilities supported by the agent for session lifecycle requests.
6694#[serde_as]
6695#[skip_serializing_none]
6696#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6697#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6698#[serde(rename_all = "camelCase")]
6699#[non_exhaustive]
6700pub struct McpCapabilities {
6701    /// Agent supports `stdio` MCP servers.
6702    ///
6703    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6704    /// Supplying `{}` means the agent supports stdio MCP server transports.
6705    #[serde_as(deserialize_as = "DefaultOnError")]
6706    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6707    #[serde(default)]
6708    pub stdio: Option<McpStdioCapabilities>,
6709    /// Agent supports `http` MCP servers.
6710    ///
6711    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6712    /// Supplying `{}` means the agent supports HTTP MCP server transports.
6713    #[serde_as(deserialize_as = "DefaultOnError")]
6714    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6715    #[serde(default)]
6716    pub http: Option<McpHttpCapabilities>,
6717    /// **UNSTABLE**
6718    ///
6719    /// This capability is not part of the spec yet, and may be removed or changed at any point.
6720    ///
6721    /// Agent supports `acp` MCP servers.
6722    ///
6723    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6724    /// Supplying `{}` means the agent supports ACP MCP server transports.
6725    #[cfg(feature = "unstable_mcp_over_acp")]
6726    #[serde_as(deserialize_as = "DefaultOnError")]
6727    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6728    #[serde(default)]
6729    pub acp: Option<McpAcpCapabilities>,
6730    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6731    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6732    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6733    ///
6734    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6735    #[serde_as(deserialize_as = "DefaultOnError")]
6736    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6737    #[serde(default)]
6738    #[serde(rename = "_meta")]
6739    pub meta: Option<Meta>,
6740}
6741
6742impl McpCapabilities {
6743    /// Builds an empty [`McpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6744    #[must_use]
6745    pub fn new() -> Self {
6746        Self::default()
6747    }
6748
6749    /// Agent supports `stdio` MCP servers.
6750    ///
6751    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6752    /// Supplying `{}` means the agent supports stdio MCP server transports.
6753    #[must_use]
6754    pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
6755        self.stdio = stdio.into_option();
6756        self
6757    }
6758
6759    /// Agent supports `http` MCP servers.
6760    ///
6761    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6762    /// Supplying `{}` means the agent supports HTTP MCP server transports.
6763    #[must_use]
6764    pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
6765        self.http = http.into_option();
6766        self
6767    }
6768
6769    /// **UNSTABLE**
6770    ///
6771    /// This capability is not part of the spec yet, and may be removed or changed at any point.
6772    ///
6773    /// Agent supports `acp` MCP servers.
6774    #[cfg(feature = "unstable_mcp_over_acp")]
6775    ///
6776    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6777    /// Supplying `{}` means the agent supports ACP MCP server transports.
6778    #[must_use]
6779    pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
6780        self.acp = acp.into_option();
6781        self
6782    }
6783
6784    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6785    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6786    /// do not recognize.
6787    ///
6788    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6789    #[must_use]
6790    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6791        self.meta = meta.into_option();
6792        self
6793    }
6794}
6795
6796/// Capabilities for stdio MCP server transports.
6797///
6798/// Supplying `{}` means the agent supports stdio MCP server transports.
6799#[serde_as]
6800#[skip_serializing_none]
6801#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6802#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6803#[non_exhaustive]
6804pub struct McpStdioCapabilities {
6805    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6806    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6807    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6808    ///
6809    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6810    #[serde_as(deserialize_as = "DefaultOnError")]
6811    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6812    #[serde(default)]
6813    #[serde(rename = "_meta")]
6814    pub meta: Option<Meta>,
6815}
6816
6817impl McpStdioCapabilities {
6818    /// Builds an empty [`McpStdioCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6819    #[must_use]
6820    pub fn new() -> Self {
6821        Self::default()
6822    }
6823
6824    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6825    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6826    /// do not recognize.
6827    ///
6828    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6829    #[must_use]
6830    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6831        self.meta = meta.into_option();
6832        self
6833    }
6834}
6835
6836/// Capabilities for HTTP MCP server transports.
6837///
6838/// Supplying `{}` means the agent supports HTTP MCP server transports.
6839#[serde_as]
6840#[skip_serializing_none]
6841#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6842#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6843#[non_exhaustive]
6844pub struct McpHttpCapabilities {
6845    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6846    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6847    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6848    ///
6849    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6850    #[serde_as(deserialize_as = "DefaultOnError")]
6851    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6852    #[serde(default)]
6853    #[serde(rename = "_meta")]
6854    pub meta: Option<Meta>,
6855}
6856
6857impl McpHttpCapabilities {
6858    /// Builds an empty [`McpHttpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6859    #[must_use]
6860    pub fn new() -> Self {
6861        Self::default()
6862    }
6863
6864    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6865    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6866    /// do not recognize.
6867    ///
6868    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6869    #[must_use]
6870    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6871        self.meta = meta.into_option();
6872        self
6873    }
6874}
6875
6876/// **UNSTABLE**
6877///
6878/// This capability is not part of the spec yet, and may be removed or changed at any point.
6879///
6880/// Capabilities for ACP MCP server transports.
6881///
6882/// Supplying `{}` means the agent supports ACP MCP server transports.
6883#[cfg(feature = "unstable_mcp_over_acp")]
6884#[serde_as]
6885#[skip_serializing_none]
6886#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6887#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6888#[non_exhaustive]
6889pub struct McpAcpCapabilities {
6890    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6891    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6892    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6893    ///
6894    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6895    #[serde_as(deserialize_as = "DefaultOnError")]
6896    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6897    #[serde(default)]
6898    #[serde(rename = "_meta")]
6899    pub meta: Option<Meta>,
6900}
6901
6902#[cfg(feature = "unstable_mcp_over_acp")]
6903impl McpAcpCapabilities {
6904    /// Builds an empty [`McpAcpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6905    #[must_use]
6906    pub fn new() -> Self {
6907        Self::default()
6908    }
6909
6910    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6911    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6912    /// do not recognize.
6913    ///
6914    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6915    #[must_use]
6916    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6917        self.meta = meta.into_option();
6918        self
6919    }
6920}
6921
6922/// Notification to cancel a session's foreground work and its prompts not yet inserted.
6923///
6924/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#cancellation)
6925#[serde_as]
6926#[skip_serializing_none]
6927#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6928#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6929#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
6930#[serde(rename_all = "camelCase")]
6931#[non_exhaustive]
6932pub struct CancelSessionNotification {
6933    /// The ID of the session to cancel operations for.
6934    pub session_id: SessionId,
6935    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6936    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6937    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6938    ///
6939    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6940    #[serde_as(deserialize_as = "DefaultOnError")]
6941    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6942    #[serde(default)]
6943    #[serde(rename = "_meta")]
6944    pub meta: Option<Meta>,
6945}
6946
6947impl CancelSessionNotification {
6948    /// Builds [`CancelSessionNotification`] with the required notification fields set. Optional fields start unset or empty.
6949    #[must_use]
6950    pub fn new(session_id: impl Into<SessionId>) -> Self {
6951        Self {
6952            session_id: session_id.into(),
6953            meta: None,
6954        }
6955    }
6956
6957    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6958    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6959    /// do not recognize.
6960    ///
6961    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6962    #[must_use]
6963    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6964        self.meta = meta.into_option();
6965        self
6966    }
6967}
6968
6969// Method schema
6970
6971/// Names of all methods that agents handle.
6972///
6973/// Provides a centralized definition of method names used in the protocol.
6974#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6975#[non_exhaustive]
6976pub struct AgentMethodNames {
6977    /// Method for initializing the connection.
6978    pub initialize: &'static str,
6979    /// Method for authenticating with the agent.
6980    pub auth_login: &'static str,
6981    /// Method for listing configurable providers.
6982    #[cfg(feature = "unstable_llm_providers")]
6983    pub providers_list: &'static str,
6984    /// Method for setting provider configuration.
6985    #[cfg(feature = "unstable_llm_providers")]
6986    pub providers_set: &'static str,
6987    /// Method for disabling a provider.
6988    #[cfg(feature = "unstable_llm_providers")]
6989    pub providers_disable: &'static str,
6990    /// Method for creating a new session.
6991    pub session_new: &'static str,
6992    /// Method for setting a configuration option for a session.
6993    pub session_set_config_option: &'static str,
6994    /// Method for sending a prompt to the agent.
6995    pub session_prompt: &'static str,
6996    /// Notification for cancelling operations.
6997    pub session_cancel: &'static str,
6998    /// Method for exchanging MCP-over-ACP messages.
6999    #[cfg(feature = "unstable_mcp_over_acp")]
7000    pub mcp_message: &'static str,
7001    /// Method for listing existing sessions.
7002    pub session_list: &'static str,
7003    /// Method for deleting an existing session.
7004    pub session_delete: &'static str,
7005    /// Method for forking an existing session.
7006    #[cfg(feature = "unstable_session_fork")]
7007    pub session_fork: &'static str,
7008    /// Method for archiving a session.
7009    #[cfg(feature = "unstable_session_archive")]
7010    pub session_archive: &'static str,
7011    /// Method for unarchiving a session.
7012    #[cfg(feature = "unstable_session_archive")]
7013    pub session_unarchive: &'static str,
7014    /// Method for checking the status of a session.
7015    #[cfg(feature = "unstable_session_status")]
7016    pub session_status: &'static str,
7017    /// Method for setting the title of a session.
7018    #[cfg(feature = "unstable_session_set_title")]
7019    pub session_set_title: &'static str,
7020    /// Method for resuming an existing session.
7021    pub session_resume: &'static str,
7022    /// Method for closing an active session.
7023    pub session_close: &'static str,
7024    /// Method for cancelling one tool call.
7025    pub tool_call_cancel: &'static str,
7026    /// Method for cancelling one terminal's process.
7027    pub terminal_cancel: &'static str,
7028    /// Method for logging out of an authenticated session.
7029    pub auth_logout: &'static str,
7030    /// Method for querying the agent's authentication state.
7031    #[cfg(feature = "unstable_auth_status")]
7032    pub auth_status: &'static str,
7033}
7034
7035/// Constant containing all agent method names.
7036pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
7037    initialize: INITIALIZE_METHOD_NAME,
7038    auth_login: AUTH_LOGIN_METHOD_NAME,
7039    #[cfg(feature = "unstable_llm_providers")]
7040    providers_list: PROVIDERS_LIST_METHOD_NAME,
7041    #[cfg(feature = "unstable_llm_providers")]
7042    providers_set: PROVIDERS_SET_METHOD_NAME,
7043    #[cfg(feature = "unstable_llm_providers")]
7044    providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
7045    session_new: SESSION_NEW_METHOD_NAME,
7046    session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
7047    session_prompt: SESSION_PROMPT_METHOD_NAME,
7048    session_cancel: SESSION_CANCEL_METHOD_NAME,
7049    #[cfg(feature = "unstable_mcp_over_acp")]
7050    mcp_message: MCP_MESSAGE_METHOD_NAME,
7051    session_list: SESSION_LIST_METHOD_NAME,
7052    session_delete: SESSION_DELETE_METHOD_NAME,
7053    #[cfg(feature = "unstable_session_fork")]
7054    session_fork: SESSION_FORK_METHOD_NAME,
7055    #[cfg(feature = "unstable_session_archive")]
7056    session_archive: SESSION_ARCHIVE_METHOD_NAME,
7057    #[cfg(feature = "unstable_session_archive")]
7058    session_unarchive: SESSION_UNARCHIVE_METHOD_NAME,
7059    #[cfg(feature = "unstable_session_status")]
7060    session_status: SESSION_STATUS_METHOD_NAME,
7061    #[cfg(feature = "unstable_session_set_title")]
7062    session_set_title: SESSION_SET_TITLE_METHOD_NAME,
7063    session_resume: SESSION_RESUME_METHOD_NAME,
7064    session_close: SESSION_CLOSE_METHOD_NAME,
7065    tool_call_cancel: TOOL_CALL_CANCEL_METHOD_NAME,
7066    terminal_cancel: TERMINAL_CANCEL_METHOD_NAME,
7067    auth_logout: AUTH_LOGOUT_METHOD_NAME,
7068    #[cfg(feature = "unstable_auth_status")]
7069    auth_status: AUTH_STATUS_METHOD_NAME,
7070};
7071
7072/// Method name for the initialize request.
7073pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
7074/// Method name for the `auth/login` request.
7075pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
7076/// Method name for listing configurable providers.
7077#[cfg(feature = "unstable_llm_providers")]
7078pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
7079/// Method name for setting provider configuration.
7080#[cfg(feature = "unstable_llm_providers")]
7081pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
7082/// Method name for disabling a provider.
7083#[cfg(feature = "unstable_llm_providers")]
7084pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
7085/// Method name for creating a new session.
7086pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
7087/// Method name for setting a configuration option for a session.
7088pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
7089/// Method name for sending a prompt.
7090pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
7091/// Method name for the cancel notification.
7092pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
7093/// Method name for listing existing sessions.
7094pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
7095/// Method name for deleting an existing session.
7096pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
7097/// Method name for forking an existing session.
7098#[cfg(feature = "unstable_session_fork")]
7099pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
7100/// Method name for archiving a session.
7101#[cfg(feature = "unstable_session_archive")]
7102pub(crate) const SESSION_ARCHIVE_METHOD_NAME: &str = "session/archive";
7103/// Method name for unarchiving a session.
7104#[cfg(feature = "unstable_session_archive")]
7105pub(crate) const SESSION_UNARCHIVE_METHOD_NAME: &str = "session/unarchive";
7106/// Method name for checking the status of a session.
7107#[cfg(feature = "unstable_session_status")]
7108pub(crate) const SESSION_STATUS_METHOD_NAME: &str = "session/status";
7109/// Method name for setting the title of a session.
7110#[cfg(feature = "unstable_session_set_title")]
7111pub(crate) const SESSION_SET_TITLE_METHOD_NAME: &str = "session/set_title";
7112/// Method name for resuming an existing session.
7113pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
7114/// Method name for closing an active session.
7115pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
7116/// Method name for cancelling one tool call.
7117pub(crate) const TOOL_CALL_CANCEL_METHOD_NAME: &str = "tool_call/cancel";
7118/// Method name for cancelling one terminal's process.
7119pub(crate) const TERMINAL_CANCEL_METHOD_NAME: &str = "terminal/cancel";
7120/// Method name for the `auth/logout` request.
7121pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
7122/// Method name for the `auth/status` request.
7123#[cfg(feature = "unstable_auth_status")]
7124pub(crate) const AUTH_STATUS_METHOD_NAME: &str = "auth/status";
7125
7126/// All possible requests that a client can send to an agent.
7127///
7128/// This enum is used internally for routing RPC requests. You typically won't need
7129/// to use this directly.
7130///
7131/// This enum encompasses all method calls from client to agent.
7132#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7133#[derive(Clone, Debug, Serialize, Deserialize)]
7134#[serde(untagged)]
7135#[cfg_attr(feature = "schemars", schemars(inline))]
7136#[non_exhaustive]
7137pub enum ClientRequest {
7138    /// Establishes the connection with a client and negotiates protocol capabilities.
7139    ///
7140    /// This method is called at the beginning of the connection, before any other request, to:
7141    /// - Negotiate the protocol version to use
7142    /// - Exchange capability information between client and agent
7143    /// - Determine available authentication methods
7144    ///
7145    /// The Client may repeat it before its first other request. The latest successful
7146    /// `initialize` is authoritative.
7147    ///
7148    /// The agent should respond with its supported protocol version and capabilities.
7149    ///
7150    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
7151    InitializeRequest(Box<InitializeRequest>),
7152    /// Authenticates the client using the specified authentication method.
7153    ///
7154    /// Agents MUST support this method when their `initialize` response included
7155    /// a non-empty `authMethods` list. Clients MUST call this method only
7156    /// with a method whose type defines a protocol-driven login flow, and MUST NOT
7157    /// call it when `authMethods` was omitted or empty.
7158    ///
7159    /// Called when the agent requires authentication before allowing session creation.
7160    /// The client provides the authentication method ID that was advertised during initialization.
7161    ///
7162    /// After successful authentication, the client can proceed to create sessions with
7163    /// `session/new` without receiving an `auth_required` (`-32000`) error.
7164    ///
7165    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
7166    LoginAuthRequest(Box<LoginAuthRequest>),
7167    /// **UNSTABLE**
7168    ///
7169    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7170    ///
7171    /// Lists providers that can be configured by the client.
7172    #[cfg(feature = "unstable_llm_providers")]
7173    ListProvidersRequest(Box<ListProvidersRequest>),
7174    /// **UNSTABLE**
7175    ///
7176    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7177    ///
7178    /// Replaces the configuration for a provider.
7179    #[cfg(feature = "unstable_llm_providers")]
7180    SetProviderRequest(Box<SetProviderRequest>),
7181    /// **UNSTABLE**
7182    ///
7183    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7184    ///
7185    /// Disables a provider.
7186    #[cfg(feature = "unstable_llm_providers")]
7187    DisableProviderRequest(Box<DisableProviderRequest>),
7188    /// Logs out of the current authenticated state.
7189    ///
7190    /// Agents MUST support this method when their `initialize` response included
7191    /// a non-empty `authMethods` list. Clients MUST NOT call this method
7192    /// when `authMethods` was omitted or empty.
7193    ///
7194    /// After a successful logout, authentication-gated requests require the
7195    /// client to complete an advertised authentication flow again. There is no
7196    /// guarantee about the behavior of already running sessions.
7197    LogoutAuthRequest(Box<LogoutAuthRequest>),
7198    /// **UNSTABLE**
7199    ///
7200    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7201    ///
7202    /// Queries the agent's current authentication state.
7203    ///
7204    /// This method is only available if the agent advertises the `auth.status` capability.
7205    /// It MUST NOT change any agent state.
7206    #[cfg(feature = "unstable_auth_status")]
7207    AuthStatusRequest(Box<AuthStatusRequest>),
7208    /// Creates a new conversation session with the agent.
7209    ///
7210    /// Sessions represent independent conversation contexts with their own history and state.
7211    ///
7212    /// The agent should:
7213    /// - Create a new session context
7214    /// - Connect to any specified MCP servers
7215    /// - Return a unique session ID for future requests
7216    ///
7217    /// May return an `auth_required` (`-32000`) error if the agent requires authentication.
7218    ///
7219    /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/v2/session-setup)
7220    NewSessionRequest(Box<NewSessionRequest>),
7221    /// Lists existing sessions known to the agent.
7222    ///
7223    /// The agent should return metadata about sessions with optional filtering and pagination support.
7224    ListSessionsRequest(Box<ListSessionsRequest>),
7225    /// Deletes an existing session from `session/list`.
7226    ///
7227    /// This method is only available if the agent advertises the `session.delete` capability.
7228    DeleteSessionRequest(Box<DeleteSessionRequest>),
7229    #[cfg(feature = "unstable_session_fork")]
7230    /// **UNSTABLE**
7231    ///
7232    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7233    ///
7234    /// Forks an existing session to create a new independent session.
7235    ///
7236    /// This method is only available if the agent advertises the `session.fork` capability.
7237    ///
7238    /// The agent should create a new session with the same conversation context as the
7239    /// original, allowing operations like generating summaries without affecting the
7240    /// original session's history.
7241    ///
7242    /// As with `session/new`, the forked session is active once the agent responds,
7243    /// set up with the request's `cwd`, `additionalDirectories`, and `mcpServers`.
7244    /// The client can prompt it without calling `session/resume` first.
7245    ForkSessionRequest(Box<ForkSessionRequest>),
7246    #[cfg(feature = "unstable_session_archive")]
7247    /// **UNSTABLE**
7248    ///
7249    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7250    ///
7251    /// Archives a session, hiding it from default `session/list` results.
7252    ///
7253    /// This method is only available if the agent advertises the `session.archive` capability.
7254    ArchiveSessionRequest(Box<ArchiveSessionRequest>),
7255    #[cfg(feature = "unstable_session_archive")]
7256    /// **UNSTABLE**
7257    ///
7258    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7259    ///
7260    /// Unarchives a session, returning it to default `session/list` results.
7261    ///
7262    /// This method is only available if the agent advertises the `session.archive` capability.
7263    UnarchiveSessionRequest(Box<UnarchiveSessionRequest>),
7264    #[cfg(feature = "unstable_session_status")]
7265    /// **UNSTABLE**
7266    ///
7267    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7268    ///
7269    /// Checks whether the Agent has a session in memory, without changing it.
7270    ///
7271    /// This method is only available if the agent advertises the `session.status` capability.
7272    SessionStatusRequest(Box<SessionStatusRequest>),
7273    #[cfg(feature = "unstable_session_set_title")]
7274    /// **UNSTABLE**
7275    ///
7276    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7277    ///
7278    /// Sets the title of a session.
7279    ///
7280    /// This method is only available if the agent advertises the `session.title` capability.
7281    ///
7282    /// The session does not need to be active on the connection. The agent stores the
7283    /// title and returns the stored title, which may be normalized.
7284    SetSessionTitleRequest(Box<SetSessionTitleRequest>),
7285    /// Resumes an existing session.
7286    ///
7287    /// The agent should resume the session context, allowing the conversation
7288    /// to continue. If `replayFrom` is set, the agent should replay
7289    /// retained conversation history before responding.
7290    ResumeSessionRequest(Box<ResumeSessionRequest>),
7291    /// Closes an active session and frees up any resources associated with it.
7292    ///
7293    /// The Agent MUST cancel any ongoing work as if `session/cancel` was called, stop the
7294    /// session's background work, and then free the resources associated with the session.
7295    CloseSessionRequest(Box<CloseSessionRequest>),
7296    /// Cancels one tool call without cancelling the rest of the session's work.
7297    ///
7298    /// This method is only available while the tool call advertises `capabilities.cancel`.
7299    /// The response means the Agent accepted the cancellation. A final `tool_call_update`
7300    /// confirms it.
7301    CancelToolCallRequest(Box<CancelToolCallRequest>),
7302    /// Stops one terminal's process without cancelling the rest of the session's work.
7303    ///
7304    /// This method is only available while the terminal advertises `capabilities.cancel`.
7305    /// The response means the Agent accepted the cancellation. A `terminal_update` with
7306    /// `exitStatus` confirms it.
7307    CancelTerminalRequest(Box<CancelTerminalRequest>),
7308    /// Sets the current value for a session configuration option.
7309    SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
7310    /// Processes a user prompt within a session.
7311    ///
7312    /// Acceptance means adding a logical user message with an ID and a position
7313    /// relative to other messages in the ACP conversation, not receipt or queueing.
7314    /// The Agent MUST return the inserted message's ID without waiting for
7315    /// processing to finish.
7316    ///
7317    /// The Agent reports the user message with the same ID through `session/update`.
7318    /// This notification may arrive before or after the response, but neither may
7319    /// precede insertion. Processing state, output, tool calls, and completion are
7320    /// also reported through session updates.
7321    ///
7322    /// An error response means the user message was not inserted. Once it is
7323    /// inserted, the Agent MUST NOT answer with an error, even if the work fails
7324    /// before the response is sent. It ends that work with the `error` stop reason.
7325    ///
7326    /// A rejected submission produces no user-message updates. Foreground state
7327    /// follows its normal lifecycle independently of the request outcome:
7328    /// `running` may precede insertion, and rejection alone does not imply `idle`.
7329    ///
7330    /// See protocol docs: [Prompt Lifecycle](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle)
7331    PromptRequest(Box<PromptRequest>),
7332    /// Handles extension method requests from the client.
7333    ///
7334    /// Extension methods provide a way to add custom functionality while maintaining
7335    /// protocol compatibility.
7336    ///
7337    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
7338    ExtMethodRequest(Box<ExtRequest>),
7339}
7340
7341impl ClientRequest {
7342    /// Returns the corresponding method name of the request.
7343    #[must_use]
7344    pub fn method(&self) -> &str {
7345        match self {
7346            Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
7347            Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
7348            #[cfg(feature = "unstable_llm_providers")]
7349            Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
7350            #[cfg(feature = "unstable_llm_providers")]
7351            Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
7352            #[cfg(feature = "unstable_llm_providers")]
7353            Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
7354            Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
7355            #[cfg(feature = "unstable_auth_status")]
7356            Self::AuthStatusRequest(_) => AGENT_METHOD_NAMES.auth_status,
7357            Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
7358            Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
7359            Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
7360            #[cfg(feature = "unstable_session_fork")]
7361            Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
7362            #[cfg(feature = "unstable_session_archive")]
7363            Self::ArchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_archive,
7364            #[cfg(feature = "unstable_session_archive")]
7365            Self::UnarchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_unarchive,
7366            #[cfg(feature = "unstable_session_status")]
7367            Self::SessionStatusRequest(_) => AGENT_METHOD_NAMES.session_status,
7368            #[cfg(feature = "unstable_session_set_title")]
7369            Self::SetSessionTitleRequest(_) => AGENT_METHOD_NAMES.session_set_title,
7370            Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
7371            Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
7372            Self::CancelToolCallRequest(_) => AGENT_METHOD_NAMES.tool_call_cancel,
7373            Self::CancelTerminalRequest(_) => AGENT_METHOD_NAMES.terminal_cancel,
7374            Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
7375            Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
7376            Self::ExtMethodRequest(ext_request) => &ext_request.method,
7377        }
7378    }
7379}
7380
7381/// All possible responses that an agent can send to a client.
7382///
7383/// This enum is used internally for routing RPC responses. You typically won't need
7384/// to use this directly - the responses are handled automatically by the connection.
7385///
7386/// These are responses to the corresponding `ClientRequest` variants.
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 AgentResponse {
7393    /// Successful result returned for a `initialize` request.
7394    InitializeResponse(Box<InitializeResponse>),
7395    /// Successful result returned for an `auth/login` request.
7396    LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
7397    /// Successful result returned for a `providers/list` request.
7398    #[cfg(feature = "unstable_llm_providers")]
7399    ListProvidersResponse(Box<ListProvidersResponse>),
7400    /// Successful result returned for a `providers/set` request.
7401    #[cfg(feature = "unstable_llm_providers")]
7402    SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
7403    /// Successful result returned for a `providers/disable` request.
7404    #[cfg(feature = "unstable_llm_providers")]
7405    DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
7406    /// Successful result returned for an `auth/logout` request.
7407    LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
7408    /// Successful result returned for an `auth/status` request.
7409    #[cfg(feature = "unstable_auth_status")]
7410    AuthStatusResponse(Box<AuthStatusResponse>),
7411    /// Successful result returned for a `session/new` request.
7412    NewSessionResponse(Box<NewSessionResponse>),
7413    /// Successful result returned for a `session/list` request.
7414    ListSessionsResponse(Box<ListSessionsResponse>),
7415    /// Successful result returned for a `session/delete` request.
7416    DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
7417    /// Successful result returned for a `session/fork` request.
7418    #[cfg(feature = "unstable_session_fork")]
7419    ForkSessionResponse(Box<ForkSessionResponse>),
7420    /// Successful result returned for a `session/archive` request.
7421    #[cfg(feature = "unstable_session_archive")]
7422    ArchiveSessionResponse(#[serde(default)] Box<ArchiveSessionResponse>),
7423    /// Successful result returned for a `session/unarchive` request.
7424    #[cfg(feature = "unstable_session_archive")]
7425    UnarchiveSessionResponse(#[serde(default)] Box<UnarchiveSessionResponse>),
7426    /// Successful result returned for a `session/status` request.
7427    #[cfg(feature = "unstable_session_status")]
7428    SessionStatusResponse(Box<SessionStatusResponse>),
7429    /// Successful result returned for a `session/set_title` request.
7430    #[cfg(feature = "unstable_session_set_title")]
7431    SetSessionTitleResponse(Box<SetSessionTitleResponse>),
7432    /// Successful result returned for a `session/resume` request.
7433    ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
7434    /// Successful result returned for a `session/close` request.
7435    CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
7436    /// Successful result returned for a `tool_call/cancel` request.
7437    CancelToolCallResponse(#[serde(default)] Box<CancelToolCallResponse>),
7438    /// Successful result returned for a `terminal/cancel` request.
7439    CancelTerminalResponse(#[serde(default)] Box<CancelTerminalResponse>),
7440    /// Successful result returned for a `session/set_config_option` request.
7441    SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
7442    /// Successful result returned for a `session/prompt` request.
7443    PromptResponse(Box<PromptResponse>),
7444    /// Successful result returned by an extension method outside the core ACP method set.
7445    ExtMethodResponse(Box<ExtResponse>),
7446}
7447
7448/// All possible notifications that a client can send to an agent.
7449///
7450/// This enum is used internally for routing RPC notifications. You typically won't need
7451/// to use this directly.
7452///
7453/// Notifications do not expect a response.
7454#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7455#[derive(Clone, Debug, Serialize, Deserialize)]
7456#[serde(untagged)]
7457#[cfg_attr(feature = "schemars", schemars(inline))]
7458#[non_exhaustive]
7459pub enum ClientNotification {
7460    /// Cancels a session's foreground work and its prompts not yet inserted.
7461    ///
7462    /// Background work is not covered, but the Agent MAY stop it too.
7463    ///
7464    /// Upon receiving this notification, the Agent SHOULD stop all language model
7465    /// requests and all tool call invocations as soon as possible.
7466    ///
7467    /// After all ongoing operations have been successfully aborted and pending
7468    /// updates have been sent, the Agent MUST send an idle `state_update` session
7469    /// update with the `cancelled` stop reason if cancellation ended active
7470    /// foreground work. No additional completion update is required if no
7471    /// foreground work was active.
7472    ///
7473    /// After cancellation completes, the Agent SHOULD ignore late answers rather
7474    /// than use them to resume the cancelled work.
7475    ///
7476    /// Agents MUST catch errors caused by aborting cancelled work and report the
7477    /// `cancelled` stop reason, not `error`.
7478    ///
7479    /// The Agent MAY send content or tool call updates after receiving
7480    /// `session/cancel`, but MUST send them before the idle `state_update` that
7481    /// reports cancellation.
7482    ///
7483    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#cancellation)
7484    CancelSessionNotification(Box<CancelSessionNotification>),
7485    /// **UNSTABLE**
7486    ///
7487    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7488    ///
7489    /// Sends an MCP-over-ACP notification.
7490    #[cfg(feature = "unstable_mcp_over_acp")]
7491    MessageMcpNotification(Box<MessageMcpNotification>),
7492    /// Handles extension notifications from the client.
7493    ///
7494    /// Extension notifications provide a way to send one-way messages for custom functionality
7495    /// while maintaining protocol compatibility.
7496    ///
7497    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
7498    ExtNotification(Box<ExtNotification>),
7499}
7500
7501impl ClientNotification {
7502    /// Returns the corresponding method name of the notification.
7503    #[must_use]
7504    pub fn method(&self) -> &str {
7505        match self {
7506            Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
7507            #[cfg(feature = "unstable_mcp_over_acp")]
7508            Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
7509            Self::ExtNotification(ext_notification) => &ext_notification.method,
7510        }
7511    }
7512}
7513
7514#[cfg(test)]
7515mod test_serialization {
7516    use std::path::PathBuf;
7517
7518    use super::*;
7519    use serde_json::json;
7520
7521    fn test_meta() -> Meta {
7522        json!({ "source": "test" }).as_object().unwrap().clone()
7523    }
7524
7525    #[test]
7526    fn list_sessions_cursor_and_session_directories_are_strict() {
7527        for cursor in [json!(null), json!("next")] {
7528            let response = json!({ "sessions": [], "nextCursor": cursor });
7529            serde_json::from_value::<ListSessionsResponse>(response).unwrap();
7530        }
7531        let malformed = json!({ "sessions": [], "nextCursor": 42 });
7532        assert!(serde_json::from_value::<ListSessionsResponse>(malformed).is_err());
7533
7534        let session = |dirs: serde_json::Value| json!({ "sessionId": "s", "cwd": "/w", "additionalDirectories": dirs });
7535        let info: SessionInfo = serde_json::from_value(session(json!(null))).unwrap();
7536        assert_eq!(info.additional_directories, Vec::<AbsolutePath>::new());
7537        for bad in [json!(["/a", 42]), json!("/a")] {
7538            assert!(serde_json::from_value::<SessionInfo>(session(bad)).is_err());
7539        }
7540        // A session with malformed directories is skipped, not truncated.
7541        let response = json!({ "sessions": [session(json!(["/a", 42]))] });
7542        let response: ListSessionsResponse = serde_json::from_value(response).unwrap();
7543        assert_eq!(response.sessions, Vec::<SessionInfo>::new());
7544    }
7545
7546    #[test]
7547    fn other_fallbacks_preserve_optional_fields_verbatim() {
7548        let valid = json!({ "type": "_custom", "methodId": "m", "name": "M",
7549            "description": "d", "_meta": { "k": 1 } });
7550        let method: AuthMethod = serde_json::from_value(valid.clone()).unwrap();
7551        assert_eq!(method.description(), Some("d"));
7552        assert_eq!(
7553            method.meta(),
7554            Some(&json!({ "k": 1 }).as_object().unwrap().clone())
7555        );
7556        assert_eq!(serde_json::to_value(&method).unwrap(), valid);
7557
7558        // Malformed optional fields are kept as sent and read as absent.
7559        let malformed = json!({ "type": "_custom", "methodId": "m", "name": "M",
7560            "description": 42, "_meta": "bad", "extra": true });
7561        let method: AuthMethod = serde_json::from_value(malformed.clone()).unwrap();
7562        assert_eq!(method.description(), None);
7563        assert_eq!(method.meta(), None);
7564        assert_eq!(serde_json::to_value(&method).unwrap(), malformed);
7565
7566        let replay = json!({ "type": "_custom", "_meta": "bad", "cursor": 3 });
7567        let decoded: ReplayFrom = serde_json::from_value(replay.clone()).unwrap();
7568        assert_eq!(serde_json::to_value(&decoded).unwrap(), replay);
7569    }
7570
7571    fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
7572        serde_json::to_string(value)
7573            .unwrap()
7574            .matches("\"_meta\"")
7575            .count()
7576    }
7577
7578    #[test]
7579    fn prompt_response_without_metadata_round_trips() {
7580        let response = PromptResponse::new(MessageId::new("message-1"));
7581        let serialized = serde_json::to_value(&response).unwrap();
7582        assert_eq!(serialized, json!({ "messageId": "message-1" }));
7583        assert_eq!(
7584            serde_json::from_value::<PromptResponse>(serialized).unwrap(),
7585            response
7586        );
7587        assert_eq!(
7588            serde_json::from_value::<PromptResponse>(json!({
7589                "messageId": "message-1",
7590                "_meta": null
7591            }))
7592            .unwrap(),
7593            response
7594        );
7595    }
7596
7597    #[test]
7598    fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
7599        let response = PromptResponse::new("message-1").meta(test_meta());
7600        let serialized = serde_json::to_value(&response).unwrap();
7601        assert_eq!(
7602            serialized,
7603            json!({
7604                "messageId": "message-1",
7605                "_meta": { "source": "test" }
7606            })
7607        );
7608
7609        let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
7610        assert_eq!(deserialized.message_id, MessageId::new("message-1"));
7611        assert_eq!(deserialized.meta, Some(test_meta()));
7612
7613        let malformed_meta: PromptResponse = serde_json::from_value(json!({
7614            "messageId": "message-2",
7615            "_meta": false
7616        }))
7617        .unwrap();
7618        assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
7619        assert_eq!(malformed_meta.meta, None);
7620
7621        for invalid in [
7622            json!({}),
7623            json!({ "messageId": null }),
7624            json!({ "messageId": 1 }),
7625            json!({ "messageId": false }),
7626            json!({ "messageId": {} }),
7627            json!({ "messageId": [] }),
7628        ] {
7629            assert!(
7630                serde_json::from_value::<PromptResponse>(invalid).is_err(),
7631                "missing, null, and non-string message IDs must be rejected"
7632            );
7633        }
7634    }
7635
7636    #[cfg(feature = "schemars")]
7637    #[test]
7638    fn prompt_response_schema_requires_non_null_string_message_id_reference() {
7639        let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
7640
7641        assert_eq!(schema["required"], json!(["messageId"]));
7642        assert_eq!(
7643            schema["properties"]["messageId"]["$ref"],
7644            "#/$defs/MessageId"
7645        );
7646        assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
7647    }
7648
7649    #[test]
7650    fn test_initialize_capabilities_default_on_malformed_values() {
7651        let request: InitializeRequest = serde_json::from_value(json!({
7652            "protocolVersion": 2,
7653            "capabilities": false,
7654            "info": {
7655                "name": "client",
7656                "version": "1.0.0"
7657            }
7658        }))
7659        .unwrap();
7660        assert_eq!(request.capabilities, ClientCapabilities::default());
7661
7662        let response: InitializeResponse = serde_json::from_value(json!({
7663            "protocolVersion": 2,
7664            "capabilities": false,
7665            "info": {
7666                "name": "agent",
7667                "version": "1.0.0"
7668            }
7669        }))
7670        .unwrap();
7671        assert_eq!(response.capabilities, AgentCapabilities::default());
7672    }
7673
7674    #[test]
7675    fn test_agent_capabilities_default_on_malformed_values() {
7676        let capabilities: AgentCapabilities = serde_json::from_value(json!({
7677            "session": false,
7678            "auth": false
7679        }))
7680        .unwrap();
7681
7682        assert!(capabilities.session.is_none());
7683        assert_eq!(capabilities.auth, None);
7684    }
7685
7686    #[test]
7687    fn test_mcp_server_stdio_serialization() {
7688        let server = McpServer::Stdio(
7689            McpServerStdio::new("test-server", "/usr/bin/server")
7690                .args(vec!["--port".to_string(), "3000".to_string()])
7691                .env(vec![EnvVariable::new("API_KEY", "secret123")]),
7692        );
7693
7694        let json = serde_json::to_value(&server).unwrap();
7695        assert_eq!(
7696            json,
7697            json!({
7698                "type": "stdio",
7699                "name": "test-server",
7700                "command": "/usr/bin/server",
7701                "args": ["--port", "3000"],
7702                "env": [
7703                    {
7704                        "name": "API_KEY",
7705                        "value": "secret123"
7706                    }
7707                ]
7708            })
7709        );
7710
7711        let deserialized: McpServer = serde_json::from_value(json).unwrap();
7712        match deserialized {
7713            McpServer::Stdio(McpServerStdio {
7714                name,
7715                command,
7716                args,
7717                env,
7718                meta: _,
7719            }) => {
7720                assert_eq!(name, "test-server");
7721                assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
7722                assert_eq!(args, vec!["--port", "3000"]);
7723                assert_eq!(env.len(), 1);
7724                assert_eq!(env[0].name, "API_KEY");
7725                assert_eq!(env[0].value, "secret123");
7726            }
7727            _ => panic!("Expected Stdio variant"),
7728        }
7729    }
7730
7731    #[test]
7732    fn test_mcp_server_empty_arrays_are_optional() {
7733        let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
7734        assert_eq!(
7735            serde_json::to_value(&stdio).unwrap(),
7736            json!({
7737                "type": "stdio",
7738                "name": "test-server",
7739                "command": "/usr/bin/server"
7740            })
7741        );
7742
7743        let McpServer::Stdio(McpServerStdio { args, env, .. }) =
7744            serde_json::from_value::<McpServer>(json!({
7745                "type": "stdio",
7746                "name": "test-server",
7747                "command": "/usr/bin/server"
7748            }))
7749            .unwrap()
7750        else {
7751            panic!("Expected Stdio variant");
7752        };
7753        assert_eq!(args, Vec::<String>::new());
7754        assert_eq!(env, []);
7755
7756        let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
7757        assert_eq!(
7758            serde_json::to_value(&http).unwrap(),
7759            json!({
7760                "type": "http",
7761                "name": "http-server",
7762                "url": "https://api.example.com"
7763            })
7764        );
7765
7766        let McpServer::Http(McpServerHttp { headers, .. }) =
7767            serde_json::from_value::<McpServer>(json!({
7768                "type": "http",
7769                "name": "http-server",
7770                "url": "https://api.example.com"
7771            }))
7772            .unwrap()
7773        else {
7774            panic!("Expected Http variant");
7775        };
7776        assert_eq!(headers, []);
7777    }
7778
7779    #[test]
7780    fn test_mcp_server_unknown_transport_serialization() {
7781        let json = json!({
7782            "type": "websocket",
7783            "name": "future-server",
7784            "url": "wss://example.com/mcp",
7785            "protocolVersion": "2026-01-01"
7786        });
7787
7788        let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
7789        let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
7790            panic!("Expected Other variant");
7791        };
7792
7793        assert_eq!(type_, "websocket");
7794        assert_eq!(fields["name"], "future-server");
7795        assert_eq!(fields["url"], "wss://example.com/mcp");
7796        assert_eq!(fields["protocolVersion"], "2026-01-01");
7797        assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
7798    }
7799
7800    #[test]
7801    fn test_mcp_server_stdio_requires_type() {
7802        let result = serde_json::from_value::<McpServer>(json!({
7803            "name": "test-server",
7804            "command": "/usr/bin/server",
7805            "args": [],
7806            "env": []
7807        }));
7808
7809        assert!(result.is_err());
7810    }
7811
7812    #[test]
7813    fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
7814        let result = serde_json::from_value::<McpServer>(json!({
7815            "type": "stdio",
7816            "name": "test-server",
7817            "args": [],
7818            "env": []
7819        }));
7820
7821        assert!(result.is_err());
7822    }
7823
7824    #[test]
7825    fn test_mcp_server_http_serialization() {
7826        let server = McpServer::Http(
7827            McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
7828                HttpHeader::new("Authorization", "Bearer token123"),
7829                HttpHeader::new("Content-Type", "application/json"),
7830            ]),
7831        );
7832
7833        let json = serde_json::to_value(&server).unwrap();
7834        assert_eq!(
7835            json,
7836            json!({
7837                "type": "http",
7838                "name": "http-server",
7839                "url": "https://api.example.com",
7840                "headers": [
7841                    {
7842                        "name": "Authorization",
7843                        "value": "Bearer token123"
7844                    },
7845                    {
7846                        "name": "Content-Type",
7847                        "value": "application/json"
7848                    }
7849                ]
7850            })
7851        );
7852
7853        let deserialized: McpServer = serde_json::from_value(json).unwrap();
7854        match deserialized {
7855            McpServer::Http(McpServerHttp {
7856                name,
7857                url,
7858                headers,
7859                meta: _,
7860            }) => {
7861                assert_eq!(name, "http-server");
7862                assert_eq!(url, "https://api.example.com");
7863                assert_eq!(headers.len(), 2);
7864                assert_eq!(headers[0].name, "Authorization");
7865                assert_eq!(headers[0].value, "Bearer token123");
7866                assert_eq!(headers[1].name, "Content-Type");
7867                assert_eq!(headers[1].value, "application/json");
7868            }
7869            _ => panic!("Expected Http variant"),
7870        }
7871    }
7872
7873    #[cfg(feature = "schemars")]
7874    #[test]
7875    fn mcp_server_http_schema_marks_url_as_uri() {
7876        let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
7877
7878        assert_eq!(schema["properties"]["url"]["format"], "uri");
7879    }
7880
7881    #[cfg(feature = "unstable_mcp_over_acp")]
7882    #[test]
7883    fn test_client_mcp_message_method_names() {
7884        use serde_json::json;
7885
7886        assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
7887
7888        let notification =
7889            MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
7890        assert_eq!(
7891            ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
7892            "mcp/message"
7893        );
7894        assert_eq!(
7895            serde_json::to_value(notification).unwrap(),
7896            json!({
7897                "serverId": "server-1",
7898                "requestId": "req-1",
7899                "method": "notifications/progress"
7900            })
7901        );
7902        let notification: MessageMcpNotification = serde_json::from_value(json!({
7903            "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
7904            "params": null, "_meta": null
7905        }))
7906        .unwrap();
7907        assert_eq!(notification.params, None);
7908        assert_eq!(notification.meta, None);
7909        for key in ["serverId", "requestId", "method"] {
7910            let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
7911            value.as_object_mut().unwrap().remove(key);
7912            assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
7913        }
7914    }
7915
7916    #[test]
7917    fn test_auth_method_names() {
7918        assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
7919        assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
7920
7921        assert_eq!(
7922            ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
7923                .method(),
7924            "auth/login"
7925        );
7926        assert_eq!(
7927            ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
7928            "auth/logout"
7929        );
7930    }
7931
7932    #[test]
7933    fn test_session_config_option_category_known_variants() {
7934        // Test serialization of known variants
7935        assert_eq!(
7936            serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
7937            json!("mode")
7938        );
7939        assert_eq!(
7940            serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
7941            json!("model")
7942        );
7943        assert_eq!(
7944            serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
7945            json!("model_config")
7946        );
7947        assert_eq!(
7948            serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
7949            json!("thought_level")
7950        );
7951
7952        // Test deserialization of known variants
7953        assert_eq!(
7954            serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
7955            SessionConfigOptionCategory::Mode
7956        );
7957        assert_eq!(
7958            serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
7959            SessionConfigOptionCategory::Model
7960        );
7961        assert_eq!(
7962            serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
7963            SessionConfigOptionCategory::ModelConfig
7964        );
7965        assert_eq!(
7966            serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
7967            SessionConfigOptionCategory::ThoughtLevel
7968        );
7969    }
7970
7971    #[test]
7972    fn test_session_config_option_category_unknown_variants() {
7973        // Test that unknown strings are captured in Other variant
7974        let unknown: SessionConfigOptionCategory =
7975            serde_json::from_str("\"some_future_category\"").unwrap();
7976        assert_eq!(
7977            unknown,
7978            SessionConfigOptionCategory::Other("some_future_category".to_string())
7979        );
7980
7981        // Test round-trip of unknown category
7982        let json = serde_json::to_value(&unknown).unwrap();
7983        assert_eq!(json, json!("some_future_category"));
7984    }
7985
7986    #[test]
7987    fn test_session_config_option_category_custom_categories() {
7988        // Category names beginning with `_` are free for custom use
7989        let custom: SessionConfigOptionCategory =
7990            serde_json::from_str("\"_my_custom_category\"").unwrap();
7991        assert_eq!(
7992            custom,
7993            SessionConfigOptionCategory::Other("_my_custom_category".to_string())
7994        );
7995
7996        // Test round-trip preserves the custom category name
7997        let json = serde_json::to_value(&custom).unwrap();
7998        assert_eq!(json, json!("_my_custom_category"));
7999
8000        // Deserialize back and verify
8001        let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
8002        assert_eq!(
8003            deserialized,
8004            SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
8005        );
8006    }
8007
8008    fn test_config_option() -> SessionConfigOption {
8009        SessionConfigOption::select(
8010            "mode",
8011            "Mode",
8012            "ask",
8013            vec![SessionConfigSelectOption::new("ask", "Ask")],
8014        )
8015    }
8016
8017    #[test]
8018    fn test_session_response_initial_state_default_empty_and_skip_serializing() {
8019        assert_eq!(
8020            serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
8021            json!({ "sessionId": "sess" })
8022        );
8023        assert_eq!(
8024            serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
8025            json!({})
8026        );
8027        #[cfg(feature = "unstable_session_fork")]
8028        assert_eq!(
8029            serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
8030            json!({ "sessionId": "fork" })
8031        );
8032
8033        let json = serde_json::to_value(
8034            NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
8035        )
8036        .unwrap();
8037        assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
8038    }
8039
8040    #[test]
8041    fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
8042        let missing: NewSessionResponse =
8043            serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
8044        assert_eq!(missing.config_options, []);
8045
8046        let null: NewSessionResponse = serde_json::from_value(json!({
8047            "sessionId": "sess",
8048            "configOptions": null
8049        }))
8050        .unwrap();
8051        assert_eq!(null.config_options, []);
8052
8053        let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
8054            "sessionId": "sess",
8055            "configOptions": "oops"
8056        }))
8057        .unwrap();
8058        assert_eq!(wrong_shape.config_options, []);
8059
8060        let valid_option = serde_json::to_value(test_config_option()).unwrap();
8061        let mixed: NewSessionResponse = serde_json::from_value(json!({
8062            "sessionId": "sess",
8063            "configOptions": ["oops", valid_option]
8064        }))
8065        .unwrap();
8066        assert_eq!(mixed.config_options.len(), 1);
8067
8068        let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
8069        assert_eq!(resume.config_options, []);
8070        #[cfg(feature = "unstable_session_fork")]
8071        {
8072            let fork: ForkSessionResponse =
8073                serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
8074            assert_eq!(fork.config_options, []);
8075        }
8076    }
8077
8078    #[test]
8079    fn test_session_response_available_commands_roundtrip_with_config_options() {
8080        use super::super::{AvailableCommandInput, Command, TextCommandInput};
8081
8082        let commands = vec![
8083            AvailableCommand::Command(
8084                Command::new("web", "Search the web")
8085                    .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
8086            ),
8087            AvailableCommand::Command(Command::new("test", "Run project tests")),
8088        ];
8089        let mut expected = json!({
8090            "sessionId": "sess",
8091            "configOptions": [test_config_option()],
8092            "availableCommands": [
8093                {
8094                    "type": "command",
8095                    "name": "web",
8096                    "description": "Search the web",
8097                    "input": { "type": "text", "hint": "Query" }
8098                },
8099                { "type": "command", "name": "test", "description": "Run project tests" }
8100            ]
8101        });
8102
8103        let new = NewSessionResponse::new("sess")
8104            .config_options(vec![test_config_option()])
8105            .available_commands(commands.clone());
8106        assert_eq!(serde_json::to_value(&new).unwrap(), expected);
8107        assert_eq!(
8108            serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
8109            new
8110        );
8111
8112        expected.as_object_mut().unwrap().remove("sessionId");
8113        let resume = ResumeSessionResponse::new()
8114            .config_options(vec![test_config_option()])
8115            .available_commands(commands.clone());
8116        assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
8117        assert_eq!(
8118            serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
8119            resume
8120        );
8121
8122        #[cfg(feature = "unstable_session_fork")]
8123        {
8124            expected["sessionId"] = json!("fork");
8125            let fork = ForkSessionResponse::new("fork")
8126                .config_options(vec![test_config_option()])
8127                .available_commands(commands);
8128            assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
8129            assert_eq!(
8130                serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
8131                fork
8132            );
8133        }
8134    }
8135
8136    #[test]
8137    fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
8138        let command =
8139            AvailableCommand::Command(super::super::Command::new("test", "Run project tests"));
8140        let config_options = vec![test_config_option()];
8141
8142        for (commands, expected) in [
8143            (None, vec![]),
8144            (Some(json!(null)), vec![]),
8145            (Some(json!([])), vec![]),
8146            (Some(json!("oops")), vec![]),
8147            (Some(json!({})), vec![]),
8148            (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
8149            (
8150                Some(json!([{ "name": "test", "description": "Run project tests" }])),
8151                vec![],
8152            ),
8153            (
8154                Some(json!([null, command, { "name": "invalid" }])),
8155                vec![command.clone()],
8156            ),
8157        ] {
8158            let mut value = json!({
8159                "sessionId": "sess",
8160                "configOptions": config_options
8161            });
8162            if let Some(commands) = commands {
8163                value["availableCommands"] = commands;
8164            }
8165
8166            let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
8167            assert_eq!(new.available_commands, expected);
8168            assert_eq!(new.config_options, config_options);
8169
8170            #[cfg(feature = "unstable_session_fork")]
8171            {
8172                let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
8173                assert_eq!(fork.available_commands, expected);
8174                assert_eq!(fork.config_options, config_options);
8175            }
8176
8177            value.as_object_mut().unwrap().remove("sessionId");
8178            let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
8179            assert_eq!(resume.available_commands, expected);
8180            assert_eq!(resume.config_options, config_options);
8181        }
8182
8183        let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
8184        assert_eq!(resume.available_commands, []);
8185    }
8186
8187    #[test]
8188    fn test_resume_session_replay_from_serialization() {
8189        assert_eq!(
8190            serde_json::to_value(ResumeSessionRequest::new(
8191                "sess_abc123",
8192                "/home/user/project"
8193            ))
8194            .unwrap(),
8195            json!({
8196                "sessionId": "sess_abc123",
8197                "cwd": "/home/user/project"
8198            })
8199        );
8200        assert_eq!(
8201            serde_json::to_value(
8202                ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8203                    .replay_from(ReplayFrom::from(ReplayFromStart::new()))
8204            )
8205            .unwrap(),
8206            json!({
8207                "sessionId": "sess_abc123",
8208                "cwd": "/home/user/project",
8209                "replayFrom": {
8210                    "type": "start"
8211                }
8212            })
8213        );
8214
8215        let replay: ResumeSessionRequest = serde_json::from_value(json!({
8216            "sessionId": "sess_abc123",
8217            "cwd": "/home/user/project",
8218            "replayFrom": {
8219                "type": "start"
8220            }
8221        }))
8222        .unwrap();
8223        assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
8224
8225        let none: ResumeSessionRequest = serde_json::from_value(json!({
8226            "sessionId": "sess_abc123",
8227            "cwd": "/home/user/project",
8228            "replayFrom": null
8229        }))
8230        .unwrap();
8231        assert!(none.replay_from.is_none());
8232    }
8233
8234    #[test]
8235    fn test_auth_method_agent_serialization() {
8236        let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
8237
8238        let json = serde_json::to_value(&method).unwrap();
8239        assert_eq!(
8240            json,
8241            json!({
8242                "methodId": "default-auth",
8243                "name": "Default Auth",
8244                "type": "agent"
8245            })
8246        );
8247        // description should be omitted when None
8248        assert!(!json.as_object().unwrap().contains_key("description"));
8249
8250        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8251        match deserialized {
8252            AuthMethod::Agent(AuthMethodAgent {
8253                method_id, name, ..
8254            }) => {
8255                assert_eq!(method_id.0.as_ref(), "default-auth");
8256                assert_eq!(name, "Default Auth");
8257            }
8258            _ => panic!("Expected Agent variant"),
8259        }
8260    }
8261
8262    #[test]
8263    fn test_auth_method_agent_deserialization() {
8264        let json = json!({
8265            "methodId": "agent-auth",
8266            "name": "Agent Auth",
8267            "type": "agent"
8268        });
8269
8270        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8271        assert!(matches!(deserialized, AuthMethod::Agent(_)));
8272    }
8273
8274    #[test]
8275    fn test_auth_method_agent_requires_type() {
8276        assert!(
8277            serde_json::from_value::<AuthMethod>(json!({
8278                "methodId": "agent-auth",
8279                "name": "Agent Auth"
8280            }))
8281            .is_err()
8282        );
8283    }
8284
8285    #[test]
8286    fn test_auth_method_agent_rejects_null_type() {
8287        assert!(
8288            serde_json::from_value::<AuthMethod>(json!({
8289                "methodId": "agent-auth",
8290                "name": "Agent Auth",
8291                "type": null
8292            }))
8293            .is_err()
8294        );
8295    }
8296
8297    #[test]
8298    fn test_auth_method_unknown_does_not_hide_malformed_agent() {
8299        assert!(
8300            serde_json::from_value::<AuthMethod>(json!({
8301                "methodId": "agent-auth",
8302                "type": "agent"
8303            }))
8304            .is_err()
8305        );
8306        assert!(
8307            serde_json::from_value::<AuthMethod>(json!({
8308                "methodId": "api-key",
8309                "type": "env_var",
8310                "vars": [{"name": "API_KEY"}]
8311            }))
8312            .is_err()
8313        );
8314    }
8315
8316    #[test]
8317    fn test_auth_method_unknown_variant_roundtrip() {
8318        let method: AuthMethod = serde_json::from_value(json!({
8319            "methodId": "oauth",
8320            "name": "OAuth",
8321            "type": "_oauth",
8322            "authorizationUrl": "https://example.com/auth"
8323        }))
8324        .unwrap();
8325
8326        assert_eq!(method.method_id().0.as_ref(), "oauth");
8327        assert_eq!(method.name(), "OAuth");
8328        let AuthMethod::Other(unknown) = method else {
8329            panic!("expected unknown auth method");
8330        };
8331        assert_eq!(unknown.type_, "_oauth");
8332        assert_eq!(
8333            unknown.fields.get("authorizationUrl"),
8334            Some(&json!("https://example.com/auth"))
8335        );
8336
8337        assert_eq!(
8338            serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
8339            json!({
8340                "methodId": "oauth",
8341                "name": "OAuth",
8342                "type": "_oauth",
8343                "authorizationUrl": "https://example.com/auth"
8344            })
8345        );
8346    }
8347
8348    #[test]
8349    fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
8350        assert!(
8351            serde_json::from_value::<AuthMethod>(json!({
8352                "methodId": "terminal-auth",
8353                "type": "terminal"
8354            }))
8355            .is_err()
8356        );
8357    }
8358
8359    #[test]
8360    fn test_tool_call_and_terminal_cancel_serialization() {
8361        assert_eq!(AGENT_METHOD_NAMES.tool_call_cancel, "tool_call/cancel");
8362        assert_eq!(AGENT_METHOD_NAMES.terminal_cancel, "terminal/cancel");
8363        assert_eq!(
8364            ClientRequest::CancelToolCallRequest(Box::new(CancelToolCallRequest::new(
8365                "sess_abc123",
8366                "call_012"
8367            )))
8368            .method(),
8369            "tool_call/cancel"
8370        );
8371        assert_eq!(
8372            ClientRequest::CancelTerminalRequest(Box::new(CancelTerminalRequest::new(
8373                "sess_abc123",
8374                "term_003"
8375            )))
8376            .method(),
8377            "terminal/cancel"
8378        );
8379        assert_eq!(
8380            serde_json::to_value(CancelToolCallRequest::new("sess_abc123", "call_012")).unwrap(),
8381            json!({"sessionId": "sess_abc123", "toolCallId": "call_012"})
8382        );
8383        assert_eq!(
8384            serde_json::to_value(CancelTerminalRequest::new("sess_abc123", "term_003")).unwrap(),
8385            json!({"sessionId": "sess_abc123", "terminalId": "term_003"})
8386        );
8387        assert!(
8388            serde_json::from_value::<CancelToolCallRequest>(json!({"sessionId": "sess_abc123"}))
8389                .is_err()
8390        );
8391        assert_eq!(
8392            serde_json::to_value(CancelTerminalResponse::new()).unwrap(),
8393            json!({})
8394        );
8395    }
8396
8397    #[test]
8398    fn test_session_delete_serialization() {
8399        assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
8400        assert_eq!(
8401            ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
8402                .method(),
8403            "session/delete"
8404        );
8405        assert_eq!(
8406            serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
8407            json!({
8408                "sessionId": "sess_abc123"
8409            })
8410        );
8411        assert_eq!(
8412            serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
8413            json!({})
8414        );
8415        assert_eq!(
8416            serde_json::to_value(
8417                SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
8418            )
8419            .unwrap(),
8420            json!({
8421                "delete": {}
8422            })
8423        );
8424    }
8425    #[cfg(feature = "unstable_session_set_title")]
8426    #[test]
8427    fn test_session_set_title_serialization() {
8428        assert_eq!(AGENT_METHOD_NAMES.session_set_title, "session/set_title");
8429        assert_eq!(
8430            ClientRequest::SetSessionTitleRequest(Box::new(SetSessionTitleRequest::new(
8431                "sess_abc123",
8432                "Investigate OAuth timeout"
8433            )))
8434            .method(),
8435            "session/set_title"
8436        );
8437        assert_eq!(
8438            serde_json::to_value(SetSessionTitleRequest::new(
8439                "sess_abc123",
8440                "Investigate OAuth timeout"
8441            ))
8442            .unwrap(),
8443            json!({
8444                "sessionId": "sess_abc123",
8445                "title": "Investigate OAuth timeout"
8446            })
8447        );
8448        assert_eq!(
8449            serde_json::to_value(SetSessionTitleResponse::new("Investigate OAuth timeout"))
8450                .unwrap(),
8451            json!({
8452                "title": "Investigate OAuth timeout"
8453            })
8454        );
8455        assert_eq!(
8456            serde_json::to_value(SessionCapabilities::new().title(SessionTitleCapabilities::new()))
8457                .unwrap(),
8458            json!({
8459                "title": {}
8460            })
8461        );
8462    }
8463
8464    #[cfg(feature = "unstable_session_set_title")]
8465    #[test]
8466    fn test_session_set_title_requires_title() {
8467        for params in [
8468            json!({"sessionId": "sess_abc123"}),
8469            json!({"sessionId": "sess_abc123", "title": null}),
8470            json!({"sessionId": "sess_abc123", "title": 42}),
8471        ] {
8472            assert!(serde_json::from_value::<SetSessionTitleRequest>(params).is_err());
8473        }
8474        for result in [json!({}), json!({"title": null})] {
8475            assert!(serde_json::from_value::<SetSessionTitleResponse>(result).is_err());
8476        }
8477    }
8478
8479    #[cfg(feature = "unstable_session_status")]
8480    #[test]
8481    fn test_session_status_serialization() {
8482        assert_eq!(AGENT_METHOD_NAMES.session_status, "session/status");
8483        assert_eq!(
8484            ClientRequest::SessionStatusRequest(Box::new(SessionStatusRequest::new("sess_abc123")))
8485                .method(),
8486            "session/status"
8487        );
8488        assert_eq!(
8489            serde_json::to_value(SessionStatusRequest::new("sess_abc123")).unwrap(),
8490            json!({"sessionId": "sess_abc123"})
8491        );
8492        assert_eq!(
8493            serde_json::to_value(SessionStatusResponse::new(SessionStatus::Saved)).unwrap(),
8494            json!({"status": "saved"})
8495        );
8496        assert_eq!(
8497            serde_json::to_value(
8498                SessionCapabilities::new().status(SessionStatusCapabilities::new())
8499            )
8500            .unwrap(),
8501            json!({"status": {}})
8502        );
8503        assert_eq!(
8504            serde_json::to_value(SessionStatusResponse::new(SessionStatus::Open).state(
8505                StateUpdate::RequiresAction(crate::v2::RequiresActionStateUpdate::new())
8506            ))
8507            .unwrap(),
8508            json!({
8509                "status": "open",
8510                "state": {"state": "requires_action"}
8511            })
8512        );
8513        for state in [json!(null), json!("running"), json!({"state": 7})] {
8514            let response = serde_json::from_value::<SessionStatusResponse>(
8515                json!({"status": "open", "state": state}),
8516            )
8517            .unwrap();
8518            assert_eq!(response.state, None);
8519        }
8520    }
8521
8522    #[cfg(feature = "unstable_session_status")]
8523    #[test]
8524    fn test_session_status_is_strict_and_open() {
8525        let custom =
8526            serde_json::from_value::<SessionStatusResponse>(json!({"status": "_suspended"}))
8527                .unwrap();
8528        assert_eq!(
8529            custom.status,
8530            SessionStatus::Other("_suspended".to_string())
8531        );
8532        assert_eq!(
8533            serde_json::to_value(&custom).unwrap(),
8534            json!({"status": "_suspended"})
8535        );
8536        for result in [json!({}), json!({"status": null}), json!({"status": 1})] {
8537            assert!(serde_json::from_value::<SessionStatusResponse>(result).is_err());
8538        }
8539        for params in [json!({}), json!({"sessionId": null})] {
8540            assert!(serde_json::from_value::<SessionStatusRequest>(params).is_err());
8541        }
8542    }
8543
8544    #[cfg(feature = "unstable_session_archive")]
8545    #[test]
8546    fn test_session_archive_serialization() {
8547        assert_eq!(AGENT_METHOD_NAMES.session_archive, "session/archive");
8548        assert_eq!(AGENT_METHOD_NAMES.session_unarchive, "session/unarchive");
8549        assert_eq!(
8550            ClientRequest::ArchiveSessionRequest(Box::new(ArchiveSessionRequest::new(
8551                "sess_abc123"
8552            )))
8553            .method(),
8554            "session/archive"
8555        );
8556        assert_eq!(
8557            ClientRequest::UnarchiveSessionRequest(Box::new(UnarchiveSessionRequest::new(
8558                "sess_abc123"
8559            )))
8560            .method(),
8561            "session/unarchive"
8562        );
8563        assert_eq!(
8564            serde_json::to_value(ArchiveSessionRequest::new("sess_abc123")).unwrap(),
8565            json!({"sessionId": "sess_abc123"})
8566        );
8567        assert_eq!(
8568            serde_json::to_value(UnarchiveSessionResponse::new()).unwrap(),
8569            json!({})
8570        );
8571        assert_eq!(
8572            serde_json::to_value(
8573                SessionCapabilities::new().archive(SessionArchiveCapabilities::new())
8574            )
8575            .unwrap(),
8576            json!({"archive": {}})
8577        );
8578        assert_eq!(
8579            serde_json::to_value(
8580                ListSessionsRequest::new()
8581                    .cwd("/home/user/project")
8582                    .archived(SessionArchiveFilter::Only)
8583            )
8584            .unwrap(),
8585            json!({"cwd": "/home/user/project", "archived": "only"})
8586        );
8587        assert_eq!(
8588            serde_json::to_value(
8589                SessionInfo::new("sess_abc123", "/home/user/project").archived(true)
8590            )
8591            .unwrap(),
8592            json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": true})
8593        );
8594    }
8595
8596    #[cfg(feature = "unstable_session_archive")]
8597    #[test]
8598    fn test_session_archive_filter_is_strict_and_open() {
8599        for (value, expected) in [
8600            (json!(null), None),
8601            (json!("include"), Some(SessionArchiveFilter::Include)),
8602            (
8603                json!("_recent"),
8604                Some(SessionArchiveFilter::Other("_recent".to_string())),
8605            ),
8606        ] {
8607            let request =
8608                serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).unwrap();
8609            assert_eq!(request.archived, expected);
8610        }
8611        for value in [json!(true), json!(1), json!({})] {
8612            assert!(
8613                serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).is_err()
8614            );
8615        }
8616        let info = serde_json::from_value::<SessionInfo>(
8617            json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": "yes"}),
8618        )
8619        .unwrap();
8620        assert_eq!(info.archived, None);
8621    }
8622
8623    #[test]
8624    fn test_session_additional_directories_serialization() {
8625        assert_eq!(
8626            serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
8627            json!({
8628                "cwd": "/home/user/project",
8629            })
8630        );
8631        assert_eq!(
8632            serde_json::to_value(
8633                NewSessionRequest::new("/home/user/project").additional_directories(vec![
8634                    PathBuf::from("/home/user/shared-lib"),
8635                    PathBuf::from("/home/user/product-docs"),
8636                ])
8637            )
8638            .unwrap(),
8639            json!({
8640                "cwd": "/home/user/project",
8641                "additionalDirectories": [
8642                    "/home/user/shared-lib",
8643                    "/home/user/product-docs"
8644                ],
8645            })
8646        );
8647        assert_eq!(
8648            serde_json::to_value(ResumeSessionRequest::new(
8649                "sess_abc123",
8650                "/home/user/project"
8651            ))
8652            .unwrap(),
8653            json!({
8654                "sessionId": "sess_abc123",
8655                "cwd": "/home/user/project",
8656            })
8657        );
8658        assert_eq!(
8659            serde_json::from_value::<ResumeSessionRequest>(json!({
8660                "sessionId": "sess_abc123",
8661                "cwd": "/home/user/project"
8662            }))
8663            .unwrap()
8664            .mcp_servers,
8665            Vec::<McpServer>::new()
8666        );
8667        assert_eq!(
8668            serde_json::from_value::<ResumeSessionRequest>(json!({
8669                "sessionId": "sess_abc123",
8670                "cwd": "/home/user/project",
8671                "mcpServers": null
8672            }))
8673            .unwrap()
8674            .mcp_servers,
8675            Vec::<McpServer>::new()
8676        );
8677        assert_eq!(
8678            serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
8679            json!({
8680                "sessionId": "sess_abc123",
8681                "cwd": "/home/user/project"
8682            })
8683        );
8684        assert_eq!(
8685            serde_json::to_value(
8686                SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
8687                    PathBuf::from("/home/user/shared-lib"),
8688                    PathBuf::from("/home/user/product-docs"),
8689                ])
8690            )
8691            .unwrap(),
8692            json!({
8693                "sessionId": "sess_abc123",
8694                "cwd": "/home/user/project",
8695                "additionalDirectories": [
8696                    "/home/user/shared-lib",
8697                    "/home/user/product-docs"
8698                ]
8699            })
8700        );
8701        assert_eq!(
8702            serde_json::from_value::<SessionInfo>(json!({
8703                "sessionId": "sess_abc123",
8704                "cwd": "/home/user/project"
8705            }))
8706            .unwrap()
8707            .additional_directories,
8708            Vec::<AbsolutePath>::new()
8709        );
8710    }
8711    #[test]
8712    fn test_session_additional_directories_capabilities_serialization() {
8713        assert_eq!(
8714            serde_json::to_value(
8715                SessionCapabilities::new()
8716                    .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
8717            )
8718            .unwrap(),
8719            json!({
8720                "additionalDirectories": {}
8721            })
8722        );
8723    }
8724
8725    #[cfg(feature = "unstable_session_system_prompt")]
8726    #[test]
8727    fn test_session_system_prompt_capabilities_serialization() {
8728        assert_eq!(
8729            serde_json::to_value(
8730                SessionCapabilities::new().system_prompt(SessionSystemPromptCapabilities::new())
8731            )
8732            .unwrap(),
8733            json!({ "systemPrompt": {} })
8734        );
8735        assert_eq!(
8736            serde_json::to_value(
8737                SessionCapabilities::new().system_prompt(
8738                    SessionSystemPromptCapabilities::new().mode(
8739                        SessionSystemPromptModeCapabilities::new()
8740                            .override_(SessionSystemPromptOverrideCapabilities::new())
8741                    )
8742                )
8743            )
8744            .unwrap(),
8745            json!({ "systemPrompt": { "mode": { "override": {} } } })
8746        );
8747        assert_eq!(
8748            serde_json::from_value::<SessionCapabilities>(
8749                json!({ "systemPrompt": { "mode": { "override": {} } } })
8750            )
8751            .unwrap()
8752            .system_prompt
8753            .and_then(|capabilities| capabilities.mode)
8754            .and_then(|mode| mode.override_),
8755            Some(SessionSystemPromptOverrideCapabilities::new())
8756        );
8757
8758        // Capabilities fall back to "not advertised" when malformed.
8759        assert_eq!(
8760            serde_json::from_value::<SessionCapabilities>(json!({ "systemPrompt": "yes" }))
8761                .unwrap()
8762                .system_prompt,
8763            None
8764        );
8765        assert_eq!(
8766            serde_json::from_value::<SessionCapabilities>(
8767                json!({ "systemPrompt": { "mode": { "override": true } } })
8768            )
8769            .unwrap()
8770            .system_prompt,
8771            Some(
8772                SessionSystemPromptCapabilities::new()
8773                    .mode(SessionSystemPromptModeCapabilities::new())
8774            )
8775        );
8776        assert_eq!(
8777            serde_json::from_value::<SessionCapabilities>(
8778                json!({ "systemPrompt": { "mode": "override" } })
8779            )
8780            .unwrap()
8781            .system_prompt,
8782            Some(SessionSystemPromptCapabilities::new())
8783        );
8784    }
8785
8786    #[cfg(feature = "unstable_session_system_prompt")]
8787    #[test]
8788    fn test_system_prompt_lifecycle_request_serialization() {
8789        let system_prompt = SystemPrompt::new(vec![ContentBlock::from(
8790            "You are a code review specialist.",
8791        )]);
8792        let system_prompt_json = json!({
8793            "mode": "append",
8794            "content": [{ "type": "text", "text": "You are a code review specialist." }]
8795        });
8796
8797        let request =
8798            NewSessionRequest::new("/home/user/project").system_prompt(system_prompt.clone());
8799        let request_json = json!({
8800            "cwd": "/home/user/project",
8801            "systemPrompt": system_prompt_json
8802        });
8803        assert_eq!(serde_json::to_value(&request).unwrap(), request_json);
8804        assert_eq!(
8805            serde_json::from_value::<NewSessionRequest>(request_json).unwrap(),
8806            request
8807        );
8808
8809        assert_eq!(
8810            serde_json::to_value(
8811                ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8812                    .system_prompt(system_prompt.clone())
8813            )
8814            .unwrap()["systemPrompt"],
8815            system_prompt_json
8816        );
8817        #[cfg(feature = "unstable_session_fork")]
8818        assert_eq!(
8819            serde_json::to_value(
8820                ForkSessionRequest::new("sess_abc123", "/home/user/project")
8821                    .system_prompt(system_prompt.clone())
8822            )
8823            .unwrap()["systemPrompt"],
8824            system_prompt_json
8825        );
8826
8827        let override_prompt =
8828            SystemPrompt::new(vec![ContentBlock::from("...")]).mode(SystemPromptMode::Override);
8829        let override_json = json!({
8830            "mode": "override",
8831            "content": [{ "type": "text", "text": "..." }]
8832        });
8833        assert_eq!(
8834            serde_json::to_value(&override_prompt).unwrap(),
8835            override_json
8836        );
8837        assert_eq!(
8838            serde_json::from_value::<SystemPrompt>(override_json).unwrap(),
8839            override_prompt
8840        );
8841    }
8842
8843    /// `SystemPrompt::new` defaults to `append` in Rust, but `mode` is required on
8844    /// the wire, so it is always serialized.
8845    #[cfg(feature = "unstable_session_system_prompt")]
8846    #[test]
8847    fn test_system_prompt_new_defaults_to_append() {
8848        let system_prompt = SystemPrompt::new(vec![ContentBlock::from("...")]);
8849        assert_eq!(system_prompt.mode, SystemPromptMode::Append);
8850        assert_eq!(
8851            serde_json::to_value(&system_prompt).unwrap(),
8852            json!({ "mode": "append", "content": [{ "type": "text", "text": "..." }] })
8853        );
8854    }
8855
8856    /// Unknown modes follow the enum variant extension rule: they parse as
8857    /// `SystemPromptMode::Other` so they are not dropped, and an agent that does
8858    /// not support them must reject the request.
8859    #[cfg(feature = "unstable_session_system_prompt")]
8860    #[test]
8861    fn test_system_prompt_preserves_unknown_modes() {
8862        for mode in ["_vendor_mode", "prepend"] {
8863            let system_prompt_json =
8864                json!({ "mode": mode, "content": [{ "type": "text", "text": "..." }] });
8865            let system_prompt: SystemPrompt =
8866                serde_json::from_value(system_prompt_json.clone()).unwrap();
8867            assert_eq!(
8868                system_prompt.mode,
8869                SystemPromptMode::Other(mode.to_string())
8870            );
8871            assert_eq!(
8872                serde_json::to_value(&system_prompt).unwrap(),
8873                system_prompt_json
8874            );
8875        }
8876    }
8877
8878    /// The receiver acts on `systemPrompt`, so malformed values fail the request
8879    /// (`-32602`) instead of creating a session without the prompt, and `null`
8880    /// is equivalent to omitting the field.
8881    #[cfg(feature = "unstable_session_system_prompt")]
8882    fn assert_system_prompt_is_strict<T>(base: &serde_json::Value)
8883    where
8884        T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
8885    {
8886        let name = std::any::type_name::<T>();
8887        let with = |system_prompt: serde_json::Value| {
8888            let mut request = base.clone();
8889            request["systemPrompt"] = system_prompt;
8890            serde_json::from_value::<T>(request)
8891        };
8892
8893        for system_prompt in [
8894            json!("You are a code review specialist."),
8895            json!({ "content": [{ "type": "text", "text": "..." }] }),
8896            json!({ "mode": null, "content": [{ "type": "text", "text": "..." }] }),
8897            json!({ "mode": 1, "content": [{ "type": "text", "text": "..." }] }),
8898            json!({ "mode": "append" }),
8899            json!({ "mode": "append", "content": "..." }),
8900            json!({ "mode": "append", "content": [{ "type": "text" }] }),
8901        ] {
8902            assert!(
8903                with(system_prompt.clone()).is_err(),
8904                "{name}.systemPrompt: {system_prompt}"
8905            );
8906        }
8907
8908        assert_eq!(
8909            with(serde_json::Value::Null).unwrap(),
8910            serde_json::from_value::<T>(base.clone()).unwrap(),
8911            "{name}.systemPrompt"
8912        );
8913    }
8914
8915    #[cfg(feature = "unstable_session_system_prompt")]
8916    #[test]
8917    fn test_session_setup_requests_reject_malformed_system_prompts() {
8918        assert!(
8919            serde_json::to_value(NewSessionRequest::new("/repo"))
8920                .unwrap()
8921                .get("systemPrompt")
8922                .is_none()
8923        );
8924        assert_system_prompt_is_strict::<NewSessionRequest>(&json!({ "cwd": "/repo" }));
8925        assert_system_prompt_is_strict::<ResumeSessionRequest>(&json!({
8926            "sessionId": "sess-1",
8927            "cwd": "/repo"
8928        }));
8929        #[cfg(feature = "unstable_session_fork")]
8930        assert_system_prompt_is_strict::<ForkSessionRequest>(&json!({
8931            "sessionId": "sess-1",
8932            "cwd": "/repo"
8933        }));
8934    }
8935
8936    /// Unknown content block types parse as `ContentBlock::Other` so they are not
8937    /// dropped. An agent that does not support them must reject the request.
8938    #[cfg(feature = "unstable_session_system_prompt")]
8939    #[test]
8940    fn test_system_prompt_preserves_unknown_content_blocks() {
8941        let request: NewSessionRequest = serde_json::from_value(json!({
8942            "cwd": "/home/user/project",
8943            "systemPrompt": {
8944                "mode": "append",
8945                "content": [{ "type": "_vendor_video", "uri": "https://example.com/intro.mp4" }]
8946            }
8947        }))
8948        .unwrap();
8949        let content = request.system_prompt.unwrap().content;
8950        assert_eq!(content.len(), 1);
8951        assert!(matches!(content[0], ContentBlock::Other(_)));
8952    }
8953
8954    #[test]
8955    fn test_auth_method_terminal_serialization() {
8956        let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
8957
8958        let json = serde_json::to_value(&method).unwrap();
8959        assert_eq!(
8960            json,
8961            json!({
8962                "methodId": "tui-auth",
8963                "name": "Terminal Auth",
8964                "type": "terminal"
8965            })
8966        );
8967        // args and env should be omitted when empty
8968        assert!(!json.as_object().unwrap().contains_key("args"));
8969        assert!(!json.as_object().unwrap().contains_key("env"));
8970
8971        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8972        match deserialized {
8973            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
8974                assert_eq!(args, Vec::<String>::new());
8975                assert_eq!(env, []);
8976            }
8977            _ => panic!("Expected Terminal variant"),
8978        }
8979    }
8980
8981    #[test]
8982    fn test_auth_method_terminal_with_args_and_env_serialization() {
8983        let method = AuthMethod::Terminal(
8984            AuthMethodTerminal::new("tui-auth", "Terminal Auth")
8985                .args(vec!["--interactive".to_string(), "--color".to_string()])
8986                .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
8987        );
8988
8989        let json = serde_json::to_value(&method).unwrap();
8990        assert_eq!(
8991            json,
8992            json!({
8993                "methodId": "tui-auth",
8994                "name": "Terminal Auth",
8995                "type": "terminal",
8996                "args": ["--interactive", "--color"],
8997                "env": [
8998                    {
8999                        "name": "TERM",
9000                        "value": "xterm-256color"
9001                    }
9002                ]
9003            })
9004        );
9005
9006        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
9007        match deserialized {
9008            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
9009                assert_eq!(args, vec!["--interactive", "--color"]);
9010                assert_eq!(env.len(), 1);
9011                assert_eq!(env[0].name, "TERM");
9012                assert_eq!(env[0].value, "xterm-256color");
9013            }
9014            _ => panic!("Expected Terminal variant"),
9015        }
9016    }
9017
9018    #[test]
9019    fn test_session_config_option_id_serialize() {
9020        let val = SessionConfigOptionValue::id("model-1");
9021        let json = serde_json::to_value(&val).unwrap();
9022        assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
9023    }
9024
9025    #[test]
9026    fn test_session_config_option_value_boolean_serialize() {
9027        let val = SessionConfigOptionValue::boolean(true);
9028        let json = serde_json::to_value(&val).unwrap();
9029        assert_eq!(json, json!({ "type": "boolean", "value": true }));
9030    }
9031
9032    #[test]
9033    fn test_session_config_option_value_deserialize_id() {
9034        let json = json!({ "type": "id", "value": "model-1" });
9035        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9036        assert_eq!(val, SessionConfigOptionValue::id("model-1"));
9037        assert_eq!(val.as_id().unwrap().to_string(), "model-1");
9038    }
9039
9040    #[test]
9041    fn test_session_config_option_value_deserialize_requires_type() {
9042        let json = json!({ "value": "model-1" });
9043        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9044        assert!(result.is_err());
9045    }
9046
9047    #[test]
9048    fn test_session_config_option_value_deserialize_boolean() {
9049        let json = json!({ "type": "boolean", "value": true });
9050        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9051        assert_eq!(val, SessionConfigOptionValue::boolean(true));
9052        assert_eq!(val.as_bool(), Some(true));
9053    }
9054
9055    #[test]
9056    fn test_session_config_option_value_deserialize_boolean_false() {
9057        let json = json!({ "type": "boolean", "value": false });
9058        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9059        assert_eq!(val, SessionConfigOptionValue::boolean(false));
9060        assert_eq!(val.as_bool(), Some(false));
9061    }
9062
9063    #[test]
9064    fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
9065        let json = json!({
9066            "type": "text",
9067            "value": "freeform input",
9068            "maxLength": 200
9069        });
9070        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9071        let SessionConfigOptionValue::Other(unknown) = val else {
9072            panic!("Expected Other variant");
9073        };
9074        assert_eq!(unknown.type_, "text");
9075        assert_eq!(unknown.value, json!("freeform input"));
9076        assert_eq!(unknown.fields["maxLength"], json!(200));
9077    }
9078
9079    #[test]
9080    fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
9081        let json = json!({
9082            "type": "range",
9083            "value": { "min": 1, "max": 5 }
9084        });
9085        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9086        let SessionConfigOptionValue::Other(unknown) = val else {
9087            panic!("Expected Other variant");
9088        };
9089        assert_eq!(unknown.type_, "range");
9090        assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
9091    }
9092
9093    #[test]
9094    fn test_session_config_option_value_roundtrip_id() {
9095        let original = SessionConfigOptionValue::id("option-a");
9096        let json = serde_json::to_value(&original).unwrap();
9097        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9098        assert_eq!(original, roundtripped);
9099    }
9100
9101    #[test]
9102    fn test_session_config_option_value_roundtrip_boolean() {
9103        let original = SessionConfigOptionValue::boolean(false);
9104        let json = serde_json::to_value(&original).unwrap();
9105        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9106        assert_eq!(original, roundtripped);
9107    }
9108
9109    #[test]
9110    fn test_session_config_option_value_roundtrip_other() {
9111        let mut fields = BTreeMap::new();
9112        fields.insert("maxLength".to_string(), json!(200));
9113        let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
9114            "text",
9115            json!("freeform input"),
9116            fields,
9117        ));
9118        let json = serde_json::to_value(&original).unwrap();
9119        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9120        assert_eq!(original, roundtripped);
9121    }
9122
9123    #[test]
9124    fn test_session_config_option_value_type_mismatch_boolean_with_string() {
9125        let json = json!({ "type": "boolean", "value": "not a bool" });
9126        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9127        assert!(result.is_err());
9128    }
9129
9130    #[test]
9131    fn test_session_config_option_value_from_impls() {
9132        let from_str: SessionConfigOptionValue = "model-1".into();
9133        assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
9134
9135        let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
9136        assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
9137
9138        let from_bool: SessionConfigOptionValue = true.into();
9139        assert_eq!(from_bool.as_bool(), Some(true));
9140    }
9141
9142    #[test]
9143    fn test_set_session_config_option_request_id() {
9144        let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
9145        let json = serde_json::to_value(&req).unwrap();
9146        assert_eq!(
9147            json,
9148            json!({
9149                "sessionId": "sess_1",
9150                "configId": "model",
9151                "type": "id",
9152                "value": "model-1"
9153            })
9154        );
9155    }
9156
9157    #[test]
9158    fn test_set_session_config_option_request_boolean() {
9159        let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
9160        let json = serde_json::to_value(&req).unwrap();
9161        assert_eq!(
9162            json,
9163            json!({
9164                "sessionId": "sess_1",
9165                "configId": "brave_mode",
9166                "type": "boolean",
9167                "value": true
9168            })
9169        );
9170    }
9171
9172    #[test]
9173    fn test_set_session_config_option_request_deserialize_requires_type() {
9174        let json = json!({
9175            "sessionId": "sess_1",
9176            "configId": "model",
9177            "value": "model-1"
9178        });
9179        let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
9180        assert!(result.is_err());
9181    }
9182
9183    #[test]
9184    fn test_set_session_config_option_request_deserialize_boolean() {
9185        let json = json!({
9186            "sessionId": "sess_1",
9187            "configId": "brave_mode",
9188            "type": "boolean",
9189            "value": true
9190        });
9191        let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9192        assert_eq!(req.value.as_bool(), Some(true));
9193    }
9194
9195    #[test]
9196    fn test_set_session_config_option_request_roundtrip_id() {
9197        let original = SetSessionConfigOptionRequest::new("s", "c", "v");
9198        let json = serde_json::to_value(&original).unwrap();
9199        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9200        assert_eq!(original, roundtripped);
9201    }
9202
9203    #[test]
9204    fn test_set_session_config_option_request_roundtrip_boolean() {
9205        let original = SetSessionConfigOptionRequest::new("s", "c", false);
9206        let json = serde_json::to_value(&original).unwrap();
9207        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9208        assert_eq!(original, roundtripped);
9209    }
9210
9211    #[test]
9212    fn test_session_config_boolean_serialization() {
9213        let cfg = SessionConfigBoolean::new(true);
9214        let json = serde_json::to_value(&cfg).unwrap();
9215        assert_eq!(json, json!({ "currentValue": true }));
9216
9217        let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
9218        assert!(deserialized.current_value);
9219    }
9220
9221    #[test]
9222    fn test_session_config_option_boolean_variant() {
9223        let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
9224            .description("Skip confirmation prompts")
9225            .meta(test_meta());
9226        assert_eq!(serialized_meta_key_count(&opt), 1);
9227
9228        let json = serde_json::to_value(&opt).unwrap();
9229        assert_eq!(
9230            json,
9231            json!({
9232                "configId": "brave_mode",
9233                "name": "Brave Mode",
9234                "description": "Skip confirmation prompts",
9235                "type": "boolean",
9236                "currentValue": false,
9237                "_meta": {
9238                    "source": "test"
9239                }
9240            })
9241        );
9242
9243        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9244        assert_eq!(deserialized.config_id.to_string(), "brave_mode");
9245        assert_eq!(deserialized.name, "Brave Mode");
9246        match deserialized.kind {
9247            SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
9248            _ => panic!("Expected Boolean kind"),
9249        }
9250    }
9251
9252    #[test]
9253    fn test_session_config_option_select_still_works() {
9254        // Make sure existing select options are unaffected
9255        let opt = SessionConfigOption::select(
9256            "model",
9257            "Model",
9258            "model-1",
9259            vec![
9260                SessionConfigSelectOption::new("model-1", "Model 1"),
9261                SessionConfigSelectOption::new("model-2", "Model 2"),
9262            ],
9263        )
9264        .meta(test_meta());
9265        assert_eq!(serialized_meta_key_count(&opt), 1);
9266
9267        let json = serde_json::to_value(&opt).unwrap();
9268        assert_eq!(json["type"], "select");
9269        assert_eq!(json["currentValue"], "model-1");
9270        assert_eq!(json["options"].as_array().unwrap().len(), 2);
9271        assert_eq!(json["_meta"]["source"], "test");
9272
9273        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9274        match deserialized.kind {
9275            SessionConfigKind::Select(ref s) => {
9276                assert_eq!(s.current_value.to_string(), "model-1");
9277            }
9278            _ => panic!("Expected Select kind"),
9279        }
9280    }
9281
9282    #[test]
9283    fn test_session_config_option_unknown_kind_roundtrip() {
9284        let option: SessionConfigOption = serde_json::from_value(json!({
9285            "configId": "verbosity",
9286            "name": "Verbosity",
9287            "type": "_slider",
9288            "currentValue": 3,
9289            "min": 0,
9290            "max": 5,
9291            "_meta": {
9292                "source": "test"
9293            }
9294        }))
9295        .unwrap();
9296
9297        assert_eq!(option.config_id.to_string(), "verbosity");
9298        assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
9299        let SessionConfigKind::Other(unknown) = &option.kind else {
9300            panic!("expected unknown config kind");
9301        };
9302        assert_eq!(unknown.type_, "_slider");
9303        assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
9304        assert!(!unknown.fields.contains_key("_meta"));
9305        assert_eq!(serialized_meta_key_count(&option), 1);
9306
9307        let json = serde_json::to_value(&option).unwrap();
9308        assert_eq!(json["type"], "_slider");
9309        assert_eq!(json["currentValue"], 3);
9310        assert_eq!(json["min"], 0);
9311        assert_eq!(json["max"], 5);
9312        assert_eq!(json["_meta"]["source"], "test");
9313    }
9314
9315    #[test]
9316    fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
9317        let mut fields = std::collections::BTreeMap::new();
9318        fields.insert("currentValue".to_string(), json!(3));
9319        fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
9320
9321        let option = SessionConfigOption::new(
9322            "verbosity",
9323            "Verbosity",
9324            SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
9325        )
9326        .meta(test_meta());
9327
9328        let SessionConfigKind::Other(unknown) = &option.kind else {
9329            panic!("expected unknown config kind");
9330        };
9331        assert!(!unknown.fields.contains_key("_meta"));
9332        assert_eq!(serialized_meta_key_count(&option), 1);
9333
9334        let json = serde_json::to_value(&option).unwrap();
9335        assert_eq!(json["type"], "_slider");
9336        assert_eq!(json["currentValue"], 3);
9337        assert_eq!(json["_meta"]["source"], "test");
9338    }
9339
9340    #[test]
9341    fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
9342        assert!(
9343            serde_json::from_value::<SessionConfigOption>(json!({
9344                "configId": "model",
9345                "name": "Model",
9346                "type": "select"
9347            }))
9348            .is_err()
9349        );
9350    }
9351
9352    #[cfg(feature = "unstable_llm_providers")]
9353    #[test]
9354    fn test_llm_protocol_known_variants() {
9355        assert_eq!(
9356            serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
9357            json!("anthropic")
9358        );
9359        assert_eq!(
9360            serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
9361            json!("openai")
9362        );
9363        assert_eq!(
9364            serde_json::to_value(&LlmProtocol::Azure).unwrap(),
9365            json!("azure")
9366        );
9367        assert_eq!(
9368            serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
9369            json!("vertex")
9370        );
9371        assert_eq!(
9372            serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
9373            json!("bedrock")
9374        );
9375
9376        assert_eq!(
9377            serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
9378            LlmProtocol::Anthropic
9379        );
9380        assert_eq!(
9381            serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
9382            LlmProtocol::OpenAi
9383        );
9384        assert_eq!(
9385            serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
9386            LlmProtocol::Azure
9387        );
9388        assert_eq!(
9389            serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
9390            LlmProtocol::Vertex
9391        );
9392        assert_eq!(
9393            serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
9394            LlmProtocol::Bedrock
9395        );
9396    }
9397
9398    #[cfg(feature = "unstable_llm_providers")]
9399    #[test]
9400    fn test_llm_protocol_unknown_variant() {
9401        let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
9402        assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
9403
9404        let json = serde_json::to_value(&unknown).unwrap();
9405        assert_eq!(json, json!("cohere"));
9406    }
9407
9408    #[cfg(feature = "unstable_llm_providers")]
9409    #[test]
9410    fn test_provider_current_config_serialization() {
9411        let config =
9412            ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
9413
9414        let json = serde_json::to_value(&config).unwrap();
9415        assert_eq!(
9416            json,
9417            json!({
9418                "apiType": "anthropic",
9419                "baseUrl": "https://api.anthropic.com"
9420            })
9421        );
9422
9423        let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
9424        assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
9425        assert_eq!(deserialized.base_url, "https://api.anthropic.com");
9426    }
9427
9428    #[cfg(feature = "unstable_llm_providers")]
9429    #[test]
9430    fn test_provider_info_with_current_config() {
9431        let info = ProviderInfo::new(
9432            "main",
9433            vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
9434            true,
9435            Some(ProviderCurrentConfig::new(
9436                LlmProtocol::Anthropic,
9437                "https://api.anthropic.com",
9438            )),
9439        );
9440
9441        let json = serde_json::to_value(&info).unwrap();
9442        assert_eq!(
9443            json,
9444            json!({
9445                "providerId": "main",
9446                "supported": ["anthropic", "openai"],
9447                "required": true,
9448                "current": {
9449                    "apiType": "anthropic",
9450                    "baseUrl": "https://api.anthropic.com"
9451                }
9452            })
9453        );
9454
9455        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9456        assert_eq!(deserialized.provider_id.to_string(), "main");
9457        assert_eq!(deserialized.supported.len(), 2);
9458        assert!(deserialized.required);
9459        assert!(deserialized.current.is_some());
9460        assert_eq!(
9461            deserialized.current.as_ref().unwrap().api_type,
9462            LlmProtocol::Anthropic
9463        );
9464    }
9465
9466    #[cfg(feature = "unstable_llm_providers")]
9467    #[test]
9468    fn test_provider_info_disabled() {
9469        let info = ProviderInfo::new(
9470            "secondary",
9471            vec![LlmProtocol::OpenAi],
9472            false,
9473            None::<ProviderCurrentConfig>,
9474        );
9475
9476        let json = serde_json::to_value(&info).unwrap();
9477        assert_eq!(
9478            json,
9479            json!({
9480                "providerId": "secondary",
9481                "supported": ["openai"],
9482                "required": false
9483            })
9484        );
9485
9486        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9487        assert_eq!(deserialized.provider_id.to_string(), "secondary");
9488        assert!(!deserialized.required);
9489        assert!(deserialized.current.is_none());
9490    }
9491
9492    #[cfg(feature = "unstable_llm_providers")]
9493    #[test]
9494    fn test_provider_info_missing_current_defaults_to_none() {
9495        // current is optional; omitting it should decode as None
9496        let json = json!({
9497            "providerId": "main",
9498            "supported": ["anthropic"],
9499            "required": true
9500        });
9501        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9502        assert!(deserialized.current.is_none());
9503    }
9504
9505    #[cfg(feature = "unstable_llm_providers")]
9506    #[test]
9507    fn test_provider_info_explicit_null_current_decodes_to_none() {
9508        // current: null and an omitted current are equivalent on the wire;
9509        // both must deserialize into None so the disabled state is preserved
9510        // regardless of which form the peer chose to send.
9511        let json = json!({
9512            "providerId": "main",
9513            "supported": ["anthropic"],
9514            "required": true,
9515            "current": null
9516        });
9517        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9518        assert!(deserialized.current.is_none());
9519    }
9520
9521    #[cfg(feature = "unstable_llm_providers")]
9522    #[test]
9523    fn test_list_providers_response_serialization() {
9524        let response = ListProvidersResponse::new(vec![ProviderInfo::new(
9525            "main",
9526            vec![LlmProtocol::Anthropic],
9527            true,
9528            Some(ProviderCurrentConfig::new(
9529                LlmProtocol::Anthropic,
9530                "https://api.anthropic.com",
9531            )),
9532        )]);
9533
9534        let json = serde_json::to_value(&response).unwrap();
9535        assert_eq!(json["providers"].as_array().unwrap().len(), 1);
9536        assert_eq!(json["providers"][0]["providerId"], "main");
9537
9538        let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
9539        assert_eq!(deserialized.providers.len(), 1);
9540    }
9541
9542    #[cfg(feature = "unstable_llm_providers")]
9543    #[test]
9544    fn test_set_provider_request_serialization() {
9545        use std::collections::HashMap;
9546
9547        let mut headers = HashMap::new();
9548        headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
9549
9550        let request =
9551            SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
9552                .headers(headers);
9553
9554        let json = serde_json::to_value(&request).unwrap();
9555        assert_eq!(
9556            json,
9557            json!({
9558                "providerId": "main",
9559                "apiType": "openai",
9560                "baseUrl": "https://api.openai.com/v1",
9561                "headers": {
9562                    "Authorization": "Bearer sk-test"
9563                }
9564            })
9565        );
9566
9567        let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
9568        assert_eq!(deserialized.provider_id.to_string(), "main");
9569        assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
9570        assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
9571        assert_eq!(deserialized.headers.len(), 1);
9572        assert_eq!(
9573            deserialized.headers.get("Authorization").unwrap(),
9574            "Bearer sk-test"
9575        );
9576    }
9577
9578    #[cfg(feature = "unstable_llm_providers")]
9579    #[test]
9580    fn test_set_provider_request_omits_empty_headers() {
9581        let request =
9582            SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
9583
9584        let json = serde_json::to_value(&request).unwrap();
9585        // headers should be omitted when empty
9586        assert!(!json.as_object().unwrap().contains_key("headers"));
9587    }
9588
9589    #[cfg(feature = "unstable_llm_providers")]
9590    #[test]
9591    fn test_disable_provider_request_serialization() {
9592        let request = DisableProviderRequest::new("secondary");
9593
9594        let json = serde_json::to_value(&request).unwrap();
9595        assert_eq!(json, json!({ "providerId": "secondary" }));
9596
9597        let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
9598        assert_eq!(deserialized.provider_id.to_string(), "secondary");
9599    }
9600
9601    #[cfg(feature = "unstable_llm_providers")]
9602    #[test]
9603    fn test_providers_capabilities_serialization() {
9604        let caps = ProvidersCapabilities::new();
9605
9606        let json = serde_json::to_value(&caps).unwrap();
9607        assert_eq!(json, json!({}));
9608
9609        let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
9610        assert!(deserialized.meta.is_none());
9611    }
9612
9613    #[cfg(feature = "unstable_llm_providers")]
9614    #[test]
9615    fn test_agent_capabilities_with_providers() {
9616        let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
9617
9618        let json = serde_json::to_value(&caps).unwrap();
9619        assert_eq!(json["providers"], json!({}));
9620
9621        let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
9622        assert!(deserialized.providers.is_some());
9623    }
9624
9625    #[test]
9626    fn test_agent_capabilities_session_is_explicit() {
9627        let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
9628        assert!(json.get("session").is_none());
9629
9630        let caps = AgentCapabilities::new().session(
9631            SessionCapabilities::new()
9632                .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
9633                .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
9634        );
9635
9636        assert_eq!(
9637            serde_json::to_value(&caps).unwrap(),
9638            json!({
9639                "session": {
9640                    "prompt": {
9641                        "image": {}
9642                    },
9643                    "mcp": {
9644                        "stdio": {}
9645                    }
9646                }
9647            })
9648        );
9649
9650        let deserialized: AgentCapabilities = serde_json::from_value(json!({
9651            "session": false
9652        }))
9653        .unwrap();
9654        assert!(deserialized.session.is_none());
9655    }
9656
9657    #[test]
9658    fn test_prompt_capabilities_serialize_supported_content_as_objects() {
9659        let caps = PromptCapabilities::new()
9660            .image(PromptImageCapabilities::new())
9661            .audio(PromptAudioCapabilities::new())
9662            .embedded_context(PromptEmbeddedContextCapabilities::new());
9663
9664        assert_eq!(
9665            serde_json::to_value(&caps).unwrap(),
9666            json!({
9667                "image": {},
9668                "audio": {},
9669                "embeddedContext": {}
9670            })
9671        );
9672
9673        let deserialized: PromptCapabilities = serde_json::from_value(json!({
9674            "image": null,
9675            "audio": false,
9676            "embeddedContext": {}
9677        }))
9678        .unwrap();
9679        assert!(deserialized.image.is_none());
9680        assert!(deserialized.audio.is_none());
9681        assert!(deserialized.embedded_context.is_some());
9682    }
9683
9684    #[test]
9685    fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
9686        let caps = McpCapabilities::new()
9687            .stdio(McpStdioCapabilities::new())
9688            .http(McpHttpCapabilities::new());
9689
9690        assert_eq!(
9691            serde_json::to_value(&caps).unwrap(),
9692            json!({
9693                "stdio": {},
9694                "http": {}
9695            })
9696        );
9697
9698        let deserialized: McpCapabilities = serde_json::from_value(json!({
9699            "stdio": null,
9700            "http": false
9701        }))
9702        .unwrap();
9703        assert!(deserialized.stdio.is_none());
9704        assert!(deserialized.http.is_none());
9705    }
9706
9707    #[cfg(feature = "unstable_mcp_over_acp")]
9708    #[test]
9709    fn test_mcp_capabilities_serialize_acp_support_as_object() {
9710        let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
9711
9712        assert_eq!(
9713            serde_json::to_value(&caps).unwrap(),
9714            json!({
9715                "acp": {}
9716            })
9717        );
9718    }
9719
9720    #[test]
9721    fn prompt_request_rejects_malformed_content_block() {
9722        use serde_json::json;
9723
9724        assert!(
9725            serde_json::from_value::<PromptRequest>(json!({
9726                "sessionId": "sess-1",
9727                "prompt": [{"type": "text"}]
9728            }))
9729            .is_err()
9730        );
9731    }
9732
9733    #[test]
9734    fn prompt_request_rejects_non_array_prompt() {
9735        use serde_json::json;
9736
9737        assert!(
9738            serde_json::from_value::<PromptRequest>(json!({
9739                "sessionId": "sess-1",
9740                "prompt": "hello"
9741            }))
9742            .is_err()
9743        );
9744    }
9745
9746    fn assert_session_setup_lists_are_strict<T>(base: &serde_json::Value)
9747    where
9748        T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
9749    {
9750        let name = std::any::type_name::<T>();
9751        let with = |field: &str, value: serde_json::Value| {
9752            let mut request = base.clone();
9753            request[field] = value;
9754            serde_json::from_value::<T>(request)
9755        };
9756
9757        for (field, value) in [
9758            // Dropping the malformed server would start the session without it.
9759            (
9760                "mcpServers",
9761                json!([
9762                    {"type": "stdio", "name": "github", "command": "/usr/bin/gh-mcp", "env": [{"name": "GITHUB_TOKEN", "value": null}]},
9763                    {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp"}
9764                ]),
9765            ),
9766            ("mcpServers", json!({"type": "stdio", "name": "fs"})),
9767            ("additionalDirectories", json!(["/repo/lib", 42])),
9768            ("additionalDirectories", json!("/repo/lib")),
9769        ] {
9770            assert!(
9771                with(field, value.clone()).is_err(),
9772                "{name}.{field}: {value}"
9773            );
9774        }
9775
9776        let empty = serde_json::from_value::<T>(base.clone()).unwrap();
9777        for field in ["mcpServers", "additionalDirectories"] {
9778            assert_eq!(
9779                with(field, serde_json::Value::Null).unwrap(),
9780                empty,
9781                "{name}.{field}"
9782            );
9783        }
9784    }
9785
9786    #[test]
9787    fn session_setup_requests_reject_malformed_mcp_servers_and_directories() {
9788        assert_session_setup_lists_are_strict::<NewSessionRequest>(&json!({"cwd": "/repo"}));
9789        assert_session_setup_lists_are_strict::<ResumeSessionRequest>(&json!({
9790            "sessionId": "sess-1",
9791            "cwd": "/repo"
9792        }));
9793        #[cfg(feature = "unstable_session_fork")]
9794        assert_session_setup_lists_are_strict::<ForkSessionRequest>(&json!({
9795            "sessionId": "sess-1",
9796            "cwd": "/repo"
9797        }));
9798    }
9799
9800    #[test]
9801    fn resume_session_request_rejects_malformed_replay_from() {
9802        let request = |replay_from: serde_json::Value| {
9803            serde_json::from_value::<ResumeSessionRequest>(json!({
9804                "sessionId": "sess-1",
9805                "cwd": "/repo",
9806                "replayFrom": replay_from
9807            }))
9808        };
9809
9810        // Treating this as omitted would reconnect without the requested replay.
9811        assert!(request(json!("start")).is_err());
9812        assert_eq!(request(serde_json::Value::Null).unwrap().replay_from, None);
9813        assert!(matches!(
9814            request(json!({"type": "start"})).unwrap().replay_from,
9815            Some(ReplayFrom::Start(_))
9816        ));
9817    }
9818
9819    #[test]
9820    fn mcp_server_lists_treat_null_as_empty() {
9821        let servers: Vec<McpServer> = serde_json::from_value(json!([
9822            {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp", "args": null, "env": null},
9823            {"type": "http", "name": "api", "url": "https://example.com/mcp", "headers": null}
9824        ]))
9825        .unwrap();
9826
9827        assert_eq!(
9828            servers,
9829            vec![
9830                McpServer::Stdio(McpServerStdio::new("fs", "/usr/bin/fs-mcp")),
9831                McpServer::Http(McpServerHttp::new("api", "https://example.com/mcp")),
9832            ]
9833        );
9834    }
9835
9836    #[test]
9837    fn terminal_auth_methods_reject_malformed_launch_config() {
9838        // The client spawns a process from `args` and `env`, so a malformed
9839        // method is dropped from `authMethods` instead of launched with
9840        // altered arguments or preserved as an unknown method.
9841        let response: InitializeResponse = serde_json::from_value(json!({
9842            "protocolVersion": 2,
9843            "info": {"name": "agent", "version": "1.0.0"},
9844            "authMethods": [
9845                {"type": "terminal", "methodId": "bad-args", "name": "Log in", "args": ["auth", "--port", 8123]},
9846                {"type": "terminal", "methodId": "bad-env", "name": "Log in", "env": [{"name": "DEBUG", "value": 1}]},
9847                {"type": "terminal", "methodId": "null-config", "name": "Log in", "args": null, "env": null},
9848                {"type": "agent", "methodId": "agent", "name": "Agent login"}
9849            ]
9850        }))
9851        .unwrap();
9852
9853        assert_eq!(
9854            response.auth_methods,
9855            vec![
9856                AuthMethod::Terminal(AuthMethodTerminal::new("null-config", "Log in")),
9857                AuthMethod::Agent(AuthMethodAgent::new("agent", "Agent login")),
9858            ]
9859        );
9860    }
9861
9862    #[cfg(feature = "unstable_auth_status")]
9863    #[test]
9864    fn auth_status_response_round_trips() {
9865        let response = AuthStatusResponse::new(AuthState::Authenticated).accounts(vec![
9866            AuthAccount::new("Anthropic API key").active(true),
9867            AuthAccount::new("Claude Max").email("user@example.com".to_string()),
9868        ]);
9869        let json = serde_json::to_value(&response).unwrap();
9870        assert_eq!(
9871            json,
9872            json!({
9873                "state": "authenticated",
9874                "accounts": [
9875                    {
9876                        "label": "Anthropic API key",
9877                        "active": true
9878                    },
9879                    {
9880                        "label": "Claude Max",
9881                        "email": "user@example.com"
9882                    }
9883                ]
9884            })
9885        );
9886        assert_eq!(
9887            serde_json::from_value::<AuthStatusResponse>(json).unwrap(),
9888            response
9889        );
9890        assert_eq!(
9891            serde_json::to_value(AuthStatusResponse::new(AuthState::NotRequired)).unwrap(),
9892            json!({"state": "not_required"})
9893        );
9894    }
9895
9896    #[cfg(feature = "unstable_auth_status")]
9897    #[test]
9898    fn auth_status_state_is_strict_and_accounts_are_lenient() {
9899        assert!(serde_json::from_value::<AuthStatusResponse>(json!({})).is_err());
9900        assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": null})).is_err());
9901        assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": 1})).is_err());
9902
9903        let response: AuthStatusResponse = serde_json::from_value(json!({
9904            "state": "_vendor_state",
9905            "accounts": [
9906                {"active": true},
9907                {
9908                    "label": "Vertex AI",
9909                    "active": "yes",
9910                    "email": ["not", "a", "string"]
9911                }
9912            ]
9913        }))
9914        .unwrap();
9915        assert_eq!(
9916            response,
9917            AuthStatusResponse::new(AuthState::Other("_vendor_state".to_string()))
9918                .accounts(vec![AuthAccount::new("Vertex AI")])
9919        );
9920        assert_eq!(
9921            serde_json::from_value::<AuthStatusResponse>(
9922                json!({"state": "unknown", "accounts": null})
9923            )
9924            .unwrap(),
9925            AuthStatusResponse::new(AuthState::Unknown)
9926        );
9927    }
9928
9929    #[cfg(feature = "unstable_auth_status")]
9930    #[test]
9931    fn auth_status_update_notification_routes() {
9932        use crate::v2::{AgentNotification, CLIENT_METHOD_NAMES};
9933
9934        assert_eq!(AGENT_METHOD_NAMES.auth_status, "auth/status");
9935        assert_eq!(CLIENT_METHOD_NAMES.auth_status_update, "auth/status_update");
9936        let notification = AgentNotification::AuthStatusUpdateNotification(Box::new(
9937            AuthStatusUpdateNotification::new(AuthState::Unauthenticated),
9938        ));
9939        assert_eq!(notification.method(), "auth/status_update");
9940        assert_eq!(
9941            serde_json::to_value(&notification).unwrap(),
9942            json!({"state": "unauthenticated"})
9943        );
9944    }
9945}