Skip to main content

agent_client_protocol_schema/v2/
agent.rs

1//! Methods and notifications the agent handles/receives.
2//!
3//! This module defines the Agent trait and all associated types for implementing
4//! an AI coding agent that follows the Agent Client Protocol (ACP).
5
6use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
7
8#[cfg(feature = "unstable_llm_providers")]
9use std::collections::HashMap;
10
11use derive_more::{Display, From};
12#[cfg(feature = "schemars")]
13use schemars::Schema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DefaultOnError, DefaultOnNull, VecSkipError, serde_as, skip_serializing_none};
16
17use super::{
18    AbsolutePath, AvailableCommand, ClientCapabilities, ContentBlock, Error, ExtNotification,
19    ExtRequest, ExtResponse, MessageId, Meta, SessionId, TerminalId, ToolCallId,
20};
21use crate::{IntoOption, ProtocolVersion};
22
23#[cfg(feature = "unstable_session_status")]
24use super::StateUpdate;
25
26#[cfg(all(feature = "unstable_auth_status", feature = "schemars"))]
27use super::AUTH_STATUS_UPDATE_NOTIFICATION;
28#[cfg(feature = "unstable_mcp_over_acp")]
29use super::mcp::{MCP_MESSAGE_METHOD_NAME, MessageMcpNotification};
30
31// Initialize
32
33/// Request parameters for the initialize method.
34///
35/// Sent by the client to establish connection and negotiate capabilities.
36///
37/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
38#[serde_as]
39#[skip_serializing_none]
40#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
42#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
43#[serde(rename_all = "camelCase")]
44#[non_exhaustive]
45pub struct InitializeRequest {
46    /// The latest protocol version supported by the client.
47    pub protocol_version: ProtocolVersion,
48    /// Information about the implementation sending this initialize request.
49    pub info: Implementation,
50    /// Capabilities supported by the client.
51    #[serde_as(deserialize_as = "DefaultOnError")]
52    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
53    #[serde(default)]
54    pub capabilities: ClientCapabilities,
55    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
56    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
57    /// Implementations MUST NOT assign meaning to keys they do not recognize.
58    ///
59    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
60    #[serde_as(deserialize_as = "DefaultOnError")]
61    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
62    #[serde(default)]
63    #[serde(rename = "_meta")]
64    pub meta: Option<Meta>,
65}
66
67impl InitializeRequest {
68    /// Builds [`InitializeRequest`] with the required request fields set. Optional fields start unset or empty.
69    #[must_use]
70    pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
71        Self {
72            protocol_version,
73            capabilities: ClientCapabilities::default(),
74            info,
75            meta: None,
76        }
77    }
78
79    /// Capabilities supported by the client.
80    #[must_use]
81    pub fn capabilities(mut self, capabilities: ClientCapabilities) -> Self {
82        self.capabilities = capabilities;
83        self
84    }
85
86    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
87    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
88    /// do not recognize.
89    ///
90    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
91    #[must_use]
92    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
93        self.meta = meta.into_option();
94        self
95    }
96}
97
98/// Response to the `initialize` method.
99///
100/// Contains the negotiated protocol version and agent capabilities.
101///
102/// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
103#[serde_as]
104#[skip_serializing_none]
105#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
107#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = INITIALIZE_METHOD_NAME)))]
108#[serde(rename_all = "camelCase")]
109#[non_exhaustive]
110pub struct InitializeResponse {
111    /// The highest protocol version supported by the agent that is at or below
112    /// the version requested by the client. If no such version exists,
113    /// initialization fails with error code -32022.
114    ///
115    /// The client must check that it supports this version and should disconnect
116    /// if it does not.
117    pub protocol_version: ProtocolVersion,
118    /// Information about the implementation sending this initialize response.
119    pub info: Implementation,
120    /// Capabilities supported by the agent.
121    #[serde_as(deserialize_as = "DefaultOnError")]
122    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
123    #[serde(default)]
124    pub capabilities: AgentCapabilities,
125    /// Authentication methods supported by the agent.
126    ///
127    /// Optional. Omitted or empty means the agent does not advertise the
128    /// `auth/login` and `auth/logout` methods. Supplying one or more methods means
129    /// the agent MUST support both `auth/login` and `auth/logout`.
130    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
131    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
132    #[serde(default, skip_serializing_if = "Vec::is_empty")]
133    pub auth_methods: Vec<AuthMethod>,
134    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
135    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
136    /// Implementations MUST NOT assign meaning to keys they do not recognize.
137    ///
138    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
139    #[serde_as(deserialize_as = "DefaultOnError")]
140    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
141    #[serde(default)]
142    #[serde(rename = "_meta")]
143    pub meta: Option<Meta>,
144}
145
146impl InitializeResponse {
147    /// Builds [`InitializeResponse`] with the required response fields set. Optional fields start unset or empty.
148    #[must_use]
149    pub fn new(protocol_version: ProtocolVersion, info: Implementation) -> Self {
150        Self {
151            protocol_version,
152            capabilities: AgentCapabilities::default(),
153            auth_methods: vec![],
154            info,
155            meta: None,
156        }
157    }
158
159    /// Capabilities supported by the agent.
160    #[must_use]
161    pub fn capabilities(mut self, capabilities: AgentCapabilities) -> Self {
162        self.capabilities = capabilities;
163        self
164    }
165
166    /// Authentication methods supported by the agent.
167    ///
168    /// Supplying one or more methods means the agent MUST support both
169    /// `auth/login` and `auth/logout`.
170    #[must_use]
171    pub fn auth_methods(mut self, auth_methods: Vec<AuthMethod>) -> Self {
172        self.auth_methods = auth_methods;
173        self
174    }
175
176    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
177    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
178    /// do not recognize.
179    ///
180    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
181    #[must_use]
182    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
183        self.meta = meta.into_option();
184        self
185    }
186}
187
188/// Metadata about the implementation of the client or agent.
189/// Describes the name and version of an ACP implementation, with an optional
190/// title for UI representation.
191#[serde_as]
192#[skip_serializing_none]
193#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
195#[serde(rename_all = "camelCase")]
196#[non_exhaustive]
197pub struct Implementation {
198    /// Intended for programmatic or logical use, but can be used as a display
199    /// name fallback if title isn't present.
200    pub name: String,
201    /// Intended for UI and end-user contexts and optimized to be human-readable
202    /// and easily understood.
203    ///
204    /// If not provided, the name should be used for display.
205    ///
206    /// Omitted and `null` are equivalent and mean the `name` is used for display.
207    #[serde_as(deserialize_as = "DefaultOnError")]
208    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
209    #[serde(default)]
210    pub title: Option<String>,
211    /// Version of the implementation. Can be displayed to the user or used
212    /// for debugging or metrics purposes. (e.g. "1.0.0").
213    pub version: String,
214    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
215    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
216    /// Implementations MUST NOT assign meaning to keys they do not recognize.
217    ///
218    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
219    #[serde_as(deserialize_as = "DefaultOnError")]
220    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
221    #[serde(default)]
222    #[serde(rename = "_meta")]
223    pub meta: Option<Meta>,
224}
225
226impl Implementation {
227    /// Builds [`Implementation`] with the required fields set. Optional fields start unset or empty.
228    #[must_use]
229    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
230        Self {
231            name: name.into(),
232            title: None,
233            version: version.into(),
234            meta: None,
235        }
236    }
237
238    /// Intended for UI and end-user contexts and optimized to be human-readable
239    /// and easily understood.
240    ///
241    /// If not provided, the name should be used for display.
242    #[must_use]
243    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
244        self.title = title.into_option();
245        self
246    }
247
248    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
249    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
250    /// do not recognize.
251    ///
252    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
253    #[must_use]
254    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
255        self.meta = meta.into_option();
256        self
257    }
258}
259
260// Authentication
261
262/// Request parameters for the `auth/login` method.
263///
264/// Specifies which authentication method to use.
265///
266/// Agents MUST support this method when their `initialize` response included
267/// a non-empty `authMethods` list. Clients MUST NOT call this method
268/// when `authMethods` was omitted or empty.
269#[serde_as]
270#[skip_serializing_none]
271#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
272#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
273#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
274#[serde(rename_all = "camelCase")]
275#[non_exhaustive]
276pub struct LoginAuthRequest {
277    /// The ID of the authentication method to use.
278    /// Must be one of the methods advertised in the initialize response.
279    pub method_id: AuthMethodId,
280    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
281    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
282    /// Implementations MUST NOT assign meaning to keys they do not recognize.
283    ///
284    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
285    #[serde_as(deserialize_as = "DefaultOnError")]
286    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
287    #[serde(default)]
288    #[serde(rename = "_meta")]
289    pub meta: Option<Meta>,
290}
291
292impl LoginAuthRequest {
293    /// Builds [`LoginAuthRequest`] with the required request fields set. Optional fields start unset or empty.
294    #[must_use]
295    pub fn new(method_id: impl Into<AuthMethodId>) -> Self {
296        Self {
297            method_id: method_id.into(),
298            meta: None,
299        }
300    }
301
302    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
303    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
304    /// do not recognize.
305    ///
306    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
307    #[must_use]
308    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
309        self.meta = meta.into_option();
310        self
311    }
312}
313
314crate::serde_util::default_on_null! {
315    /// Response to the `auth/login` method.
316    #[serde_as]
317    #[skip_serializing_none]
318    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
319    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
320    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGIN_METHOD_NAME)))]
321    #[serde(rename_all = "camelCase")]
322    #[non_exhaustive]
323    pub struct LoginAuthResponse {
324        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
325        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
326        /// Implementations MUST NOT assign meaning to keys they do not recognize.
327        ///
328        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
329        #[serde_as(deserialize_as = "DefaultOnError")]
330        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
331        #[serde(default)]
332        #[serde(rename = "_meta")]
333        pub meta: Option<Meta>,
334    }
335}
336
337impl LoginAuthResponse {
338    /// Builds [`LoginAuthResponse`] with the required response fields set. Optional fields start unset or empty.
339    #[must_use]
340    pub fn new() -> Self {
341        Self::default()
342    }
343
344    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
345    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
346    /// do not recognize.
347    ///
348    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
349    #[must_use]
350    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
351        self.meta = meta.into_option();
352        self
353    }
354}
355
356// Logout
357
358crate::serde_util::default_on_null! {
359    /// Request parameters for the `auth/logout` method.
360    ///
361    /// Terminates the current authenticated session.
362    ///
363    /// Agents MUST support this method when their `initialize` response included
364    /// a non-empty `authMethods` list. Clients MUST NOT call this method
365    /// when `authMethods` was omitted or empty.
366    #[serde_as]
367    #[skip_serializing_none]
368    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
369    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
370    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
371    #[serde(rename_all = "camelCase")]
372    #[non_exhaustive]
373    pub struct LogoutAuthRequest {
374        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
375        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
376        /// Implementations MUST NOT assign meaning to keys they do not recognize.
377        ///
378        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
379        #[serde_as(deserialize_as = "DefaultOnError")]
380        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
381        #[serde(default)]
382        #[serde(rename = "_meta")]
383        pub meta: Option<Meta>,
384    }
385}
386
387impl LogoutAuthRequest {
388    /// Builds [`LogoutAuthRequest`] with the required request fields set. Optional fields start unset or empty.
389    #[must_use]
390    pub fn new() -> Self {
391        Self::default()
392    }
393
394    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
395    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
396    /// do not recognize.
397    ///
398    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
399    #[must_use]
400    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
401        self.meta = meta.into_option();
402        self
403    }
404}
405
406crate::serde_util::default_on_null! {
407    /// Response to the `auth/logout` method.
408    #[serde_as]
409    #[skip_serializing_none]
410    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
411    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
412    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_LOGOUT_METHOD_NAME)))]
413    #[serde(rename_all = "camelCase")]
414    #[non_exhaustive]
415    pub struct LogoutAuthResponse {
416        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
417        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
418        /// Implementations MUST NOT assign meaning to keys they do not recognize.
419        ///
420        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
421        #[serde_as(deserialize_as = "DefaultOnError")]
422        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
423        #[serde(default)]
424        #[serde(rename = "_meta")]
425        pub meta: Option<Meta>,
426    }
427}
428
429impl LogoutAuthResponse {
430    /// Builds [`LogoutAuthResponse`] with the required response fields set. Optional fields start unset or empty.
431    #[must_use]
432    pub fn new() -> Self {
433        Self::default()
434    }
435
436    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
437    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
438    /// do not recognize.
439    ///
440    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
441    #[must_use]
442    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
443        self.meta = meta.into_option();
444        self
445    }
446}
447
448// Auth status
449
450#[cfg(feature = "unstable_auth_status")]
451crate::serde_util::default_on_null! {
452    /// **UNSTABLE**
453    ///
454    /// This capability is not part of the spec yet, and may be removed or changed at any point.
455    ///
456    /// Request parameters for the `auth/status` method.
457    ///
458    /// Queries the agent's current authentication state without changing it.
459    ///
460    /// Agents MUST NOT reject it with `auth_required` (`-32000`), since Clients call it to learn
461    /// whether authentication is required.
462    #[serde_as]
463    #[skip_serializing_none]
464    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
465    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
466    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
467    #[serde(rename_all = "camelCase")]
468    #[non_exhaustive]
469    pub struct AuthStatusRequest {
470        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
471        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
472        /// Implementations MUST NOT assign meaning to keys they do not recognize.
473        ///
474        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
475        #[serde_as(deserialize_as = "DefaultOnError")]
476        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
477        #[serde(default)]
478        #[serde(rename = "_meta")]
479        pub meta: Option<Meta>,
480    }
481}
482
483#[cfg(feature = "unstable_auth_status")]
484impl AuthStatusRequest {
485    /// Builds [`AuthStatusRequest`] with the required request fields set. Optional fields start unset or empty.
486    #[must_use]
487    pub fn new() -> Self {
488        Self::default()
489    }
490
491    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
492    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
493    /// do not recognize.
494    ///
495    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
496    #[must_use]
497    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
498        self.meta = meta.into_option();
499        self
500    }
501}
502
503/// **UNSTABLE**
504///
505/// This capability is not part of the spec yet, and may be removed or changed at any point.
506///
507/// Response to the `auth/status` method.
508#[cfg(feature = "unstable_auth_status")]
509#[serde_as]
510#[skip_serializing_none]
511#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
513#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = AUTH_STATUS_METHOD_NAME)))]
514#[serde(rename_all = "camelCase")]
515#[non_exhaustive]
516pub struct AuthStatusResponse {
517    /// Whether the agent can start sessions without the user signing in first.
518    ///
519    /// This is the only field clients should act on. Clients MUST treat values they
520    /// do not recognize as `unknown`.
521    pub state: AuthState,
522    /// The accounts the agent has configured, for display.
523    ///
524    /// Optional. Omitted and empty are equivalent and mean no accounts are reported.
525    /// Agents that hold credentials for several providers or accounts SHOULD list each
526    /// of them and mark the ones in effect with `active`.
527    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
528    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
529    #[serde(default, skip_serializing_if = "Vec::is_empty")]
530    pub accounts: Vec<AuthAccount>,
531    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
532    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
533    /// Implementations MUST NOT assign meaning to keys they do not recognize.
534    ///
535    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
536    #[serde_as(deserialize_as = "DefaultOnError")]
537    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
538    #[serde(default)]
539    #[serde(rename = "_meta")]
540    pub meta: Option<Meta>,
541}
542
543#[cfg(feature = "unstable_auth_status")]
544impl AuthStatusResponse {
545    /// Builds [`AuthStatusResponse`] with the required fields set. Optional fields start unset or empty.
546    #[must_use]
547    pub fn new(state: AuthState) -> Self {
548        Self {
549            state,
550            accounts: vec![],
551            meta: None,
552        }
553    }
554
555    /// The accounts the agent has configured, for display.
556    #[must_use]
557    pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
558        self.accounts = accounts;
559        self
560    }
561
562    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
563    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
564    /// do not recognize.
565    ///
566    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
567    #[must_use]
568    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
569        self.meta = meta.into_option();
570        self
571    }
572}
573
574/// **UNSTABLE**
575///
576/// This capability is not part of the spec yet, and may be removed or changed at any point.
577///
578/// Notification sent by the agent when its authentication status changes.
579///
580/// Carries the same fields as the `auth/status` response. Agents that advertise the
581/// `auth.status` capability MAY send it at any time after `initialize`, and SHOULD only
582/// send it when the reported status differs from the last one sent on the connection.
583#[cfg(feature = "unstable_auth_status")]
584#[serde_as]
585#[skip_serializing_none]
586#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
587#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
588#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "client", "x-method" = AUTH_STATUS_UPDATE_NOTIFICATION)))]
589#[serde(rename_all = "camelCase")]
590#[non_exhaustive]
591pub struct AuthStatusUpdateNotification {
592    /// Whether the agent can start sessions without the user signing in first.
593    ///
594    /// This is the only field clients should act on. Clients MUST treat values they
595    /// do not recognize as `unknown`.
596    pub state: AuthState,
597    /// The accounts the agent has configured, for display.
598    ///
599    /// Optional. Omitted and empty are equivalent and mean no accounts are reported.
600    /// Agents that hold credentials for several providers or accounts SHOULD list each
601    /// of them and mark the ones in effect with `active`.
602    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
603    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
604    #[serde(default, skip_serializing_if = "Vec::is_empty")]
605    pub accounts: Vec<AuthAccount>,
606    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
607    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
608    /// Implementations MUST NOT assign meaning to keys they do not recognize.
609    ///
610    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
611    #[serde_as(deserialize_as = "DefaultOnError")]
612    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
613    #[serde(default)]
614    #[serde(rename = "_meta")]
615    pub meta: Option<Meta>,
616}
617
618#[cfg(feature = "unstable_auth_status")]
619impl AuthStatusUpdateNotification {
620    /// Builds [`AuthStatusUpdateNotification`] with the required fields set. Optional fields start unset or empty.
621    #[must_use]
622    pub fn new(state: AuthState) -> Self {
623        Self {
624            state,
625            accounts: vec![],
626            meta: None,
627        }
628    }
629
630    /// The accounts the agent has configured, for display.
631    #[must_use]
632    pub fn accounts(mut self, accounts: Vec<AuthAccount>) -> Self {
633        self.accounts = accounts;
634        self
635    }
636
637    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
638    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
639    /// do not recognize.
640    ///
641    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
642    #[must_use]
643    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
644        self.meta = meta.into_option();
645        self
646    }
647}
648
649/// **UNSTABLE**
650///
651/// This capability is not part of the spec yet, and may be removed or changed at any point.
652///
653/// The agent's overall authentication state.
654#[cfg(feature = "unstable_auth_status")]
655#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
656#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
657#[serde(rename_all = "snake_case")]
658#[non_exhaustive]
659pub enum AuthState {
660    /// Credentials are configured. This does not guarantee that they are valid.
661    Authenticated,
662    /// The user needs to sign in. This includes credentials that are missing,
663    /// expired, or known to be invalid.
664    Unauthenticated,
665    /// No authentication is needed, for example because the agent uses a local model.
666    NotRequired,
667    /// The agent cannot currently determine the state.
668    Unknown,
669    /// Custom or future authentication state.
670    ///
671    /// Values beginning with `_` are reserved for implementation-specific
672    /// extensions. Unknown values that do not begin with `_` are reserved for
673    /// future ACP variants.
674    #[serde(untagged)]
675    Other(String),
676}
677
678/// **UNSTABLE**
679///
680/// This capability is not part of the spec yet, and may be removed or changed at any point.
681///
682/// An account or credential the agent has configured, such as a signed-in user
683/// account or an API key.
684///
685/// All fields are display information only. Clients MUST NOT use them as stable
686/// account identifiers, and SHOULD NOT log them or send them elsewhere, such as to
687/// telemetry.
688#[cfg(feature = "unstable_auth_status")]
689#[serde_as]
690#[skip_serializing_none]
691#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
692#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
693#[serde(rename_all = "camelCase")]
694#[non_exhaustive]
695pub struct AuthAccount {
696    /// Human-readable name for the account, suitable for display on its own,
697    /// for example `Claude Max`, `OpenAI API key`, or `Vertex AI`.
698    pub label: String,
699    /// Whether the agent currently uses this account by default.
700    ///
701    /// Several accounts MAY be active, for example one per provider.
702    ///
703    /// Optional. Omitted and `null` are equivalent and mean the agent does not indicate whether this account is in effect.
704    #[serde_as(deserialize_as = "DefaultOnError")]
705    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
706    #[serde(default)]
707    pub active: Option<bool>,
708    /// Display name of the user, such as a username or full name.
709    ///
710    /// Optional. Omitted and `null` are equivalent and mean no name is reported.
711    #[serde_as(deserialize_as = "DefaultOnError")]
712    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
713    #[serde(default)]
714    pub name: Option<String>,
715    /// Email address of the user.
716    ///
717    /// Optional. Omitted and `null` are equivalent and mean no email is reported.
718    #[serde_as(deserialize_as = "DefaultOnError")]
719    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
720    #[serde(default)]
721    pub email: Option<String>,
722    /// Display name of the organization, team, or workspace the account is using.
723    ///
724    /// Optional. Omitted and `null` are equivalent and mean no organization is reported.
725    #[serde_as(deserialize_as = "DefaultOnError")]
726    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
727    #[serde(default)]
728    pub organization: Option<String>,
729    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
730    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
731    /// Implementations MUST NOT assign meaning to keys they do not recognize.
732    ///
733    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
734    #[serde_as(deserialize_as = "DefaultOnError")]
735    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
736    #[serde(default)]
737    #[serde(rename = "_meta")]
738    pub meta: Option<Meta>,
739}
740
741#[cfg(feature = "unstable_auth_status")]
742impl AuthAccount {
743    /// Builds [`AuthAccount`] with the required fields set. Optional fields start unset or empty.
744    #[must_use]
745    pub fn new(label: impl Into<String>) -> Self {
746        Self {
747            label: label.into(),
748            active: None,
749            name: None,
750            email: None,
751            organization: None,
752            meta: None,
753        }
754    }
755
756    /// Whether the agent currently uses this account by default.
757    #[must_use]
758    pub fn active(mut self, active: impl IntoOption<bool>) -> Self {
759        self.active = active.into_option();
760        self
761    }
762
763    /// Display name of the user, such as a username or full name.
764    #[must_use]
765    pub fn name(mut self, name: impl IntoOption<String>) -> Self {
766        self.name = name.into_option();
767        self
768    }
769
770    /// Email address of the user.
771    #[must_use]
772    pub fn email(mut self, email: impl IntoOption<String>) -> Self {
773        self.email = email.into_option();
774        self
775    }
776
777    /// Display name of the organization, team, or workspace the account is using.
778    #[must_use]
779    pub fn organization(mut self, organization: impl IntoOption<String>) -> Self {
780        self.organization = organization.into_option();
781        self
782    }
783
784    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
785    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
786    /// do not recognize.
787    ///
788    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
789    #[must_use]
790    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
791        self.meta = meta.into_option();
792        self
793    }
794}
795
796/// **UNSTABLE**
797///
798/// This capability is not part of the spec yet, and may be removed or changed at any point.
799///
800/// Auth status capabilities supported by the agent.
801///
802/// Supplying `{}` means the agent supports the `auth/status` method.
803#[cfg(feature = "unstable_auth_status")]
804#[serde_as]
805#[skip_serializing_none]
806#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
807#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
808#[non_exhaustive]
809pub struct AuthStatusCapabilities {
810    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
811    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
812    /// Implementations MUST NOT assign meaning to keys they do not recognize.
813    ///
814    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
815    #[serde_as(deserialize_as = "DefaultOnError")]
816    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
817    #[serde(default)]
818    #[serde(rename = "_meta")]
819    pub meta: Option<Meta>,
820}
821
822#[cfg(feature = "unstable_auth_status")]
823impl AuthStatusCapabilities {
824    /// Builds an empty [`AuthStatusCapabilities`]. Use builder methods to advertise supported sub-capabilities.
825    #[must_use]
826    pub fn new() -> Self {
827        Self::default()
828    }
829
830    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
831    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
832    /// do not recognize.
833    ///
834    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
835    #[must_use]
836    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
837        self.meta = meta.into_option();
838        self
839    }
840}
841
842/// Authentication-related extension capabilities supported by the agent.
843///
844/// This object does not advertise support for `auth/login` or `auth/logout`.
845/// Those methods are advertised by a non-empty `authMethods` list in the
846/// `initialize` response.
847#[serde_as]
848#[skip_serializing_none]
849#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
850#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
851#[serde(rename_all = "camelCase")]
852#[non_exhaustive]
853pub struct AgentAuthCapabilities {
854    /// **UNSTABLE**
855    ///
856    /// This capability is not part of the spec yet, and may be removed or changed at any point.
857    ///
858    /// Whether the agent supports the `auth/status` method.
859    ///
860    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
861    /// Supplying `{}` means the agent supports the `auth/status` method.
862    #[cfg(feature = "unstable_auth_status")]
863    #[serde_as(deserialize_as = "DefaultOnError")]
864    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
865    #[serde(default)]
866    pub status: Option<AuthStatusCapabilities>,
867    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
868    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
869    /// Implementations MUST NOT assign meaning to keys they do not recognize.
870    ///
871    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
872    #[serde_as(deserialize_as = "DefaultOnError")]
873    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
874    #[serde(default)]
875    #[serde(rename = "_meta")]
876    pub meta: Option<Meta>,
877}
878
879impl AgentAuthCapabilities {
880    /// Builds an empty [`AgentAuthCapabilities`].
881    #[must_use]
882    pub fn new() -> Self {
883        Self::default()
884    }
885
886    /// **UNSTABLE**
887    ///
888    /// This capability is not part of the spec yet, and may be removed or changed at any point.
889    ///
890    /// Whether the agent supports the `auth/status` method.
891    #[cfg(feature = "unstable_auth_status")]
892    #[must_use]
893    pub fn status(mut self, status: impl IntoOption<AuthStatusCapabilities>) -> Self {
894        self.status = status.into_option();
895        self
896    }
897
898    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
899    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
900    /// do not recognize.
901    ///
902    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
903    #[must_use]
904    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
905        self.meta = meta.into_option();
906        self
907    }
908}
909
910/// Typed identifier used for auth method values on the wire.
911#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
912#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
913#[serde(transparent)]
914#[from(forward)]
915#[non_exhaustive]
916pub struct AuthMethodId(pub Arc<str>);
917
918impl AuthMethodId {
919    /// Wraps a protocol string as a typed [`AuthMethodId`].
920    #[must_use]
921    pub fn new(id: impl Into<Self>) -> Self {
922        id.into()
923    }
924}
925
926/// Describes an available authentication method.
927///
928/// The `type` field acts as the discriminator in the serialized JSON form.
929#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
930#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
931#[serde(tag = "type", rename_all = "snake_case")]
932#[non_exhaustive]
933pub enum AuthMethod {
934    /// Client runs the configured agent program as a separate interactive
935    /// process, without passing this method to `auth/login`.
936    Terminal(AuthMethodTerminal),
937    /// Agent handles authentication itself through `auth/login`.
938    ///
939    /// The `type` discriminator value is `agent`.
940    Agent(AuthMethodAgent),
941    /// Custom or future authentication method.
942    ///
943    /// Values beginning with `_` are reserved for implementation-specific
944    /// extensions. Unknown values that do not begin with `_` are reserved for
945    /// future ACP variants.
946    ///
947    /// Clients that do not understand this method type should preserve the raw
948    /// payload when storing, replaying, proxying, or forwarding initialization
949    /// data, and otherwise ignore the method or display it generically.
950    #[serde(untagged)]
951    Other(OtherAuthMethod),
952}
953
954impl AuthMethod {
955    /// The unique identifier for this authentication method.
956    #[must_use]
957    pub fn method_id(&self) -> &AuthMethodId {
958        match self {
959            Self::Agent(a) => &a.method_id,
960            Self::Other(a) => &a.method_id,
961            Self::Terminal(t) => &t.method_id,
962        }
963    }
964
965    /// The human-readable name of this authentication method.
966    #[must_use]
967    pub fn name(&self) -> &str {
968        match self {
969            Self::Agent(a) => &a.name,
970            Self::Other(a) => &a.name,
971            Self::Terminal(t) => &t.name,
972        }
973    }
974
975    /// Optional human-readable description of this authentication method.
976    #[must_use]
977    pub fn description(&self) -> Option<&str> {
978        match self {
979            Self::Agent(a) => a.description.as_deref(),
980            Self::Other(a) => a
981                .fields
982                .get("description")
983                .and_then(serde_json::Value::as_str),
984            Self::Terminal(t) => t.description.as_deref(),
985        }
986    }
987
988    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
989    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
990    /// do not recognize.
991    ///
992    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
993    #[must_use]
994    pub fn meta(&self) -> Option<&Meta> {
995        match self {
996            Self::Agent(a) => a.meta.as_ref(),
997            Self::Other(a) => a.fields.get("_meta").and_then(serde_json::Value::as_object),
998            Self::Terminal(t) => t.meta.as_ref(),
999        }
1000    }
1001}
1002
1003/// Custom or future authentication method payload.
1004#[serde_as]
1005#[skip_serializing_none]
1006#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1007#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1008#[cfg_attr(feature = "schemars", schemars(inline))]
1009#[cfg_attr(feature = "schemars", schemars(transform = other_auth_method_schema))]
1010#[serde(rename_all = "camelCase")]
1011#[non_exhaustive]
1012pub struct OtherAuthMethod {
1013    /// Custom or future authentication method type.
1014    ///
1015    /// Values beginning with `_` are reserved for implementation-specific
1016    /// extensions. Unknown values that do not begin with `_` are reserved for
1017    /// future ACP variants.
1018    #[serde(rename = "type")]
1019    pub type_: String,
1020    /// Unique identifier for this authentication method.
1021    pub method_id: AuthMethodId,
1022    /// Human-readable name of the authentication method.
1023    pub name: String,
1024    /// The remaining fields of the unknown authentication method payload, preserved verbatim,
1025    /// including `description` and `_meta`.
1026    #[serde(flatten)]
1027    pub fields: BTreeMap<String, serde_json::Value>,
1028}
1029
1030impl OtherAuthMethod {
1031    /// Builds [`OtherAuthMethod`] from an unknown discriminator and preserves the remaining extension fields.
1032    #[must_use]
1033    pub fn new(
1034        type_: impl Into<String>,
1035        method_id: impl Into<AuthMethodId>,
1036        name: impl Into<String>,
1037        mut fields: BTreeMap<String, serde_json::Value>,
1038    ) -> Self {
1039        fields.remove("type");
1040        fields.remove("methodId");
1041        fields.remove("name");
1042        Self {
1043            type_: type_.into(),
1044            method_id: method_id.into(),
1045            name: name.into(),
1046            fields,
1047        }
1048    }
1049}
1050
1051impl<'de> Deserialize<'de> for OtherAuthMethod {
1052    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1053    where
1054        D: serde::Deserializer<'de>,
1055    {
1056        #[derive(Deserialize)]
1057        #[serde(rename_all = "camelCase")]
1058        struct RawOtherAuthMethod {
1059            #[serde(rename = "type")]
1060            type_: String,
1061            method_id: AuthMethodId,
1062            name: String,
1063            #[serde(flatten)]
1064            fields: BTreeMap<String, serde_json::Value>,
1065        }
1066
1067        let raw = RawOtherAuthMethod::deserialize(deserializer)?;
1068        if is_known_auth_method_type(&raw.type_) {
1069            return Err(serde::de::Error::custom(format!(
1070                "known authentication method `{}` did not match its schema",
1071                raw.type_
1072            )));
1073        }
1074
1075        Ok(Self {
1076            type_: raw.type_,
1077            method_id: raw.method_id,
1078            name: raw.name,
1079            fields: raw.fields,
1080        })
1081    }
1082}
1083
1084fn is_known_auth_method_type(type_: &str) -> bool {
1085    matches!(type_, "agent" | "terminal")
1086}
1087
1088#[cfg(feature = "schemars")]
1089fn other_auth_method_schema(schema: &mut Schema) {
1090    super::schema_util::reject_known_string_discriminators(schema, "type", &["agent", "terminal"]);
1091}
1092
1093/// Agent handles authentication itself through `auth/login`.
1094///
1095/// The `type` discriminator value is `agent`.
1096#[serde_as]
1097#[skip_serializing_none]
1098#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1099#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1100#[serde(rename_all = "camelCase")]
1101#[non_exhaustive]
1102pub struct AuthMethodAgent {
1103    /// Unique identifier for this authentication method.
1104    pub method_id: AuthMethodId,
1105    /// Human-readable name of the authentication method.
1106    pub name: String,
1107    /// Optional human-readable description of this authentication method.
1108    ///
1109    /// Omitted and `null` are equivalent and mean no description is provided.
1110    #[serde_as(deserialize_as = "DefaultOnError")]
1111    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1112    #[serde(default)]
1113    pub description: Option<String>,
1114    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1115    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1116    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1117    ///
1118    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1119    #[serde_as(deserialize_as = "DefaultOnError")]
1120    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1121    #[serde(default)]
1122    #[serde(rename = "_meta")]
1123    pub meta: Option<Meta>,
1124}
1125
1126impl AuthMethodAgent {
1127    /// Builds [`AuthMethodAgent`] with the required fields set. Optional fields start unset or empty.
1128    #[must_use]
1129    pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1130        Self {
1131            method_id: method_id.into(),
1132            name: name.into(),
1133            description: None,
1134            meta: None,
1135        }
1136    }
1137
1138    /// Optional human-readable description of this authentication method.
1139    #[must_use]
1140    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1141        self.description = description.into_option();
1142        self
1143    }
1144
1145    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1146    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1147    /// do not recognize.
1148    ///
1149    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1150    #[must_use]
1151    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1152        self.meta = meta.into_option();
1153        self
1154    }
1155}
1156
1157/// Terminal-based authentication method.
1158///
1159/// The client runs the configured agent program as a separate interactive
1160/// process for the user to authenticate via a TUI. Agents MUST advertise this
1161/// method only when the client enabled its terminal authentication capability.
1162/// A zero exit status signals success. Any other termination signals failure.
1163/// The client MUST NOT pass this method to `auth/login`.
1164#[serde_as]
1165#[skip_serializing_none]
1166#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1168#[serde(rename_all = "camelCase")]
1169#[non_exhaustive]
1170pub struct AuthMethodTerminal {
1171    /// Unique identifier for this authentication method.
1172    pub method_id: AuthMethodId,
1173    /// Human-readable name of the authentication method.
1174    pub name: String,
1175    /// Optional human-readable description of this authentication method.
1176    ///
1177    /// Omitted and `null` are equivalent and mean no description is provided.
1178    #[serde_as(deserialize_as = "DefaultOnError")]
1179    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1180    #[serde(default)]
1181    pub description: Option<String>,
1182    /// Additional arguments to append to the configured agent invocation for terminal auth.
1183    #[serde_as(deserialize_as = "DefaultOnNull")]
1184    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1185    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1186    pub args: Vec<String>,
1187    /// Additional environment variables to set on the configured agent invocation for terminal auth.
1188    /// Names MUST be unique. These values override same-named variables in the
1189    /// base launch configuration.
1190    #[serde_as(deserialize_as = "DefaultOnNull")]
1191    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1192    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1193    pub env: Vec<EnvVariable>,
1194    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1195    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1196    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1197    ///
1198    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1199    #[serde_as(deserialize_as = "DefaultOnError")]
1200    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1201    #[serde(default)]
1202    #[serde(rename = "_meta")]
1203    pub meta: Option<Meta>,
1204}
1205
1206impl AuthMethodTerminal {
1207    /// Builds [`AuthMethodTerminal`] with the required fields set. Optional fields start unset or empty.
1208    #[must_use]
1209    pub fn new(method_id: impl Into<AuthMethodId>, name: impl Into<String>) -> Self {
1210        Self {
1211            method_id: method_id.into(),
1212            name: name.into(),
1213            description: None,
1214            args: Vec::new(),
1215            env: Vec::new(),
1216            meta: None,
1217        }
1218    }
1219
1220    /// Additional arguments to append to the configured agent invocation for terminal auth.
1221    #[must_use]
1222    pub fn args(mut self, args: Vec<String>) -> Self {
1223        self.args = args;
1224        self
1225    }
1226
1227    /// Additional environment variables to set on the configured agent invocation for terminal auth.
1228    /// Names MUST be unique. These values override same-named variables in the
1229    /// base launch configuration.
1230    #[must_use]
1231    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
1232        self.env = env;
1233        self
1234    }
1235
1236    /// Optional human-readable description of this authentication method.
1237    #[must_use]
1238    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1239        self.description = description.into_option();
1240        self
1241    }
1242
1243    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1244    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1245    /// do not recognize.
1246    ///
1247    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1248    #[must_use]
1249    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1250        self.meta = meta.into_option();
1251        self
1252    }
1253}
1254
1255// New session
1256
1257/// Request parameters for creating a new session.
1258///
1259/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/v2/session-setup#creating-a-session)
1260#[serde_as]
1261#[skip_serializing_none]
1262#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1264#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1265#[serde(rename_all = "camelCase")]
1266#[non_exhaustive]
1267pub struct NewSessionRequest {
1268    /// The working directory for this session. Must be an absolute path.
1269    pub cwd: AbsolutePath,
1270    /// Additional workspace roots for this session. Each path must be absolute.
1271    ///
1272    /// These expand the session's workspace scope without changing `cwd`, which
1273    /// remains the base for relative paths. When omitted or empty, no
1274    /// additional roots are activated for the new session.
1275    #[serde_as(deserialize_as = "DefaultOnNull")]
1276    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1277    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1278    pub additional_directories: Vec<AbsolutePath>,
1279    /// List of MCP (Model Context Protocol) servers the agent should connect to.
1280    #[serde_as(deserialize_as = "DefaultOnNull")]
1281    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1282    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1283    pub mcp_servers: Vec<McpServer>,
1284    /// **UNSTABLE**
1285    ///
1286    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1287    ///
1288    /// System-level instructions for the new session.
1289    ///
1290    /// Optional. Omitted and `null` are equivalent and mean the Agent uses its
1291    /// own default instructions. Clients MUST only send this when the Agent
1292    /// advertises `session.systemPrompt`.
1293    #[cfg(feature = "unstable_session_system_prompt")]
1294    #[serde(default)]
1295    pub system_prompt: Option<SystemPrompt>,
1296    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1297    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1298    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1299    ///
1300    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1301    #[serde_as(deserialize_as = "DefaultOnError")]
1302    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1303    #[serde(default)]
1304    #[serde(rename = "_meta")]
1305    pub meta: Option<Meta>,
1306}
1307
1308impl NewSessionRequest {
1309    /// Builds [`NewSessionRequest`] with the required request fields set. Optional fields start unset or empty.
1310    #[must_use]
1311    pub fn new(cwd: impl Into<AbsolutePath>) -> Self {
1312        Self {
1313            cwd: cwd.into(),
1314            additional_directories: vec![],
1315            mcp_servers: vec![],
1316            #[cfg(feature = "unstable_session_system_prompt")]
1317            system_prompt: None,
1318            meta: None,
1319        }
1320    }
1321
1322    /// Additional workspace roots for this session. Each path must be absolute.
1323    #[must_use]
1324    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1325    where
1326        I: IntoIterator<Item = P>,
1327        P: Into<AbsolutePath>,
1328    {
1329        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1330        self
1331    }
1332
1333    /// List of MCP (Model Context Protocol) servers the agent should connect to.
1334    #[must_use]
1335    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1336        self.mcp_servers = mcp_servers;
1337        self
1338    }
1339
1340    /// **UNSTABLE**
1341    ///
1342    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1343    ///
1344    /// System-level instructions for the new session.
1345    #[cfg(feature = "unstable_session_system_prompt")]
1346    #[must_use]
1347    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1348        self.system_prompt = system_prompt.into_option();
1349        self
1350    }
1351
1352    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1353    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1354    /// do not recognize.
1355    ///
1356    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1357    #[must_use]
1358    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1359        self.meta = meta.into_option();
1360        self
1361    }
1362}
1363
1364/// Response from creating a new session.
1365///
1366/// New sessions default to `idle` until a state update reports otherwise.
1367/// Updates may arrive before or after this response. Clients MUST NOT overwrite
1368/// an already-reported state when the response arrives.
1369///
1370/// See protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/v2/session-setup#creating-a-session)
1371#[serde_as]
1372#[skip_serializing_none]
1373#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1374#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1375#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_NEW_METHOD_NAME)))]
1376#[serde(rename_all = "camelCase")]
1377#[non_exhaustive]
1378pub struct NewSessionResponse {
1379    /// Unique identifier for the created session.
1380    ///
1381    /// Used in all subsequent requests for this conversation.
1382    pub session_id: SessionId,
1383    /// Initial session configuration options.
1384    ///
1385    /// Optional. Omitted or empty means no config options are advertised.
1386    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1387    /// Later `config_option_update` notifications replace this list.
1388    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1389    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1390    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1391    pub config_options: Vec<SessionConfigOption>,
1392    /// Initial commands the agent can execute in this session.
1393    ///
1394    /// Optional. Omitted or empty means no initial commands are advertised.
1395    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1396    /// Later `available_commands_update` notifications replace this list.
1397    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1398    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1399    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1400    pub available_commands: Vec<AvailableCommand>,
1401    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1402    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1403    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1404    ///
1405    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1406    #[serde_as(deserialize_as = "DefaultOnError")]
1407    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1408    #[serde(default)]
1409    #[serde(rename = "_meta")]
1410    pub meta: Option<Meta>,
1411}
1412
1413impl NewSessionResponse {
1414    /// Builds [`NewSessionResponse`] with the required response fields set. Optional fields start unset or empty.
1415    #[must_use]
1416    pub fn new(session_id: impl Into<SessionId>) -> Self {
1417        Self {
1418            session_id: session_id.into(),
1419            config_options: Vec::new(),
1420            available_commands: Vec::new(),
1421            meta: None,
1422        }
1423    }
1424
1425    /// Initial session configuration options.
1426    ///
1427    /// Optional. Omitted or empty means no config options are advertised.
1428    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1429    /// Later `config_option_update` notifications replace this list.
1430    #[must_use]
1431    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1432        self.config_options = config_options;
1433        self
1434    }
1435
1436    /// Initial commands the agent can execute in this session.
1437    #[must_use]
1438    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1439        self.available_commands = available_commands;
1440        self
1441    }
1442
1443    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1444    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1445    /// do not recognize.
1446    ///
1447    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1448    #[must_use]
1449    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1450        self.meta = meta.into_option();
1451        self
1452    }
1453}
1454
1455// Fork session
1456
1457/// **UNSTABLE**
1458///
1459/// This capability is not part of the spec yet, and may be removed or changed at any point.
1460///
1461/// Request parameters for forking an existing session.
1462///
1463/// Creates a new session based on the context of an existing one, allowing
1464/// operations like generating summaries without affecting the original session's history.
1465///
1466/// Only available if the Agent supports the `session.fork` capability.
1467#[cfg(feature = "unstable_session_fork")]
1468#[serde_as]
1469#[skip_serializing_none]
1470#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1471#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1472#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1473#[serde(rename_all = "camelCase")]
1474#[non_exhaustive]
1475pub struct ForkSessionRequest {
1476    /// The ID of the session to fork.
1477    pub session_id: SessionId,
1478    /// The working directory for this session. Must be an absolute path.
1479    pub cwd: AbsolutePath,
1480    /// Additional workspace roots to activate for this session. Each path must be absolute.
1481    ///
1482    /// When omitted or empty, no additional roots are activated. When non-empty,
1483    /// this is the complete resulting additional-root list for the forked
1484    /// 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 additional_directories: Vec<AbsolutePath>,
1489    /// List of MCP servers to connect to for this session.
1490    #[serde_as(deserialize_as = "DefaultOnNull")]
1491    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1492    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1493    pub mcp_servers: Vec<McpServer>,
1494    /// **UNSTABLE**
1495    ///
1496    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1497    ///
1498    /// System-level instructions for the forked session.
1499    ///
1500    /// Optional. Omitted and `null` are equivalent. The Agent may then keep a
1501    /// system prompt it stored with the source session or use its default
1502    /// instructions. Clients MUST only send this when the Agent advertises
1503    /// `session.systemPrompt`, and SHOULD send the same value they used for the
1504    /// source session.
1505    #[cfg(feature = "unstable_session_system_prompt")]
1506    #[serde(default)]
1507    pub system_prompt: Option<SystemPrompt>,
1508    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1509    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1510    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1511    ///
1512    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1513    #[serde_as(deserialize_as = "DefaultOnError")]
1514    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1515    #[serde(default)]
1516    #[serde(rename = "_meta")]
1517    pub meta: Option<Meta>,
1518}
1519
1520#[cfg(feature = "unstable_session_fork")]
1521impl ForkSessionRequest {
1522    /// Builds [`ForkSessionRequest`] with the required request fields set. Optional fields start unset or empty.
1523    #[must_use]
1524    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1525        Self {
1526            session_id: session_id.into(),
1527            cwd: cwd.into(),
1528            additional_directories: vec![],
1529            mcp_servers: vec![],
1530            #[cfg(feature = "unstable_session_system_prompt")]
1531            system_prompt: None,
1532            meta: None,
1533        }
1534    }
1535
1536    /// Additional workspace roots to activate for this session. Each path must be absolute.
1537    #[must_use]
1538    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1539    where
1540        I: IntoIterator<Item = P>,
1541        P: Into<AbsolutePath>,
1542    {
1543        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1544        self
1545    }
1546
1547    /// List of MCP servers to connect to for this session.
1548    #[must_use]
1549    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1550        self.mcp_servers = mcp_servers;
1551        self
1552    }
1553
1554    /// **UNSTABLE**
1555    ///
1556    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1557    ///
1558    /// System-level instructions for the forked session.
1559    #[cfg(feature = "unstable_session_system_prompt")]
1560    #[must_use]
1561    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1562        self.system_prompt = system_prompt.into_option();
1563        self
1564    }
1565
1566    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1567    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1568    /// do not recognize.
1569    ///
1570    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1571    #[must_use]
1572    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1573        self.meta = meta.into_option();
1574        self
1575    }
1576}
1577
1578/// **UNSTABLE**
1579///
1580/// This capability is not part of the spec yet, and may be removed or changed at any point.
1581///
1582/// Response from forking an existing session.
1583#[cfg(feature = "unstable_session_fork")]
1584#[serde_as]
1585#[skip_serializing_none]
1586#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1587#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1588#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_FORK_METHOD_NAME)))]
1589#[serde(rename_all = "camelCase")]
1590#[non_exhaustive]
1591pub struct ForkSessionResponse {
1592    /// Unique identifier for the newly created forked session.
1593    pub session_id: SessionId,
1594    /// Initial session configuration options.
1595    ///
1596    /// Optional. Omitted or empty means no config options are advertised.
1597    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1598    /// Later `config_option_update` notifications replace this list.
1599    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1600    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1601    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1602    pub config_options: Vec<SessionConfigOption>,
1603    /// Initial commands the agent can execute in this session.
1604    ///
1605    /// Optional. Omitted or empty means no initial commands are advertised.
1606    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1607    /// Later `available_commands_update` notifications replace this list.
1608    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1609    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1610    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1611    pub available_commands: Vec<AvailableCommand>,
1612    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1613    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1614    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1615    ///
1616    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1617    #[serde_as(deserialize_as = "DefaultOnError")]
1618    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1619    #[serde(default)]
1620    #[serde(rename = "_meta")]
1621    pub meta: Option<Meta>,
1622}
1623
1624#[cfg(feature = "unstable_session_fork")]
1625impl ForkSessionResponse {
1626    /// Builds [`ForkSessionResponse`] with the required response fields set. Optional fields start unset or empty.
1627    #[must_use]
1628    pub fn new(session_id: impl Into<SessionId>) -> Self {
1629        Self {
1630            session_id: session_id.into(),
1631            config_options: Vec::new(),
1632            available_commands: Vec::new(),
1633            meta: None,
1634        }
1635    }
1636
1637    /// Initial session configuration options.
1638    ///
1639    /// Optional. Omitted or empty means no config options are advertised.
1640    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1641    /// Later `config_option_update` notifications replace this list.
1642    #[must_use]
1643    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
1644        self.config_options = config_options;
1645        self
1646    }
1647
1648    /// Initial commands the agent can execute in this session.
1649    #[must_use]
1650    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
1651        self.available_commands = available_commands;
1652        self
1653    }
1654
1655    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1656    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1657    /// do not recognize.
1658    ///
1659    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1660    #[must_use]
1661    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1662        self.meta = meta.into_option();
1663        self
1664    }
1665}
1666
1667// Resume session
1668
1669/// Request parameters for resuming an existing session.
1670///
1671/// Resumes an existing session and optionally replays retained conversation
1672/// history according to `replayFrom`.
1673#[serde_as]
1674#[skip_serializing_none]
1675#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1676#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1677#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1678#[serde(rename_all = "camelCase")]
1679#[non_exhaustive]
1680pub struct ResumeSessionRequest {
1681    /// The ID of the session to resume.
1682    pub session_id: SessionId,
1683    /// The working directory for this session. Must be an absolute path.
1684    ///
1685    /// Clients MUST send the session's working directory. Agents MAY reject a different directory
1686    /// with `-32602`, and MAY normalize paths when comparing.
1687    pub cwd: AbsolutePath,
1688    /// Additional workspace roots to activate for this session. Each path must be absolute.
1689    ///
1690    /// When omitted or empty, no additional roots are activated. When non-empty,
1691    /// this is the complete resulting additional-root list for the resumed
1692    /// session. It may differ from any previously used or reported list as long as
1693    /// the request `cwd` matches the session's `cwd`.
1694    #[serde_as(deserialize_as = "DefaultOnNull")]
1695    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1696    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1697    pub additional_directories: Vec<AbsolutePath>,
1698    /// List of MCP servers to connect to for this session.
1699    ///
1700    /// The complete set of Client-provided servers. Omitted and `[]` mean none. Agents SHOULD
1701    /// NOT restore servers from earlier requests.
1702    #[serde_as(deserialize_as = "DefaultOnNull")]
1703    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
1704    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1705    pub mcp_servers: Vec<McpServer>,
1706    /// Inclusive cursor describing where conversation replay should begin.
1707    ///
1708    /// Optional. Omitted and `null` are equivalent and mean the Agent should resume without
1709    /// replaying previous conversation history. Replay cursors are inclusive:
1710    /// replay includes the position identified by the cursor. Supplying
1711    /// `{ "type": "start" }` means the Agent should replay all retained
1712    /// conversation history before responding.
1713    ///
1714    /// Agents MUST reject unsupported cursor types or malformed cursor values
1715    /// with `-32602` (Invalid params), rather than substituting another cursor
1716    /// or resuming without replay.
1717    #[serde(default)]
1718    pub replay_from: Option<ReplayFrom>,
1719    /// **UNSTABLE**
1720    ///
1721    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1722    ///
1723    /// System-level instructions for the resumed session.
1724    ///
1725    /// Optional. Omitted and `null` are equivalent. The Agent may then keep a
1726    /// system prompt it stored with the session or use its default instructions.
1727    /// Clients MUST only send this when the Agent advertises
1728    /// `session.systemPrompt`, and SHOULD send the same value they used when the
1729    /// session was created.
1730    #[cfg(feature = "unstable_session_system_prompt")]
1731    #[serde(default)]
1732    pub system_prompt: Option<SystemPrompt>,
1733    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1734    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1735    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1736    ///
1737    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1738    #[serde_as(deserialize_as = "DefaultOnError")]
1739    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1740    #[serde(default)]
1741    #[serde(rename = "_meta")]
1742    pub meta: Option<Meta>,
1743}
1744
1745impl ResumeSessionRequest {
1746    /// Builds [`ResumeSessionRequest`] with the required request fields set. Optional fields start unset or empty.
1747    #[must_use]
1748    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
1749        Self {
1750            session_id: session_id.into(),
1751            cwd: cwd.into(),
1752            additional_directories: vec![],
1753            mcp_servers: vec![],
1754            replay_from: None,
1755            #[cfg(feature = "unstable_session_system_prompt")]
1756            system_prompt: None,
1757            meta: None,
1758        }
1759    }
1760
1761    /// Additional workspace roots to activate for this session. Each path must be absolute.
1762    #[must_use]
1763    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
1764    where
1765        I: IntoIterator<Item = P>,
1766        P: Into<AbsolutePath>,
1767    {
1768        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
1769        self
1770    }
1771
1772    /// List of MCP servers to connect to for this session.
1773    #[must_use]
1774    pub fn mcp_servers(mut self, mcp_servers: Vec<McpServer>) -> Self {
1775        self.mcp_servers = mcp_servers;
1776        self
1777    }
1778
1779    /// Inclusive cursor describing where conversation replay should begin.
1780    ///
1781    /// Omitted and `null` are equivalent and mean the Agent should resume without replaying
1782    /// previous conversation history. Replay cursors are inclusive: replay
1783    /// includes the position identified by the cursor. Supplying
1784    /// `{ "type": "start" }` means the Agent should replay all retained
1785    /// conversation history before responding.
1786    #[must_use]
1787    pub fn replay_from(mut self, replay_from: impl IntoOption<ReplayFrom>) -> Self {
1788        self.replay_from = replay_from.into_option();
1789        self
1790    }
1791
1792    /// **UNSTABLE**
1793    ///
1794    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1795    ///
1796    /// System-level instructions for the resumed session.
1797    #[cfg(feature = "unstable_session_system_prompt")]
1798    #[must_use]
1799    pub fn system_prompt(mut self, system_prompt: impl IntoOption<SystemPrompt>) -> Self {
1800        self.system_prompt = system_prompt.into_option();
1801        self
1802    }
1803
1804    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1805    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1806    /// do not recognize.
1807    ///
1808    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1809    #[must_use]
1810    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1811        self.meta = meta.into_option();
1812        self
1813    }
1814}
1815
1816/// Inclusive cursor describing where replayed session history should begin.
1817///
1818/// Replay includes the position identified by the cursor.
1819#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1820#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1821#[serde(tag = "type", rename_all = "snake_case")]
1822#[non_exhaustive]
1823pub enum ReplayFrom {
1824    /// Replay all retained conversation history from its first replayable entry.
1825    Start(ReplayFromStart),
1826    /// Custom or future replay cursor.
1827    ///
1828    /// Values beginning with `_` are reserved for implementation-specific
1829    /// extensions. Unknown values that do not begin with `_` are reserved for
1830    /// future ACP variants.
1831    ///
1832    /// Intermediaries SHOULD preserve the raw payload when storing, replaying,
1833    /// proxying, or forwarding requests. Agents that do not support this cursor
1834    /// MUST reject the request with `-32602` (Invalid params), rather than
1835    /// substituting another cursor or resuming without replay.
1836    #[serde(untagged)]
1837    Other(OtherReplayFrom),
1838}
1839
1840impl From<ReplayFromStart> for ReplayFrom {
1841    fn from(replay_from: ReplayFromStart) -> Self {
1842        Self::Start(replay_from)
1843    }
1844}
1845
1846/// Inclusive replay cursor requesting replay from the start of retained conversation history.
1847#[serde_as]
1848#[skip_serializing_none]
1849#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1850#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1851#[serde(rename_all = "camelCase")]
1852#[non_exhaustive]
1853pub struct ReplayFromStart {
1854    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1855    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1856    /// Implementations MUST NOT assign meaning to keys they do not recognize.
1857    ///
1858    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1859    #[serde_as(deserialize_as = "DefaultOnError")]
1860    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1861    #[serde(default)]
1862    #[serde(rename = "_meta")]
1863    pub meta: Option<Meta>,
1864}
1865
1866impl ReplayFromStart {
1867    /// Builds [`ReplayFromStart`].
1868    #[must_use]
1869    pub fn new() -> Self {
1870        Self::default()
1871    }
1872
1873    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1874    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
1875    /// do not recognize.
1876    ///
1877    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1878    #[must_use]
1879    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1880        self.meta = meta.into_option();
1881        self
1882    }
1883}
1884
1885/// Custom or future replay cursor payload.
1886#[serde_as]
1887#[skip_serializing_none]
1888#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1889#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
1890#[cfg_attr(feature = "schemars", schemars(inline))]
1891#[cfg_attr(feature = "schemars", schemars(transform = other_replay_from_schema))]
1892#[serde(rename_all = "camelCase")]
1893#[non_exhaustive]
1894pub struct OtherReplayFrom {
1895    /// Custom or future replay cursor type.
1896    ///
1897    /// Values beginning with `_` are reserved for implementation-specific
1898    /// extensions. Unknown values that do not begin with `_` are reserved for
1899    /// future ACP variants.
1900    #[serde(rename = "type")]
1901    pub type_: String,
1902    /// The remaining fields of the unknown replay cursor payload, preserved verbatim,
1903    /// including `_meta`.
1904    #[serde(flatten)]
1905    pub fields: BTreeMap<String, serde_json::Value>,
1906}
1907
1908impl OtherReplayFrom {
1909    /// Builds [`OtherReplayFrom`] from an unknown discriminator and preserves the remaining extension fields.
1910    #[must_use]
1911    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1912        fields.remove("type");
1913        Self {
1914            type_: type_.into(),
1915            fields,
1916        }
1917    }
1918}
1919
1920impl<'de> Deserialize<'de> for OtherReplayFrom {
1921    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1922    where
1923        D: serde::Deserializer<'de>,
1924    {
1925        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1926        let type_ = fields
1927            .remove("type")
1928            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1929        let serde_json::Value::String(type_) = type_ else {
1930            return Err(serde::de::Error::custom("`type` must be a string"));
1931        };
1932
1933        if is_known_replay_from_type(&type_) {
1934            return Err(serde::de::Error::custom(format!(
1935                "known replay cursor `{type_}` did not match its schema"
1936            )));
1937        }
1938
1939        Ok(Self { type_, fields })
1940    }
1941}
1942
1943fn is_known_replay_from_type(type_: &str) -> bool {
1944    matches!(type_, "start")
1945}
1946
1947#[cfg(feature = "schemars")]
1948fn other_replay_from_schema(schema: &mut Schema) {
1949    super::schema_util::reject_known_string_discriminators(schema, "type", &["start"]);
1950}
1951
1952crate::serde_util::default_on_null! {
1953    /// Response from resuming an existing session.
1954    ///
1955    /// The agent MUST send a current `state_update` after any requested history
1956    /// replay and before this response, even without replay. The update reports
1957    /// foreground state after resuming. The response alone does not imply `idle`.
1958    ///
1959    /// See protocol docs: [Current-State Synchronization](https://agentclientprotocol.com/protocol/v2/session-setup#current-state-synchronization)
1960    #[serde_as]
1961    #[skip_serializing_none]
1962    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1963    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
1964    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_RESUME_METHOD_NAME)))]
1965    #[serde(rename_all = "camelCase")]
1966    #[non_exhaustive]
1967    pub struct ResumeSessionResponse {
1968        /// Initial session configuration options.
1969        ///
1970        /// Optional. Omitted or empty means no config options are advertised.
1971        /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1972        /// Later `config_option_update` notifications replace this list.
1973        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1974        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1975        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1976        pub config_options: Vec<SessionConfigOption>,
1977        /// Initial commands the agent can execute in this session.
1978        ///
1979        /// Optional. Omitted or empty means no initial commands are advertised.
1980        /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
1981        /// Later `available_commands_update` notifications replace this list.
1982        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
1983        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
1984        #[serde(default, skip_serializing_if = "Vec::is_empty")]
1985        pub available_commands: Vec<AvailableCommand>,
1986        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1987        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
1988        /// Implementations MUST NOT assign meaning to keys they do not recognize.
1989        ///
1990        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
1991        #[serde_as(deserialize_as = "DefaultOnError")]
1992        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
1993        #[serde(default)]
1994        #[serde(rename = "_meta")]
1995        pub meta: Option<Meta>,
1996    }
1997}
1998
1999impl ResumeSessionResponse {
2000    /// Builds [`ResumeSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2001    #[must_use]
2002    pub fn new() -> Self {
2003        Self::default()
2004    }
2005
2006    /// Initial session configuration options.
2007    ///
2008    /// Optional. Omitted or empty means no config options are advertised.
2009    /// Senders MUST use an array, not `null`. Receivers treat `null` like omission.
2010    /// Later `config_option_update` notifications replace this list.
2011    #[must_use]
2012    pub fn config_options(mut self, config_options: Vec<SessionConfigOption>) -> Self {
2013        self.config_options = config_options;
2014        self
2015    }
2016
2017    /// Initial commands the agent can execute in this session.
2018    #[must_use]
2019    pub fn available_commands(mut self, available_commands: Vec<AvailableCommand>) -> Self {
2020        self.available_commands = available_commands;
2021        self
2022    }
2023
2024    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2025    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2026    /// do not recognize.
2027    ///
2028    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2029    #[must_use]
2030    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2031        self.meta = meta.into_option();
2032        self
2033    }
2034}
2035
2036// Close session
2037
2038/// Request parameters for closing an active session.
2039///
2040/// The Agent MUST cancel any ongoing work related to the session as if `session/cancel`
2041/// was called, then free the resources associated with the session. Unlike `session/cancel`,
2042/// closing also stops the session's background work. After the close response, the Agent
2043/// sends no other messages for the session on that connection.
2044///
2045/// Closing a session that does not exist or is not active SHOULD succeed, and Clients SHOULD
2046/// treat a `-32002` response as a successful close.
2047///
2048/// When closing cancels active foreground work, the Agent MUST send the final
2049/// updates for that work and the idle `state_update` with `stopReason: "cancelled"`
2050/// before responding successfully to `session/close`. Closing an idle session
2051/// does not itself require another idle update.
2052///
2053/// Closing also cancels pending `session/prompt` requests whose messages have not
2054/// been inserted, including held prompts. If closing prevents a prompt's user message from being inserted, the
2055/// Agent MUST answer that request with `-32800` (Request Cancelled). If the message
2056/// has already been inserted, the Agent MUST respond successfully with its
2057/// `messageId`. Prompt responses need not precede the close response.
2058#[serde_as]
2059#[skip_serializing_none]
2060#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2061#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2062#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2063#[serde(rename_all = "camelCase")]
2064#[non_exhaustive]
2065pub struct CloseSessionRequest {
2066    /// The ID of the session to close.
2067    pub session_id: SessionId,
2068    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2069    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2070    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2071    ///
2072    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2073    #[serde_as(deserialize_as = "DefaultOnError")]
2074    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2075    #[serde(default)]
2076    #[serde(rename = "_meta")]
2077    pub meta: Option<Meta>,
2078}
2079
2080impl CloseSessionRequest {
2081    /// Builds [`CloseSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2082    #[must_use]
2083    pub fn new(session_id: impl Into<SessionId>) -> Self {
2084        Self {
2085            session_id: session_id.into(),
2086            meta: None,
2087        }
2088    }
2089
2090    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2091    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2092    /// do not recognize.
2093    ///
2094    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2095    #[must_use]
2096    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2097        self.meta = meta.into_option();
2098        self
2099    }
2100}
2101
2102crate::serde_util::default_on_null! {
2103    /// Response from closing a session.
2104    #[serde_as]
2105    #[skip_serializing_none]
2106    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2107    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2108    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CLOSE_METHOD_NAME)))]
2109    #[serde(rename_all = "camelCase")]
2110    #[non_exhaustive]
2111    pub struct CloseSessionResponse {
2112        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2113        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2114        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2115        ///
2116        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2117        #[serde_as(deserialize_as = "DefaultOnError")]
2118        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2119        #[serde(default)]
2120        #[serde(rename = "_meta")]
2121        pub meta: Option<Meta>,
2122    }
2123}
2124
2125impl CloseSessionResponse {
2126    /// Builds [`CloseSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2127    #[must_use]
2128    pub fn new() -> Self {
2129        Self::default()
2130    }
2131
2132    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2133    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2134    /// do not recognize.
2135    ///
2136    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2137    #[must_use]
2138    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2139        self.meta = meta.into_option();
2140        self
2141    }
2142}
2143
2144// Session status
2145
2146/// **UNSTABLE**
2147///
2148/// This capability is not part of the spec yet, and may be removed or changed at any point.
2149///
2150/// Request parameters for checking the status of a session.
2151///
2152/// Only available if the Agent supports the `session.status` capability.
2153///
2154/// `session/status` MUST NOT have side effects. The Agent does not load, resume, create,
2155/// close, or change the session, and does not attach the requesting connection to it.
2156/// A session the Agent does not know gets `-32002` with the requested `sessionId` in `data`.
2157#[cfg(feature = "unstable_session_status")]
2158#[serde_as]
2159#[skip_serializing_none]
2160#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2162#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2163#[serde(rename_all = "camelCase")]
2164#[non_exhaustive]
2165pub struct SessionStatusRequest {
2166    /// The ID of the session to check.
2167    pub session_id: SessionId,
2168    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2169    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2170    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2171    ///
2172    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2173    #[serde_as(deserialize_as = "DefaultOnError")]
2174    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2175    #[serde(default)]
2176    #[serde(rename = "_meta")]
2177    pub meta: Option<Meta>,
2178}
2179
2180#[cfg(feature = "unstable_session_status")]
2181impl SessionStatusRequest {
2182    /// Builds [`SessionStatusRequest`] with the required request fields set. Optional fields start unset or empty.
2183    #[must_use]
2184    pub fn new(session_id: impl Into<SessionId>) -> Self {
2185        Self {
2186            session_id: session_id.into(),
2187            meta: None,
2188        }
2189    }
2190
2191    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2192    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2193    /// do not recognize.
2194    ///
2195    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2196    #[must_use]
2197    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2198        self.meta = meta.into_option();
2199        self
2200    }
2201}
2202
2203/// **UNSTABLE**
2204///
2205/// This capability is not part of the spec yet, and may be removed or changed at any point.
2206///
2207/// Response from checking the status of a session.
2208///
2209/// The result is a snapshot. A session reported `open` may be closed or evicted before
2210/// the Client's next request.
2211#[cfg(feature = "unstable_session_status")]
2212#[serde_as]
2213#[skip_serializing_none]
2214#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2216#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_STATUS_METHOD_NAME)))]
2217#[serde(rename_all = "camelCase")]
2218#[non_exhaustive]
2219pub struct SessionStatusResponse {
2220    /// Whether the Agent has the session in memory.
2221    ///
2222    /// Required. Clients MUST treat a value they do not recognize as `saved`.
2223    pub status: SessionStatus,
2224    /// The session's current foreground state, as the same snapshot the Agent sends in
2225    /// `state_update` notifications.
2226    ///
2227    /// Optional. Agents SHOULD include it when `status` is `open`. Omitted, `null`, and
2228    /// malformed values mean the Agent is not reporting foreground state, and Clients
2229    /// MUST NOT read that as `idle`. This is a snapshot, not a new transition. Clients
2230    /// MUST NOT count an `idle` snapshot as a new completion.
2231    #[serde_as(deserialize_as = "DefaultOnError")]
2232    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2233    #[serde(default)]
2234    pub state: Option<StateUpdate>,
2235    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2236    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2237    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2238    ///
2239    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2240    #[serde_as(deserialize_as = "DefaultOnError")]
2241    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2242    #[serde(default)]
2243    #[serde(rename = "_meta")]
2244    pub meta: Option<Meta>,
2245}
2246
2247#[cfg(feature = "unstable_session_status")]
2248impl SessionStatusResponse {
2249    /// Builds [`SessionStatusResponse`] with the required response fields set. Optional fields start unset or empty.
2250    #[must_use]
2251    pub fn new(status: SessionStatus) -> Self {
2252        Self {
2253            status,
2254            state: None,
2255            meta: None,
2256        }
2257    }
2258
2259    /// The session's current foreground state, as the same snapshot the Agent sends in
2260    /// `state_update` notifications.
2261    #[must_use]
2262    pub fn state(mut self, state: impl IntoOption<StateUpdate>) -> Self {
2263        self.state = state.into_option();
2264        self
2265    }
2266
2267    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2268    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2269    /// do not recognize.
2270    ///
2271    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2272    #[must_use]
2273    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2274        self.meta = meta.into_option();
2275        self
2276    }
2277}
2278
2279/// **UNSTABLE**
2280///
2281/// This capability is not part of the spec yet, and may be removed or changed at any point.
2282///
2283/// Whether the Agent has a session in memory, as reported by `session/status`.
2284#[cfg(feature = "unstable_session_status")]
2285#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2286#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2287#[serde(rename_all = "snake_case")]
2288#[non_exhaustive]
2289pub enum SessionStatus {
2290    /// The Agent has the session in memory, for any connection.
2291    ///
2292    /// This does not mean the requesting connection is attached to it. A Client that did
2293    /// not create or resume the session on its own connection MUST still call
2294    /// `session/resume` before sending `session/prompt` or expecting
2295    /// `session/update` notifications.
2296    Open,
2297    /// The Agent knows the session and can resume it, but does not have it in memory.
2298    ///
2299    /// The Client can call `session/resume` to start it.
2300    Saved,
2301    /// Custom or future session status.
2302    ///
2303    /// Values beginning with `_` are reserved for implementation-specific
2304    /// extensions. Unknown values that do not begin with `_` are reserved for
2305    /// future ACP variants. Clients MUST treat a value they do not recognize as
2306    /// `saved`.
2307    #[serde(untagged)]
2308    Other(String),
2309}
2310
2311// Cancel tool call
2312
2313/// Request parameters for cancelling one tool call.
2314///
2315/// Clients MUST NOT send it unless the tool call currently advertises `capabilities.cancel`.
2316///
2317/// The Agent responds once it has accepted the cancellation. The response does not mean
2318/// the work has stopped. The Agent confirms it with a `tool_call_update` to a final
2319/// status: normally `cancelled`, or `completed` or `failed` if the work finished first.
2320///
2321/// The Agent responds with `-32602` if the tool call is unknown, already final, or not
2322/// cancellable. Cancelling a tool call does not change the foreground state by itself.
2323///
2324/// See protocol docs: [Cancelling Tool Calls](https://agentclientprotocol.com/protocol/v2/tool-calls#cancelling-tool-calls)
2325#[serde_as]
2326#[skip_serializing_none]
2327#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2328#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2329#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2330#[serde(rename_all = "camelCase")]
2331#[non_exhaustive]
2332pub struct CancelToolCallRequest {
2333    /// The ID of the session that owns the tool call.
2334    pub session_id: SessionId,
2335    /// The ID of the tool call to cancel.
2336    pub tool_call_id: ToolCallId,
2337    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2338    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2339    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2340    ///
2341    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2342    #[serde_as(deserialize_as = "DefaultOnError")]
2343    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2344    #[serde(default)]
2345    #[serde(rename = "_meta")]
2346    pub meta: Option<Meta>,
2347}
2348
2349impl CancelToolCallRequest {
2350    /// Builds [`CancelToolCallRequest`] with the required request fields set. Optional fields start unset or empty.
2351    #[must_use]
2352    pub fn new(session_id: impl Into<SessionId>, tool_call_id: impl Into<ToolCallId>) -> Self {
2353        Self {
2354            session_id: session_id.into(),
2355            tool_call_id: tool_call_id.into(),
2356            meta: None,
2357        }
2358    }
2359
2360    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2361    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2362    /// do not recognize.
2363    ///
2364    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2365    #[must_use]
2366    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2367        self.meta = meta.into_option();
2368        self
2369    }
2370}
2371
2372crate::serde_util::default_on_null! {
2373    /// Response from cancelling a tool call.
2374    #[serde_as]
2375    #[skip_serializing_none]
2376    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2377    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2378    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TOOL_CALL_CANCEL_METHOD_NAME)))]
2379    #[serde(rename_all = "camelCase")]
2380    #[non_exhaustive]
2381    pub struct CancelToolCallResponse {
2382        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2383        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2384        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2385        ///
2386        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2387        #[serde_as(deserialize_as = "DefaultOnError")]
2388        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2389        #[serde(default)]
2390        #[serde(rename = "_meta")]
2391        pub meta: Option<Meta>,
2392    }
2393}
2394
2395impl CancelToolCallResponse {
2396    /// Builds [`CancelToolCallResponse`] with the required response fields set. Optional fields start unset or empty.
2397    #[must_use]
2398    pub fn new() -> Self {
2399        Self::default()
2400    }
2401
2402    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2403    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2404    /// do not recognize.
2405    ///
2406    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2407    #[must_use]
2408    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2409        self.meta = meta.into_option();
2410        self
2411    }
2412}
2413
2414// Cancel terminal
2415
2416/// Request parameters for cancelling one terminal.
2417///
2418/// Clients MUST NOT send it unless the terminal currently advertises `capabilities.cancel`.
2419///
2420/// The Agent responds once it has accepted the cancellation. The response does not mean
2421/// the work has stopped. The Agent confirms it with a `terminal_update` that sets
2422/// `exitStatus`. Cancelling stops the process but does not release the terminal, and does
2423/// not change the status of a tool call that references it.
2424///
2425/// The Agent responds with `-32602` if the terminal is unknown, has already exited, or is not
2426/// cancellable. Cancelling a terminal does not change the foreground state by itself.
2427///
2428/// See protocol docs: [Cancelling Terminals](https://agentclientprotocol.com/protocol/v2/tool-calls#cancelling-terminals)
2429#[serde_as]
2430#[skip_serializing_none]
2431#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2432#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2433#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2434#[serde(rename_all = "camelCase")]
2435#[non_exhaustive]
2436pub struct CancelTerminalRequest {
2437    /// The ID of the session that owns the terminal.
2438    pub session_id: SessionId,
2439    /// The ID of the terminal to cancel.
2440    pub terminal_id: TerminalId,
2441    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2442    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2443    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2444    ///
2445    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2446    #[serde_as(deserialize_as = "DefaultOnError")]
2447    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2448    #[serde(default)]
2449    #[serde(rename = "_meta")]
2450    pub meta: Option<Meta>,
2451}
2452
2453impl CancelTerminalRequest {
2454    /// Builds [`CancelTerminalRequest`] with the required request fields set. Optional fields start unset or empty.
2455    #[must_use]
2456    pub fn new(session_id: impl Into<SessionId>, terminal_id: impl Into<TerminalId>) -> Self {
2457        Self {
2458            session_id: session_id.into(),
2459            terminal_id: terminal_id.into(),
2460            meta: None,
2461        }
2462    }
2463
2464    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2465    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2466    /// do not recognize.
2467    ///
2468    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2469    #[must_use]
2470    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2471        self.meta = meta.into_option();
2472        self
2473    }
2474}
2475
2476crate::serde_util::default_on_null! {
2477    /// Response from cancelling a terminal.
2478    #[serde_as]
2479    #[skip_serializing_none]
2480    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2481    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2482    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = TERMINAL_CANCEL_METHOD_NAME)))]
2483    #[serde(rename_all = "camelCase")]
2484    #[non_exhaustive]
2485    pub struct CancelTerminalResponse {
2486        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2487        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2488        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2489        ///
2490        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2491        #[serde_as(deserialize_as = "DefaultOnError")]
2492        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2493        #[serde(default)]
2494        #[serde(rename = "_meta")]
2495        pub meta: Option<Meta>,
2496    }
2497}
2498
2499impl CancelTerminalResponse {
2500    /// Builds [`CancelTerminalResponse`] with the required response fields set. Optional fields start unset or empty.
2501    #[must_use]
2502    pub fn new() -> Self {
2503        Self::default()
2504    }
2505
2506    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2507    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2508    /// do not recognize.
2509    ///
2510    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2511    #[must_use]
2512    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2513        self.meta = meta.into_option();
2514        self
2515    }
2516}
2517
2518// List sessions
2519
2520/// An opaque cursor used to paginate `session/list` results.
2521#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2522#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
2523#[serde(transparent)]
2524#[from(Arc<str>, String, &str, &mut str, Box<str>, Cow<'_, str>)]
2525#[non_exhaustive]
2526pub struct SessionListCursor(pub Arc<str>);
2527
2528impl SessionListCursor {
2529    /// Wraps a protocol string as a typed [`SessionListCursor`].
2530    #[must_use]
2531    pub fn new(cursor: impl Into<Self>) -> Self {
2532        cursor.into()
2533    }
2534}
2535
2536impl AsRef<str> for SessionListCursor {
2537    fn as_ref(&self) -> &str {
2538        &self.0
2539    }
2540}
2541
2542impl From<&String> for SessionListCursor {
2543    fn from(cursor: &String) -> Self {
2544        Self(cursor.as_str().into())
2545    }
2546}
2547
2548macro_rules! impl_session_list_cursor_option_conversion {
2549    ($source:ty) => {
2550        impl IntoOption<SessionListCursor> for $source {
2551            fn into_option(self) -> Option<SessionListCursor> {
2552                Some(self.into())
2553            }
2554        }
2555    };
2556}
2557
2558impl_session_list_cursor_option_conversion!(Arc<str>);
2559impl_session_list_cursor_option_conversion!(String);
2560impl_session_list_cursor_option_conversion!(&str);
2561impl_session_list_cursor_option_conversion!(&mut str);
2562impl_session_list_cursor_option_conversion!(&String);
2563impl_session_list_cursor_option_conversion!(Box<str>);
2564impl_session_list_cursor_option_conversion!(Cow<'_, str>);
2565
2566crate::serde_util::default_on_null! {
2567    /// Request parameters for listing existing sessions.
2568    #[serde_as]
2569    #[skip_serializing_none]
2570    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2571    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2572    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2573    #[serde(rename_all = "camelCase")]
2574    #[non_exhaustive]
2575    pub struct ListSessionsRequest {
2576        /// Filter sessions by working directory. Must be an absolute path.
2577        ///
2578        /// Only sessions whose `cwd` is this same directory match. Subdirectories and
2579        /// `additionalDirectories` don't. Agents MAY normalize paths when comparing.
2580        ///
2581        /// Omitted and `null` are equivalent and mean sessions are not filtered by working directory.
2582        #[serde(default)]
2583        pub cwd: Option<AbsolutePath>,
2584        /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
2585        ///
2586        /// Omitted and `null` are equivalent and mean the first page is requested.
2587        #[serde(default)]
2588        pub cursor: Option<SessionListCursor>,
2589        /// **UNSTABLE**
2590        ///
2591        /// This capability is not part of the spec yet, and may be removed or changed at any point.
2592        ///
2593        /// Filter sessions by archive state.
2594        ///
2595        /// Optional. Omitted and `null` are equivalent and mean `exclude`. Only send this when
2596        /// the Agent advertises the `session.archive` capability. The filter combines with `cwd` and
2597        /// applies before pagination. Clients keep the same value when following `nextCursor`.
2598        #[cfg(feature = "unstable_session_archive")]
2599        #[serde(default)]
2600        pub archived: Option<SessionArchiveFilter>,
2601        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2602        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2603        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2604        ///
2605        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2606        #[serde_as(deserialize_as = "DefaultOnError")]
2607        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2608        #[serde(default)]
2609        #[serde(rename = "_meta")]
2610        pub meta: Option<Meta>,
2611    }
2612}
2613
2614impl ListSessionsRequest {
2615    /// Builds [`ListSessionsRequest`] with the required request fields set. Optional fields start unset or empty.
2616    #[must_use]
2617    pub fn new() -> Self {
2618        Self::default()
2619    }
2620
2621    /// Filter sessions by working directory. Must be an absolute path.
2622    #[must_use]
2623    pub fn cwd(mut self, cwd: impl IntoOption<AbsolutePath>) -> Self {
2624        self.cwd = cwd.into_option();
2625        self
2626    }
2627
2628    /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination
2629    #[must_use]
2630    pub fn cursor(mut self, cursor: impl IntoOption<SessionListCursor>) -> Self {
2631        self.cursor = cursor.into_option();
2632        self
2633    }
2634
2635    /// **UNSTABLE**
2636    ///
2637    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2638    ///
2639    /// Filter sessions by archive state.
2640    #[cfg(feature = "unstable_session_archive")]
2641    #[must_use]
2642    pub fn archived(mut self, archived: impl IntoOption<SessionArchiveFilter>) -> Self {
2643        self.archived = archived.into_option();
2644        self
2645    }
2646
2647    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2648    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2649    /// do not recognize.
2650    ///
2651    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2652    #[must_use]
2653    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2654        self.meta = meta.into_option();
2655        self
2656    }
2657}
2658
2659/// Response from listing sessions.
2660#[serde_as]
2661#[skip_serializing_none]
2662#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2663#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2664#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_LIST_METHOD_NAME)))]
2665#[serde(rename_all = "camelCase")]
2666#[non_exhaustive]
2667pub struct ListSessionsResponse {
2668    /// Array of session information objects.
2669    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
2670    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
2671    pub sessions: Vec<SessionInfo>,
2672    /// Opaque cursor token. If present, pass this in the next request's cursor parameter
2673    /// to fetch the next page. Omitted and `null` are equivalent and mean there are no more
2674    /// results. An empty string is a valid cursor, not the end of results.
2675    #[serde(default)]
2676    pub next_cursor: Option<SessionListCursor>,
2677    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2678    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2679    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2680    ///
2681    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2682    #[serde_as(deserialize_as = "DefaultOnError")]
2683    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2684    #[serde(default)]
2685    #[serde(rename = "_meta")]
2686    pub meta: Option<Meta>,
2687}
2688
2689impl ListSessionsResponse {
2690    /// Builds [`ListSessionsResponse`] with the required response fields set. Optional fields start unset or empty.
2691    #[must_use]
2692    pub fn new(sessions: Vec<SessionInfo>) -> Self {
2693        Self {
2694            sessions,
2695            next_cursor: None,
2696            meta: None,
2697        }
2698    }
2699
2700    /// Sets or clears the optional `nextCursor` field.
2701    #[must_use]
2702    pub fn next_cursor(mut self, next_cursor: impl IntoOption<SessionListCursor>) -> Self {
2703        self.next_cursor = next_cursor.into_option();
2704        self
2705    }
2706
2707    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2708    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2709    /// do not recognize.
2710    ///
2711    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2712    #[must_use]
2713    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2714        self.meta = meta.into_option();
2715        self
2716    }
2717}
2718
2719// Delete session
2720
2721/// Request parameters for deleting an existing session from `session/list`.
2722///
2723/// Only available if the Agent supports the `session.delete` capability.
2724#[serde_as]
2725#[skip_serializing_none]
2726#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2727#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2728#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2729#[serde(rename_all = "camelCase")]
2730#[non_exhaustive]
2731pub struct DeleteSessionRequest {
2732    /// The ID of the session to delete.
2733    pub session_id: SessionId,
2734    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2735    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2736    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2737    ///
2738    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2739    #[serde_as(deserialize_as = "DefaultOnError")]
2740    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2741    #[serde(default)]
2742    #[serde(rename = "_meta")]
2743    pub meta: Option<Meta>,
2744}
2745
2746impl DeleteSessionRequest {
2747    /// Builds [`DeleteSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2748    #[must_use]
2749    pub fn new(session_id: impl Into<SessionId>) -> Self {
2750        Self {
2751            session_id: session_id.into(),
2752            meta: None,
2753        }
2754    }
2755
2756    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2757    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2758    /// do not recognize.
2759    ///
2760    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2761    #[must_use]
2762    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2763        self.meta = meta.into_option();
2764        self
2765    }
2766}
2767
2768crate::serde_util::default_on_null! {
2769    /// Response from deleting a session.
2770    #[serde_as]
2771    #[skip_serializing_none]
2772    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2773    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2774    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_DELETE_METHOD_NAME)))]
2775    #[serde(rename_all = "camelCase")]
2776    #[non_exhaustive]
2777    pub struct DeleteSessionResponse {
2778        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2779        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2780        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2781        ///
2782        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2783        #[serde_as(deserialize_as = "DefaultOnError")]
2784        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2785        #[serde(default)]
2786        #[serde(rename = "_meta")]
2787        pub meta: Option<Meta>,
2788    }
2789}
2790
2791impl DeleteSessionResponse {
2792    /// Builds [`DeleteSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2793    #[must_use]
2794    pub fn new() -> Self {
2795        Self::default()
2796    }
2797
2798    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2799    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2800    /// do not recognize.
2801    ///
2802    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2803    #[must_use]
2804    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2805        self.meta = meta.into_option();
2806        self
2807    }
2808}
2809
2810// Archive session
2811
2812/// **UNSTABLE**
2813///
2814/// This capability is not part of the spec yet, and may be removed or changed at any point.
2815///
2816/// Request parameters for archiving a session.
2817///
2818/// Only available if the Agent supports the `session.archive` capability.
2819///
2820/// Archiving hides the session from default `session/list` results. It keeps the session ID
2821/// and saved history. If the session is active, the Agent MUST cancel any ongoing work and
2822/// free the session's resources as if `session/close` had been called, then archive it.
2823/// Archiving an archived session succeeds. A session that does not exist gets `-32002`.
2824///
2825#[cfg(feature = "unstable_session_archive")]
2826#[serde_as]
2827#[skip_serializing_none]
2828#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2829#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2830#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2831#[serde(rename_all = "camelCase")]
2832#[non_exhaustive]
2833pub struct ArchiveSessionRequest {
2834    /// The ID of the session to archive.
2835    pub session_id: SessionId,
2836    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2837    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2838    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2839    ///
2840    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2841    #[serde_as(deserialize_as = "DefaultOnError")]
2842    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2843    #[serde(default)]
2844    #[serde(rename = "_meta")]
2845    pub meta: Option<Meta>,
2846}
2847
2848#[cfg(feature = "unstable_session_archive")]
2849impl ArchiveSessionRequest {
2850    /// Builds [`ArchiveSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2851    #[must_use]
2852    pub fn new(session_id: impl Into<SessionId>) -> Self {
2853        Self {
2854            session_id: session_id.into(),
2855            meta: None,
2856        }
2857    }
2858
2859    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2860    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2861    /// do not recognize.
2862    ///
2863    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2864    #[must_use]
2865    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2866        self.meta = meta.into_option();
2867        self
2868    }
2869}
2870
2871#[cfg(feature = "unstable_session_archive")]
2872crate::serde_util::default_on_null! {
2873    /// **UNSTABLE**
2874    ///
2875    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2876    ///
2877    /// Response from archiving a session.
2878    #[serde_as]
2879    #[skip_serializing_none]
2880    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2881    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2882    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_ARCHIVE_METHOD_NAME)))]
2883    #[serde(rename_all = "camelCase")]
2884    #[non_exhaustive]
2885    pub struct ArchiveSessionResponse {
2886        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2887        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2888        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2889        ///
2890        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2891        #[serde_as(deserialize_as = "DefaultOnError")]
2892        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2893        #[serde(default)]
2894        #[serde(rename = "_meta")]
2895        pub meta: Option<Meta>,
2896    }
2897}
2898
2899#[cfg(feature = "unstable_session_archive")]
2900impl ArchiveSessionResponse {
2901    /// Builds [`ArchiveSessionResponse`] with the required response fields set. Optional fields start unset or empty.
2902    #[must_use]
2903    pub fn new() -> Self {
2904        Self::default()
2905    }
2906
2907    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2908    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2909    /// do not recognize.
2910    ///
2911    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2912    #[must_use]
2913    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2914        self.meta = meta.into_option();
2915        self
2916    }
2917}
2918
2919/// **UNSTABLE**
2920///
2921/// This capability is not part of the spec yet, and may be removed or changed at any point.
2922///
2923/// Request parameters for unarchiving a session.
2924///
2925/// Only available if the Agent supports the `session.archive` capability.
2926///
2927/// Unarchiving returns the session to default `session/list` results with the same ID and
2928/// saved history. It never loads or resumes the session. Clients that want to continue an
2929/// archived session SHOULD unarchive it before calling `session/resume`.
2930/// Unarchiving a session that is not archived succeeds. A session that does not exist
2931/// gets `-32002`.
2932///
2933#[cfg(feature = "unstable_session_archive")]
2934#[serde_as]
2935#[skip_serializing_none]
2936#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2937#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2938#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2939#[serde(rename_all = "camelCase")]
2940#[non_exhaustive]
2941pub struct UnarchiveSessionRequest {
2942    /// The ID of the session to unarchive.
2943    pub session_id: SessionId,
2944    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2945    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2946    /// Implementations MUST NOT assign meaning to keys they do not recognize.
2947    ///
2948    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2949    #[serde_as(deserialize_as = "DefaultOnError")]
2950    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
2951    #[serde(default)]
2952    #[serde(rename = "_meta")]
2953    pub meta: Option<Meta>,
2954}
2955
2956#[cfg(feature = "unstable_session_archive")]
2957impl UnarchiveSessionRequest {
2958    /// Builds [`UnarchiveSessionRequest`] with the required request fields set. Optional fields start unset or empty.
2959    #[must_use]
2960    pub fn new(session_id: impl Into<SessionId>) -> Self {
2961        Self {
2962            session_id: session_id.into(),
2963            meta: None,
2964        }
2965    }
2966
2967    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2968    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
2969    /// do not recognize.
2970    ///
2971    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2972    #[must_use]
2973    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2974        self.meta = meta.into_option();
2975        self
2976    }
2977}
2978
2979#[cfg(feature = "unstable_session_archive")]
2980crate::serde_util::default_on_null! {
2981    /// **UNSTABLE**
2982    ///
2983    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2984    ///
2985    /// Response from unarchiving a session.
2986    #[serde_as]
2987    #[skip_serializing_none]
2988    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2989    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
2990    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_UNARCHIVE_METHOD_NAME)))]
2991    #[serde(rename_all = "camelCase")]
2992    #[non_exhaustive]
2993    pub struct UnarchiveSessionResponse {
2994        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2995        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
2996        /// Implementations MUST NOT assign meaning to keys they do not recognize.
2997        ///
2998        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
2999        #[serde_as(deserialize_as = "DefaultOnError")]
3000        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3001        #[serde(default)]
3002        #[serde(rename = "_meta")]
3003        pub meta: Option<Meta>,
3004    }
3005}
3006
3007#[cfg(feature = "unstable_session_archive")]
3008impl UnarchiveSessionResponse {
3009    /// Builds [`UnarchiveSessionResponse`] with the required response fields set. Optional fields start unset or empty.
3010    #[must_use]
3011    pub fn new() -> Self {
3012        Self::default()
3013    }
3014
3015    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3016    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3017    /// do not recognize.
3018    ///
3019    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3020    #[must_use]
3021    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3022        self.meta = meta.into_option();
3023        self
3024    }
3025}
3026
3027/// **UNSTABLE**
3028///
3029/// This capability is not part of the spec yet, and may be removed or changed at any point.
3030///
3031/// Which sessions `session/list` returns based on their archive state.
3032///
3033/// Unknown values are preserved so they can be stored, proxied, or forwarded. An Agent MUST
3034/// return `-32602` for a value it does not support rather than treating it as `exclude`.
3035#[cfg(feature = "unstable_session_archive")]
3036#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3037#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
3038#[serde(rename_all = "snake_case")]
3039#[non_exhaustive]
3040pub enum SessionArchiveFilter {
3041    /// Only sessions that are not archived. This is the default.
3042    #[default]
3043    Exclude,
3044    /// Both archived sessions and sessions that are not archived.
3045    Include,
3046    /// Only archived sessions.
3047    Only,
3048    /// Custom or future archive filter.
3049    ///
3050    /// Values beginning with `_` are reserved for implementation-specific
3051    /// extensions. Unknown values that do not begin with `_` are reserved for
3052    /// future ACP variants.
3053    #[serde(untagged)]
3054    Other(String),
3055}
3056
3057/// Information about a session returned by session/list
3058#[serde_as]
3059#[skip_serializing_none]
3060#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3061#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3062#[serde(rename_all = "camelCase")]
3063#[non_exhaustive]
3064pub struct SessionInfo {
3065    /// Unique identifier for the session
3066    pub session_id: SessionId,
3067    /// The working directory for this session. Must be an absolute path.
3068    pub cwd: AbsolutePath,
3069    /// Additional workspace roots reported for this session. Each path must be absolute.
3070    ///
3071    /// When present, this is the complete ordered additional-root list reported
3072    /// by the Agent. Omitted and empty values are equivalent: the response
3073    /// reports no additional roots.
3074    #[serde_as(deserialize_as = "DefaultOnNull")]
3075    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
3076    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3077    pub additional_directories: Vec<AbsolutePath>,
3078
3079    /// Human-readable title for the session
3080    ///
3081    /// Omitted and `null` are equivalent and mean no title is provided.
3082    #[serde_as(deserialize_as = "DefaultOnError")]
3083    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3084    #[serde(default)]
3085    pub title: Option<String>,
3086    /// RFC 3339 timestamp of last activity.
3087    ///
3088    /// Omitted and `null` are equivalent and mean the last activity time is not reported.
3089    #[serde_as(deserialize_as = "DefaultOnError")]
3090    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time")))]
3091    #[serde(default)]
3092    pub updated_at: Option<String>,
3093    /// **UNSTABLE**
3094    ///
3095    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3096    ///
3097    /// Whether the session is archived.
3098    ///
3099    /// Optional. Omitted and `null` are equivalent and mean the archive state is not reported.
3100    /// Agents that advertise the `session.archive` capability MUST include it on every listed session.
3101    #[cfg(feature = "unstable_session_archive")]
3102    #[serde_as(deserialize_as = "DefaultOnError")]
3103    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3104    #[serde(default)]
3105    pub archived: Option<bool>,
3106    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3107    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3108    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3109    ///
3110    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3111    #[serde_as(deserialize_as = "DefaultOnError")]
3112    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3113    #[serde(default)]
3114    #[serde(rename = "_meta")]
3115    pub meta: Option<Meta>,
3116}
3117
3118impl SessionInfo {
3119    /// Builds [`SessionInfo`] with the required fields set. Optional fields start unset or empty.
3120    #[must_use]
3121    pub fn new(session_id: impl Into<SessionId>, cwd: impl Into<AbsolutePath>) -> Self {
3122        Self {
3123            session_id: session_id.into(),
3124            cwd: cwd.into(),
3125            additional_directories: vec![],
3126            title: None,
3127            updated_at: None,
3128            #[cfg(feature = "unstable_session_archive")]
3129            archived: None,
3130            meta: None,
3131        }
3132    }
3133
3134    /// Additional workspace roots reported for this session. Each path must be absolute.
3135    #[must_use]
3136    pub fn additional_directories<I, P>(mut self, additional_directories: I) -> Self
3137    where
3138        I: IntoIterator<Item = P>,
3139        P: Into<AbsolutePath>,
3140    {
3141        self.additional_directories = additional_directories.into_iter().map(Into::into).collect();
3142        self
3143    }
3144
3145    /// Human-readable title for the session
3146    #[must_use]
3147    pub fn title(mut self, title: impl IntoOption<String>) -> Self {
3148        self.title = title.into_option();
3149        self
3150    }
3151
3152    /// RFC 3339 timestamp of last activity.
3153    #[must_use]
3154    pub fn updated_at(mut self, updated_at: impl IntoOption<String>) -> Self {
3155        self.updated_at = updated_at.into_option();
3156        self
3157    }
3158
3159    /// **UNSTABLE**
3160    ///
3161    /// This capability is not part of the spec yet, and may be removed or changed at any point.
3162    ///
3163    /// Whether the session is archived.
3164    #[cfg(feature = "unstable_session_archive")]
3165    #[must_use]
3166    pub fn archived(mut self, archived: impl IntoOption<bool>) -> Self {
3167        self.archived = archived.into_option();
3168        self
3169    }
3170
3171    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3172    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3173    /// do not recognize.
3174    ///
3175    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3176    #[must_use]
3177    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3178        self.meta = meta.into_option();
3179        self
3180    }
3181}
3182
3183// Set session title
3184
3185/// **UNSTABLE**
3186///
3187/// This capability is not part of the spec yet, and may be removed or changed at any point.
3188///
3189/// Request parameters for setting the title of a session.
3190///
3191/// Only available if the Agent supports the `session.title` capability.
3192///
3193/// The session does not need to be active on the connection. Any session the Agent would
3194/// return from `session/list` can be retitled. A session that does not exist gets `-32002`.
3195///
3196/// The Agent responds only after it has stored the title. After a successful response, the
3197/// Agent SHOULD NOT replace the title with one it generates automatically.
3198#[cfg(feature = "unstable_session_set_title")]
3199#[serde_as]
3200#[skip_serializing_none]
3201#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3202#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3203#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3204#[serde(rename_all = "camelCase")]
3205#[non_exhaustive]
3206pub struct SetSessionTitleRequest {
3207    /// The ID of the session to retitle.
3208    pub session_id: SessionId,
3209    /// The new title for the session.
3210    ///
3211    /// Required and non-null. Omission and explicit `null` are both invalid. The Agent MAY
3212    /// normalize the title before storing it, for example by trimming whitespace or
3213    /// truncating it. The Agent rejects a title that fails its validation, including one
3214    /// that is empty after normalization, with `-32602`.
3215    pub title: String,
3216    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3217    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3218    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3219    ///
3220    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3221    #[serde_as(deserialize_as = "DefaultOnError")]
3222    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3223    #[serde(default)]
3224    #[serde(rename = "_meta")]
3225    pub meta: Option<Meta>,
3226}
3227
3228#[cfg(feature = "unstable_session_set_title")]
3229impl SetSessionTitleRequest {
3230    /// Builds [`SetSessionTitleRequest`] with the required request fields set. Optional fields start unset or empty.
3231    #[must_use]
3232    pub fn new(session_id: impl Into<SessionId>, title: impl Into<String>) -> Self {
3233        Self {
3234            session_id: session_id.into(),
3235            title: title.into(),
3236            meta: None,
3237        }
3238    }
3239
3240    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3241    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3242    /// do not recognize.
3243    ///
3244    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3245    #[must_use]
3246    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3247        self.meta = meta.into_option();
3248        self
3249    }
3250}
3251
3252/// **UNSTABLE**
3253///
3254/// This capability is not part of the spec yet, and may be removed or changed at any point.
3255///
3256/// Response from setting the title of a session.
3257#[cfg(feature = "unstable_session_set_title")]
3258#[serde_as]
3259#[skip_serializing_none]
3260#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3261#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3262#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_TITLE_METHOD_NAME)))]
3263#[serde(rename_all = "camelCase")]
3264#[non_exhaustive]
3265pub struct SetSessionTitleResponse {
3266    /// The title the Agent stored.
3267    ///
3268    /// This differs from the requested title if the Agent normalized it. Clients SHOULD
3269    /// display this value rather than the one they sent.
3270    pub title: String,
3271    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3272    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3273    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3274    ///
3275    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3276    #[serde_as(deserialize_as = "DefaultOnError")]
3277    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3278    #[serde(default)]
3279    #[serde(rename = "_meta")]
3280    pub meta: Option<Meta>,
3281}
3282
3283#[cfg(feature = "unstable_session_set_title")]
3284impl SetSessionTitleResponse {
3285    /// Builds [`SetSessionTitleResponse`] with the required response fields set. Optional fields start unset or empty.
3286    #[must_use]
3287    pub fn new(title: impl Into<String>) -> Self {
3288        Self {
3289            title: title.into(),
3290            meta: None,
3291        }
3292    }
3293
3294    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3295    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3296    /// do not recognize.
3297    ///
3298    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3299    #[must_use]
3300    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3301        self.meta = meta.into_option();
3302        self
3303    }
3304}
3305
3306// Session config options
3307
3308/// Unique identifier for a session configuration option.
3309#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3310#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3311#[serde(transparent)]
3312#[from(forward)]
3313#[non_exhaustive]
3314pub struct SessionConfigId(pub Arc<str>);
3315
3316impl SessionConfigId {
3317    /// Wraps a protocol string as a typed [`SessionConfigId`].
3318    #[must_use]
3319    pub fn new(id: impl Into<Self>) -> Self {
3320        id.into()
3321    }
3322}
3323
3324/// Unique identifier for a session configuration option value.
3325#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3326#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3327#[serde(transparent)]
3328#[from(forward)]
3329#[non_exhaustive]
3330pub struct SessionConfigValueId(pub Arc<str>);
3331
3332impl SessionConfigValueId {
3333    /// Wraps a protocol string as a typed [`SessionConfigValueId`].
3334    #[must_use]
3335    pub fn new(id: impl Into<Self>) -> Self {
3336        id.into()
3337    }
3338}
3339
3340/// Unique identifier for a session configuration option value group.
3341#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3342#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, From, Display)]
3343#[serde(transparent)]
3344#[from(forward)]
3345#[non_exhaustive]
3346pub struct SessionConfigGroupId(pub Arc<str>);
3347
3348impl SessionConfigGroupId {
3349    /// Wraps a protocol string as a typed [`SessionConfigGroupId`].
3350    #[must_use]
3351    pub fn new(id: impl Into<Self>) -> Self {
3352        id.into()
3353    }
3354}
3355
3356/// A possible value for a session configuration option.
3357#[serde_as]
3358#[skip_serializing_none]
3359#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3360#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3361#[serde(rename_all = "camelCase")]
3362#[non_exhaustive]
3363pub struct SessionConfigSelectOption {
3364    /// Unique identifier for this option value.
3365    pub value: SessionConfigValueId,
3366    /// Human-readable label for this option value.
3367    pub name: String,
3368    /// Optional description for this option value.
3369    ///
3370    /// Omitted and `null` are equivalent and mean no description is provided.
3371    #[serde_as(deserialize_as = "DefaultOnError")]
3372    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3373    #[serde(default)]
3374    pub description: Option<String>,
3375    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3376    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3377    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3378    ///
3379    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3380    #[serde_as(deserialize_as = "DefaultOnError")]
3381    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3382    #[serde(default)]
3383    #[serde(rename = "_meta")]
3384    pub meta: Option<Meta>,
3385}
3386
3387impl SessionConfigSelectOption {
3388    /// Builds [`SessionConfigSelectOption`] with the required fields set. Optional fields start unset or empty.
3389    #[must_use]
3390    pub fn new(value: impl Into<SessionConfigValueId>, name: impl Into<String>) -> Self {
3391        Self {
3392            value: value.into(),
3393            name: name.into(),
3394            description: None,
3395            meta: None,
3396        }
3397    }
3398
3399    /// Sets or clears the optional `description` field.
3400    #[must_use]
3401    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3402        self.description = description.into_option();
3403        self
3404    }
3405
3406    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3407    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3408    /// do not recognize.
3409    ///
3410    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3411    #[must_use]
3412    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3413        self.meta = meta.into_option();
3414        self
3415    }
3416}
3417
3418/// A group of possible values for a session configuration option.
3419#[serde_as]
3420#[skip_serializing_none]
3421#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3423#[serde(rename_all = "camelCase")]
3424#[non_exhaustive]
3425pub struct SessionConfigSelectGroup {
3426    /// Unique identifier for this group.
3427    pub group_id: SessionConfigGroupId,
3428    /// Human-readable label for this group.
3429    pub name: String,
3430    /// The set of option values in this group. MUST contain at least one value.
3431    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
3432    #[cfg_attr(feature = "schemars", schemars(length(min = 1), extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
3433    pub options: Vec<SessionConfigSelectOption>,
3434    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3435    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3436    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3437    ///
3438    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3439    #[serde_as(deserialize_as = "DefaultOnError")]
3440    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3441    #[serde(default)]
3442    #[serde(rename = "_meta")]
3443    pub meta: Option<Meta>,
3444}
3445
3446impl SessionConfigSelectGroup {
3447    /// Builds [`SessionConfigSelectGroup`] with the required fields set. Optional fields start unset or empty.
3448    #[must_use]
3449    pub fn new(
3450        group_id: impl Into<SessionConfigGroupId>,
3451        name: impl Into<String>,
3452        options: Vec<SessionConfigSelectOption>,
3453    ) -> Self {
3454        Self {
3455            group_id: group_id.into(),
3456            name: name.into(),
3457            options,
3458            meta: None,
3459        }
3460    }
3461
3462    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3463    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3464    /// do not recognize.
3465    ///
3466    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3467    #[must_use]
3468    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3469        self.meta = meta.into_option();
3470        self
3471    }
3472}
3473
3474/// Possible values for a session configuration option.
3475#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3476#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3477#[serde(untagged)]
3478#[non_exhaustive]
3479pub enum SessionConfigSelectOptions {
3480    /// A flat list of options with no grouping.
3481    Ungrouped(
3482        #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectOption>,
3483    ),
3484    /// A list of options grouped under headers.
3485    Grouped(
3486        #[cfg_attr(feature = "schemars", schemars(length(min = 1)))] Vec<SessionConfigSelectGroup>,
3487    ),
3488}
3489
3490impl From<Vec<SessionConfigSelectOption>> for SessionConfigSelectOptions {
3491    fn from(options: Vec<SessionConfigSelectOption>) -> Self {
3492        SessionConfigSelectOptions::Ungrouped(options)
3493    }
3494}
3495
3496impl From<Vec<SessionConfigSelectGroup>> for SessionConfigSelectOptions {
3497    fn from(groups: Vec<SessionConfigSelectGroup>) -> Self {
3498        SessionConfigSelectOptions::Grouped(groups)
3499    }
3500}
3501
3502/// A single-value selector (dropdown) session configuration option payload.
3503#[skip_serializing_none]
3504#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3505#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3506#[serde(rename_all = "camelCase")]
3507#[non_exhaustive]
3508pub struct SessionConfigSelect {
3509    /// The currently selected value. MUST be one of the `value`s in `options`.
3510    pub current_value: SessionConfigValueId,
3511    /// The set of selectable options. MUST contain at least one value, and each `value` MUST be
3512    /// unique within the option, including across groups.
3513    pub options: SessionConfigSelectOptions,
3514}
3515
3516impl SessionConfigSelect {
3517    /// Builds [`SessionConfigSelect`] with the required fields set. Optional fields start unset or empty.
3518    #[must_use]
3519    pub fn new(
3520        current_value: impl Into<SessionConfigValueId>,
3521        options: impl Into<SessionConfigSelectOptions>,
3522    ) -> Self {
3523        Self {
3524            current_value: current_value.into(),
3525            options: options.into(),
3526        }
3527    }
3528}
3529
3530/// A boolean on/off toggle session configuration option payload.
3531#[skip_serializing_none]
3532#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3533#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3534#[serde(rename_all = "camelCase")]
3535#[non_exhaustive]
3536pub struct SessionConfigBoolean {
3537    /// The current value of the boolean option.
3538    pub current_value: bool,
3539}
3540
3541impl SessionConfigBoolean {
3542    /// Builds [`SessionConfigBoolean`] with the required fields set. Optional fields start unset or empty.
3543    #[must_use]
3544    pub fn new(current_value: bool) -> Self {
3545        Self { current_value }
3546    }
3547}
3548
3549/// Semantic category for a session configuration option.
3550///
3551/// This is intended to help Clients distinguish broadly common selectors (e.g. model selector vs
3552/// session mode selector vs thought/reasoning level) for UX purposes (keyboard shortcuts, icons,
3553/// placement). It MUST NOT be required for correctness. Clients MUST handle missing or unknown
3554/// categories gracefully.
3555///
3556/// Category names beginning with `_` are free for custom use, like other ACP extension methods.
3557/// Category names that do not begin with `_` are reserved for the ACP spec.
3558#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3559#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3560#[serde(rename_all = "snake_case")]
3561#[non_exhaustive]
3562pub enum SessionConfigOptionCategory {
3563    /// Session mode selector.
3564    Mode,
3565    /// Model selector.
3566    Model,
3567    /// Model-related configuration parameter.
3568    ModelConfig,
3569    /// Thought/reasoning level selector.
3570    ThoughtLevel,
3571    /// Custom or future category.
3572    ///
3573    /// Values beginning with `_` are reserved for implementation-specific
3574    /// extensions. Unknown values that do not begin with `_` are reserved for
3575    /// future ACP variants.
3576    #[serde(untagged)]
3577    Other(String),
3578}
3579
3580/// Type-specific session configuration option payload.
3581#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3582#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3583#[serde(tag = "type", rename_all = "snake_case")]
3584#[non_exhaustive]
3585pub enum SessionConfigKind {
3586    /// Single-value selector (dropdown).
3587    Select(SessionConfigSelect),
3588    /// Boolean on/off toggle.
3589    Boolean(SessionConfigBoolean),
3590    /// Custom or future session configuration option payload.
3591    ///
3592    /// Values beginning with `_` are reserved for implementation-specific
3593    /// extensions. Unknown values that do not begin with `_` are reserved for
3594    /// future ACP variants.
3595    ///
3596    /// Clients that do not understand this option type should preserve the raw
3597    /// payload when storing, replaying, proxying, or forwarding configuration
3598    /// data, and otherwise ignore the option. The Agent keeps using its `currentValue`.
3599    #[serde(untagged)]
3600    Other(OtherSessionConfigKind),
3601}
3602
3603/// Custom or future session configuration option payload.
3604#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3605#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3606#[cfg_attr(feature = "schemars", schemars(inline))]
3607#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_kind_schema))]
3608#[serde(rename_all = "camelCase")]
3609#[non_exhaustive]
3610pub struct OtherSessionConfigKind {
3611    /// Custom or future session configuration option type.
3612    ///
3613    /// Values beginning with `_` are reserved for implementation-specific
3614    /// extensions. Unknown values that do not begin with `_` are reserved for
3615    /// future ACP variants.
3616    #[serde(rename = "type")]
3617    pub type_: String,
3618    /// Additional fields from the unknown session configuration option payload.
3619    #[serde(flatten)]
3620    pub fields: BTreeMap<String, serde_json::Value>,
3621}
3622
3623impl OtherSessionConfigKind {
3624    /// Builds [`OtherSessionConfigKind`] from an unknown discriminator and preserves the remaining extension fields.
3625    #[must_use]
3626    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
3627        fields.remove("type");
3628        fields.remove("_meta");
3629        Self {
3630            type_: type_.into(),
3631            fields,
3632        }
3633    }
3634}
3635
3636impl<'de> Deserialize<'de> for OtherSessionConfigKind {
3637    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3638    where
3639        D: serde::Deserializer<'de>,
3640    {
3641        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3642        let type_ = fields
3643            .remove("type")
3644            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3645        let serde_json::Value::String(type_) = type_ else {
3646            return Err(serde::de::Error::custom("`type` must be a string"));
3647        };
3648
3649        if is_known_session_config_kind_type(&type_) {
3650            return Err(serde::de::Error::custom(format!(
3651                "known session configuration option `{type_}` did not match its schema"
3652            )));
3653        }
3654
3655        Ok(Self { type_, fields })
3656    }
3657}
3658
3659fn is_known_session_config_kind_type(type_: &str) -> bool {
3660    matches!(type_, "select" | "boolean")
3661}
3662
3663#[cfg(feature = "schemars")]
3664fn other_session_config_kind_schema(schema: &mut Schema) {
3665    super::schema_util::reject_known_string_discriminators(schema, "type", &["select", "boolean"]);
3666}
3667
3668/// A session configuration option selector and its current state.
3669#[serde_as]
3670#[skip_serializing_none]
3671#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3672#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3673#[serde(rename_all = "camelCase")]
3674#[non_exhaustive]
3675pub struct SessionConfigOption {
3676    /// Unique identifier for the configuration option.
3677    pub config_id: SessionConfigId,
3678    /// Human-readable label for the option.
3679    pub name: String,
3680    /// Optional description for the Client to display to the user.
3681    ///
3682    /// Omitted and `null` are equivalent and mean no description is provided.
3683    #[serde_as(deserialize_as = "DefaultOnError")]
3684    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3685    #[serde(default)]
3686    pub description: Option<String>,
3687    /// Optional semantic category for this option (UX only).
3688    ///
3689    /// Omitted and `null` are equivalent and mean no category is provided.
3690    #[serde_as(deserialize_as = "DefaultOnError")]
3691    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3692    #[serde(default)]
3693    pub category: Option<SessionConfigOptionCategory>,
3694    /// Type-specific fields for this configuration option.
3695    #[serde(flatten)]
3696    pub kind: SessionConfigKind,
3697    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3698    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
3699    /// Implementations MUST NOT assign meaning to keys they do not recognize.
3700    ///
3701    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3702    #[serde_as(deserialize_as = "DefaultOnError")]
3703    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
3704    #[serde(default)]
3705    #[serde(rename = "_meta")]
3706    pub meta: Option<Meta>,
3707}
3708
3709impl SessionConfigOption {
3710    /// Builds [`SessionConfigOption`] with the required fields set. Optional fields start unset or empty.
3711    #[must_use]
3712    pub fn new(
3713        config_id: impl Into<SessionConfigId>,
3714        name: impl Into<String>,
3715        kind: SessionConfigKind,
3716    ) -> Self {
3717        Self {
3718            config_id: config_id.into(),
3719            name: name.into(),
3720            description: None,
3721            category: None,
3722            kind,
3723            meta: None,
3724        }
3725    }
3726
3727    /// Builds a select-style session configuration option with its current value and choices.
3728    #[must_use]
3729    pub fn select(
3730        config_id: impl Into<SessionConfigId>,
3731        name: impl Into<String>,
3732        current_value: impl Into<SessionConfigValueId>,
3733        options: impl Into<SessionConfigSelectOptions>,
3734    ) -> Self {
3735        Self::new(
3736            config_id,
3737            name,
3738            SessionConfigKind::Select(SessionConfigSelect::new(current_value, options)),
3739        )
3740    }
3741
3742    /// Builds a boolean-style session configuration option with its current value.
3743    #[must_use]
3744    pub fn boolean(
3745        config_id: impl Into<SessionConfigId>,
3746        name: impl Into<String>,
3747        current_value: bool,
3748    ) -> Self {
3749        Self::new(
3750            config_id,
3751            name,
3752            SessionConfigKind::Boolean(SessionConfigBoolean::new(current_value)),
3753        )
3754    }
3755
3756    /// Sets or clears the optional `description` field.
3757    #[must_use]
3758    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
3759        self.description = description.into_option();
3760        self
3761    }
3762
3763    /// Sets or clears the optional `category` field.
3764    #[must_use]
3765    pub fn category(mut self, category: impl IntoOption<SessionConfigOptionCategory>) -> Self {
3766        self.category = category.into_option();
3767        self
3768    }
3769
3770    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
3771    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
3772    /// do not recognize.
3773    ///
3774    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
3775    #[must_use]
3776    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
3777        self.meta = meta.into_option();
3778        self
3779    }
3780}
3781
3782/// The value to set for a session configuration option.
3783///
3784/// The `type` field acts as the discriminator in the serialized JSON form.
3785///
3786/// The `type` discriminator describes the *shape* of the value, not the option
3787/// kind. For example every option kind that picks from a list of ids
3788/// (`select`, `radio`, …) would use [`Id`](Self::Id), while a future freeform
3789/// text option would get its own variant.
3790#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3791#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3792#[serde(tag = "type", rename_all = "snake_case")]
3793#[non_exhaustive]
3794pub enum SessionConfigOptionValue {
3795    /// A `SessionConfigValueId` string value (`type: "id"`).
3796    Id {
3797        /// The value ID.
3798        value: SessionConfigValueId,
3799    },
3800    /// A boolean value (`type: "boolean"`).
3801    Boolean {
3802        /// The boolean value.
3803        value: bool,
3804    },
3805    /// Custom or future session configuration option value payload.
3806    ///
3807    /// Values beginning with `_` are reserved for implementation-specific
3808    /// extensions. Unknown values that do not begin with `_` are reserved for
3809    /// future ACP variants.
3810    ///
3811    /// Agents MUST reject a `session/set_config_option` request with a value type
3812    /// they do not support with `-32602`.
3813    #[serde(untagged)]
3814    Other(OtherSessionConfigOptionValue),
3815}
3816
3817/// Custom or future session configuration option value payload.
3818#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3819#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
3820#[cfg_attr(feature = "schemars", schemars(inline))]
3821#[cfg_attr(feature = "schemars", schemars(transform = other_session_config_option_value_schema))]
3822#[serde(rename_all = "camelCase")]
3823#[non_exhaustive]
3824pub struct OtherSessionConfigOptionValue {
3825    /// Custom or future session configuration option value type.
3826    ///
3827    /// Values beginning with `_` are reserved for implementation-specific
3828    /// extensions. Unknown values that do not begin with `_` are reserved for
3829    /// future ACP variants.
3830    #[serde(rename = "type")]
3831    pub type_: String,
3832    /// Raw value payload for the custom or future value type.
3833    pub value: serde_json::Value,
3834    /// Additional fields from the unknown session configuration option value payload.
3835    #[serde(flatten)]
3836    pub fields: BTreeMap<String, serde_json::Value>,
3837}
3838
3839impl OtherSessionConfigOptionValue {
3840    /// Builds [`OtherSessionConfigOptionValue`] from an unknown discriminator and preserves the remaining extension fields.
3841    #[must_use]
3842    pub fn new(
3843        type_: impl Into<String>,
3844        value: serde_json::Value,
3845        mut fields: BTreeMap<String, serde_json::Value>,
3846    ) -> Self {
3847        fields.remove("type");
3848        fields.remove("value");
3849        fields.remove("_meta");
3850        Self {
3851            type_: type_.into(),
3852            value,
3853            fields,
3854        }
3855    }
3856}
3857
3858impl<'de> Deserialize<'de> for OtherSessionConfigOptionValue {
3859    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3860    where
3861        D: serde::Deserializer<'de>,
3862    {
3863        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3864        let type_ = fields
3865            .remove("type")
3866            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
3867        let serde_json::Value::String(type_) = type_ else {
3868            return Err(serde::de::Error::custom("`type` must be a string"));
3869        };
3870
3871        if is_known_session_config_option_value_type(&type_) {
3872            return Err(serde::de::Error::custom(format!(
3873                "known session configuration option value `{type_}` did not match its schema"
3874            )));
3875        }
3876
3877        let value = fields
3878            .remove("value")
3879            .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3880
3881        Ok(Self {
3882            type_,
3883            value,
3884            fields,
3885        })
3886    }
3887}
3888
3889impl<'de> Deserialize<'de> for SessionConfigOptionValue {
3890    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3891    where
3892        D: serde::Deserializer<'de>,
3893    {
3894        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
3895        let type_ = fields.remove("type");
3896        let value = fields
3897            .remove("value")
3898            .ok_or_else(|| serde::de::Error::missing_field("value"))?;
3899
3900        let type_ = type_.ok_or_else(|| serde::de::Error::missing_field("type"))?;
3901
3902        let serde_json::Value::String(type_) = type_ else {
3903            return Err(serde::de::Error::custom("`type` must be a string"));
3904        };
3905
3906        match type_.as_str() {
3907            "id" => {
3908                let value = serde_json::from_value(value).map_err(|error| {
3909                    serde::de::Error::custom(format!(
3910                        "`value` must be a string for `type: id`: {error}"
3911                    ))
3912                })?;
3913                Ok(Self::Id { value })
3914            }
3915            "boolean" => {
3916                let value = serde_json::from_value(value).map_err(|error| {
3917                    serde::de::Error::custom(format!(
3918                        "`value` must be a boolean for `type: boolean`: {error}"
3919                    ))
3920                })?;
3921                Ok(Self::Boolean { value })
3922            }
3923            _ => Ok(Self::Other(OtherSessionConfigOptionValue {
3924                type_,
3925                value,
3926                fields,
3927            })),
3928        }
3929    }
3930}
3931
3932fn is_known_session_config_option_value_type(type_: &str) -> bool {
3933    matches!(type_, "id" | "boolean")
3934}
3935
3936#[cfg(feature = "schemars")]
3937fn other_session_config_option_value_schema(schema: &mut Schema) {
3938    super::schema_util::reject_known_string_discriminators(schema, "type", &["id", "boolean"]);
3939}
3940
3941impl SessionConfigOptionValue {
3942    /// Create an id option value (used by `select` and other id-based option types).
3943    #[must_use]
3944    pub fn id(id: impl Into<SessionConfigValueId>) -> Self {
3945        Self::Id { value: id.into() }
3946    }
3947
3948    /// Create a boolean option value.
3949    #[must_use]
3950    pub fn boolean(val: bool) -> Self {
3951        Self::Boolean { value: val }
3952    }
3953
3954    /// Return the inner [`SessionConfigValueId`] if this is a
3955    /// [`Id`](Self::Id) value.
3956    #[must_use]
3957    pub fn as_id(&self) -> Option<&SessionConfigValueId> {
3958        match self {
3959            Self::Id { value } => Some(value),
3960            _ => None,
3961        }
3962    }
3963
3964    /// Return the inner [`bool`] if this is a [`Boolean`](Self::Boolean) value.
3965    #[must_use]
3966    pub fn as_bool(&self) -> Option<bool> {
3967        match self {
3968            Self::Boolean { value } => Some(*value),
3969            _ => None,
3970        }
3971    }
3972}
3973
3974impl From<SessionConfigValueId> for SessionConfigOptionValue {
3975    fn from(value: SessionConfigValueId) -> Self {
3976        Self::Id { value }
3977    }
3978}
3979
3980impl From<bool> for SessionConfigOptionValue {
3981    fn from(value: bool) -> Self {
3982        Self::Boolean { value }
3983    }
3984}
3985
3986impl From<&str> for SessionConfigOptionValue {
3987    fn from(value: &str) -> Self {
3988        Self::Id {
3989            value: SessionConfigValueId::new(value),
3990        }
3991    }
3992}
3993
3994/// Request parameters for setting a session configuration option.
3995///
3996/// Agents MUST support this method for any session where they advertise config options.
3997/// Agents MUST reject an unknown `configId` or invalid value with `-32602` and leave the
3998/// configuration unchanged.
3999///
4000/// See protocol docs: [Session Config Options](https://agentclientprotocol.com/protocol/v2/session-config-options#setting-a-config-option)
4001#[serde_as]
4002#[skip_serializing_none]
4003#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4004#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4005#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4006#[serde(rename_all = "camelCase")]
4007#[non_exhaustive]
4008pub struct SetSessionConfigOptionRequest {
4009    /// The ID of the session to set the configuration option for.
4010    pub session_id: SessionId,
4011    /// The ID of the configuration option to set.
4012    pub config_id: SessionConfigId,
4013    /// The value to set, including a `type` discriminator and the raw `value`.
4014    ///
4015    /// Payloads must send `type: "id"` for id-based options.
4016    #[serde(flatten)]
4017    pub value: SessionConfigOptionValue,
4018    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4019    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4020    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4021    ///
4022    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4023    #[serde_as(deserialize_as = "DefaultOnError")]
4024    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4025    #[serde(default)]
4026    #[serde(rename = "_meta")]
4027    pub meta: Option<Meta>,
4028}
4029
4030impl SetSessionConfigOptionRequest {
4031    /// Builds [`SetSessionConfigOptionRequest`] with the required request fields set. Optional fields start unset or empty.
4032    #[must_use]
4033    pub fn new(
4034        session_id: impl Into<SessionId>,
4035        config_id: impl Into<SessionConfigId>,
4036        value: impl Into<SessionConfigOptionValue>,
4037    ) -> Self {
4038        Self {
4039            session_id: session_id.into(),
4040            config_id: config_id.into(),
4041            value: value.into(),
4042            meta: None,
4043        }
4044    }
4045
4046    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4047    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4048    /// do not recognize.
4049    ///
4050    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4051    #[must_use]
4052    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4053        self.meta = meta.into_option();
4054        self
4055    }
4056}
4057
4058/// Response to `session/set_config_option` method.
4059#[serde_as]
4060#[skip_serializing_none]
4061#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4062#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4063#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_SET_CONFIG_OPTION_METHOD_NAME)))]
4064#[serde(rename_all = "camelCase")]
4065#[non_exhaustive]
4066pub struct SetSessionConfigOptionResponse {
4067    /// The full set of configuration options and their current values.
4068    #[serde_as(deserialize_as = "VecSkipError<_>")]
4069    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-skip-invalid-items" = true)))]
4070    pub config_options: Vec<SessionConfigOption>,
4071    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4072    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4073    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4074    ///
4075    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4076    #[serde_as(deserialize_as = "DefaultOnError")]
4077    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4078    #[serde(default)]
4079    #[serde(rename = "_meta")]
4080    pub meta: Option<Meta>,
4081}
4082
4083impl SetSessionConfigOptionResponse {
4084    /// Builds [`SetSessionConfigOptionResponse`] with the required response fields set. Optional fields start unset or empty.
4085    #[must_use]
4086    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
4087        Self {
4088            config_options,
4089            meta: None,
4090        }
4091    }
4092
4093    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4094    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4095    /// do not recognize.
4096    ///
4097    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4098    #[must_use]
4099    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4100        self.meta = meta.into_option();
4101        self
4102    }
4103}
4104
4105// MCP
4106
4107/// Configuration for connecting to an MCP (Model Context Protocol) server.
4108///
4109/// MCP servers provide tools and context that the agent can use when
4110/// processing prompts.
4111///
4112/// See protocol docs: [MCP Servers](https://agentclientprotocol.com/protocol/v2/session-setup#mcp-servers)
4113#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4114#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4115#[serde(tag = "type", rename_all = "snake_case")]
4116#[non_exhaustive]
4117pub enum McpServer {
4118    /// HTTP transport configuration
4119    ///
4120    /// Only available when the Agent capabilities include `session.mcp.http`.
4121    Http(McpServerHttp),
4122    /// **UNSTABLE**
4123    ///
4124    /// This capability is not part of the spec yet, and may be removed or changed at any point.
4125    ///
4126    /// ACP transport configuration
4127    ///
4128    /// Only available when the Agent capabilities include `session.mcp.acp`.
4129    /// The MCP server is provided by an ACP component and communicates over the ACP channel.
4130    #[cfg(feature = "unstable_mcp_over_acp")]
4131    Acp(McpServerAcp),
4132    /// Stdio transport configuration
4133    ///
4134    /// Only available when the Agent capabilities include `session.mcp.stdio`.
4135    Stdio(McpServerStdio),
4136    /// Custom or future MCP server transport configuration.
4137    ///
4138    /// Values beginning with `_` are reserved for implementation-specific
4139    /// extensions. Unknown values that do not begin with `_` are reserved for
4140    /// future ACP variants.
4141    ///
4142    /// Receivers that do not understand this transport should preserve the raw
4143    /// payload when storing, replaying, proxying, or forwarding session setup
4144    /// data. Agents MUST reject a request with a transport they do not support
4145    /// with `-32602`.
4146    #[serde(untagged)]
4147    Other(OtherMcpServer),
4148}
4149
4150/// Custom or future MCP server transport payload.
4151#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4152#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
4153#[cfg_attr(feature = "schemars", schemars(inline))]
4154#[cfg_attr(feature = "schemars", schemars(transform = other_mcp_server_schema))]
4155#[serde(rename_all = "camelCase")]
4156#[non_exhaustive]
4157pub struct OtherMcpServer {
4158    /// Custom or future MCP server transport type.
4159    ///
4160    /// Values beginning with `_` are reserved for implementation-specific
4161    /// extensions. Unknown values that do not begin with `_` are reserved for
4162    /// future ACP variants.
4163    #[serde(rename = "type")]
4164    pub type_: String,
4165    /// Additional fields from the unknown MCP server transport payload.
4166    #[serde(flatten)]
4167    pub fields: BTreeMap<String, serde_json::Value>,
4168}
4169
4170impl OtherMcpServer {
4171    /// Builds [`OtherMcpServer`] from an unknown discriminator and preserves the remaining extension fields.
4172    #[must_use]
4173    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
4174        fields.remove("type");
4175        Self {
4176            type_: type_.into(),
4177            fields,
4178        }
4179    }
4180}
4181
4182impl<'de> Deserialize<'de> for OtherMcpServer {
4183    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4184    where
4185        D: serde::Deserializer<'de>,
4186    {
4187        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4188        let type_ = fields
4189            .remove("type")
4190            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
4191        let serde_json::Value::String(type_) = type_ else {
4192            return Err(serde::de::Error::custom("`type` must be a string"));
4193        };
4194
4195        if is_known_mcp_server_type(&type_) {
4196            return Err(serde::de::Error::custom(format!(
4197                "known MCP server transport `{type_}` did not match its schema"
4198            )));
4199        }
4200
4201        Ok(Self { type_, fields })
4202    }
4203}
4204
4205fn is_known_mcp_server_type(type_: &str) -> bool {
4206    match type_ {
4207        "http" | "stdio" => true,
4208        #[cfg(feature = "unstable_mcp_over_acp")]
4209        "acp" => true,
4210        _ => false,
4211    }
4212}
4213
4214#[cfg(feature = "schemars")]
4215fn other_mcp_server_schema(schema: &mut Schema) {
4216    super::schema_util::reject_known_string_discriminators(
4217        schema,
4218        "type",
4219        &[
4220            "http",
4221            "stdio",
4222            #[cfg(feature = "unstable_mcp_over_acp")]
4223            "acp",
4224        ],
4225    );
4226}
4227
4228/// HTTP transport configuration for MCP.
4229#[serde_as]
4230#[skip_serializing_none]
4231#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4232#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4233#[serde(rename_all = "camelCase")]
4234#[non_exhaustive]
4235pub struct McpServerHttp {
4236    /// Human-readable name identifying this MCP server.
4237    pub name: String,
4238    /// URL to the MCP server.
4239    #[cfg_attr(feature = "schemars", schemars(url))]
4240    pub url: String,
4241    /// HTTP headers to set when making requests to the MCP server. Headers the MCP transport
4242    /// defines are owned by the transport, and Agents use the transport's values for them.
4243    #[serde_as(deserialize_as = "DefaultOnNull")]
4244    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4245    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4246    pub headers: Vec<HttpHeader>,
4247    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4248    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4249    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4250    ///
4251    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4252    #[serde_as(deserialize_as = "DefaultOnError")]
4253    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4254    #[serde(default)]
4255    #[serde(rename = "_meta")]
4256    pub meta: Option<Meta>,
4257}
4258
4259impl McpServerHttp {
4260    /// Builds [`McpServerHttp`] with the required fields set. Optional fields start unset or empty.
4261    #[must_use]
4262    pub fn new(name: impl Into<String>, url: impl Into<String>) -> Self {
4263        Self {
4264            name: name.into(),
4265            url: url.into(),
4266            headers: Vec::new(),
4267            meta: None,
4268        }
4269    }
4270
4271    /// HTTP headers to set when making requests to the MCP server. Headers the MCP transport
4272    /// defines are owned by the transport, and Agents use the transport's values for them.
4273    #[must_use]
4274    pub fn headers(mut self, headers: Vec<HttpHeader>) -> Self {
4275        self.headers = headers;
4276        self
4277    }
4278
4279    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4280    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4281    /// do not recognize.
4282    ///
4283    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4284    #[must_use]
4285    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4286        self.meta = meta.into_option();
4287        self
4288    }
4289}
4290
4291/// **UNSTABLE**
4292///
4293/// This capability is not part of the spec yet, and may be removed or changed at any point.
4294///
4295/// Unique identifier for an MCP server using the ACP transport.
4296///
4297/// The value is opaque and generated by the ACP component providing the MCP server. It is
4298/// used by `mcp/message` to route requests to the component that declared the server.
4299#[cfg(feature = "unstable_mcp_over_acp")]
4300#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4301#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
4302#[serde(transparent)]
4303#[from(forward)]
4304#[non_exhaustive]
4305pub struct McpServerAcpId(pub Arc<str>);
4306
4307#[cfg(feature = "unstable_mcp_over_acp")]
4308impl McpServerAcpId {
4309    /// Wraps a protocol string as a typed [`McpServerAcpId`].
4310    #[must_use]
4311    pub fn new(id: impl Into<Self>) -> Self {
4312        id.into()
4313    }
4314}
4315
4316/// **UNSTABLE**
4317///
4318/// This capability is not part of the spec yet, and may be removed or changed at any point.
4319///
4320/// ACP transport configuration for MCP.
4321///
4322/// The MCP server is provided by an ACP component and communicates over the ACP channel
4323/// using `mcp/message`.
4324#[serde_as]
4325#[skip_serializing_none]
4326#[cfg(feature = "unstable_mcp_over_acp")]
4327#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4328#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4329#[serde(rename_all = "camelCase")]
4330#[non_exhaustive]
4331pub struct McpServerAcp {
4332    /// Human-readable name identifying this MCP server.
4333    pub name: String,
4334    /// Unique identifier for this MCP server, generated by the component providing it.
4335    ///
4336    /// Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible
4337    /// on the same ACP connection.
4338    pub server_id: McpServerAcpId,
4339    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4340    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4341    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4342    ///
4343    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4344    #[serde_as(deserialize_as = "DefaultOnError")]
4345    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4346    #[serde(default)]
4347    #[serde(rename = "_meta")]
4348    pub meta: Option<Meta>,
4349}
4350
4351#[cfg(feature = "unstable_mcp_over_acp")]
4352impl McpServerAcp {
4353    /// Builds [`McpServerAcp`] with the required fields set. Optional fields start unset or empty.
4354    #[must_use]
4355    pub fn new(name: impl Into<String>, server_id: impl Into<McpServerAcpId>) -> Self {
4356        Self {
4357            name: name.into(),
4358            server_id: server_id.into(),
4359            meta: None,
4360        }
4361    }
4362
4363    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4364    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4365    /// do not recognize.
4366    ///
4367    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4368    #[must_use]
4369    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4370        self.meta = meta.into_option();
4371        self
4372    }
4373}
4374
4375/// Stdio transport configuration for MCP.
4376#[serde_as]
4377#[skip_serializing_none]
4378#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4379#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4380#[serde(rename_all = "camelCase")]
4381#[non_exhaustive]
4382pub struct McpServerStdio {
4383    /// Human-readable name identifying this MCP server.
4384    pub name: String,
4385    /// Absolute path to the MCP server executable.
4386    pub command: AbsolutePath,
4387    /// Command-line arguments to pass to the MCP server.
4388    #[serde_as(deserialize_as = "DefaultOnNull")]
4389    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4390    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4391    pub args: Vec<String>,
4392    /// Environment variables to set when launching the MCP server. Names SHOULD be unique.
4393    /// Agents MAY reject duplicates with `-32602`, and otherwise the last entry wins.
4394    #[serde_as(deserialize_as = "DefaultOnNull")]
4395    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-null" = true)))]
4396    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4397    pub env: Vec<EnvVariable>,
4398    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4399    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4400    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4401    ///
4402    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4403    #[serde_as(deserialize_as = "DefaultOnError")]
4404    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4405    #[serde(default)]
4406    #[serde(rename = "_meta")]
4407    pub meta: Option<Meta>,
4408}
4409
4410impl McpServerStdio {
4411    /// Builds [`McpServerStdio`] with the required fields set. Optional fields start unset or empty.
4412    #[must_use]
4413    pub fn new(name: impl Into<String>, command: impl Into<AbsolutePath>) -> Self {
4414        Self {
4415            name: name.into(),
4416            command: command.into(),
4417            args: Vec::new(),
4418            env: Vec::new(),
4419            meta: None,
4420        }
4421    }
4422
4423    /// Command-line arguments to pass to the MCP server.
4424    #[must_use]
4425    pub fn args(mut self, args: Vec<String>) -> Self {
4426        self.args = args;
4427        self
4428    }
4429
4430    /// Environment variables to set when launching the MCP server. Names SHOULD be unique.
4431    /// Agents MAY reject duplicates with `-32602`, and otherwise the last entry wins.
4432    #[must_use]
4433    pub fn env(mut self, env: Vec<EnvVariable>) -> Self {
4434        self.env = env;
4435        self
4436    }
4437
4438    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4439    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4440    /// do not recognize.
4441    ///
4442    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4443    #[must_use]
4444    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4445        self.meta = meta.into_option();
4446        self
4447    }
4448}
4449
4450/// An environment variable to set when launching a process.
4451#[serde_as]
4452#[skip_serializing_none]
4453#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4454#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4455#[serde(rename_all = "camelCase")]
4456#[non_exhaustive]
4457pub struct EnvVariable {
4458    /// The name of the environment variable.
4459    pub name: String,
4460    /// The value to set for the environment variable.
4461    pub value: String,
4462    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4463    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4464    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4465    ///
4466    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4467    #[serde_as(deserialize_as = "DefaultOnError")]
4468    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4469    #[serde(default)]
4470    #[serde(rename = "_meta")]
4471    pub meta: Option<Meta>,
4472}
4473
4474impl EnvVariable {
4475    /// Builds [`EnvVariable`] with the required fields set. Optional fields start unset or empty.
4476    #[must_use]
4477    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4478        Self {
4479            name: name.into(),
4480            value: value.into(),
4481            meta: None,
4482        }
4483    }
4484
4485    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4486    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4487    /// do not recognize.
4488    ///
4489    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4490    #[must_use]
4491    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4492        self.meta = meta.into_option();
4493        self
4494    }
4495}
4496
4497/// An HTTP header to set when making requests to the MCP server.
4498#[serde_as]
4499#[skip_serializing_none]
4500#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4501#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4502#[serde(rename_all = "camelCase")]
4503#[non_exhaustive]
4504pub struct HttpHeader {
4505    /// The name of the HTTP header.
4506    pub name: String,
4507    /// The value to set for the HTTP header.
4508    pub value: String,
4509    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4510    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4511    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4512    ///
4513    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4514    #[serde_as(deserialize_as = "DefaultOnError")]
4515    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4516    #[serde(default)]
4517    #[serde(rename = "_meta")]
4518    pub meta: Option<Meta>,
4519}
4520
4521impl HttpHeader {
4522    /// Builds [`HttpHeader`] with the required fields set. Optional fields start unset or empty.
4523    #[must_use]
4524    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
4525        Self {
4526            name: name.into(),
4527            value: value.into(),
4528            meta: None,
4529        }
4530    }
4531
4532    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4533    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4534    /// do not recognize.
4535    ///
4536    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4537    #[must_use]
4538    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4539        self.meta = meta.into_option();
4540        self
4541    }
4542}
4543
4544// System prompt
4545
4546/// **UNSTABLE**
4547///
4548/// This capability is not part of the spec yet, and may be removed or changed at any point.
4549///
4550/// Client-provided system-level instructions for a session.
4551///
4552/// Clients MUST only send this when the Agent advertises
4553/// `session.systemPrompt`. Agents that cannot apply it, for example because of
4554/// an unadvertised mode or an unsupported content block type, MUST return an
4555/// error instead of creating, resuming, or forking the session.
4556#[cfg(feature = "unstable_session_system_prompt")]
4557#[serde_as]
4558#[skip_serializing_none]
4559#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4560#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4561#[serde(rename_all = "camelCase")]
4562#[non_exhaustive]
4563pub struct SystemPrompt {
4564    /// How the Agent combines `content` with its own instructions.
4565    ///
4566    /// Required. Agents MUST return an error for a mode they do not support rather than
4567    /// treating it as `append` or `override`.
4568    pub mode: SystemPromptMode,
4569    /// The system prompt content.
4570    ///
4571    /// Agents advertising the capability MUST accept `text` content blocks.
4572    /// Clients MUST NOT send other content block types until a future
4573    /// capability allows them, and Agents MUST return an error for content
4574    /// blocks they do not support, including unknown types.
4575    ///
4576    /// Clients MUST NOT send an empty array. Agents MAY reject one with
4577    /// `-32602` without creating, resuming, or forking the session.
4578    pub content: Vec<ContentBlock>,
4579    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4580    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4581    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4582    ///
4583    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4584    #[serde_as(deserialize_as = "DefaultOnError")]
4585    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4586    #[serde(default)]
4587    #[serde(rename = "_meta")]
4588    pub meta: Option<Meta>,
4589}
4590
4591#[cfg(feature = "unstable_session_system_prompt")]
4592impl SystemPrompt {
4593    /// Builds [`SystemPrompt`] with the required fields set. Optional fields start unset or empty.
4594    ///
4595    /// `mode` starts as [`SystemPromptMode::Append`]. Use [`SystemPrompt::mode`]
4596    /// to request another mode.
4597    #[must_use]
4598    pub fn new(content: Vec<ContentBlock>) -> Self {
4599        Self {
4600            mode: SystemPromptMode::default(),
4601            content,
4602            meta: None,
4603        }
4604    }
4605
4606    /// How the Agent combines `content` with its own instructions.
4607    #[must_use]
4608    pub fn mode(mut self, mode: SystemPromptMode) -> Self {
4609        self.mode = mode;
4610        self
4611    }
4612
4613    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4614    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4615    /// do not recognize.
4616    ///
4617    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4618    #[must_use]
4619    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4620        self.meta = meta.into_option();
4621        self
4622    }
4623}
4624
4625/// **UNSTABLE**
4626///
4627/// This capability is not part of the spec yet, and may be removed or changed at any point.
4628///
4629/// How the Agent combines a client-provided system prompt with its own instructions.
4630///
4631/// Unknown modes are preserved so they can be stored,
4632/// proxied, or forwarded. An Agent MUST return an error for a mode it does not
4633/// support rather than treating it as `append` or `override`.
4634#[cfg(feature = "unstable_session_system_prompt")]
4635#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4636#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
4637#[serde(rename_all = "snake_case")]
4638#[non_exhaustive]
4639pub enum SystemPromptMode {
4640    /// Add the content to the Agent's own instructions rather than replacing them.
4641    ///
4642    /// Every Agent that advertises `session.systemPrompt` supports this mode.
4643    /// Agents SHOULD give the content system-level precedence, for example in the
4644    /// model's system prompt or developer instructions rather than in user-turn
4645    /// content. Where it is placed and how it is formatted is implementation-defined.
4646    #[default]
4647    Append,
4648    /// Replace the Agent's built-in default instructions with the content.
4649    ///
4650    /// This does not change tool availability, MCP servers, permissions, or session
4651    /// configuration. Context the Agent supplies from the workspace or environment
4652    /// MAY still be included. Clients that use this mode take responsibility for any
4653    /// tool guidance or safety instructions the Agent's default instructions
4654    /// provided, and MUST only use it when the Agent advertises
4655    /// `session.systemPrompt.mode.override`.
4656    Override,
4657    /// Custom or future system prompt mode.
4658    ///
4659    /// Values beginning with `_` are reserved for implementation-specific
4660    /// extensions. Unknown values that do not begin with `_` are reserved for
4661    /// future ACP variants.
4662    #[serde(untagged)]
4663    Other(String),
4664}
4665
4666// Prompt
4667
4668/// Request parameters for sending a user prompt to the agent.
4669///
4670/// Contains the user's message and any additional context.
4671///
4672/// See protocol docs: [User Message](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#1-user-message)
4673#[serde_as]
4674#[skip_serializing_none]
4675#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4676#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4677#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4678#[serde(rename_all = "camelCase")]
4679#[non_exhaustive]
4680pub struct PromptRequest {
4681    /// The ID of the session to send this user message to
4682    pub session_id: SessionId,
4683    /// The blocks of content that compose the user's message.
4684    ///
4685    /// As a baseline, the Agent MUST support `text` and `resource_link` content blocks,
4686    /// while other variants are optionally enabled via `PromptCapabilities`.
4687    ///
4688    /// The Client MUST adapt its interface according to `PromptCapabilities`.
4689    ///
4690    /// The client MAY include referenced pieces of context as either
4691    /// `resource` or `resource_link` content blocks.
4692    ///
4693    /// When available, `resource` is preferred
4694    /// as it avoids extra round-trips and allows the message to include
4695    /// pieces of context from sources the agent may not have access to.
4696    ///
4697    /// Clients MUST NOT send an empty array. Agents MAY reject one with `-32602` without
4698    /// inserting it.
4699    #[cfg_attr(feature = "schemars", schemars(length(min = 1)))]
4700    pub prompt: Vec<ContentBlock>,
4701    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4702    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4703    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4704    ///
4705    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4706    #[serde_as(deserialize_as = "DefaultOnError")]
4707    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4708    #[serde(default)]
4709    #[serde(rename = "_meta")]
4710    pub meta: Option<Meta>,
4711}
4712
4713impl PromptRequest {
4714    /// Builds [`PromptRequest`] with the required request fields set. Optional fields start unset or empty.
4715    #[must_use]
4716    pub fn new(session_id: impl Into<SessionId>, prompt: Vec<ContentBlock>) -> Self {
4717        Self {
4718            session_id: session_id.into(),
4719            prompt,
4720            meta: None,
4721        }
4722    }
4723
4724    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4725    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4726    /// do not recognize.
4727    ///
4728    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4729    #[must_use]
4730    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4731        self.meta = meta.into_option();
4732        self
4733    }
4734}
4735
4736/// Response acknowledging that a user prompt was inserted into the ACP conversation.
4737///
4738/// This response does not indicate that the prompt was merely received or held, nor that the
4739/// agent has finished processing it.
4740/// Processing and completion are reported through `state_update` session updates.
4741///
4742/// Insertion is the logical addition of a user message with a message ID and a position
4743/// relative to other messages in the ACP conversation.
4744///
4745/// See protocol docs: [Prompt Accepted](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#2-prompt-accepted)
4746#[serde_as]
4747#[skip_serializing_none]
4748#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4749#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4750#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_PROMPT_METHOD_NAME)))]
4751#[serde(rename_all = "camelCase")]
4752#[non_exhaustive]
4753pub struct PromptResponse {
4754    /// Identifies the user message inserted into the ACP conversation.
4755    ///
4756    /// Required and non-null. Omission and explicit `null` are both invalid.
4757    ///
4758    /// The corresponding user-message session update carries this same identifier and may arrive
4759    /// before or after this response. Agents must echo the message during the live session, but are
4760    /// not required to retain it. If retained and replayed, the message keeps this identifier.
4761    pub message_id: MessageId,
4762    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4763    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
4764    /// Implementations MUST NOT assign meaning to keys they do not recognize.
4765    ///
4766    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4767    #[serde_as(deserialize_as = "DefaultOnError")]
4768    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4769    #[serde(default)]
4770    #[serde(rename = "_meta")]
4771    pub meta: Option<Meta>,
4772}
4773
4774impl PromptResponse {
4775    /// Builds [`PromptResponse`] with the required response fields set. Optional fields start unset or empty.
4776    #[must_use]
4777    pub fn new(message_id: impl Into<MessageId>) -> Self {
4778        Self {
4779            message_id: message_id.into(),
4780            meta: None,
4781        }
4782    }
4783
4784    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
4785    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
4786    /// do not recognize.
4787    ///
4788    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
4789    #[must_use]
4790    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
4791        self.meta = meta.into_option();
4792        self
4793    }
4794}
4795
4796/// Reasons why an agent stops active session work.
4797///
4798/// See protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#stop-reasons)
4799#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4800#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
4801#[serde(tag = "stopReason", rename_all = "snake_case")]
4802#[non_exhaustive]
4803pub enum StopReason {
4804    /// The active work ended successfully.
4805    /// Includes locally handled commands that required no model call.
4806    EndTurn,
4807    /// The active work ended because the agent reached the maximum number of tokens.
4808    MaxTokens,
4809    /// The active work ended because the agent reached the maximum number of
4810    /// model requests for the foreground work.
4811    MaxTurnRequests,
4812    /// The active work ended because the agent refused to continue.
4813    Refusal,
4814    /// The user or client chose to stop the work: via `session/cancel`, by
4815    /// closing the session, or through an interaction the agent offers, such
4816    /// as a permission option that tells the agent to stop. Work the agent
4817    /// stops for its own reasons uses another reason, such as `error` for an
4818    /// internal timeout.
4819    ///
4820    /// Agents MUST report this stop reason on an idle `state_update` when
4821    /// cancellation ends active foreground work, even if aborting causes
4822    /// exceptions. Agents MUST catch these errors and report `cancelled`, not
4823    /// `error`.
4824    Cancelled,
4825    /// The active work ended because something failed.
4826    ///
4827    /// This includes pre-insertion foreground work. A prompt failure before
4828    /// insertion also requires an error response to `session/prompt`. After
4829    /// insertion, the prompt's response remains successful.
4830    Error(ErrorStopReason),
4831    /// Custom or future stop reason.
4832    ///
4833    /// Values beginning with `_` are reserved for implementation-specific
4834    /// extensions. Unknown values that do not begin with `_` are reserved for
4835    /// future ACP variants.
4836    #[serde(untagged)]
4837    Other(OtherStopReason),
4838}
4839
4840impl From<ErrorStopReason> for StopReason {
4841    fn from(value: ErrorStopReason) -> Self {
4842        Self::Error(value)
4843    }
4844}
4845
4846/// Details of a failure that ended active work.
4847#[serde_as]
4848#[skip_serializing_none]
4849#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4850#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4851#[serde(rename_all = "camelCase")]
4852#[non_exhaustive]
4853pub struct ErrorStopReason {
4854    /// The failure, as a JSON-RPC error object.
4855    ///
4856    /// Optional. Omitted and `null` are equivalent and mean the agent is not reporting failure details.
4857    /// Agents SHOULD include it.
4858    #[serde_as(deserialize_as = "DefaultOnError")]
4859    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4860    #[serde(default)]
4861    pub error: Option<Box<Error>>,
4862}
4863
4864impl ErrorStopReason {
4865    /// Builds [`ErrorStopReason`] with the required fields set. Optional fields start unset or empty.
4866    #[must_use]
4867    pub fn new() -> Self {
4868        Self::default()
4869    }
4870
4871    /// The failure, as a JSON-RPC error object.
4872    #[must_use]
4873    pub fn error(mut self, error: impl IntoOption<Error>) -> Self {
4874        self.error = error.into_option().map(Box::new);
4875        self
4876    }
4877}
4878
4879/// Custom or future stop reason payload.
4880#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4881#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
4882#[cfg_attr(feature = "schemars", schemars(inline))]
4883#[cfg_attr(feature = "schemars", schemars(transform = other_stop_reason_schema))]
4884#[serde(rename_all = "camelCase")]
4885#[non_exhaustive]
4886pub struct OtherStopReason {
4887    /// Custom or future stop reason.
4888    ///
4889    /// Values beginning with `_` are reserved for implementation-specific
4890    /// extensions. Unknown values that do not begin with `_` are reserved for
4891    /// future ACP variants.
4892    pub stop_reason: String,
4893    /// Additional fields from the unknown stop reason payload.
4894    #[serde(flatten)]
4895    pub fields: BTreeMap<String, serde_json::Value>,
4896}
4897
4898impl OtherStopReason {
4899    /// Builds [`OtherStopReason`] from an unknown discriminator and preserves the remaining extension fields.
4900    #[must_use]
4901    pub fn new(
4902        stop_reason: impl Into<String>,
4903        mut fields: BTreeMap<String, serde_json::Value>,
4904    ) -> Self {
4905        remove_idle_state_update_fields(&mut fields);
4906        Self {
4907            stop_reason: stop_reason.into(),
4908            fields,
4909        }
4910    }
4911}
4912
4913impl<'de> Deserialize<'de> for OtherStopReason {
4914    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4915    where
4916        D: serde::Deserializer<'de>,
4917    {
4918        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
4919        let stop_reason = fields
4920            .remove("stopReason")
4921            .ok_or_else(|| serde::de::Error::missing_field("stopReason"))?;
4922        let serde_json::Value::String(stop_reason) = stop_reason else {
4923            return Err(serde::de::Error::custom("`stopReason` must be a string"));
4924        };
4925
4926        if KNOWN_STOP_REASONS.contains(&stop_reason.as_str()) {
4927            return Err(serde::de::Error::custom(format!(
4928                "known stop reason `{stop_reason}` did not match its schema"
4929            )));
4930        }
4931        remove_idle_state_update_fields(&mut fields);
4932
4933        Ok(Self {
4934            stop_reason,
4935            fields,
4936        })
4937    }
4938}
4939
4940const KNOWN_STOP_REASONS: &[&str] = &[
4941    "end_turn",
4942    "max_tokens",
4943    "max_turn_requests",
4944    "refusal",
4945    "cancelled",
4946    "error",
4947];
4948
4949/// Keeps the idle update's own fields out of an unknown stop reason's payload.
4950fn remove_idle_state_update_fields(fields: &mut BTreeMap<String, serde_json::Value>) {
4951    fields.remove("stopReason");
4952    fields.remove("usage");
4953    fields.remove("_meta");
4954}
4955
4956#[cfg(feature = "schemars")]
4957fn other_stop_reason_schema(schema: &mut Schema) {
4958    super::schema_util::reject_known_string_discriminators(
4959        schema,
4960        "stopReason",
4961        KNOWN_STOP_REASONS,
4962    );
4963}
4964
4965/// **UNSTABLE**
4966///
4967/// This capability is not part of the spec yet, and may be removed or changed at any point.
4968///
4969/// Token usage for the foreground work that just completed, not cumulative for the session.
4970#[cfg(feature = "unstable_end_turn_token_usage")]
4971#[serde_as]
4972#[skip_serializing_none]
4973#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4974#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4975#[serde(rename_all = "camelCase")]
4976#[non_exhaustive]
4977pub struct Usage {
4978    /// Sum of all token types for the completed work.
4979    pub total_tokens: u64,
4980    /// Input tokens for the completed work.
4981    pub input_tokens: u64,
4982    /// Output tokens for the completed work.
4983    pub output_tokens: u64,
4984    /// Thought/reasoning tokens for the completed work.
4985    ///
4986    /// Omitted and `null` are equivalent and mean the count is not reported.
4987    #[serde_as(deserialize_as = "DefaultOnError")]
4988    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4989    #[serde(default)]
4990    pub thought_tokens: Option<u64>,
4991    /// Cache read tokens for the completed work.
4992    ///
4993    /// Omitted and `null` are equivalent and mean the count is not reported.
4994    #[serde_as(deserialize_as = "DefaultOnError")]
4995    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
4996    #[serde(default)]
4997    pub cached_read_tokens: Option<u64>,
4998    /// Cache write tokens for the completed work.
4999    ///
5000    /// Omitted and `null` are equivalent and mean the count is not reported.
5001    #[serde_as(deserialize_as = "DefaultOnError")]
5002    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5003    #[serde(default)]
5004    pub cached_write_tokens: Option<u64>,
5005    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5006    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5007    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5008    ///
5009    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5010    #[serde_as(deserialize_as = "DefaultOnError")]
5011    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5012    #[serde(default)]
5013    #[serde(rename = "_meta")]
5014    pub meta: Option<Meta>,
5015}
5016
5017#[cfg(feature = "unstable_end_turn_token_usage")]
5018impl Usage {
5019    /// Builds [`Usage`] with the required fields set. Optional fields start unset or empty.
5020    #[must_use]
5021    pub fn new(total_tokens: u64, input_tokens: u64, output_tokens: u64) -> Self {
5022        Self {
5023            total_tokens,
5024            input_tokens,
5025            output_tokens,
5026            thought_tokens: None,
5027            cached_read_tokens: None,
5028            cached_write_tokens: None,
5029            meta: None,
5030        }
5031    }
5032
5033    /// Thought/reasoning tokens for the completed work.
5034    #[must_use]
5035    pub fn thought_tokens(mut self, thought_tokens: impl IntoOption<u64>) -> Self {
5036        self.thought_tokens = thought_tokens.into_option();
5037        self
5038    }
5039
5040    /// Cache read tokens for the completed work.
5041    #[must_use]
5042    pub fn cached_read_tokens(mut self, cached_read_tokens: impl IntoOption<u64>) -> Self {
5043        self.cached_read_tokens = cached_read_tokens.into_option();
5044        self
5045    }
5046
5047    /// Cache write tokens for the completed work.
5048    #[must_use]
5049    pub fn cached_write_tokens(mut self, cached_write_tokens: impl IntoOption<u64>) -> Self {
5050        self.cached_write_tokens = cached_write_tokens.into_option();
5051        self
5052    }
5053
5054    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5055    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5056    /// do not recognize.
5057    ///
5058    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5059    #[must_use]
5060    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5061        self.meta = meta.into_option();
5062        self
5063    }
5064}
5065
5066// Providers
5067
5068/// **UNSTABLE**
5069///
5070/// This capability is not part of the spec yet, and may be removed or changed at any point.
5071///
5072/// Well-known API protocol identifiers for LLM providers.
5073///
5074/// Agents and clients MUST handle unknown protocol identifiers gracefully.
5075///
5076/// Protocol names beginning with `_` are free for custom use, like other ACP extension methods.
5077/// Protocol names that do not begin with `_` are reserved for the ACP spec.
5078#[cfg(feature = "unstable_llm_providers")]
5079#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5080#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5081#[serde(rename_all = "snake_case")]
5082#[non_exhaustive]
5083#[expect(clippy::doc_markdown)]
5084pub enum LlmProtocol {
5085    /// Anthropic API protocol.
5086    Anthropic,
5087    /// OpenAI API protocol.
5088    #[serde(rename = "openai")]
5089    OpenAi,
5090    /// Azure OpenAI API protocol.
5091    Azure,
5092    /// Google Vertex AI API protocol.
5093    Vertex,
5094    /// AWS Bedrock API protocol.
5095    Bedrock,
5096    /// Custom or future protocol.
5097    ///
5098    /// Values beginning with `_` are reserved for implementation-specific
5099    /// extensions. Unknown values that do not begin with `_` are reserved for
5100    /// future ACP variants.
5101    #[serde(untagged)]
5102    Other(String),
5103}
5104
5105/// **UNSTABLE**
5106///
5107/// This capability is not part of the spec yet, and may be removed or changed at any point.
5108///
5109/// Current effective non-secret routing configuration for a provider.
5110#[cfg(feature = "unstable_llm_providers")]
5111#[serde_as]
5112#[skip_serializing_none]
5113#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5114#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5115#[serde(rename_all = "camelCase")]
5116#[non_exhaustive]
5117pub struct ProviderCurrentConfig {
5118    /// Protocol currently used by this provider.
5119    pub api_type: LlmProtocol,
5120    /// Base URL currently used by this provider.
5121    #[cfg_attr(feature = "schemars", schemars(url))]
5122    pub base_url: String,
5123    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5124    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5125    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5126    ///
5127    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5128    #[serde_as(deserialize_as = "DefaultOnError")]
5129    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5130    #[serde(default)]
5131    #[serde(rename = "_meta")]
5132    pub meta: Option<Meta>,
5133}
5134
5135#[cfg(feature = "unstable_llm_providers")]
5136impl ProviderCurrentConfig {
5137    /// Builds [`ProviderCurrentConfig`] with the required fields set. Optional fields start unset or empty.
5138    #[must_use]
5139    pub fn new(api_type: LlmProtocol, base_url: impl Into<String>) -> Self {
5140        Self {
5141            api_type,
5142            base_url: base_url.into(),
5143            meta: None,
5144        }
5145    }
5146
5147    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5148    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5149    /// do not recognize.
5150    ///
5151    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5152    #[must_use]
5153    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5154        self.meta = meta.into_option();
5155        self
5156    }
5157}
5158
5159/// **UNSTABLE**
5160///
5161/// This capability is not part of the spec yet, and may be removed or changed at any point.
5162///
5163/// Unique identifier for a configurable LLM provider.
5164#[cfg(feature = "unstable_llm_providers")]
5165#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Display, From)]
5167#[serde(transparent)]
5168#[from(forward)]
5169#[non_exhaustive]
5170pub struct ProviderId(pub Arc<str>);
5171
5172#[cfg(feature = "unstable_llm_providers")]
5173impl ProviderId {
5174    /// Wraps a protocol string as a typed [`ProviderId`].
5175    #[must_use]
5176    pub fn new(id: impl Into<Self>) -> Self {
5177        id.into()
5178    }
5179}
5180
5181/// **UNSTABLE**
5182///
5183/// This capability is not part of the spec yet, and may be removed or changed at any point.
5184///
5185/// Information about a configurable LLM provider.
5186#[cfg(feature = "unstable_llm_providers")]
5187#[serde_as]
5188#[skip_serializing_none]
5189#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5190#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5191#[serde(rename_all = "camelCase")]
5192#[non_exhaustive]
5193pub struct ProviderInfo {
5194    /// Provider identifier, for example "main" or "openai".
5195    pub provider_id: ProviderId,
5196    /// Supported protocol types for this provider.
5197    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_>>")]
5198    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true)))]
5199    pub supported: Vec<LlmProtocol>,
5200    /// Whether this provider is mandatory and cannot be disabled via `providers/disable`.
5201    /// If true, clients must not call `providers/disable` for this provider ID.
5202    pub required: bool,
5203    /// Current effective non-secret routing config.
5204    /// Null or omitted means provider is disabled.
5205    #[serde(default)]
5206    pub current: Option<ProviderCurrentConfig>,
5207    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5208    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5209    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5210    ///
5211    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5212    #[serde_as(deserialize_as = "DefaultOnError")]
5213    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5214    #[serde(default)]
5215    #[serde(rename = "_meta")]
5216    pub meta: Option<Meta>,
5217}
5218
5219#[cfg(feature = "unstable_llm_providers")]
5220impl ProviderInfo {
5221    /// Builds [`ProviderInfo`] with the required fields set. Optional fields start unset or empty.
5222    #[must_use]
5223    pub fn new(
5224        provider_id: impl Into<ProviderId>,
5225        supported: Vec<LlmProtocol>,
5226        required: bool,
5227        current: impl IntoOption<ProviderCurrentConfig>,
5228    ) -> Self {
5229        Self {
5230            provider_id: provider_id.into(),
5231            supported,
5232            required,
5233            current: current.into_option(),
5234            meta: None,
5235        }
5236    }
5237
5238    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5239    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5240    /// do not recognize.
5241    ///
5242    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5243    #[must_use]
5244    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5245        self.meta = meta.into_option();
5246        self
5247    }
5248}
5249
5250#[cfg(feature = "unstable_llm_providers")]
5251crate::serde_util::default_on_null! {
5252    /// **UNSTABLE**
5253    ///
5254    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5255    ///
5256    /// Request parameters for `providers/list`.
5257    #[serde_as]
5258    #[skip_serializing_none]
5259    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5260    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5261    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5262    #[serde(rename_all = "camelCase")]
5263    #[non_exhaustive]
5264    pub struct ListProvidersRequest {
5265        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5266        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5267        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5268        ///
5269        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5270        #[serde_as(deserialize_as = "DefaultOnError")]
5271        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5272        #[serde(default)]
5273        #[serde(rename = "_meta")]
5274        pub meta: Option<Meta>,
5275    }
5276}
5277
5278#[cfg(feature = "unstable_llm_providers")]
5279impl ListProvidersRequest {
5280    /// Builds [`ListProvidersRequest`] with the required request fields set. Optional fields start unset or empty.
5281    #[must_use]
5282    pub fn new() -> Self {
5283        Self::default()
5284    }
5285
5286    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5287    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5288    /// do not recognize.
5289    ///
5290    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5291    #[must_use]
5292    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5293        self.meta = meta.into_option();
5294        self
5295    }
5296}
5297
5298/// **UNSTABLE**
5299///
5300/// This capability is not part of the spec yet, and may be removed or changed at any point.
5301///
5302/// Response to `providers/list`.
5303#[cfg(feature = "unstable_llm_providers")]
5304#[serde_as]
5305#[skip_serializing_none]
5306#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5308#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_LIST_METHOD_NAME)))]
5309#[serde(rename_all = "camelCase")]
5310#[non_exhaustive]
5311pub struct ListProvidersResponse {
5312    /// Configurable providers with current routing info suitable for UI display.
5313    pub providers: Vec<ProviderInfo>,
5314    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5315    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5316    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5317    ///
5318    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5319    #[serde_as(deserialize_as = "DefaultOnError")]
5320    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5321    #[serde(default)]
5322    #[serde(rename = "_meta")]
5323    pub meta: Option<Meta>,
5324}
5325
5326#[cfg(feature = "unstable_llm_providers")]
5327impl ListProvidersResponse {
5328    /// Builds [`ListProvidersResponse`] with the required response fields set. Optional fields start unset or empty.
5329    #[must_use]
5330    pub fn new(providers: Vec<ProviderInfo>) -> Self {
5331        Self {
5332            providers,
5333            meta: None,
5334        }
5335    }
5336
5337    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5338    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5339    /// do not recognize.
5340    ///
5341    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5342    #[must_use]
5343    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5344        self.meta = meta.into_option();
5345        self
5346    }
5347}
5348
5349/// **UNSTABLE**
5350///
5351/// This capability is not part of the spec yet, and may be removed or changed at any point.
5352///
5353/// Request parameters for `providers/set`.
5354///
5355/// Replaces the full configuration for one provider ID.
5356#[cfg(feature = "unstable_llm_providers")]
5357#[serde_as]
5358#[skip_serializing_none]
5359#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5360#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
5361#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5362#[serde(rename_all = "camelCase")]
5363#[non_exhaustive]
5364pub struct SetProviderRequest {
5365    /// Provider ID to configure.
5366    pub provider_id: ProviderId,
5367    /// Protocol type for this provider.
5368    pub api_type: LlmProtocol,
5369    /// Base URL for requests sent through this provider.
5370    #[cfg_attr(feature = "schemars", schemars(url))]
5371    pub base_url: String,
5372    /// Full headers map for this provider.
5373    /// May include authorization, routing, or other integration-specific headers.
5374    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
5375    pub headers: HashMap<String, String>,
5376    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5377    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5378    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5379    ///
5380    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5381    #[serde_as(deserialize_as = "DefaultOnError")]
5382    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5383    #[serde(default)]
5384    #[serde(rename = "_meta")]
5385    pub meta: Option<Meta>,
5386}
5387
5388#[cfg(feature = "unstable_llm_providers")]
5389impl SetProviderRequest {
5390    /// Builds [`SetProviderRequest`] with the required request fields set. Optional fields start unset or empty.
5391    #[must_use]
5392    pub fn new(
5393        provider_id: impl Into<ProviderId>,
5394        api_type: LlmProtocol,
5395        base_url: impl Into<String>,
5396    ) -> Self {
5397        Self {
5398            provider_id: provider_id.into(),
5399            api_type,
5400            base_url: base_url.into(),
5401            headers: HashMap::new(),
5402            meta: None,
5403        }
5404    }
5405
5406    /// Full headers map for this provider.
5407    /// May include authorization, routing, or other integration-specific headers.
5408    #[must_use]
5409    pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
5410        self.headers = headers;
5411        self
5412    }
5413
5414    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5415    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5416    /// do not recognize.
5417    ///
5418    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5419    #[must_use]
5420    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5421        self.meta = meta.into_option();
5422        self
5423    }
5424}
5425
5426#[cfg(feature = "unstable_llm_providers")]
5427crate::serde_util::default_on_null! {
5428    /// **UNSTABLE**
5429    ///
5430    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5431    ///
5432    /// Response to `providers/set`.
5433    #[serde_as]
5434    #[skip_serializing_none]
5435    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5436    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5437    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_SET_METHOD_NAME)))]
5438    #[serde(rename_all = "camelCase")]
5439    #[non_exhaustive]
5440    pub struct SetProviderResponse {
5441        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5442        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5443        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5444        ///
5445        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5446        #[serde_as(deserialize_as = "DefaultOnError")]
5447        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5448        #[serde(default)]
5449        #[serde(rename = "_meta")]
5450        pub meta: Option<Meta>,
5451    }
5452}
5453
5454#[cfg(feature = "unstable_llm_providers")]
5455impl SetProviderResponse {
5456    /// Builds [`SetProviderResponse`] with the required response fields set. Optional fields start unset or empty.
5457    #[must_use]
5458    pub fn new() -> Self {
5459        Self::default()
5460    }
5461
5462    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5463    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5464    /// do not recognize.
5465    ///
5466    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5467    #[must_use]
5468    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5469        self.meta = meta.into_option();
5470        self
5471    }
5472}
5473
5474/// **UNSTABLE**
5475///
5476/// This capability is not part of the spec yet, and may be removed or changed at any point.
5477///
5478/// Request parameters for `providers/disable`.
5479#[cfg(feature = "unstable_llm_providers")]
5480#[serde_as]
5481#[skip_serializing_none]
5482#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5483#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5484#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5485#[serde(rename_all = "camelCase")]
5486#[non_exhaustive]
5487pub struct DisableProviderRequest {
5488    /// Provider ID to disable.
5489    pub provider_id: ProviderId,
5490    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5491    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5492    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5493    ///
5494    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5495    #[serde_as(deserialize_as = "DefaultOnError")]
5496    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5497    #[serde(default)]
5498    #[serde(rename = "_meta")]
5499    pub meta: Option<Meta>,
5500}
5501
5502#[cfg(feature = "unstable_llm_providers")]
5503impl DisableProviderRequest {
5504    /// Builds [`DisableProviderRequest`] with the required request fields set. Optional fields start unset or empty.
5505    #[must_use]
5506    pub fn new(provider_id: impl Into<ProviderId>) -> Self {
5507        Self {
5508            provider_id: provider_id.into(),
5509            meta: None,
5510        }
5511    }
5512
5513    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5514    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5515    /// do not recognize.
5516    ///
5517    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5518    #[must_use]
5519    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5520        self.meta = meta.into_option();
5521        self
5522    }
5523}
5524
5525#[cfg(feature = "unstable_llm_providers")]
5526crate::serde_util::default_on_null! {
5527    /// **UNSTABLE**
5528    ///
5529    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5530    ///
5531    /// Response to `providers/disable`.
5532    #[serde_as]
5533    #[skip_serializing_none]
5534    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5535    #[derive(Default, Debug, Clone, Serialize, PartialEq, Eq)]
5536    #[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = PROVIDERS_DISABLE_METHOD_NAME)))]
5537    #[serde(rename_all = "camelCase")]
5538    #[non_exhaustive]
5539    pub struct DisableProviderResponse {
5540        /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5541        /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5542        /// Implementations MUST NOT assign meaning to keys they do not recognize.
5543        ///
5544        /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5545        #[serde_as(deserialize_as = "DefaultOnError")]
5546        #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5547        #[serde(default)]
5548        #[serde(rename = "_meta")]
5549        pub meta: Option<Meta>,
5550    }
5551}
5552
5553#[cfg(feature = "unstable_llm_providers")]
5554impl DisableProviderResponse {
5555    /// Builds [`DisableProviderResponse`] with the required response fields set. Optional fields start unset or empty.
5556    #[must_use]
5557    pub fn new() -> Self {
5558        Self::default()
5559    }
5560
5561    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5562    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5563    /// do not recognize.
5564    ///
5565    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5566    #[must_use]
5567    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5568        self.meta = meta.into_option();
5569        self
5570    }
5571}
5572
5573// Capabilities
5574
5575/// Capabilities supported by the agent.
5576///
5577/// Advertised during initialization to inform the client about
5578/// available features and content types.
5579///
5580/// See protocol docs: [Agent Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#agent-capabilities)
5581#[serde_as]
5582#[skip_serializing_none]
5583#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5584#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5585#[serde(rename_all = "camelCase")]
5586#[non_exhaustive]
5587pub struct AgentCapabilities {
5588    /// Session capabilities supported by the agent.
5589    ///
5590    /// Optional. Omitted and `null` are equivalent and mean the agent does not support the
5591    /// `session/*` methods. Supplying `{}` means the agent supports the
5592    /// baseline session methods: `session/new`, `session/list`,
5593    /// `session/resume`, `session/close`, `session/prompt`, `session/cancel`,
5594    /// and `session/update`.
5595    #[serde_as(deserialize_as = "DefaultOnError")]
5596    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5597    #[serde(default)]
5598    pub session: Option<SessionCapabilities>,
5599    /// Authentication-related extension capabilities supported by the agent.
5600    ///
5601    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise any
5602    /// authentication-related extensions. This field does not advertise support
5603    /// for `auth/login` or `auth/logout`. Those methods are advertised by a
5604    /// non-empty `authMethods` list in the `initialize` response.
5605    #[serde_as(deserialize_as = "DefaultOnError")]
5606    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5607    #[serde(default)]
5608    pub auth: Option<AgentAuthCapabilities>,
5609    /// **UNSTABLE**
5610    ///
5611    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5612    ///
5613    /// Provider configuration capabilities supported by the agent.
5614    ///
5615    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5616    /// Supplying `{}` means the agent supports provider configuration methods.
5617    #[cfg(feature = "unstable_llm_providers")]
5618    #[serde_as(deserialize_as = "DefaultOnError")]
5619    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5620    #[serde(default)]
5621    pub providers: Option<ProvidersCapabilities>,
5622    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5623    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5624    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5625    ///
5626    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5627    #[serde_as(deserialize_as = "DefaultOnError")]
5628    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5629    #[serde(default)]
5630    #[serde(rename = "_meta")]
5631    pub meta: Option<Meta>,
5632}
5633
5634impl AgentCapabilities {
5635    /// Builds an empty [`AgentCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5636    #[must_use]
5637    pub fn new() -> Self {
5638        Self::default()
5639    }
5640
5641    /// Session capabilities supported by the agent.
5642    ///
5643    /// Omitted and `null` are equivalent and mean the agent does not support the `session/*`
5644    /// methods. Supplying `{}` means the agent supports the baseline
5645    /// session methods: `session/new`, `session/list`, `session/resume`,
5646    /// `session/close`, `session/prompt`, `session/cancel`, and
5647    /// `session/update`.
5648    #[must_use]
5649    pub fn session(mut self, session: impl IntoOption<SessionCapabilities>) -> Self {
5650        self.session = session.into_option();
5651        self
5652    }
5653
5654    /// Authentication-related extension capabilities supported by the agent.
5655    ///
5656    /// This field does not advertise support for `auth/login` or `auth/logout`.
5657    #[must_use]
5658    pub fn auth(mut self, auth: impl IntoOption<AgentAuthCapabilities>) -> Self {
5659        self.auth = auth.into_option();
5660        self
5661    }
5662
5663    /// **UNSTABLE**
5664    ///
5665    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5666    ///
5667    /// Provider configuration capabilities supported by the agent.
5668    #[cfg(feature = "unstable_llm_providers")]
5669    #[must_use]
5670    pub fn providers(mut self, providers: impl IntoOption<ProvidersCapabilities>) -> Self {
5671        self.providers = providers.into_option();
5672        self
5673    }
5674
5675    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5676    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5677    /// do not recognize.
5678    ///
5679    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5680    #[must_use]
5681    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5682        self.meta = meta.into_option();
5683        self
5684    }
5685}
5686
5687/// **UNSTABLE**
5688///
5689/// This capability is not part of the spec yet, and may be removed or changed at any point.
5690///
5691/// Provider configuration capabilities supported by the agent.
5692///
5693/// Supplying `{}` means the agent supports provider configuration methods.
5694#[cfg(feature = "unstable_llm_providers")]
5695#[serde_as]
5696#[skip_serializing_none]
5697#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5698#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5699#[non_exhaustive]
5700pub struct ProvidersCapabilities {
5701    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5702    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5703    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5704    ///
5705    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5706    #[serde_as(deserialize_as = "DefaultOnError")]
5707    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5708    #[serde(default)]
5709    #[serde(rename = "_meta")]
5710    pub meta: Option<Meta>,
5711}
5712
5713#[cfg(feature = "unstable_llm_providers")]
5714impl ProvidersCapabilities {
5715    /// Builds an empty [`ProvidersCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5716    #[must_use]
5717    pub fn new() -> Self {
5718        Self::default()
5719    }
5720
5721    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5722    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
5723    /// do not recognize.
5724    ///
5725    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5726    #[must_use]
5727    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
5728        self.meta = meta.into_option();
5729        self
5730    }
5731}
5732
5733/// Session capabilities supported by the agent.
5734///
5735/// Supplying `{}` means the agent supports the baseline session methods:
5736/// `session/new`, `session/list`, `session/resume`, `session/close`,
5737/// `session/prompt`, `session/cancel`, and `session/update`.
5738///
5739/// Agents that support sessions **MAY** support additional session methods,
5740/// prompt content types, and MCP transports by specifying additional
5741/// capabilities.
5742///
5743/// See protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#session-capabilities)
5744#[serde_as]
5745#[skip_serializing_none]
5746#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
5747#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5748#[serde(rename_all = "camelCase")]
5749#[non_exhaustive]
5750pub struct SessionCapabilities {
5751    /// Prompt capabilities supported by the agent in `session/prompt` requests.
5752    ///
5753    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise any
5754    /// prompt extensions beyond the baseline text and resource-link content
5755    /// required by `session/prompt`.
5756    #[serde_as(deserialize_as = "DefaultOnError")]
5757    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5758    #[serde(default)]
5759    pub prompt: Option<PromptCapabilities>,
5760    /// MCP capabilities supported by the agent for session lifecycle requests.
5761    ///
5762    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise MCP
5763    /// server transport support for sessions.
5764    #[serde_as(deserialize_as = "DefaultOnError")]
5765    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5766    #[serde(default)]
5767    pub mcp: Option<McpCapabilities>,
5768    /// Whether the agent supports `session/delete`.
5769    ///
5770    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5771    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
5772    #[serde_as(deserialize_as = "DefaultOnError")]
5773    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5774    #[serde(default)]
5775    pub delete: Option<SessionDeleteCapabilities>,
5776    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
5777    ///
5778    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5779    /// Supplying `{}` means the agent supports `additionalDirectories` on
5780    /// supported session lifecycle requests.
5781    ///
5782    /// Agents may return `SessionInfo.additionalDirectories` to report the
5783    /// complete ordered additional-root list associated with a listed session.
5784    #[serde_as(deserialize_as = "DefaultOnError")]
5785    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5786    #[serde(default)]
5787    pub additional_directories: Option<SessionAdditionalDirectoriesCapabilities>,
5788    /// **UNSTABLE**
5789    ///
5790    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5791    ///
5792    /// Whether the agent supports `systemPrompt` on session lifecycle requests.
5793    ///
5794    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5795    /// Supplying `{}` means the agent supports `systemPrompt` in `append` mode.
5796    #[cfg(feature = "unstable_session_system_prompt")]
5797    #[serde_as(deserialize_as = "DefaultOnError")]
5798    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5799    #[serde(default)]
5800    pub system_prompt: Option<SessionSystemPromptCapabilities>,
5801    /// **UNSTABLE**
5802    ///
5803    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5804    ///
5805    /// Whether the agent supports `session/fork`.
5806    ///
5807    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5808    /// Supplying `{}` means the agent supports forking sessions.
5809    #[cfg(feature = "unstable_session_fork")]
5810    #[serde_as(deserialize_as = "DefaultOnError")]
5811    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5812    #[serde(default)]
5813    pub fork: Option<SessionForkCapabilities>,
5814    /// **UNSTABLE**
5815    ///
5816    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5817    ///
5818    /// Whether the agent supports `session/archive` and `session/unarchive`.
5819    ///
5820    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5821    /// Supplying `{}` means the agent supports both methods, the `archived` filter on
5822    /// `session/list`, and `SessionInfo.archived`.
5823    #[cfg(feature = "unstable_session_archive")]
5824    #[serde_as(deserialize_as = "DefaultOnError")]
5825    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5826    #[serde(default)]
5827    pub archive: Option<SessionArchiveCapabilities>,
5828    /// **UNSTABLE**
5829    ///
5830    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5831    ///
5832    /// Whether the agent supports `session/status`.
5833    ///
5834    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5835    /// Supplying `{}` means the agent supports checking session status.
5836    #[cfg(feature = "unstable_session_status")]
5837    #[serde_as(deserialize_as = "DefaultOnError")]
5838    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5839    #[serde(default)]
5840    pub status: Option<SessionStatusCapabilities>,
5841    /// **UNSTABLE**
5842    ///
5843    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5844    ///
5845    /// Whether the agent supports `session/set_title`.
5846    ///
5847    /// Agents report titles through `session/list` and `session_info_update` without this
5848    /// capability. It only advertises that Clients can set titles.
5849    ///
5850    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
5851    /// Supplying `{}` means the agent supports setting session titles.
5852    #[cfg(feature = "unstable_session_set_title")]
5853    #[serde_as(deserialize_as = "DefaultOnError")]
5854    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5855    #[serde(default)]
5856    pub title: Option<SessionTitleCapabilities>,
5857    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
5858    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
5859    /// Implementations MUST NOT assign meaning to keys they do not recognize.
5860    ///
5861    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
5862    #[serde_as(deserialize_as = "DefaultOnError")]
5863    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
5864    #[serde(default)]
5865    #[serde(rename = "_meta")]
5866    pub meta: Option<Meta>,
5867}
5868
5869impl SessionCapabilities {
5870    /// Builds an empty [`SessionCapabilities`]. Use builder methods to advertise supported sub-capabilities.
5871    #[must_use]
5872    pub fn new() -> Self {
5873        Self::default()
5874    }
5875
5876    /// Prompt capabilities supported by the agent in `session/prompt` requests.
5877    ///
5878    /// Omitted and `null` are equivalent and mean the agent does not advertise any prompt
5879    /// extensions beyond the baseline text and resource-link content required by
5880    /// `session/prompt`.
5881    #[must_use]
5882    pub fn prompt(mut self, prompt: impl IntoOption<PromptCapabilities>) -> Self {
5883        self.prompt = prompt.into_option();
5884        self
5885    }
5886
5887    /// MCP capabilities supported by the agent for session lifecycle requests.
5888    ///
5889    /// Omitted and `null` are equivalent and mean the agent does not advertise MCP server
5890    /// transport support for sessions.
5891    #[must_use]
5892    pub fn mcp(mut self, mcp: impl IntoOption<McpCapabilities>) -> Self {
5893        self.mcp = mcp.into_option();
5894        self
5895    }
5896
5897    /// Whether the agent supports `session/delete`.
5898    ///
5899    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5900    /// Supplying `{}` means the agent supports deleting sessions from `session/list`.
5901    #[must_use]
5902    pub fn delete(mut self, delete: impl IntoOption<SessionDeleteCapabilities>) -> Self {
5903        self.delete = delete.into_option();
5904        self
5905    }
5906
5907    /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests.
5908    ///
5909    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5910    /// Supplying `{}` means the agent supports `additionalDirectories` on
5911    /// supported session lifecycle requests.
5912    ///
5913    /// Agents may return `SessionInfo.additionalDirectories` to report the
5914    /// complete ordered additional-root list associated with a listed session.
5915    #[must_use]
5916    pub fn additional_directories(
5917        mut self,
5918        additional_directories: impl IntoOption<SessionAdditionalDirectoriesCapabilities>,
5919    ) -> Self {
5920        self.additional_directories = additional_directories.into_option();
5921        self
5922    }
5923
5924    /// **UNSTABLE**
5925    ///
5926    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5927    ///
5928    /// Whether the agent supports `systemPrompt` on session lifecycle requests.
5929    ///
5930    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5931    /// Supplying `{}` means the agent supports `systemPrompt` in `append` mode.
5932    #[cfg(feature = "unstable_session_system_prompt")]
5933    #[must_use]
5934    pub fn system_prompt(
5935        mut self,
5936        system_prompt: impl IntoOption<SessionSystemPromptCapabilities>,
5937    ) -> Self {
5938        self.system_prompt = system_prompt.into_option();
5939        self
5940    }
5941
5942    #[cfg(feature = "unstable_session_fork")]
5943    /// Whether the agent supports `session/fork`.
5944    ///
5945    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5946    /// Supplying `{}` means the agent supports forking sessions.
5947    #[must_use]
5948    pub fn fork(mut self, fork: impl IntoOption<SessionForkCapabilities>) -> Self {
5949        self.fork = fork.into_option();
5950        self
5951    }
5952
5953    /// **UNSTABLE**
5954    ///
5955    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5956    ///
5957    /// Whether the agent supports `session/archive` and `session/unarchive`.
5958    ///
5959    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5960    /// Supplying `{}` means the agent supports both methods, the `archived` filter on
5961    /// `session/list`, and `SessionInfo.archived`.
5962    #[cfg(feature = "unstable_session_archive")]
5963    #[must_use]
5964    pub fn archive(mut self, archive: impl IntoOption<SessionArchiveCapabilities>) -> Self {
5965        self.archive = archive.into_option();
5966        self
5967    }
5968
5969    /// **UNSTABLE**
5970    ///
5971    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5972    ///
5973    /// Whether the agent supports `session/status`.
5974    ///
5975    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5976    /// Supplying `{}` means the agent supports checking session status.
5977    #[cfg(feature = "unstable_session_status")]
5978    #[must_use]
5979    pub fn status(mut self, status: impl IntoOption<SessionStatusCapabilities>) -> Self {
5980        self.status = status.into_option();
5981        self
5982    }
5983
5984    /// **UNSTABLE**
5985    ///
5986    /// This capability is not part of the spec yet, and may be removed or changed at any point.
5987    ///
5988    /// Whether the agent supports `session/set_title`.
5989    ///
5990    /// Agents report titles through `session/list` and `session_info_update` without this
5991    /// capability. It only advertises that Clients can set titles.
5992    ///
5993    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
5994    /// Supplying `{}` means the agent supports setting session titles.
5995    #[cfg(feature = "unstable_session_set_title")]
5996    #[must_use]
5997    pub fn title(mut self, title: impl IntoOption<SessionTitleCapabilities>) -> Self {
5998        self.title = title.into_option();
5999        self
6000    }
6001
6002    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6003    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6004    /// do not recognize.
6005    ///
6006    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6007    #[must_use]
6008    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6009        self.meta = meta.into_option();
6010        self
6011    }
6012}
6013
6014/// Capabilities for the `session/delete` method.
6015///
6016/// Supplying `{}` means the agent supports deleting sessions from `session/list`.
6017#[serde_as]
6018#[skip_serializing_none]
6019#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6020#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6021#[non_exhaustive]
6022pub struct SessionDeleteCapabilities {
6023    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6024    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6025    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6026    ///
6027    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6028    #[serde_as(deserialize_as = "DefaultOnError")]
6029    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6030    #[serde(default)]
6031    #[serde(rename = "_meta")]
6032    pub meta: Option<Meta>,
6033}
6034
6035impl SessionDeleteCapabilities {
6036    /// Builds an empty [`SessionDeleteCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6037    #[must_use]
6038    pub fn new() -> Self {
6039        Self::default()
6040    }
6041
6042    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6043    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6044    /// do not recognize.
6045    ///
6046    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6047    #[must_use]
6048    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6049        self.meta = meta.into_option();
6050        self
6051    }
6052}
6053
6054/// Capabilities for additional session directories support.
6055///
6056/// Supplying `{}` means the agent supports the `additionalDirectories` field on
6057/// supported session lifecycle requests. Agents may return
6058/// `SessionInfo.additionalDirectories` in `session/list` to report the
6059/// complete ordered additional-root list associated with a listed session.
6060#[serde_as]
6061#[skip_serializing_none]
6062#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6063#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6064#[non_exhaustive]
6065pub struct SessionAdditionalDirectoriesCapabilities {
6066    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6067    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6068    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6069    ///
6070    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6071    #[serde_as(deserialize_as = "DefaultOnError")]
6072    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6073    #[serde(default)]
6074    #[serde(rename = "_meta")]
6075    pub meta: Option<Meta>,
6076}
6077
6078impl SessionAdditionalDirectoriesCapabilities {
6079    /// Builds an empty [`SessionAdditionalDirectoriesCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6080    #[must_use]
6081    pub fn new() -> Self {
6082        Self::default()
6083    }
6084
6085    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6086    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6087    /// do not recognize.
6088    ///
6089    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6090    #[must_use]
6091    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6092        self.meta = meta.into_option();
6093        self
6094    }
6095}
6096
6097/// **UNSTABLE**
6098///
6099/// This capability is not part of the spec yet, and may be removed or changed at any point.
6100///
6101/// Capabilities for client-provided system prompts.
6102///
6103/// Supplying `{}` means the agent supports `systemPrompt` on session lifecycle
6104/// requests in `append` mode.
6105#[cfg(feature = "unstable_session_system_prompt")]
6106#[serde_as]
6107#[skip_serializing_none]
6108#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6109#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6110#[serde(rename_all = "camelCase")]
6111#[non_exhaustive]
6112pub struct SessionSystemPromptCapabilities {
6113    /// Delivery modes the agent supports in addition to `append`.
6114    ///
6115    /// Optional. Omitted and `null` are equivalent and mean the agent supports only `append`.
6116    #[serde_as(deserialize_as = "DefaultOnError")]
6117    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6118    #[serde(default)]
6119    pub mode: Option<SessionSystemPromptModeCapabilities>,
6120    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6121    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6122    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6123    ///
6124    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6125    #[serde_as(deserialize_as = "DefaultOnError")]
6126    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6127    #[serde(default)]
6128    #[serde(rename = "_meta")]
6129    pub meta: Option<Meta>,
6130}
6131
6132#[cfg(feature = "unstable_session_system_prompt")]
6133impl SessionSystemPromptCapabilities {
6134    /// Builds an empty [`SessionSystemPromptCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6135    #[must_use]
6136    pub fn new() -> Self {
6137        Self::default()
6138    }
6139
6140    /// Delivery modes the agent supports in addition to `append`.
6141    ///
6142    /// Omitted and `null` are equivalent and mean the agent supports only `append`.
6143    #[must_use]
6144    pub fn mode(mut self, mode: impl IntoOption<SessionSystemPromptModeCapabilities>) -> Self {
6145        self.mode = mode.into_option();
6146        self
6147    }
6148
6149    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6150    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6151    /// do not recognize.
6152    ///
6153    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6154    #[must_use]
6155    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6156        self.meta = meta.into_option();
6157        self
6158    }
6159}
6160
6161/// **UNSTABLE**
6162///
6163/// This capability is not part of the spec yet, and may be removed or changed at any point.
6164///
6165/// System prompt delivery modes the agent supports in addition to `append`.
6166///
6167/// `append` has no entry because every agent advertising `session.systemPrompt`
6168/// supports it.
6169#[cfg(feature = "unstable_session_system_prompt")]
6170#[serde_as]
6171#[skip_serializing_none]
6172#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6173#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6174#[serde(rename_all = "camelCase")]
6175#[non_exhaustive]
6176pub struct SessionSystemPromptModeCapabilities {
6177    /// Whether the agent supports `override` mode.
6178    ///
6179    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6180    /// Supplying `{}` means the agent supports replacing its built-in default
6181    /// instructions with the client-provided system prompt.
6182    #[serde_as(deserialize_as = "DefaultOnError")]
6183    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6184    #[serde(default, rename = "override")]
6185    pub override_: Option<SessionSystemPromptOverrideCapabilities>,
6186    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6187    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6188    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6189    ///
6190    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6191    #[serde_as(deserialize_as = "DefaultOnError")]
6192    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6193    #[serde(default)]
6194    #[serde(rename = "_meta")]
6195    pub meta: Option<Meta>,
6196}
6197
6198#[cfg(feature = "unstable_session_system_prompt")]
6199impl SessionSystemPromptModeCapabilities {
6200    /// Builds an empty [`SessionSystemPromptModeCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6201    #[must_use]
6202    pub fn new() -> Self {
6203        Self::default()
6204    }
6205
6206    /// Whether the agent supports `override` mode.
6207    ///
6208    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6209    /// Supplying `{}` means the agent supports replacing its built-in default
6210    /// instructions with the client-provided system prompt.
6211    #[must_use]
6212    pub fn override_(
6213        mut self,
6214        override_: impl IntoOption<SessionSystemPromptOverrideCapabilities>,
6215    ) -> Self {
6216        self.override_ = override_.into_option();
6217        self
6218    }
6219
6220    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6221    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6222    /// do not recognize.
6223    ///
6224    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6225    #[must_use]
6226    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6227        self.meta = meta.into_option();
6228        self
6229    }
6230}
6231
6232/// **UNSTABLE**
6233///
6234/// This capability is not part of the spec yet, and may be removed or changed at any point.
6235///
6236/// Capabilities for the `override` system prompt mode.
6237///
6238/// Supplying `{}` means the agent supports replacing its built-in default
6239/// instructions with the client-provided system prompt.
6240#[cfg(feature = "unstable_session_system_prompt")]
6241#[serde_as]
6242#[skip_serializing_none]
6243#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6244#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6245#[non_exhaustive]
6246pub struct SessionSystemPromptOverrideCapabilities {
6247    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6248    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6249    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6250    ///
6251    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6252    #[serde_as(deserialize_as = "DefaultOnError")]
6253    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6254    #[serde(default)]
6255    #[serde(rename = "_meta")]
6256    pub meta: Option<Meta>,
6257}
6258
6259#[cfg(feature = "unstable_session_system_prompt")]
6260impl SessionSystemPromptOverrideCapabilities {
6261    /// Builds an empty [`SessionSystemPromptOverrideCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6262    #[must_use]
6263    pub fn new() -> Self {
6264        Self::default()
6265    }
6266
6267    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6268    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6269    /// do not recognize.
6270    ///
6271    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6272    #[must_use]
6273    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6274        self.meta = meta.into_option();
6275        self
6276    }
6277}
6278
6279/// **UNSTABLE**
6280///
6281/// This capability is not part of the spec yet, and may be removed or changed at any point.
6282///
6283/// Capabilities for the `session/fork` method.
6284///
6285/// Supplying `{}` means the agent supports forking sessions.
6286#[cfg(feature = "unstable_session_fork")]
6287#[serde_as]
6288#[skip_serializing_none]
6289#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6290#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6291#[non_exhaustive]
6292pub struct SessionForkCapabilities {
6293    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6294    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6295    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6296    ///
6297    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6298    #[serde_as(deserialize_as = "DefaultOnError")]
6299    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6300    #[serde(default)]
6301    #[serde(rename = "_meta")]
6302    pub meta: Option<Meta>,
6303}
6304
6305#[cfg(feature = "unstable_session_fork")]
6306impl SessionForkCapabilities {
6307    /// Builds an empty [`SessionForkCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6308    #[must_use]
6309    pub fn new() -> Self {
6310        Self::default()
6311    }
6312
6313    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6314    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6315    /// do not recognize.
6316    ///
6317    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6318    #[must_use]
6319    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6320        self.meta = meta.into_option();
6321        self
6322    }
6323}
6324
6325/// **UNSTABLE**
6326///
6327/// This capability is not part of the spec yet, and may be removed or changed at any point.
6328///
6329/// Capabilities for `session/archive` and `session/unarchive`.
6330///
6331/// Supplying `{}` means the agent supports both methods, the `archived` filter on
6332/// `session/list`, and `SessionInfo.archived`.
6333#[cfg(feature = "unstable_session_archive")]
6334#[serde_as]
6335#[skip_serializing_none]
6336#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6337#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6338#[non_exhaustive]
6339pub struct SessionArchiveCapabilities {
6340    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6341    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6342    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6343    ///
6344    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6345    #[serde_as(deserialize_as = "DefaultOnError")]
6346    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6347    #[serde(default)]
6348    #[serde(rename = "_meta")]
6349    pub meta: Option<Meta>,
6350}
6351
6352#[cfg(feature = "unstable_session_archive")]
6353impl SessionArchiveCapabilities {
6354    /// Builds an empty [`SessionArchiveCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6355    #[must_use]
6356    pub fn new() -> Self {
6357        Self::default()
6358    }
6359
6360    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6361    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6362    /// do not recognize.
6363    ///
6364    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6365    #[must_use]
6366    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6367        self.meta = meta.into_option();
6368        self
6369    }
6370}
6371
6372/// **UNSTABLE**
6373///
6374/// This capability is not part of the spec yet, and may be removed or changed at any point.
6375///
6376/// Capabilities for the `session/status` method.
6377///
6378/// Supplying `{}` means the agent supports checking session status.
6379#[cfg(feature = "unstable_session_status")]
6380#[serde_as]
6381#[skip_serializing_none]
6382#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6383#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6384#[non_exhaustive]
6385pub struct SessionStatusCapabilities {
6386    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6387    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6388    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6389    ///
6390    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6391    #[serde_as(deserialize_as = "DefaultOnError")]
6392    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6393    #[serde(default)]
6394    #[serde(rename = "_meta")]
6395    pub meta: Option<Meta>,
6396}
6397
6398#[cfg(feature = "unstable_session_status")]
6399impl SessionStatusCapabilities {
6400    /// Builds an empty [`SessionStatusCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6401    #[must_use]
6402    pub fn new() -> Self {
6403        Self::default()
6404    }
6405
6406    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6407    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6408    /// do not recognize.
6409    ///
6410    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6411    #[must_use]
6412    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6413        self.meta = meta.into_option();
6414        self
6415    }
6416}
6417
6418/// **UNSTABLE**
6419///
6420/// This capability is not part of the spec yet, and may be removed or changed at any point.
6421///
6422/// Capabilities for Client-set session titles.
6423///
6424/// Supplying `{}` means the agent supports `session/set_title`. Agents report titles
6425/// through `session/list` and `session_info_update` without this capability.
6426#[cfg(feature = "unstable_session_set_title")]
6427#[serde_as]
6428#[skip_serializing_none]
6429#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6430#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6431#[non_exhaustive]
6432pub struct SessionTitleCapabilities {
6433    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6434    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6435    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6436    ///
6437    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6438    #[serde_as(deserialize_as = "DefaultOnError")]
6439    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6440    #[serde(default)]
6441    #[serde(rename = "_meta")]
6442    pub meta: Option<Meta>,
6443}
6444
6445#[cfg(feature = "unstable_session_set_title")]
6446impl SessionTitleCapabilities {
6447    /// Builds an empty [`SessionTitleCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6448    #[must_use]
6449    pub fn new() -> Self {
6450        Self::default()
6451    }
6452
6453    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6454    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6455    /// do not recognize.
6456    ///
6457    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6458    #[must_use]
6459    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6460        self.meta = meta.into_option();
6461        self
6462    }
6463}
6464
6465/// Prompt capabilities supported by the agent in `session/prompt` requests.
6466///
6467/// Baseline agent functionality requires support for `text`
6468/// and `resource_link` content blocks in prompt requests.
6469///
6470/// Other variants must be explicitly opted in to.
6471/// Capabilities for different types of content in prompt requests.
6472///
6473/// Indicates which content types beyond the baseline (text and resource links)
6474/// the agent can process.
6475///
6476/// See protocol docs: [Session Prompt Capabilities](https://agentclientprotocol.com/protocol/v2/initialization#session-prompt-capabilities)
6477#[serde_as]
6478#[skip_serializing_none]
6479#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6480#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6481#[serde(rename_all = "camelCase")]
6482#[non_exhaustive]
6483pub struct PromptCapabilities {
6484    /// Agent supports `image` content blocks.
6485    ///
6486    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6487    /// Supplying `{}` means the agent supports image content in prompts.
6488    #[serde_as(deserialize_as = "DefaultOnError")]
6489    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6490    #[serde(default)]
6491    pub image: Option<PromptImageCapabilities>,
6492    /// Agent supports `audio` content blocks.
6493    ///
6494    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6495    /// Supplying `{}` means the agent supports audio content in prompts.
6496    #[serde_as(deserialize_as = "DefaultOnError")]
6497    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6498    #[serde(default)]
6499    pub audio: Option<PromptAudioCapabilities>,
6500    /// Agent supports embedded context in `session/prompt` requests.
6501    ///
6502    /// When enabled, the Client is allowed to include `resource` content blocks
6503    /// in prompt requests for pieces of context that are referenced in the message.
6504    ///
6505    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6506    /// Supplying `{}` means the agent supports embedded context in prompts.
6507    #[serde_as(deserialize_as = "DefaultOnError")]
6508    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6509    #[serde(default)]
6510    pub embedded_context: Option<PromptEmbeddedContextCapabilities>,
6511    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6512    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6513    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6514    ///
6515    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6516    #[serde_as(deserialize_as = "DefaultOnError")]
6517    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6518    #[serde(default)]
6519    #[serde(rename = "_meta")]
6520    pub meta: Option<Meta>,
6521}
6522
6523impl PromptCapabilities {
6524    /// Builds an empty [`PromptCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6525    #[must_use]
6526    pub fn new() -> Self {
6527        Self::default()
6528    }
6529
6530    /// Agent supports `image` content blocks.
6531    ///
6532    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6533    /// Supplying `{}` means the agent supports image content in prompts.
6534    #[must_use]
6535    pub fn image(mut self, image: impl IntoOption<PromptImageCapabilities>) -> Self {
6536        self.image = image.into_option();
6537        self
6538    }
6539
6540    /// Agent supports `audio` content blocks.
6541    ///
6542    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6543    /// Supplying `{}` means the agent supports audio content in prompts.
6544    #[must_use]
6545    pub fn audio(mut self, audio: impl IntoOption<PromptAudioCapabilities>) -> Self {
6546        self.audio = audio.into_option();
6547        self
6548    }
6549
6550    /// Agent supports embedded context in `session/prompt` requests.
6551    ///
6552    /// When enabled, the Client is allowed to include `resource` content blocks
6553    /// in prompt requests for pieces of context that are referenced in the message.
6554    ///
6555    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6556    /// Supplying `{}` means the agent supports embedded context in prompts.
6557    #[must_use]
6558    pub fn embedded_context(
6559        mut self,
6560        embedded_context: impl IntoOption<PromptEmbeddedContextCapabilities>,
6561    ) -> Self {
6562        self.embedded_context = embedded_context.into_option();
6563        self
6564    }
6565
6566    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6567    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6568    /// do not recognize.
6569    ///
6570    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6571    #[must_use]
6572    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6573        self.meta = meta.into_option();
6574        self
6575    }
6576}
6577
6578/// Capabilities for image content in prompt requests.
6579///
6580/// Supplying `{}` means the agent supports image content in prompts.
6581#[serde_as]
6582#[skip_serializing_none]
6583#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6584#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6585#[non_exhaustive]
6586pub struct PromptImageCapabilities {
6587    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6588    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6589    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6590    ///
6591    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6592    #[serde_as(deserialize_as = "DefaultOnError")]
6593    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6594    #[serde(default)]
6595    #[serde(rename = "_meta")]
6596    pub meta: Option<Meta>,
6597}
6598
6599impl PromptImageCapabilities {
6600    /// Builds an empty [`PromptImageCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6601    #[must_use]
6602    pub fn new() -> Self {
6603        Self::default()
6604    }
6605
6606    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6607    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6608    /// do not recognize.
6609    ///
6610    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6611    #[must_use]
6612    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6613        self.meta = meta.into_option();
6614        self
6615    }
6616}
6617
6618/// Capabilities for audio content in prompt requests.
6619///
6620/// Supplying `{}` means the agent supports audio content in prompts.
6621#[serde_as]
6622#[skip_serializing_none]
6623#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6624#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6625#[non_exhaustive]
6626pub struct PromptAudioCapabilities {
6627    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6628    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6629    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6630    ///
6631    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6632    #[serde_as(deserialize_as = "DefaultOnError")]
6633    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6634    #[serde(default)]
6635    #[serde(rename = "_meta")]
6636    pub meta: Option<Meta>,
6637}
6638
6639impl PromptAudioCapabilities {
6640    /// Builds an empty [`PromptAudioCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6641    #[must_use]
6642    pub fn new() -> Self {
6643        Self::default()
6644    }
6645
6646    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6647    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6648    /// do not recognize.
6649    ///
6650    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6651    #[must_use]
6652    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6653        self.meta = meta.into_option();
6654        self
6655    }
6656}
6657
6658/// Capabilities for embedded context in prompt requests.
6659///
6660/// Supplying `{}` means the agent supports embedded context in prompts.
6661#[serde_as]
6662#[skip_serializing_none]
6663#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6664#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6665#[non_exhaustive]
6666pub struct PromptEmbeddedContextCapabilities {
6667    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6668    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6669    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6670    ///
6671    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6672    #[serde_as(deserialize_as = "DefaultOnError")]
6673    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6674    #[serde(default)]
6675    #[serde(rename = "_meta")]
6676    pub meta: Option<Meta>,
6677}
6678
6679impl PromptEmbeddedContextCapabilities {
6680    /// Builds an empty [`PromptEmbeddedContextCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6681    #[must_use]
6682    pub fn new() -> Self {
6683        Self::default()
6684    }
6685
6686    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6687    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6688    /// do not recognize.
6689    ///
6690    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6691    #[must_use]
6692    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6693        self.meta = meta.into_option();
6694        self
6695    }
6696}
6697
6698/// MCP capabilities supported by the agent for session lifecycle requests.
6699#[serde_as]
6700#[skip_serializing_none]
6701#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6702#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6703#[serde(rename_all = "camelCase")]
6704#[non_exhaustive]
6705pub struct McpCapabilities {
6706    /// Agent supports `stdio` MCP servers.
6707    ///
6708    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6709    /// Supplying `{}` means the agent supports stdio MCP server transports.
6710    #[serde_as(deserialize_as = "DefaultOnError")]
6711    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6712    #[serde(default)]
6713    pub stdio: Option<McpStdioCapabilities>,
6714    /// Agent supports `http` MCP servers.
6715    ///
6716    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6717    /// Supplying `{}` means the agent supports HTTP MCP server transports.
6718    #[serde_as(deserialize_as = "DefaultOnError")]
6719    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6720    #[serde(default)]
6721    pub http: Option<McpHttpCapabilities>,
6722    /// **UNSTABLE**
6723    ///
6724    /// This capability is not part of the spec yet, and may be removed or changed at any point.
6725    ///
6726    /// Agent supports `acp` MCP servers.
6727    ///
6728    /// Optional. Omitted and `null` are equivalent and mean the agent does not advertise support.
6729    /// Supplying `{}` means the agent supports ACP MCP server transports.
6730    #[cfg(feature = "unstable_mcp_over_acp")]
6731    #[serde_as(deserialize_as = "DefaultOnError")]
6732    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6733    #[serde(default)]
6734    pub acp: Option<McpAcpCapabilities>,
6735    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6736    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6737    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6738    ///
6739    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6740    #[serde_as(deserialize_as = "DefaultOnError")]
6741    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6742    #[serde(default)]
6743    #[serde(rename = "_meta")]
6744    pub meta: Option<Meta>,
6745}
6746
6747impl McpCapabilities {
6748    /// Builds an empty [`McpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6749    #[must_use]
6750    pub fn new() -> Self {
6751        Self::default()
6752    }
6753
6754    /// Agent supports `stdio` MCP servers.
6755    ///
6756    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6757    /// Supplying `{}` means the agent supports stdio MCP server transports.
6758    #[must_use]
6759    pub fn stdio(mut self, stdio: impl IntoOption<McpStdioCapabilities>) -> Self {
6760        self.stdio = stdio.into_option();
6761        self
6762    }
6763
6764    /// Agent supports `http` MCP servers.
6765    ///
6766    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6767    /// Supplying `{}` means the agent supports HTTP MCP server transports.
6768    #[must_use]
6769    pub fn http(mut self, http: impl IntoOption<McpHttpCapabilities>) -> Self {
6770        self.http = http.into_option();
6771        self
6772    }
6773
6774    /// **UNSTABLE**
6775    ///
6776    /// This capability is not part of the spec yet, and may be removed or changed at any point.
6777    ///
6778    /// Agent supports `acp` MCP servers.
6779    #[cfg(feature = "unstable_mcp_over_acp")]
6780    ///
6781    /// Omitted and `null` are equivalent and mean the agent does not advertise support.
6782    /// Supplying `{}` means the agent supports ACP MCP server transports.
6783    #[must_use]
6784    pub fn acp(mut self, acp: impl IntoOption<McpAcpCapabilities>) -> Self {
6785        self.acp = acp.into_option();
6786        self
6787    }
6788
6789    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6790    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6791    /// do not recognize.
6792    ///
6793    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6794    #[must_use]
6795    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6796        self.meta = meta.into_option();
6797        self
6798    }
6799}
6800
6801/// Capabilities for stdio MCP server transports.
6802///
6803/// Supplying `{}` means the agent supports stdio MCP server transports.
6804#[serde_as]
6805#[skip_serializing_none]
6806#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6807#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6808#[non_exhaustive]
6809pub struct McpStdioCapabilities {
6810    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6811    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6812    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6813    ///
6814    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6815    #[serde_as(deserialize_as = "DefaultOnError")]
6816    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6817    #[serde(default)]
6818    #[serde(rename = "_meta")]
6819    pub meta: Option<Meta>,
6820}
6821
6822impl McpStdioCapabilities {
6823    /// Builds an empty [`McpStdioCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6824    #[must_use]
6825    pub fn new() -> Self {
6826        Self::default()
6827    }
6828
6829    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6830    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6831    /// do not recognize.
6832    ///
6833    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6834    #[must_use]
6835    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6836        self.meta = meta.into_option();
6837        self
6838    }
6839}
6840
6841/// Capabilities for HTTP MCP server transports.
6842///
6843/// Supplying `{}` means the agent supports HTTP MCP server transports.
6844#[serde_as]
6845#[skip_serializing_none]
6846#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6847#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6848#[non_exhaustive]
6849pub struct McpHttpCapabilities {
6850    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6851    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6852    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6853    ///
6854    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6855    #[serde_as(deserialize_as = "DefaultOnError")]
6856    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6857    #[serde(default)]
6858    #[serde(rename = "_meta")]
6859    pub meta: Option<Meta>,
6860}
6861
6862impl McpHttpCapabilities {
6863    /// Builds an empty [`McpHttpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6864    #[must_use]
6865    pub fn new() -> Self {
6866        Self::default()
6867    }
6868
6869    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6870    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6871    /// do not recognize.
6872    ///
6873    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6874    #[must_use]
6875    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6876        self.meta = meta.into_option();
6877        self
6878    }
6879}
6880
6881/// **UNSTABLE**
6882///
6883/// This capability is not part of the spec yet, and may be removed or changed at any point.
6884///
6885/// Capabilities for ACP MCP server transports.
6886///
6887/// Supplying `{}` means the agent supports ACP MCP server transports.
6888#[cfg(feature = "unstable_mcp_over_acp")]
6889#[serde_as]
6890#[skip_serializing_none]
6891#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6892#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6893#[non_exhaustive]
6894pub struct McpAcpCapabilities {
6895    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6896    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6897    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6898    ///
6899    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6900    #[serde_as(deserialize_as = "DefaultOnError")]
6901    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6902    #[serde(default)]
6903    #[serde(rename = "_meta")]
6904    pub meta: Option<Meta>,
6905}
6906
6907#[cfg(feature = "unstable_mcp_over_acp")]
6908impl McpAcpCapabilities {
6909    /// Builds an empty [`McpAcpCapabilities`]. Use builder methods to advertise supported sub-capabilities.
6910    #[must_use]
6911    pub fn new() -> Self {
6912        Self::default()
6913    }
6914
6915    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6916    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6917    /// do not recognize.
6918    ///
6919    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6920    #[must_use]
6921    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6922        self.meta = meta.into_option();
6923        self
6924    }
6925}
6926
6927/// Notification to cancel a session's foreground work and its prompts not yet inserted.
6928///
6929/// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#cancellation)
6930#[serde_as]
6931#[skip_serializing_none]
6932#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
6933#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6934#[cfg_attr(feature = "schemars", schemars(extend("x-side" = "agent", "x-method" = SESSION_CANCEL_METHOD_NAME)))]
6935#[serde(rename_all = "camelCase")]
6936#[non_exhaustive]
6937pub struct CancelSessionNotification {
6938    /// The ID of the session to cancel operations for.
6939    pub session_id: SessionId,
6940    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6941    /// metadata to their interactions. Omitted and `null` are equivalent and mean no metadata.
6942    /// Implementations MUST NOT assign meaning to keys they do not recognize.
6943    ///
6944    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6945    #[serde_as(deserialize_as = "DefaultOnError")]
6946    #[cfg_attr(feature = "schemars", schemars(extend("x-deserialize-default-on-error" = true)))]
6947    #[serde(default)]
6948    #[serde(rename = "_meta")]
6949    pub meta: Option<Meta>,
6950}
6951
6952impl CancelSessionNotification {
6953    /// Builds [`CancelSessionNotification`] with the required notification fields set. Optional fields start unset or empty.
6954    #[must_use]
6955    pub fn new(session_id: impl Into<SessionId>) -> Self {
6956        Self {
6957            session_id: session_id.into(),
6958            meta: None,
6959        }
6960    }
6961
6962    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
6963    /// metadata to their interactions. Implementations MUST NOT assign meaning to keys they
6964    /// do not recognize.
6965    ///
6966    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
6967    #[must_use]
6968    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
6969        self.meta = meta.into_option();
6970        self
6971    }
6972}
6973
6974// Method schema
6975
6976/// Names of all methods that agents handle.
6977///
6978/// Provides a centralized definition of method names used in the protocol.
6979#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6980#[non_exhaustive]
6981pub struct AgentMethodNames {
6982    /// Method for initializing the connection.
6983    pub initialize: &'static str,
6984    /// Method for authenticating with the agent.
6985    pub auth_login: &'static str,
6986    /// Method for listing configurable providers.
6987    #[cfg(feature = "unstable_llm_providers")]
6988    pub providers_list: &'static str,
6989    /// Method for setting provider configuration.
6990    #[cfg(feature = "unstable_llm_providers")]
6991    pub providers_set: &'static str,
6992    /// Method for disabling a provider.
6993    #[cfg(feature = "unstable_llm_providers")]
6994    pub providers_disable: &'static str,
6995    /// Method for creating a new session.
6996    pub session_new: &'static str,
6997    /// Method for setting a configuration option for a session.
6998    pub session_set_config_option: &'static str,
6999    /// Method for sending a prompt to the agent.
7000    pub session_prompt: &'static str,
7001    /// Notification for cancelling operations.
7002    pub session_cancel: &'static str,
7003    /// Method for exchanging MCP-over-ACP messages.
7004    #[cfg(feature = "unstable_mcp_over_acp")]
7005    pub mcp_message: &'static str,
7006    /// Method for listing existing sessions.
7007    pub session_list: &'static str,
7008    /// Method for deleting an existing session.
7009    pub session_delete: &'static str,
7010    /// Method for forking an existing session.
7011    #[cfg(feature = "unstable_session_fork")]
7012    pub session_fork: &'static str,
7013    /// Method for archiving a session.
7014    #[cfg(feature = "unstable_session_archive")]
7015    pub session_archive: &'static str,
7016    /// Method for unarchiving a session.
7017    #[cfg(feature = "unstable_session_archive")]
7018    pub session_unarchive: &'static str,
7019    /// Method for checking the status of a session.
7020    #[cfg(feature = "unstable_session_status")]
7021    pub session_status: &'static str,
7022    /// Method for setting the title of a session.
7023    #[cfg(feature = "unstable_session_set_title")]
7024    pub session_set_title: &'static str,
7025    /// Method for resuming an existing session.
7026    pub session_resume: &'static str,
7027    /// Method for closing an active session.
7028    pub session_close: &'static str,
7029    /// Method for cancelling one tool call.
7030    pub tool_call_cancel: &'static str,
7031    /// Method for cancelling one terminal's process.
7032    pub terminal_cancel: &'static str,
7033    /// Method for logging out of an authenticated session.
7034    pub auth_logout: &'static str,
7035    /// Method for querying the agent's authentication state.
7036    #[cfg(feature = "unstable_auth_status")]
7037    pub auth_status: &'static str,
7038}
7039
7040/// Constant containing all agent method names.
7041pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames {
7042    initialize: INITIALIZE_METHOD_NAME,
7043    auth_login: AUTH_LOGIN_METHOD_NAME,
7044    #[cfg(feature = "unstable_llm_providers")]
7045    providers_list: PROVIDERS_LIST_METHOD_NAME,
7046    #[cfg(feature = "unstable_llm_providers")]
7047    providers_set: PROVIDERS_SET_METHOD_NAME,
7048    #[cfg(feature = "unstable_llm_providers")]
7049    providers_disable: PROVIDERS_DISABLE_METHOD_NAME,
7050    session_new: SESSION_NEW_METHOD_NAME,
7051    session_set_config_option: SESSION_SET_CONFIG_OPTION_METHOD_NAME,
7052    session_prompt: SESSION_PROMPT_METHOD_NAME,
7053    session_cancel: SESSION_CANCEL_METHOD_NAME,
7054    #[cfg(feature = "unstable_mcp_over_acp")]
7055    mcp_message: MCP_MESSAGE_METHOD_NAME,
7056    session_list: SESSION_LIST_METHOD_NAME,
7057    session_delete: SESSION_DELETE_METHOD_NAME,
7058    #[cfg(feature = "unstable_session_fork")]
7059    session_fork: SESSION_FORK_METHOD_NAME,
7060    #[cfg(feature = "unstable_session_archive")]
7061    session_archive: SESSION_ARCHIVE_METHOD_NAME,
7062    #[cfg(feature = "unstable_session_archive")]
7063    session_unarchive: SESSION_UNARCHIVE_METHOD_NAME,
7064    #[cfg(feature = "unstable_session_status")]
7065    session_status: SESSION_STATUS_METHOD_NAME,
7066    #[cfg(feature = "unstable_session_set_title")]
7067    session_set_title: SESSION_SET_TITLE_METHOD_NAME,
7068    session_resume: SESSION_RESUME_METHOD_NAME,
7069    session_close: SESSION_CLOSE_METHOD_NAME,
7070    tool_call_cancel: TOOL_CALL_CANCEL_METHOD_NAME,
7071    terminal_cancel: TERMINAL_CANCEL_METHOD_NAME,
7072    auth_logout: AUTH_LOGOUT_METHOD_NAME,
7073    #[cfg(feature = "unstable_auth_status")]
7074    auth_status: AUTH_STATUS_METHOD_NAME,
7075};
7076
7077/// Method name for the initialize request.
7078pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize";
7079/// Method name for the `auth/login` request.
7080pub(crate) const AUTH_LOGIN_METHOD_NAME: &str = "auth/login";
7081/// Method name for listing configurable providers.
7082#[cfg(feature = "unstable_llm_providers")]
7083pub(crate) const PROVIDERS_LIST_METHOD_NAME: &str = "providers/list";
7084/// Method name for setting provider configuration.
7085#[cfg(feature = "unstable_llm_providers")]
7086pub(crate) const PROVIDERS_SET_METHOD_NAME: &str = "providers/set";
7087/// Method name for disabling a provider.
7088#[cfg(feature = "unstable_llm_providers")]
7089pub(crate) const PROVIDERS_DISABLE_METHOD_NAME: &str = "providers/disable";
7090/// Method name for creating a new session.
7091pub(crate) const SESSION_NEW_METHOD_NAME: &str = "session/new";
7092/// Method name for setting a configuration option for a session.
7093pub(crate) const SESSION_SET_CONFIG_OPTION_METHOD_NAME: &str = "session/set_config_option";
7094/// Method name for sending a prompt.
7095pub(crate) const SESSION_PROMPT_METHOD_NAME: &str = "session/prompt";
7096/// Method name for the cancel notification.
7097pub(crate) const SESSION_CANCEL_METHOD_NAME: &str = "session/cancel";
7098/// Method name for listing existing sessions.
7099pub(crate) const SESSION_LIST_METHOD_NAME: &str = "session/list";
7100/// Method name for deleting an existing session.
7101pub(crate) const SESSION_DELETE_METHOD_NAME: &str = "session/delete";
7102/// Method name for forking an existing session.
7103#[cfg(feature = "unstable_session_fork")]
7104pub(crate) const SESSION_FORK_METHOD_NAME: &str = "session/fork";
7105/// Method name for archiving a session.
7106#[cfg(feature = "unstable_session_archive")]
7107pub(crate) const SESSION_ARCHIVE_METHOD_NAME: &str = "session/archive";
7108/// Method name for unarchiving a session.
7109#[cfg(feature = "unstable_session_archive")]
7110pub(crate) const SESSION_UNARCHIVE_METHOD_NAME: &str = "session/unarchive";
7111/// Method name for checking the status of a session.
7112#[cfg(feature = "unstable_session_status")]
7113pub(crate) const SESSION_STATUS_METHOD_NAME: &str = "session/status";
7114/// Method name for setting the title of a session.
7115#[cfg(feature = "unstable_session_set_title")]
7116pub(crate) const SESSION_SET_TITLE_METHOD_NAME: &str = "session/set_title";
7117/// Method name for resuming an existing session.
7118pub(crate) const SESSION_RESUME_METHOD_NAME: &str = "session/resume";
7119/// Method name for closing an active session.
7120pub(crate) const SESSION_CLOSE_METHOD_NAME: &str = "session/close";
7121/// Method name for cancelling one tool call.
7122pub(crate) const TOOL_CALL_CANCEL_METHOD_NAME: &str = "tool_call/cancel";
7123/// Method name for cancelling one terminal's process.
7124pub(crate) const TERMINAL_CANCEL_METHOD_NAME: &str = "terminal/cancel";
7125/// Method name for the `auth/logout` request.
7126pub(crate) const AUTH_LOGOUT_METHOD_NAME: &str = "auth/logout";
7127/// Method name for the `auth/status` request.
7128#[cfg(feature = "unstable_auth_status")]
7129pub(crate) const AUTH_STATUS_METHOD_NAME: &str = "auth/status";
7130
7131/// All possible requests that a client can send to an agent.
7132///
7133/// This enum is used internally for routing RPC requests. You typically won't need
7134/// to use this directly.
7135///
7136/// This enum encompasses all method calls from client to agent.
7137#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7138#[derive(Clone, Debug, Serialize, Deserialize)]
7139#[serde(untagged)]
7140#[cfg_attr(feature = "schemars", schemars(inline))]
7141#[non_exhaustive]
7142pub enum ClientRequest {
7143    /// Establishes the connection with a client and negotiates protocol capabilities.
7144    ///
7145    /// This method is called at the beginning of the connection, before any other request, to:
7146    /// - Negotiate the protocol version to use
7147    /// - Exchange capability information between client and agent
7148    /// - Determine available authentication methods
7149    ///
7150    /// The Client may repeat it before its first other request. The latest successful
7151    /// `initialize` is authoritative.
7152    ///
7153    /// The agent should respond with its supported protocol version and capabilities.
7154    ///
7155    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
7156    InitializeRequest(Box<InitializeRequest>),
7157    /// Authenticates the client using the specified authentication method.
7158    ///
7159    /// Agents MUST support this method when their `initialize` response included
7160    /// a non-empty `authMethods` list. Clients MUST call this method only
7161    /// with a method whose type defines a protocol-driven login flow, and MUST NOT
7162    /// call it when `authMethods` was omitted or empty.
7163    ///
7164    /// Called when the agent requires authentication before allowing session creation.
7165    /// The client provides the authentication method ID that was advertised during initialization.
7166    ///
7167    /// After successful authentication, the client can proceed to create sessions with
7168    /// `session/new` without receiving an `auth_required` (`-32000`) error.
7169    ///
7170    /// See protocol docs: [Initialization](https://agentclientprotocol.com/protocol/v2/initialization)
7171    LoginAuthRequest(Box<LoginAuthRequest>),
7172    /// **UNSTABLE**
7173    ///
7174    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7175    ///
7176    /// Lists providers that can be configured by the client.
7177    #[cfg(feature = "unstable_llm_providers")]
7178    ListProvidersRequest(Box<ListProvidersRequest>),
7179    /// **UNSTABLE**
7180    ///
7181    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7182    ///
7183    /// Replaces the configuration for a provider.
7184    #[cfg(feature = "unstable_llm_providers")]
7185    SetProviderRequest(Box<SetProviderRequest>),
7186    /// **UNSTABLE**
7187    ///
7188    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7189    ///
7190    /// Disables a provider.
7191    #[cfg(feature = "unstable_llm_providers")]
7192    DisableProviderRequest(Box<DisableProviderRequest>),
7193    /// Logs out of the current authenticated state.
7194    ///
7195    /// Agents MUST support this method when their `initialize` response included
7196    /// a non-empty `authMethods` list. Clients MUST NOT call this method
7197    /// when `authMethods` was omitted or empty.
7198    ///
7199    /// After a successful logout, authentication-gated requests require the
7200    /// client to complete an advertised authentication flow again. There is no
7201    /// guarantee about the behavior of already running sessions.
7202    LogoutAuthRequest(Box<LogoutAuthRequest>),
7203    /// **UNSTABLE**
7204    ///
7205    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7206    ///
7207    /// Queries the agent's current authentication state.
7208    ///
7209    /// This method is only available if the agent advertises the `auth.status` capability.
7210    /// It MUST NOT change any agent state, and agents MUST NOT reject it with `auth_required`.
7211    #[cfg(feature = "unstable_auth_status")]
7212    AuthStatusRequest(Box<AuthStatusRequest>),
7213    /// Creates a new conversation session with the agent.
7214    ///
7215    /// Sessions represent independent conversation contexts with their own history and state.
7216    ///
7217    /// The agent should:
7218    /// - Create a new session context
7219    /// - Connect to any specified MCP servers
7220    /// - Return a unique session ID for future requests
7221    ///
7222    /// May return an `auth_required` (`-32000`) error if the agent requires authentication.
7223    ///
7224    /// See protocol docs: [Session Setup](https://agentclientprotocol.com/protocol/v2/session-setup)
7225    NewSessionRequest(Box<NewSessionRequest>),
7226    /// Lists existing sessions known to the agent.
7227    ///
7228    /// The agent should return metadata about sessions with optional filtering and pagination support.
7229    ListSessionsRequest(Box<ListSessionsRequest>),
7230    /// Deletes an existing session from `session/list`.
7231    ///
7232    /// This method is only available if the agent advertises the `session.delete` capability.
7233    DeleteSessionRequest(Box<DeleteSessionRequest>),
7234    #[cfg(feature = "unstable_session_fork")]
7235    /// **UNSTABLE**
7236    ///
7237    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7238    ///
7239    /// Forks an existing session to create a new independent session.
7240    ///
7241    /// This method is only available if the agent advertises the `session.fork` capability.
7242    ///
7243    /// The agent should create a new session with the same conversation context as the
7244    /// original, allowing operations like generating summaries without affecting the
7245    /// original session's history.
7246    ///
7247    /// As with `session/new`, the forked session is active once the agent responds,
7248    /// set up with the request's `cwd`, `additionalDirectories`, and `mcpServers`.
7249    /// The client can prompt it without calling `session/resume` first.
7250    ForkSessionRequest(Box<ForkSessionRequest>),
7251    #[cfg(feature = "unstable_session_archive")]
7252    /// **UNSTABLE**
7253    ///
7254    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7255    ///
7256    /// Archives a session, hiding it from default `session/list` results.
7257    ///
7258    /// This method is only available if the agent advertises the `session.archive` capability.
7259    ArchiveSessionRequest(Box<ArchiveSessionRequest>),
7260    #[cfg(feature = "unstable_session_archive")]
7261    /// **UNSTABLE**
7262    ///
7263    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7264    ///
7265    /// Unarchives a session, returning it to default `session/list` results.
7266    ///
7267    /// This method is only available if the agent advertises the `session.archive` capability.
7268    UnarchiveSessionRequest(Box<UnarchiveSessionRequest>),
7269    #[cfg(feature = "unstable_session_status")]
7270    /// **UNSTABLE**
7271    ///
7272    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7273    ///
7274    /// Checks whether the Agent has a session in memory, without changing it.
7275    ///
7276    /// This method is only available if the agent advertises the `session.status` capability.
7277    SessionStatusRequest(Box<SessionStatusRequest>),
7278    #[cfg(feature = "unstable_session_set_title")]
7279    /// **UNSTABLE**
7280    ///
7281    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7282    ///
7283    /// Sets the title of a session.
7284    ///
7285    /// This method is only available if the agent advertises the `session.title` capability.
7286    ///
7287    /// The session does not need to be active on the connection. The agent stores the
7288    /// title and returns the stored title, which may be normalized.
7289    SetSessionTitleRequest(Box<SetSessionTitleRequest>),
7290    /// Resumes an existing session.
7291    ///
7292    /// The agent should resume the session context, allowing the conversation
7293    /// to continue. If `replayFrom` is set, the agent should replay
7294    /// retained conversation history before responding.
7295    ResumeSessionRequest(Box<ResumeSessionRequest>),
7296    /// Closes an active session and frees up any resources associated with it.
7297    ///
7298    /// The Agent MUST cancel any ongoing work as if `session/cancel` was called, stop the
7299    /// session's background work, and then free the resources associated with the session.
7300    CloseSessionRequest(Box<CloseSessionRequest>),
7301    /// Cancels one tool call without cancelling the rest of the session's work.
7302    ///
7303    /// This method is only available while the tool call advertises `capabilities.cancel`.
7304    /// The response means the Agent accepted the cancellation. A final `tool_call_update`
7305    /// confirms it.
7306    CancelToolCallRequest(Box<CancelToolCallRequest>),
7307    /// Stops one terminal's process without cancelling the rest of the session's work.
7308    ///
7309    /// This method is only available while the terminal advertises `capabilities.cancel`.
7310    /// The response means the Agent accepted the cancellation. A `terminal_update` with
7311    /// `exitStatus` confirms it.
7312    CancelTerminalRequest(Box<CancelTerminalRequest>),
7313    /// Sets the current value for a session configuration option.
7314    SetSessionConfigOptionRequest(Box<SetSessionConfigOptionRequest>),
7315    /// Processes a user prompt within a session.
7316    ///
7317    /// Acceptance means adding a logical user message with an ID and a position
7318    /// relative to other messages in the ACP conversation, not receipt or queueing.
7319    /// The Agent MUST return the inserted message's ID without waiting for
7320    /// processing to finish.
7321    ///
7322    /// The Agent reports the user message with the same ID through `session/update`.
7323    /// This notification may arrive before or after the response, but neither may
7324    /// precede insertion. Processing state, output, tool calls, and completion are
7325    /// also reported through session updates.
7326    ///
7327    /// An error response means the user message was not inserted. Once it is
7328    /// inserted, the Agent MUST NOT answer with an error, even if the work fails
7329    /// before the response is sent. It ends that work with the `error` stop reason.
7330    ///
7331    /// A rejected submission produces no user-message updates. Foreground state
7332    /// follows its normal lifecycle independently of the request outcome:
7333    /// `running` may precede insertion, and rejection alone does not imply `idle`.
7334    ///
7335    /// See protocol docs: [Prompt Lifecycle](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle)
7336    PromptRequest(Box<PromptRequest>),
7337    /// Handles extension method requests from the client.
7338    ///
7339    /// Extension methods provide a way to add custom functionality while maintaining
7340    /// protocol compatibility.
7341    ///
7342    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
7343    ExtMethodRequest(Box<ExtRequest>),
7344}
7345
7346impl ClientRequest {
7347    /// Returns the corresponding method name of the request.
7348    #[must_use]
7349    pub fn method(&self) -> &str {
7350        match self {
7351            Self::InitializeRequest(_) => AGENT_METHOD_NAMES.initialize,
7352            Self::LoginAuthRequest(_) => AGENT_METHOD_NAMES.auth_login,
7353            #[cfg(feature = "unstable_llm_providers")]
7354            Self::ListProvidersRequest(_) => AGENT_METHOD_NAMES.providers_list,
7355            #[cfg(feature = "unstable_llm_providers")]
7356            Self::SetProviderRequest(_) => AGENT_METHOD_NAMES.providers_set,
7357            #[cfg(feature = "unstable_llm_providers")]
7358            Self::DisableProviderRequest(_) => AGENT_METHOD_NAMES.providers_disable,
7359            Self::LogoutAuthRequest(_) => AGENT_METHOD_NAMES.auth_logout,
7360            #[cfg(feature = "unstable_auth_status")]
7361            Self::AuthStatusRequest(_) => AGENT_METHOD_NAMES.auth_status,
7362            Self::NewSessionRequest(_) => AGENT_METHOD_NAMES.session_new,
7363            Self::ListSessionsRequest(_) => AGENT_METHOD_NAMES.session_list,
7364            Self::DeleteSessionRequest(_) => AGENT_METHOD_NAMES.session_delete,
7365            #[cfg(feature = "unstable_session_fork")]
7366            Self::ForkSessionRequest(_) => AGENT_METHOD_NAMES.session_fork,
7367            #[cfg(feature = "unstable_session_archive")]
7368            Self::ArchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_archive,
7369            #[cfg(feature = "unstable_session_archive")]
7370            Self::UnarchiveSessionRequest(_) => AGENT_METHOD_NAMES.session_unarchive,
7371            #[cfg(feature = "unstable_session_status")]
7372            Self::SessionStatusRequest(_) => AGENT_METHOD_NAMES.session_status,
7373            #[cfg(feature = "unstable_session_set_title")]
7374            Self::SetSessionTitleRequest(_) => AGENT_METHOD_NAMES.session_set_title,
7375            Self::ResumeSessionRequest(_) => AGENT_METHOD_NAMES.session_resume,
7376            Self::CloseSessionRequest(_) => AGENT_METHOD_NAMES.session_close,
7377            Self::CancelToolCallRequest(_) => AGENT_METHOD_NAMES.tool_call_cancel,
7378            Self::CancelTerminalRequest(_) => AGENT_METHOD_NAMES.terminal_cancel,
7379            Self::SetSessionConfigOptionRequest(_) => AGENT_METHOD_NAMES.session_set_config_option,
7380            Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt,
7381            Self::ExtMethodRequest(ext_request) => &ext_request.method,
7382        }
7383    }
7384}
7385
7386/// All possible responses that an agent can send to a client.
7387///
7388/// This enum is used internally for routing RPC responses. You typically won't need
7389/// to use this directly - the responses are handled automatically by the connection.
7390///
7391/// These are responses to the corresponding `ClientRequest` variants.
7392#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7393#[derive(Clone, Debug, Serialize, Deserialize)]
7394#[serde(untagged)]
7395#[cfg_attr(feature = "schemars", schemars(inline))]
7396#[non_exhaustive]
7397pub enum AgentResponse {
7398    /// Successful result returned for a `initialize` request.
7399    InitializeResponse(Box<InitializeResponse>),
7400    /// Successful result returned for an `auth/login` request.
7401    LoginAuthResponse(#[serde(default)] Box<LoginAuthResponse>),
7402    /// Successful result returned for a `providers/list` request.
7403    #[cfg(feature = "unstable_llm_providers")]
7404    ListProvidersResponse(Box<ListProvidersResponse>),
7405    /// Successful result returned for a `providers/set` request.
7406    #[cfg(feature = "unstable_llm_providers")]
7407    SetProviderResponse(#[serde(default)] Box<SetProviderResponse>),
7408    /// Successful result returned for a `providers/disable` request.
7409    #[cfg(feature = "unstable_llm_providers")]
7410    DisableProviderResponse(#[serde(default)] Box<DisableProviderResponse>),
7411    /// Successful result returned for an `auth/logout` request.
7412    LogoutAuthResponse(#[serde(default)] Box<LogoutAuthResponse>),
7413    /// Successful result returned for an `auth/status` request.
7414    #[cfg(feature = "unstable_auth_status")]
7415    AuthStatusResponse(Box<AuthStatusResponse>),
7416    /// Successful result returned for a `session/new` request.
7417    NewSessionResponse(Box<NewSessionResponse>),
7418    /// Successful result returned for a `session/list` request.
7419    ListSessionsResponse(Box<ListSessionsResponse>),
7420    /// Successful result returned for a `session/delete` request.
7421    DeleteSessionResponse(#[serde(default)] Box<DeleteSessionResponse>),
7422    /// Successful result returned for a `session/fork` request.
7423    #[cfg(feature = "unstable_session_fork")]
7424    ForkSessionResponse(Box<ForkSessionResponse>),
7425    /// Successful result returned for a `session/archive` request.
7426    #[cfg(feature = "unstable_session_archive")]
7427    ArchiveSessionResponse(#[serde(default)] Box<ArchiveSessionResponse>),
7428    /// Successful result returned for a `session/unarchive` request.
7429    #[cfg(feature = "unstable_session_archive")]
7430    UnarchiveSessionResponse(#[serde(default)] Box<UnarchiveSessionResponse>),
7431    /// Successful result returned for a `session/status` request.
7432    #[cfg(feature = "unstable_session_status")]
7433    SessionStatusResponse(Box<SessionStatusResponse>),
7434    /// Successful result returned for a `session/set_title` request.
7435    #[cfg(feature = "unstable_session_set_title")]
7436    SetSessionTitleResponse(Box<SetSessionTitleResponse>),
7437    /// Successful result returned for a `session/resume` request.
7438    ResumeSessionResponse(#[serde(default)] Box<ResumeSessionResponse>),
7439    /// Successful result returned for a `session/close` request.
7440    CloseSessionResponse(#[serde(default)] Box<CloseSessionResponse>),
7441    /// Successful result returned for a `tool_call/cancel` request.
7442    CancelToolCallResponse(#[serde(default)] Box<CancelToolCallResponse>),
7443    /// Successful result returned for a `terminal/cancel` request.
7444    CancelTerminalResponse(#[serde(default)] Box<CancelTerminalResponse>),
7445    /// Successful result returned for a `session/set_config_option` request.
7446    SetSessionConfigOptionResponse(Box<SetSessionConfigOptionResponse>),
7447    /// Successful result returned for a `session/prompt` request.
7448    PromptResponse(Box<PromptResponse>),
7449    /// Successful result returned by an extension method outside the core ACP method set.
7450    ExtMethodResponse(Box<ExtResponse>),
7451}
7452
7453/// All possible notifications that a client can send to an agent.
7454///
7455/// This enum is used internally for routing RPC notifications. You typically won't need
7456/// to use this directly.
7457///
7458/// Notifications do not expect a response.
7459#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
7460#[derive(Clone, Debug, Serialize, Deserialize)]
7461#[serde(untagged)]
7462#[cfg_attr(feature = "schemars", schemars(inline))]
7463#[non_exhaustive]
7464pub enum ClientNotification {
7465    /// Cancels a session's foreground work and its prompts not yet inserted.
7466    ///
7467    /// Work in the latest `background` list is not covered and keeps running. The
7468    /// Agent MAY stop background activity it does not list.
7469    ///
7470    /// Upon receiving this notification, the Agent SHOULD stop all language model
7471    /// requests and all tool call invocations of the cancelled foreground work as soon
7472    /// as possible.
7473    ///
7474    /// The Agent MUST stop waiting for pending `session/request_permission` requests of
7475    /// the cancelled work and treat them as not approved. It SHOULD cancel each of them
7476    /// with `$/cancel_request`.
7477    ///
7478    /// After all ongoing operations have been successfully aborted and pending
7479    /// updates have been sent, the Agent MUST send an idle `state_update` session
7480    /// update with the `cancelled` stop reason if cancellation ended active
7481    /// foreground work. No additional completion update is required if no
7482    /// foreground work was active.
7483    ///
7484    /// After cancellation completes, the Agent SHOULD ignore late answers rather
7485    /// than use them to resume the cancelled work.
7486    ///
7487    /// Agents MUST catch errors caused by aborting cancelled work and report the
7488    /// `cancelled` stop reason, not `error`.
7489    ///
7490    /// The Agent MAY send content or tool call updates after receiving
7491    /// `session/cancel`, but MUST send them before the idle `state_update` that
7492    /// reports cancellation.
7493    ///
7494    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/v2/prompt-lifecycle#cancellation)
7495    CancelSessionNotification(Box<CancelSessionNotification>),
7496    /// **UNSTABLE**
7497    ///
7498    /// This capability is not part of the spec yet, and may be removed or changed at any point.
7499    ///
7500    /// Sends an MCP-over-ACP notification.
7501    #[cfg(feature = "unstable_mcp_over_acp")]
7502    MessageMcpNotification(Box<MessageMcpNotification>),
7503    /// Handles extension notifications from the client.
7504    ///
7505    /// Extension notifications provide a way to send one-way messages for custom functionality
7506    /// while maintaining protocol compatibility.
7507    ///
7508    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/extensibility)
7509    ExtNotification(Box<ExtNotification>),
7510}
7511
7512impl ClientNotification {
7513    /// Returns the corresponding method name of the notification.
7514    #[must_use]
7515    pub fn method(&self) -> &str {
7516        match self {
7517            Self::CancelSessionNotification(_) => AGENT_METHOD_NAMES.session_cancel,
7518            #[cfg(feature = "unstable_mcp_over_acp")]
7519            Self::MessageMcpNotification(_) => AGENT_METHOD_NAMES.mcp_message,
7520            Self::ExtNotification(ext_notification) => &ext_notification.method,
7521        }
7522    }
7523}
7524
7525#[cfg(test)]
7526mod test_serialization {
7527    use std::path::PathBuf;
7528
7529    use super::*;
7530    use serde_json::json;
7531
7532    fn test_meta() -> Meta {
7533        json!({ "source": "test" }).as_object().unwrap().clone()
7534    }
7535
7536    #[test]
7537    fn list_sessions_cursor_and_session_directories_are_strict() {
7538        for cursor in [json!(null), json!("next")] {
7539            let response = json!({ "sessions": [], "nextCursor": cursor });
7540            serde_json::from_value::<ListSessionsResponse>(response).unwrap();
7541        }
7542        let malformed = json!({ "sessions": [], "nextCursor": 42 });
7543        assert!(serde_json::from_value::<ListSessionsResponse>(malformed).is_err());
7544
7545        let session = |dirs: serde_json::Value| json!({ "sessionId": "s", "cwd": "/w", "additionalDirectories": dirs });
7546        let info: SessionInfo = serde_json::from_value(session(json!(null))).unwrap();
7547        assert_eq!(info.additional_directories, Vec::<AbsolutePath>::new());
7548        for bad in [json!(["/a", 42]), json!("/a")] {
7549            assert!(serde_json::from_value::<SessionInfo>(session(bad)).is_err());
7550        }
7551        // A session with malformed directories is skipped, not truncated.
7552        let response = json!({ "sessions": [session(json!(["/a", 42]))] });
7553        let response: ListSessionsResponse = serde_json::from_value(response).unwrap();
7554        assert_eq!(response.sessions, Vec::<SessionInfo>::new());
7555    }
7556
7557    #[test]
7558    fn other_fallbacks_preserve_optional_fields_verbatim() {
7559        let valid = json!({ "type": "_custom", "methodId": "m", "name": "M",
7560            "description": "d", "_meta": { "k": 1 } });
7561        let method: AuthMethod = serde_json::from_value(valid.clone()).unwrap();
7562        assert_eq!(method.description(), Some("d"));
7563        assert_eq!(
7564            method.meta(),
7565            Some(&json!({ "k": 1 }).as_object().unwrap().clone())
7566        );
7567        assert_eq!(serde_json::to_value(&method).unwrap(), valid);
7568
7569        // Malformed optional fields are kept as sent and read as absent.
7570        let malformed = json!({ "type": "_custom", "methodId": "m", "name": "M",
7571            "description": 42, "_meta": "bad", "extra": true });
7572        let method: AuthMethod = serde_json::from_value(malformed.clone()).unwrap();
7573        assert_eq!(method.description(), None);
7574        assert_eq!(method.meta(), None);
7575        assert_eq!(serde_json::to_value(&method).unwrap(), malformed);
7576
7577        let replay = json!({ "type": "_custom", "_meta": "bad", "cursor": 3 });
7578        let decoded: ReplayFrom = serde_json::from_value(replay.clone()).unwrap();
7579        assert_eq!(serde_json::to_value(&decoded).unwrap(), replay);
7580    }
7581
7582    fn serialized_meta_key_count(value: &impl serde::Serialize) -> usize {
7583        serde_json::to_string(value)
7584            .unwrap()
7585            .matches("\"_meta\"")
7586            .count()
7587    }
7588
7589    #[test]
7590    fn prompt_response_without_metadata_round_trips() {
7591        let response = PromptResponse::new(MessageId::new("message-1"));
7592        let serialized = serde_json::to_value(&response).unwrap();
7593        assert_eq!(serialized, json!({ "messageId": "message-1" }));
7594        assert_eq!(
7595            serde_json::from_value::<PromptResponse>(serialized).unwrap(),
7596            response
7597        );
7598        assert_eq!(
7599            serde_json::from_value::<PromptResponse>(json!({
7600                "messageId": "message-1",
7601                "_meta": null
7602            }))
7603            .unwrap(),
7604            response
7605        );
7606    }
7607
7608    #[test]
7609    fn prompt_response_requires_message_id_and_preserves_metadata_tolerance() {
7610        let response = PromptResponse::new("message-1").meta(test_meta());
7611        let serialized = serde_json::to_value(&response).unwrap();
7612        assert_eq!(
7613            serialized,
7614            json!({
7615                "messageId": "message-1",
7616                "_meta": { "source": "test" }
7617            })
7618        );
7619
7620        let deserialized: PromptResponse = serde_json::from_value(serialized).unwrap();
7621        assert_eq!(deserialized.message_id, MessageId::new("message-1"));
7622        assert_eq!(deserialized.meta, Some(test_meta()));
7623
7624        let malformed_meta: PromptResponse = serde_json::from_value(json!({
7625            "messageId": "message-2",
7626            "_meta": false
7627        }))
7628        .unwrap();
7629        assert_eq!(malformed_meta.message_id, MessageId::new("message-2"));
7630        assert_eq!(malformed_meta.meta, None);
7631
7632        for invalid in [
7633            json!({}),
7634            json!({ "messageId": null }),
7635            json!({ "messageId": 1 }),
7636            json!({ "messageId": false }),
7637            json!({ "messageId": {} }),
7638            json!({ "messageId": [] }),
7639        ] {
7640            assert!(
7641                serde_json::from_value::<PromptResponse>(invalid).is_err(),
7642                "missing, null, and non-string message IDs must be rejected"
7643            );
7644        }
7645    }
7646
7647    #[cfg(feature = "schemars")]
7648    #[test]
7649    fn prompt_response_schema_requires_non_null_string_message_id_reference() {
7650        let schema = serde_json::to_value(schemars::schema_for!(PromptResponse)).unwrap();
7651
7652        assert_eq!(schema["required"], json!(["messageId"]));
7653        assert_eq!(
7654            schema["properties"]["messageId"]["$ref"],
7655            "#/$defs/MessageId"
7656        );
7657        assert_eq!(schema["$defs"]["MessageId"]["type"], "string");
7658    }
7659
7660    #[test]
7661    fn test_initialize_capabilities_default_on_malformed_values() {
7662        let request: InitializeRequest = serde_json::from_value(json!({
7663            "protocolVersion": 2,
7664            "capabilities": false,
7665            "info": {
7666                "name": "client",
7667                "version": "1.0.0"
7668            }
7669        }))
7670        .unwrap();
7671        assert_eq!(request.capabilities, ClientCapabilities::default());
7672
7673        let response: InitializeResponse = serde_json::from_value(json!({
7674            "protocolVersion": 2,
7675            "capabilities": false,
7676            "info": {
7677                "name": "agent",
7678                "version": "1.0.0"
7679            }
7680        }))
7681        .unwrap();
7682        assert_eq!(response.capabilities, AgentCapabilities::default());
7683    }
7684
7685    #[test]
7686    fn test_agent_capabilities_default_on_malformed_values() {
7687        let capabilities: AgentCapabilities = serde_json::from_value(json!({
7688            "session": false,
7689            "auth": false
7690        }))
7691        .unwrap();
7692
7693        assert!(capabilities.session.is_none());
7694        assert_eq!(capabilities.auth, None);
7695    }
7696
7697    #[test]
7698    fn test_mcp_server_stdio_serialization() {
7699        let server = McpServer::Stdio(
7700            McpServerStdio::new("test-server", "/usr/bin/server")
7701                .args(vec!["--port".to_string(), "3000".to_string()])
7702                .env(vec![EnvVariable::new("API_KEY", "secret123")]),
7703        );
7704
7705        let json = serde_json::to_value(&server).unwrap();
7706        assert_eq!(
7707            json,
7708            json!({
7709                "type": "stdio",
7710                "name": "test-server",
7711                "command": "/usr/bin/server",
7712                "args": ["--port", "3000"],
7713                "env": [
7714                    {
7715                        "name": "API_KEY",
7716                        "value": "secret123"
7717                    }
7718                ]
7719            })
7720        );
7721
7722        let deserialized: McpServer = serde_json::from_value(json).unwrap();
7723        match deserialized {
7724            McpServer::Stdio(McpServerStdio {
7725                name,
7726                command,
7727                args,
7728                env,
7729                meta: _,
7730            }) => {
7731                assert_eq!(name, "test-server");
7732                assert_eq!(command, AbsolutePath::new("/usr/bin/server"));
7733                assert_eq!(args, vec!["--port", "3000"]);
7734                assert_eq!(env.len(), 1);
7735                assert_eq!(env[0].name, "API_KEY");
7736                assert_eq!(env[0].value, "secret123");
7737            }
7738            _ => panic!("Expected Stdio variant"),
7739        }
7740    }
7741
7742    #[test]
7743    fn test_mcp_server_empty_arrays_are_optional() {
7744        let stdio = McpServer::Stdio(McpServerStdio::new("test-server", "/usr/bin/server"));
7745        assert_eq!(
7746            serde_json::to_value(&stdio).unwrap(),
7747            json!({
7748                "type": "stdio",
7749                "name": "test-server",
7750                "command": "/usr/bin/server"
7751            })
7752        );
7753
7754        let McpServer::Stdio(McpServerStdio { args, env, .. }) =
7755            serde_json::from_value::<McpServer>(json!({
7756                "type": "stdio",
7757                "name": "test-server",
7758                "command": "/usr/bin/server"
7759            }))
7760            .unwrap()
7761        else {
7762            panic!("Expected Stdio variant");
7763        };
7764        assert_eq!(args, Vec::<String>::new());
7765        assert_eq!(env, []);
7766
7767        let http = McpServer::Http(McpServerHttp::new("http-server", "https://api.example.com"));
7768        assert_eq!(
7769            serde_json::to_value(&http).unwrap(),
7770            json!({
7771                "type": "http",
7772                "name": "http-server",
7773                "url": "https://api.example.com"
7774            })
7775        );
7776
7777        let McpServer::Http(McpServerHttp { headers, .. }) =
7778            serde_json::from_value::<McpServer>(json!({
7779                "type": "http",
7780                "name": "http-server",
7781                "url": "https://api.example.com"
7782            }))
7783            .unwrap()
7784        else {
7785            panic!("Expected Http variant");
7786        };
7787        assert_eq!(headers, []);
7788    }
7789
7790    #[test]
7791    fn test_mcp_server_unknown_transport_serialization() {
7792        let json = json!({
7793            "type": "websocket",
7794            "name": "future-server",
7795            "url": "wss://example.com/mcp",
7796            "protocolVersion": "2026-01-01"
7797        });
7798
7799        let deserialized: McpServer = serde_json::from_value(json.clone()).unwrap();
7800        let McpServer::Other(OtherMcpServer { type_, fields }) = &deserialized else {
7801            panic!("Expected Other variant");
7802        };
7803
7804        assert_eq!(type_, "websocket");
7805        assert_eq!(fields["name"], "future-server");
7806        assert_eq!(fields["url"], "wss://example.com/mcp");
7807        assert_eq!(fields["protocolVersion"], "2026-01-01");
7808        assert_eq!(serde_json::to_value(&deserialized).unwrap(), json);
7809    }
7810
7811    #[test]
7812    fn test_mcp_server_stdio_requires_type() {
7813        let result = serde_json::from_value::<McpServer>(json!({
7814            "name": "test-server",
7815            "command": "/usr/bin/server",
7816            "args": [],
7817            "env": []
7818        }));
7819
7820        assert!(result.is_err());
7821    }
7822
7823    #[test]
7824    fn test_mcp_server_unknown_does_not_hide_malformed_known_transport() {
7825        let result = serde_json::from_value::<McpServer>(json!({
7826            "type": "stdio",
7827            "name": "test-server",
7828            "args": [],
7829            "env": []
7830        }));
7831
7832        assert!(result.is_err());
7833    }
7834
7835    #[test]
7836    fn test_mcp_server_http_serialization() {
7837        let server = McpServer::Http(
7838            McpServerHttp::new("http-server", "https://api.example.com").headers(vec![
7839                HttpHeader::new("Authorization", "Bearer token123"),
7840                HttpHeader::new("Content-Type", "application/json"),
7841            ]),
7842        );
7843
7844        let json = serde_json::to_value(&server).unwrap();
7845        assert_eq!(
7846            json,
7847            json!({
7848                "type": "http",
7849                "name": "http-server",
7850                "url": "https://api.example.com",
7851                "headers": [
7852                    {
7853                        "name": "Authorization",
7854                        "value": "Bearer token123"
7855                    },
7856                    {
7857                        "name": "Content-Type",
7858                        "value": "application/json"
7859                    }
7860                ]
7861            })
7862        );
7863
7864        let deserialized: McpServer = serde_json::from_value(json).unwrap();
7865        match deserialized {
7866            McpServer::Http(McpServerHttp {
7867                name,
7868                url,
7869                headers,
7870                meta: _,
7871            }) => {
7872                assert_eq!(name, "http-server");
7873                assert_eq!(url, "https://api.example.com");
7874                assert_eq!(headers.len(), 2);
7875                assert_eq!(headers[0].name, "Authorization");
7876                assert_eq!(headers[0].value, "Bearer token123");
7877                assert_eq!(headers[1].name, "Content-Type");
7878                assert_eq!(headers[1].value, "application/json");
7879            }
7880            _ => panic!("Expected Http variant"),
7881        }
7882    }
7883
7884    #[cfg(feature = "schemars")]
7885    #[test]
7886    fn mcp_server_http_schema_marks_url_as_uri() {
7887        let schema = serde_json::to_value(schemars::schema_for!(McpServerHttp)).unwrap();
7888
7889        assert_eq!(schema["properties"]["url"]["format"], "uri");
7890    }
7891
7892    #[cfg(feature = "unstable_mcp_over_acp")]
7893    #[test]
7894    fn test_client_mcp_message_method_names() {
7895        use serde_json::json;
7896
7897        assert_eq!(AGENT_METHOD_NAMES.mcp_message, "mcp/message");
7898
7899        let notification =
7900            MessageMcpNotification::new("server-1", "req-1", "notifications/progress");
7901        assert_eq!(
7902            ClientNotification::MessageMcpNotification(Box::new(notification.clone())).method(),
7903            "mcp/message"
7904        );
7905        assert_eq!(
7906            serde_json::to_value(notification).unwrap(),
7907            json!({
7908                "serverId": "server-1",
7909                "requestId": "req-1",
7910                "method": "notifications/progress"
7911            })
7912        );
7913        let notification: MessageMcpNotification = serde_json::from_value(json!({
7914            "serverId": "server-1", "requestId": "req-1", "method": "notifications/progress",
7915            "params": null, "_meta": null
7916        }))
7917        .unwrap();
7918        assert_eq!(notification.params, None);
7919        assert_eq!(notification.meta, None);
7920        for key in ["serverId", "requestId", "method"] {
7921            let mut value = json!({"serverId":"server-1", "requestId":"req-1", "method":"notifications/progress"});
7922            value.as_object_mut().unwrap().remove(key);
7923            assert!(serde_json::from_value::<MessageMcpNotification>(value).is_err());
7924        }
7925    }
7926
7927    #[test]
7928    fn test_auth_method_names() {
7929        assert_eq!(AGENT_METHOD_NAMES.auth_login, "auth/login");
7930        assert_eq!(AGENT_METHOD_NAMES.auth_logout, "auth/logout");
7931
7932        assert_eq!(
7933            ClientRequest::LoginAuthRequest(Box::new(LoginAuthRequest::new("agent-login")))
7934                .method(),
7935            "auth/login"
7936        );
7937        assert_eq!(
7938            ClientRequest::LogoutAuthRequest(Box::new(LogoutAuthRequest::new())).method(),
7939            "auth/logout"
7940        );
7941    }
7942
7943    #[test]
7944    fn test_session_config_option_category_known_variants() {
7945        // Test serialization of known variants
7946        assert_eq!(
7947            serde_json::to_value(&SessionConfigOptionCategory::Mode).unwrap(),
7948            json!("mode")
7949        );
7950        assert_eq!(
7951            serde_json::to_value(&SessionConfigOptionCategory::Model).unwrap(),
7952            json!("model")
7953        );
7954        assert_eq!(
7955            serde_json::to_value(&SessionConfigOptionCategory::ModelConfig).unwrap(),
7956            json!("model_config")
7957        );
7958        assert_eq!(
7959            serde_json::to_value(&SessionConfigOptionCategory::ThoughtLevel).unwrap(),
7960            json!("thought_level")
7961        );
7962
7963        // Test deserialization of known variants
7964        assert_eq!(
7965            serde_json::from_str::<SessionConfigOptionCategory>("\"mode\"").unwrap(),
7966            SessionConfigOptionCategory::Mode
7967        );
7968        assert_eq!(
7969            serde_json::from_str::<SessionConfigOptionCategory>("\"model\"").unwrap(),
7970            SessionConfigOptionCategory::Model
7971        );
7972        assert_eq!(
7973            serde_json::from_str::<SessionConfigOptionCategory>("\"model_config\"").unwrap(),
7974            SessionConfigOptionCategory::ModelConfig
7975        );
7976        assert_eq!(
7977            serde_json::from_str::<SessionConfigOptionCategory>("\"thought_level\"").unwrap(),
7978            SessionConfigOptionCategory::ThoughtLevel
7979        );
7980    }
7981
7982    #[test]
7983    fn test_session_config_option_category_unknown_variants() {
7984        // Test that unknown strings are captured in Other variant
7985        let unknown: SessionConfigOptionCategory =
7986            serde_json::from_str("\"some_future_category\"").unwrap();
7987        assert_eq!(
7988            unknown,
7989            SessionConfigOptionCategory::Other("some_future_category".to_string())
7990        );
7991
7992        // Test round-trip of unknown category
7993        let json = serde_json::to_value(&unknown).unwrap();
7994        assert_eq!(json, json!("some_future_category"));
7995    }
7996
7997    #[test]
7998    fn test_session_config_option_category_custom_categories() {
7999        // Category names beginning with `_` are free for custom use
8000        let custom: SessionConfigOptionCategory =
8001            serde_json::from_str("\"_my_custom_category\"").unwrap();
8002        assert_eq!(
8003            custom,
8004            SessionConfigOptionCategory::Other("_my_custom_category".to_string())
8005        );
8006
8007        // Test round-trip preserves the custom category name
8008        let json = serde_json::to_value(&custom).unwrap();
8009        assert_eq!(json, json!("_my_custom_category"));
8010
8011        // Deserialize back and verify
8012        let deserialized: SessionConfigOptionCategory = serde_json::from_value(json).unwrap();
8013        assert_eq!(
8014            deserialized,
8015            SessionConfigOptionCategory::Other("_my_custom_category".to_string()),
8016        );
8017    }
8018
8019    fn test_config_option() -> SessionConfigOption {
8020        SessionConfigOption::select(
8021            "mode",
8022            "Mode",
8023            "ask",
8024            vec![SessionConfigSelectOption::new("ask", "Ask")],
8025        )
8026    }
8027
8028    #[test]
8029    fn test_session_response_initial_state_default_empty_and_skip_serializing() {
8030        assert_eq!(
8031            serde_json::to_value(NewSessionResponse::new("sess")).unwrap(),
8032            json!({ "sessionId": "sess" })
8033        );
8034        assert_eq!(
8035            serde_json::to_value(ResumeSessionResponse::new()).unwrap(),
8036            json!({})
8037        );
8038        #[cfg(feature = "unstable_session_fork")]
8039        assert_eq!(
8040            serde_json::to_value(ForkSessionResponse::new("fork")).unwrap(),
8041            json!({ "sessionId": "fork" })
8042        );
8043
8044        let json = serde_json::to_value(
8045            NewSessionResponse::new("sess").config_options(vec![test_config_option()]),
8046        )
8047        .unwrap();
8048        assert_eq!(json["configOptions"].as_array().unwrap().len(), 1);
8049    }
8050
8051    #[test]
8052    fn test_session_response_config_options_deserialize_missing_null_and_invalid() {
8053        let missing: NewSessionResponse =
8054            serde_json::from_value(json!({ "sessionId": "sess" })).unwrap();
8055        assert_eq!(missing.config_options, []);
8056
8057        let null: NewSessionResponse = serde_json::from_value(json!({
8058            "sessionId": "sess",
8059            "configOptions": null
8060        }))
8061        .unwrap();
8062        assert_eq!(null.config_options, []);
8063
8064        let wrong_shape: NewSessionResponse = serde_json::from_value(json!({
8065            "sessionId": "sess",
8066            "configOptions": "oops"
8067        }))
8068        .unwrap();
8069        assert_eq!(wrong_shape.config_options, []);
8070
8071        let valid_option = serde_json::to_value(test_config_option()).unwrap();
8072        let mixed: NewSessionResponse = serde_json::from_value(json!({
8073            "sessionId": "sess",
8074            "configOptions": ["oops", valid_option]
8075        }))
8076        .unwrap();
8077        assert_eq!(mixed.config_options.len(), 1);
8078
8079        let resume: ResumeSessionResponse = serde_json::from_value(json!({})).unwrap();
8080        assert_eq!(resume.config_options, []);
8081        #[cfg(feature = "unstable_session_fork")]
8082        {
8083            let fork: ForkSessionResponse =
8084                serde_json::from_value(json!({ "sessionId": "fork" })).unwrap();
8085            assert_eq!(fork.config_options, []);
8086        }
8087    }
8088
8089    #[test]
8090    fn test_session_response_available_commands_roundtrip_with_config_options() {
8091        use super::super::{AvailableCommandInput, Command, TextCommandInput};
8092
8093        let commands = vec![
8094            AvailableCommand::Command(
8095                Command::new("web", "Search the web")
8096                    .input(AvailableCommandInput::Text(TextCommandInput::new("Query"))),
8097            ),
8098            AvailableCommand::Command(Command::new("test", "Run project tests")),
8099        ];
8100        let mut expected = json!({
8101            "sessionId": "sess",
8102            "configOptions": [test_config_option()],
8103            "availableCommands": [
8104                {
8105                    "type": "command",
8106                    "name": "web",
8107                    "description": "Search the web",
8108                    "input": { "type": "text", "hint": "Query" }
8109                },
8110                { "type": "command", "name": "test", "description": "Run project tests" }
8111            ]
8112        });
8113
8114        let new = NewSessionResponse::new("sess")
8115            .config_options(vec![test_config_option()])
8116            .available_commands(commands.clone());
8117        assert_eq!(serde_json::to_value(&new).unwrap(), expected);
8118        assert_eq!(
8119            serde_json::from_value::<NewSessionResponse>(expected.clone()).unwrap(),
8120            new
8121        );
8122
8123        expected.as_object_mut().unwrap().remove("sessionId");
8124        let resume = ResumeSessionResponse::new()
8125            .config_options(vec![test_config_option()])
8126            .available_commands(commands.clone());
8127        assert_eq!(serde_json::to_value(&resume).unwrap(), expected);
8128        assert_eq!(
8129            serde_json::from_value::<ResumeSessionResponse>(expected.clone()).unwrap(),
8130            resume
8131        );
8132
8133        #[cfg(feature = "unstable_session_fork")]
8134        {
8135            expected["sessionId"] = json!("fork");
8136            let fork = ForkSessionResponse::new("fork")
8137                .config_options(vec![test_config_option()])
8138                .available_commands(commands);
8139            assert_eq!(serde_json::to_value(&fork).unwrap(), expected);
8140            assert_eq!(
8141                serde_json::from_value::<ForkSessionResponse>(expected).unwrap(),
8142                fork
8143            );
8144        }
8145    }
8146
8147    #[test]
8148    fn test_session_response_available_commands_deserialize_missing_null_and_invalid() {
8149        let command =
8150            AvailableCommand::Command(super::super::Command::new("test", "Run project tests"));
8151        let config_options = vec![test_config_option()];
8152
8153        for (commands, expected) in [
8154            (None, vec![]),
8155            (Some(json!(null)), vec![]),
8156            (Some(json!([])), vec![]),
8157            (Some(json!("oops")), vec![]),
8158            (Some(json!({})), vec![]),
8159            (Some(json!([null, {}, { "name": "invalid" }])), vec![]),
8160            (
8161                Some(json!([{ "name": "test", "description": "Run project tests" }])),
8162                vec![],
8163            ),
8164            (
8165                Some(json!([null, command, { "name": "invalid" }])),
8166                vec![command.clone()],
8167            ),
8168        ] {
8169            let mut value = json!({
8170                "sessionId": "sess",
8171                "configOptions": config_options
8172            });
8173            if let Some(commands) = commands {
8174                value["availableCommands"] = commands;
8175            }
8176
8177            let new: NewSessionResponse = serde_json::from_value(value.clone()).unwrap();
8178            assert_eq!(new.available_commands, expected);
8179            assert_eq!(new.config_options, config_options);
8180
8181            #[cfg(feature = "unstable_session_fork")]
8182            {
8183                let fork: ForkSessionResponse = serde_json::from_value(value.clone()).unwrap();
8184                assert_eq!(fork.available_commands, expected);
8185                assert_eq!(fork.config_options, config_options);
8186            }
8187
8188            value.as_object_mut().unwrap().remove("sessionId");
8189            let resume: ResumeSessionResponse = serde_json::from_value(value).unwrap();
8190            assert_eq!(resume.available_commands, expected);
8191            assert_eq!(resume.config_options, config_options);
8192        }
8193
8194        let resume: ResumeSessionResponse = serde_json::from_value(json!(null)).unwrap();
8195        assert_eq!(resume.available_commands, []);
8196    }
8197
8198    #[test]
8199    fn test_resume_session_replay_from_serialization() {
8200        assert_eq!(
8201            serde_json::to_value(ResumeSessionRequest::new(
8202                "sess_abc123",
8203                "/home/user/project"
8204            ))
8205            .unwrap(),
8206            json!({
8207                "sessionId": "sess_abc123",
8208                "cwd": "/home/user/project"
8209            })
8210        );
8211        assert_eq!(
8212            serde_json::to_value(
8213                ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8214                    .replay_from(ReplayFrom::from(ReplayFromStart::new()))
8215            )
8216            .unwrap(),
8217            json!({
8218                "sessionId": "sess_abc123",
8219                "cwd": "/home/user/project",
8220                "replayFrom": {
8221                    "type": "start"
8222                }
8223            })
8224        );
8225
8226        let replay: ResumeSessionRequest = serde_json::from_value(json!({
8227            "sessionId": "sess_abc123",
8228            "cwd": "/home/user/project",
8229            "replayFrom": {
8230                "type": "start"
8231            }
8232        }))
8233        .unwrap();
8234        assert!(matches!(replay.replay_from, Some(ReplayFrom::Start(_))));
8235
8236        let none: ResumeSessionRequest = serde_json::from_value(json!({
8237            "sessionId": "sess_abc123",
8238            "cwd": "/home/user/project",
8239            "replayFrom": null
8240        }))
8241        .unwrap();
8242        assert!(none.replay_from.is_none());
8243    }
8244
8245    #[test]
8246    fn test_auth_method_agent_serialization() {
8247        let method = AuthMethod::Agent(AuthMethodAgent::new("default-auth", "Default Auth"));
8248
8249        let json = serde_json::to_value(&method).unwrap();
8250        assert_eq!(
8251            json,
8252            json!({
8253                "methodId": "default-auth",
8254                "name": "Default Auth",
8255                "type": "agent"
8256            })
8257        );
8258        // description should be omitted when None
8259        assert!(!json.as_object().unwrap().contains_key("description"));
8260
8261        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8262        match deserialized {
8263            AuthMethod::Agent(AuthMethodAgent {
8264                method_id, name, ..
8265            }) => {
8266                assert_eq!(method_id.0.as_ref(), "default-auth");
8267                assert_eq!(name, "Default Auth");
8268            }
8269            _ => panic!("Expected Agent variant"),
8270        }
8271    }
8272
8273    #[test]
8274    fn test_auth_method_agent_deserialization() {
8275        let json = json!({
8276            "methodId": "agent-auth",
8277            "name": "Agent Auth",
8278            "type": "agent"
8279        });
8280
8281        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8282        assert!(matches!(deserialized, AuthMethod::Agent(_)));
8283    }
8284
8285    #[test]
8286    fn test_auth_method_agent_requires_type() {
8287        assert!(
8288            serde_json::from_value::<AuthMethod>(json!({
8289                "methodId": "agent-auth",
8290                "name": "Agent Auth"
8291            }))
8292            .is_err()
8293        );
8294    }
8295
8296    #[test]
8297    fn test_auth_method_agent_rejects_null_type() {
8298        assert!(
8299            serde_json::from_value::<AuthMethod>(json!({
8300                "methodId": "agent-auth",
8301                "name": "Agent Auth",
8302                "type": null
8303            }))
8304            .is_err()
8305        );
8306    }
8307
8308    #[test]
8309    fn test_auth_method_unknown_does_not_hide_malformed_agent() {
8310        assert!(
8311            serde_json::from_value::<AuthMethod>(json!({
8312                "methodId": "agent-auth",
8313                "type": "agent"
8314            }))
8315            .is_err()
8316        );
8317        assert!(
8318            serde_json::from_value::<AuthMethod>(json!({
8319                "methodId": "api-key",
8320                "type": "env_var",
8321                "vars": [{"name": "API_KEY"}]
8322            }))
8323            .is_err()
8324        );
8325    }
8326
8327    #[test]
8328    fn test_auth_method_unknown_variant_roundtrip() {
8329        let method: AuthMethod = serde_json::from_value(json!({
8330            "methodId": "oauth",
8331            "name": "OAuth",
8332            "type": "_oauth",
8333            "authorizationUrl": "https://example.com/auth"
8334        }))
8335        .unwrap();
8336
8337        assert_eq!(method.method_id().0.as_ref(), "oauth");
8338        assert_eq!(method.name(), "OAuth");
8339        let AuthMethod::Other(unknown) = method else {
8340            panic!("expected unknown auth method");
8341        };
8342        assert_eq!(unknown.type_, "_oauth");
8343        assert_eq!(
8344            unknown.fields.get("authorizationUrl"),
8345            Some(&json!("https://example.com/auth"))
8346        );
8347
8348        assert_eq!(
8349            serde_json::to_value(AuthMethod::Other(unknown)).unwrap(),
8350            json!({
8351                "methodId": "oauth",
8352                "name": "OAuth",
8353                "type": "_oauth",
8354                "authorizationUrl": "https://example.com/auth"
8355            })
8356        );
8357    }
8358
8359    #[test]
8360    fn test_auth_method_unknown_does_not_hide_malformed_known_variant() {
8361        assert!(
8362            serde_json::from_value::<AuthMethod>(json!({
8363                "methodId": "terminal-auth",
8364                "type": "terminal"
8365            }))
8366            .is_err()
8367        );
8368    }
8369
8370    #[test]
8371    fn test_tool_call_and_terminal_cancel_serialization() {
8372        assert_eq!(AGENT_METHOD_NAMES.tool_call_cancel, "tool_call/cancel");
8373        assert_eq!(AGENT_METHOD_NAMES.terminal_cancel, "terminal/cancel");
8374        assert_eq!(
8375            ClientRequest::CancelToolCallRequest(Box::new(CancelToolCallRequest::new(
8376                "sess_abc123",
8377                "call_012"
8378            )))
8379            .method(),
8380            "tool_call/cancel"
8381        );
8382        assert_eq!(
8383            ClientRequest::CancelTerminalRequest(Box::new(CancelTerminalRequest::new(
8384                "sess_abc123",
8385                "term_003"
8386            )))
8387            .method(),
8388            "terminal/cancel"
8389        );
8390        assert_eq!(
8391            serde_json::to_value(CancelToolCallRequest::new("sess_abc123", "call_012")).unwrap(),
8392            json!({"sessionId": "sess_abc123", "toolCallId": "call_012"})
8393        );
8394        assert_eq!(
8395            serde_json::to_value(CancelTerminalRequest::new("sess_abc123", "term_003")).unwrap(),
8396            json!({"sessionId": "sess_abc123", "terminalId": "term_003"})
8397        );
8398        assert!(
8399            serde_json::from_value::<CancelToolCallRequest>(json!({"sessionId": "sess_abc123"}))
8400                .is_err()
8401        );
8402        assert_eq!(
8403            serde_json::to_value(CancelTerminalResponse::new()).unwrap(),
8404            json!({})
8405        );
8406    }
8407
8408    #[test]
8409    fn test_session_delete_serialization() {
8410        assert_eq!(AGENT_METHOD_NAMES.session_delete, "session/delete");
8411        assert_eq!(
8412            ClientRequest::DeleteSessionRequest(Box::new(DeleteSessionRequest::new("sess_abc123")))
8413                .method(),
8414            "session/delete"
8415        );
8416        assert_eq!(
8417            serde_json::to_value(DeleteSessionRequest::new("sess_abc123")).unwrap(),
8418            json!({
8419                "sessionId": "sess_abc123"
8420            })
8421        );
8422        assert_eq!(
8423            serde_json::to_value(DeleteSessionResponse::new()).unwrap(),
8424            json!({})
8425        );
8426        assert_eq!(
8427            serde_json::to_value(
8428                SessionCapabilities::new().delete(SessionDeleteCapabilities::new())
8429            )
8430            .unwrap(),
8431            json!({
8432                "delete": {}
8433            })
8434        );
8435    }
8436    #[cfg(feature = "unstable_session_set_title")]
8437    #[test]
8438    fn test_session_set_title_serialization() {
8439        assert_eq!(AGENT_METHOD_NAMES.session_set_title, "session/set_title");
8440        assert_eq!(
8441            ClientRequest::SetSessionTitleRequest(Box::new(SetSessionTitleRequest::new(
8442                "sess_abc123",
8443                "Investigate OAuth timeout"
8444            )))
8445            .method(),
8446            "session/set_title"
8447        );
8448        assert_eq!(
8449            serde_json::to_value(SetSessionTitleRequest::new(
8450                "sess_abc123",
8451                "Investigate OAuth timeout"
8452            ))
8453            .unwrap(),
8454            json!({
8455                "sessionId": "sess_abc123",
8456                "title": "Investigate OAuth timeout"
8457            })
8458        );
8459        assert_eq!(
8460            serde_json::to_value(SetSessionTitleResponse::new("Investigate OAuth timeout"))
8461                .unwrap(),
8462            json!({
8463                "title": "Investigate OAuth timeout"
8464            })
8465        );
8466        assert_eq!(
8467            serde_json::to_value(SessionCapabilities::new().title(SessionTitleCapabilities::new()))
8468                .unwrap(),
8469            json!({
8470                "title": {}
8471            })
8472        );
8473    }
8474
8475    #[cfg(feature = "unstable_session_set_title")]
8476    #[test]
8477    fn test_session_set_title_requires_title() {
8478        for params in [
8479            json!({"sessionId": "sess_abc123"}),
8480            json!({"sessionId": "sess_abc123", "title": null}),
8481            json!({"sessionId": "sess_abc123", "title": 42}),
8482        ] {
8483            assert!(serde_json::from_value::<SetSessionTitleRequest>(params).is_err());
8484        }
8485        for result in [json!({}), json!({"title": null})] {
8486            assert!(serde_json::from_value::<SetSessionTitleResponse>(result).is_err());
8487        }
8488    }
8489
8490    #[cfg(feature = "unstable_session_status")]
8491    #[test]
8492    fn test_session_status_serialization() {
8493        assert_eq!(AGENT_METHOD_NAMES.session_status, "session/status");
8494        assert_eq!(
8495            ClientRequest::SessionStatusRequest(Box::new(SessionStatusRequest::new("sess_abc123")))
8496                .method(),
8497            "session/status"
8498        );
8499        assert_eq!(
8500            serde_json::to_value(SessionStatusRequest::new("sess_abc123")).unwrap(),
8501            json!({"sessionId": "sess_abc123"})
8502        );
8503        assert_eq!(
8504            serde_json::to_value(SessionStatusResponse::new(SessionStatus::Saved)).unwrap(),
8505            json!({"status": "saved"})
8506        );
8507        assert_eq!(
8508            serde_json::to_value(
8509                SessionCapabilities::new().status(SessionStatusCapabilities::new())
8510            )
8511            .unwrap(),
8512            json!({"status": {}})
8513        );
8514        assert_eq!(
8515            serde_json::to_value(SessionStatusResponse::new(SessionStatus::Open).state(
8516                StateUpdate::RequiresAction(crate::v2::RequiresActionStateUpdate::new())
8517            ))
8518            .unwrap(),
8519            json!({
8520                "status": "open",
8521                "state": {"state": "requires_action"}
8522            })
8523        );
8524        for state in [json!(null), json!("running"), json!({"state": 7})] {
8525            let response = serde_json::from_value::<SessionStatusResponse>(
8526                json!({"status": "open", "state": state}),
8527            )
8528            .unwrap();
8529            assert_eq!(response.state, None);
8530        }
8531    }
8532
8533    #[cfg(feature = "unstable_session_status")]
8534    #[test]
8535    fn test_session_status_is_strict_and_open() {
8536        let custom =
8537            serde_json::from_value::<SessionStatusResponse>(json!({"status": "_suspended"}))
8538                .unwrap();
8539        assert_eq!(
8540            custom.status,
8541            SessionStatus::Other("_suspended".to_string())
8542        );
8543        assert_eq!(
8544            serde_json::to_value(&custom).unwrap(),
8545            json!({"status": "_suspended"})
8546        );
8547        for result in [json!({}), json!({"status": null}), json!({"status": 1})] {
8548            assert!(serde_json::from_value::<SessionStatusResponse>(result).is_err());
8549        }
8550        for params in [json!({}), json!({"sessionId": null})] {
8551            assert!(serde_json::from_value::<SessionStatusRequest>(params).is_err());
8552        }
8553    }
8554
8555    #[cfg(feature = "unstable_session_archive")]
8556    #[test]
8557    fn test_session_archive_serialization() {
8558        assert_eq!(AGENT_METHOD_NAMES.session_archive, "session/archive");
8559        assert_eq!(AGENT_METHOD_NAMES.session_unarchive, "session/unarchive");
8560        assert_eq!(
8561            ClientRequest::ArchiveSessionRequest(Box::new(ArchiveSessionRequest::new(
8562                "sess_abc123"
8563            )))
8564            .method(),
8565            "session/archive"
8566        );
8567        assert_eq!(
8568            ClientRequest::UnarchiveSessionRequest(Box::new(UnarchiveSessionRequest::new(
8569                "sess_abc123"
8570            )))
8571            .method(),
8572            "session/unarchive"
8573        );
8574        assert_eq!(
8575            serde_json::to_value(ArchiveSessionRequest::new("sess_abc123")).unwrap(),
8576            json!({"sessionId": "sess_abc123"})
8577        );
8578        assert_eq!(
8579            serde_json::to_value(UnarchiveSessionResponse::new()).unwrap(),
8580            json!({})
8581        );
8582        assert_eq!(
8583            serde_json::to_value(
8584                SessionCapabilities::new().archive(SessionArchiveCapabilities::new())
8585            )
8586            .unwrap(),
8587            json!({"archive": {}})
8588        );
8589        assert_eq!(
8590            serde_json::to_value(
8591                ListSessionsRequest::new()
8592                    .cwd("/home/user/project")
8593                    .archived(SessionArchiveFilter::Only)
8594            )
8595            .unwrap(),
8596            json!({"cwd": "/home/user/project", "archived": "only"})
8597        );
8598        assert_eq!(
8599            serde_json::to_value(
8600                SessionInfo::new("sess_abc123", "/home/user/project").archived(true)
8601            )
8602            .unwrap(),
8603            json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": true})
8604        );
8605    }
8606
8607    #[cfg(feature = "unstable_session_archive")]
8608    #[test]
8609    fn test_session_archive_filter_is_strict_and_open() {
8610        for (value, expected) in [
8611            (json!(null), None),
8612            (json!("include"), Some(SessionArchiveFilter::Include)),
8613            (
8614                json!("_recent"),
8615                Some(SessionArchiveFilter::Other("_recent".to_string())),
8616            ),
8617        ] {
8618            let request =
8619                serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).unwrap();
8620            assert_eq!(request.archived, expected);
8621        }
8622        for value in [json!(true), json!(1), json!({})] {
8623            assert!(
8624                serde_json::from_value::<ListSessionsRequest>(json!({"archived": value})).is_err()
8625            );
8626        }
8627        let info = serde_json::from_value::<SessionInfo>(
8628            json!({"sessionId": "sess_abc123", "cwd": "/home/user/project", "archived": "yes"}),
8629        )
8630        .unwrap();
8631        assert_eq!(info.archived, None);
8632    }
8633
8634    #[test]
8635    fn test_session_additional_directories_serialization() {
8636        assert_eq!(
8637            serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(),
8638            json!({
8639                "cwd": "/home/user/project",
8640            })
8641        );
8642        assert_eq!(
8643            serde_json::to_value(
8644                NewSessionRequest::new("/home/user/project").additional_directories(vec![
8645                    PathBuf::from("/home/user/shared-lib"),
8646                    PathBuf::from("/home/user/product-docs"),
8647                ])
8648            )
8649            .unwrap(),
8650            json!({
8651                "cwd": "/home/user/project",
8652                "additionalDirectories": [
8653                    "/home/user/shared-lib",
8654                    "/home/user/product-docs"
8655                ],
8656            })
8657        );
8658        assert_eq!(
8659            serde_json::to_value(ResumeSessionRequest::new(
8660                "sess_abc123",
8661                "/home/user/project"
8662            ))
8663            .unwrap(),
8664            json!({
8665                "sessionId": "sess_abc123",
8666                "cwd": "/home/user/project",
8667            })
8668        );
8669        assert_eq!(
8670            serde_json::from_value::<ResumeSessionRequest>(json!({
8671                "sessionId": "sess_abc123",
8672                "cwd": "/home/user/project"
8673            }))
8674            .unwrap()
8675            .mcp_servers,
8676            Vec::<McpServer>::new()
8677        );
8678        assert_eq!(
8679            serde_json::from_value::<ResumeSessionRequest>(json!({
8680                "sessionId": "sess_abc123",
8681                "cwd": "/home/user/project",
8682                "mcpServers": null
8683            }))
8684            .unwrap()
8685            .mcp_servers,
8686            Vec::<McpServer>::new()
8687        );
8688        assert_eq!(
8689            serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(),
8690            json!({
8691                "sessionId": "sess_abc123",
8692                "cwd": "/home/user/project"
8693            })
8694        );
8695        assert_eq!(
8696            serde_json::to_value(
8697                SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![
8698                    PathBuf::from("/home/user/shared-lib"),
8699                    PathBuf::from("/home/user/product-docs"),
8700                ])
8701            )
8702            .unwrap(),
8703            json!({
8704                "sessionId": "sess_abc123",
8705                "cwd": "/home/user/project",
8706                "additionalDirectories": [
8707                    "/home/user/shared-lib",
8708                    "/home/user/product-docs"
8709                ]
8710            })
8711        );
8712        assert_eq!(
8713            serde_json::from_value::<SessionInfo>(json!({
8714                "sessionId": "sess_abc123",
8715                "cwd": "/home/user/project"
8716            }))
8717            .unwrap()
8718            .additional_directories,
8719            Vec::<AbsolutePath>::new()
8720        );
8721    }
8722    #[test]
8723    fn test_session_additional_directories_capabilities_serialization() {
8724        assert_eq!(
8725            serde_json::to_value(
8726                SessionCapabilities::new()
8727                    .additional_directories(SessionAdditionalDirectoriesCapabilities::new())
8728            )
8729            .unwrap(),
8730            json!({
8731                "additionalDirectories": {}
8732            })
8733        );
8734    }
8735
8736    #[cfg(feature = "unstable_session_system_prompt")]
8737    #[test]
8738    fn test_session_system_prompt_capabilities_serialization() {
8739        assert_eq!(
8740            serde_json::to_value(
8741                SessionCapabilities::new().system_prompt(SessionSystemPromptCapabilities::new())
8742            )
8743            .unwrap(),
8744            json!({ "systemPrompt": {} })
8745        );
8746        assert_eq!(
8747            serde_json::to_value(
8748                SessionCapabilities::new().system_prompt(
8749                    SessionSystemPromptCapabilities::new().mode(
8750                        SessionSystemPromptModeCapabilities::new()
8751                            .override_(SessionSystemPromptOverrideCapabilities::new())
8752                    )
8753                )
8754            )
8755            .unwrap(),
8756            json!({ "systemPrompt": { "mode": { "override": {} } } })
8757        );
8758        assert_eq!(
8759            serde_json::from_value::<SessionCapabilities>(
8760                json!({ "systemPrompt": { "mode": { "override": {} } } })
8761            )
8762            .unwrap()
8763            .system_prompt
8764            .and_then(|capabilities| capabilities.mode)
8765            .and_then(|mode| mode.override_),
8766            Some(SessionSystemPromptOverrideCapabilities::new())
8767        );
8768
8769        // Capabilities fall back to "not advertised" when malformed.
8770        assert_eq!(
8771            serde_json::from_value::<SessionCapabilities>(json!({ "systemPrompt": "yes" }))
8772                .unwrap()
8773                .system_prompt,
8774            None
8775        );
8776        assert_eq!(
8777            serde_json::from_value::<SessionCapabilities>(
8778                json!({ "systemPrompt": { "mode": { "override": true } } })
8779            )
8780            .unwrap()
8781            .system_prompt,
8782            Some(
8783                SessionSystemPromptCapabilities::new()
8784                    .mode(SessionSystemPromptModeCapabilities::new())
8785            )
8786        );
8787        assert_eq!(
8788            serde_json::from_value::<SessionCapabilities>(
8789                json!({ "systemPrompt": { "mode": "override" } })
8790            )
8791            .unwrap()
8792            .system_prompt,
8793            Some(SessionSystemPromptCapabilities::new())
8794        );
8795    }
8796
8797    #[cfg(feature = "unstable_session_system_prompt")]
8798    #[test]
8799    fn test_system_prompt_lifecycle_request_serialization() {
8800        let system_prompt = SystemPrompt::new(vec![ContentBlock::from(
8801            "You are a code review specialist.",
8802        )]);
8803        let system_prompt_json = json!({
8804            "mode": "append",
8805            "content": [{ "type": "text", "text": "You are a code review specialist." }]
8806        });
8807
8808        let request =
8809            NewSessionRequest::new("/home/user/project").system_prompt(system_prompt.clone());
8810        let request_json = json!({
8811            "cwd": "/home/user/project",
8812            "systemPrompt": system_prompt_json
8813        });
8814        assert_eq!(serde_json::to_value(&request).unwrap(), request_json);
8815        assert_eq!(
8816            serde_json::from_value::<NewSessionRequest>(request_json).unwrap(),
8817            request
8818        );
8819
8820        assert_eq!(
8821            serde_json::to_value(
8822                ResumeSessionRequest::new("sess_abc123", "/home/user/project")
8823                    .system_prompt(system_prompt.clone())
8824            )
8825            .unwrap()["systemPrompt"],
8826            system_prompt_json
8827        );
8828        #[cfg(feature = "unstable_session_fork")]
8829        assert_eq!(
8830            serde_json::to_value(
8831                ForkSessionRequest::new("sess_abc123", "/home/user/project")
8832                    .system_prompt(system_prompt.clone())
8833            )
8834            .unwrap()["systemPrompt"],
8835            system_prompt_json
8836        );
8837
8838        let override_prompt =
8839            SystemPrompt::new(vec![ContentBlock::from("...")]).mode(SystemPromptMode::Override);
8840        let override_json = json!({
8841            "mode": "override",
8842            "content": [{ "type": "text", "text": "..." }]
8843        });
8844        assert_eq!(
8845            serde_json::to_value(&override_prompt).unwrap(),
8846            override_json
8847        );
8848        assert_eq!(
8849            serde_json::from_value::<SystemPrompt>(override_json).unwrap(),
8850            override_prompt
8851        );
8852    }
8853
8854    /// `SystemPrompt::new` defaults to `append` in Rust, but `mode` is required on
8855    /// the wire, so it is always serialized.
8856    #[cfg(feature = "unstable_session_system_prompt")]
8857    #[test]
8858    fn test_system_prompt_new_defaults_to_append() {
8859        let system_prompt = SystemPrompt::new(vec![ContentBlock::from("...")]);
8860        assert_eq!(system_prompt.mode, SystemPromptMode::Append);
8861        assert_eq!(
8862            serde_json::to_value(&system_prompt).unwrap(),
8863            json!({ "mode": "append", "content": [{ "type": "text", "text": "..." }] })
8864        );
8865    }
8866
8867    /// Unknown modes follow the enum variant extension rule: they parse as
8868    /// `SystemPromptMode::Other` so they are not dropped, and an agent that does
8869    /// not support them must reject the request.
8870    #[cfg(feature = "unstable_session_system_prompt")]
8871    #[test]
8872    fn test_system_prompt_preserves_unknown_modes() {
8873        for mode in ["_vendor_mode", "prepend"] {
8874            let system_prompt_json =
8875                json!({ "mode": mode, "content": [{ "type": "text", "text": "..." }] });
8876            let system_prompt: SystemPrompt =
8877                serde_json::from_value(system_prompt_json.clone()).unwrap();
8878            assert_eq!(
8879                system_prompt.mode,
8880                SystemPromptMode::Other(mode.to_string())
8881            );
8882            assert_eq!(
8883                serde_json::to_value(&system_prompt).unwrap(),
8884                system_prompt_json
8885            );
8886        }
8887    }
8888
8889    /// The receiver acts on `systemPrompt`, so malformed values fail the request
8890    /// (`-32602`) instead of creating a session without the prompt, and `null`
8891    /// is equivalent to omitting the field.
8892    #[cfg(feature = "unstable_session_system_prompt")]
8893    fn assert_system_prompt_is_strict<T>(base: &serde_json::Value)
8894    where
8895        T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
8896    {
8897        let name = std::any::type_name::<T>();
8898        let with = |system_prompt: serde_json::Value| {
8899            let mut request = base.clone();
8900            request["systemPrompt"] = system_prompt;
8901            serde_json::from_value::<T>(request)
8902        };
8903
8904        for system_prompt in [
8905            json!("You are a code review specialist."),
8906            json!({ "content": [{ "type": "text", "text": "..." }] }),
8907            json!({ "mode": null, "content": [{ "type": "text", "text": "..." }] }),
8908            json!({ "mode": 1, "content": [{ "type": "text", "text": "..." }] }),
8909            json!({ "mode": "append" }),
8910            json!({ "mode": "append", "content": "..." }),
8911            json!({ "mode": "append", "content": [{ "type": "text" }] }),
8912        ] {
8913            assert!(
8914                with(system_prompt.clone()).is_err(),
8915                "{name}.systemPrompt: {system_prompt}"
8916            );
8917        }
8918
8919        assert_eq!(
8920            with(serde_json::Value::Null).unwrap(),
8921            serde_json::from_value::<T>(base.clone()).unwrap(),
8922            "{name}.systemPrompt"
8923        );
8924    }
8925
8926    #[cfg(feature = "unstable_session_system_prompt")]
8927    #[test]
8928    fn test_session_setup_requests_reject_malformed_system_prompts() {
8929        assert!(
8930            serde_json::to_value(NewSessionRequest::new("/repo"))
8931                .unwrap()
8932                .get("systemPrompt")
8933                .is_none()
8934        );
8935        assert_system_prompt_is_strict::<NewSessionRequest>(&json!({ "cwd": "/repo" }));
8936        assert_system_prompt_is_strict::<ResumeSessionRequest>(&json!({
8937            "sessionId": "sess-1",
8938            "cwd": "/repo"
8939        }));
8940        #[cfg(feature = "unstable_session_fork")]
8941        assert_system_prompt_is_strict::<ForkSessionRequest>(&json!({
8942            "sessionId": "sess-1",
8943            "cwd": "/repo"
8944        }));
8945    }
8946
8947    /// Unknown content block types parse as `ContentBlock::Other` so they are not
8948    /// dropped. An agent that does not support them must reject the request.
8949    #[cfg(feature = "unstable_session_system_prompt")]
8950    #[test]
8951    fn test_system_prompt_preserves_unknown_content_blocks() {
8952        let request: NewSessionRequest = serde_json::from_value(json!({
8953            "cwd": "/home/user/project",
8954            "systemPrompt": {
8955                "mode": "append",
8956                "content": [{ "type": "_vendor_video", "uri": "https://example.com/intro.mp4" }]
8957            }
8958        }))
8959        .unwrap();
8960        let content = request.system_prompt.unwrap().content;
8961        assert_eq!(content.len(), 1);
8962        assert!(matches!(content[0], ContentBlock::Other(_)));
8963    }
8964
8965    #[test]
8966    fn test_auth_method_terminal_serialization() {
8967        let method = AuthMethod::Terminal(AuthMethodTerminal::new("tui-auth", "Terminal Auth"));
8968
8969        let json = serde_json::to_value(&method).unwrap();
8970        assert_eq!(
8971            json,
8972            json!({
8973                "methodId": "tui-auth",
8974                "name": "Terminal Auth",
8975                "type": "terminal"
8976            })
8977        );
8978        // args and env should be omitted when empty
8979        assert!(!json.as_object().unwrap().contains_key("args"));
8980        assert!(!json.as_object().unwrap().contains_key("env"));
8981
8982        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
8983        match deserialized {
8984            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
8985                assert_eq!(args, Vec::<String>::new());
8986                assert_eq!(env, []);
8987            }
8988            _ => panic!("Expected Terminal variant"),
8989        }
8990    }
8991
8992    #[test]
8993    fn test_auth_method_terminal_with_args_and_env_serialization() {
8994        let method = AuthMethod::Terminal(
8995            AuthMethodTerminal::new("tui-auth", "Terminal Auth")
8996                .args(vec!["--interactive".to_string(), "--color".to_string()])
8997                .env(vec![EnvVariable::new("TERM", "xterm-256color")]),
8998        );
8999
9000        let json = serde_json::to_value(&method).unwrap();
9001        assert_eq!(
9002            json,
9003            json!({
9004                "methodId": "tui-auth",
9005                "name": "Terminal Auth",
9006                "type": "terminal",
9007                "args": ["--interactive", "--color"],
9008                "env": [
9009                    {
9010                        "name": "TERM",
9011                        "value": "xterm-256color"
9012                    }
9013                ]
9014            })
9015        );
9016
9017        let deserialized: AuthMethod = serde_json::from_value(json).unwrap();
9018        match deserialized {
9019            AuthMethod::Terminal(AuthMethodTerminal { args, env, .. }) => {
9020                assert_eq!(args, vec!["--interactive", "--color"]);
9021                assert_eq!(env.len(), 1);
9022                assert_eq!(env[0].name, "TERM");
9023                assert_eq!(env[0].value, "xterm-256color");
9024            }
9025            _ => panic!("Expected Terminal variant"),
9026        }
9027    }
9028
9029    #[test]
9030    fn test_session_config_option_id_serialize() {
9031        let val = SessionConfigOptionValue::id("model-1");
9032        let json = serde_json::to_value(&val).unwrap();
9033        assert_eq!(json, json!({ "type": "id", "value": "model-1" }));
9034    }
9035
9036    #[test]
9037    fn test_session_config_option_value_boolean_serialize() {
9038        let val = SessionConfigOptionValue::boolean(true);
9039        let json = serde_json::to_value(&val).unwrap();
9040        assert_eq!(json, json!({ "type": "boolean", "value": true }));
9041    }
9042
9043    #[test]
9044    fn test_session_config_option_value_deserialize_id() {
9045        let json = json!({ "type": "id", "value": "model-1" });
9046        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9047        assert_eq!(val, SessionConfigOptionValue::id("model-1"));
9048        assert_eq!(val.as_id().unwrap().to_string(), "model-1");
9049    }
9050
9051    #[test]
9052    fn test_session_config_option_value_deserialize_requires_type() {
9053        let json = json!({ "value": "model-1" });
9054        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9055        assert!(result.is_err());
9056    }
9057
9058    #[test]
9059    fn test_session_config_option_value_deserialize_boolean() {
9060        let json = json!({ "type": "boolean", "value": true });
9061        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9062        assert_eq!(val, SessionConfigOptionValue::boolean(true));
9063        assert_eq!(val.as_bool(), Some(true));
9064    }
9065
9066    #[test]
9067    fn test_session_config_option_value_deserialize_boolean_false() {
9068        let json = json!({ "type": "boolean", "value": false });
9069        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9070        assert_eq!(val, SessionConfigOptionValue::boolean(false));
9071        assert_eq!(val.as_bool(), Some(false));
9072    }
9073
9074    #[test]
9075    fn test_session_config_option_value_deserialize_unknown_type_with_string_value() {
9076        let json = json!({
9077            "type": "text",
9078            "value": "freeform input",
9079            "maxLength": 200
9080        });
9081        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9082        let SessionConfigOptionValue::Other(unknown) = val else {
9083            panic!("Expected Other variant");
9084        };
9085        assert_eq!(unknown.type_, "text");
9086        assert_eq!(unknown.value, json!("freeform input"));
9087        assert_eq!(unknown.fields["maxLength"], json!(200));
9088    }
9089
9090    #[test]
9091    fn test_session_config_option_value_deserialize_unknown_type_with_object_value() {
9092        let json = json!({
9093            "type": "range",
9094            "value": { "min": 1, "max": 5 }
9095        });
9096        let val: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9097        let SessionConfigOptionValue::Other(unknown) = val else {
9098            panic!("Expected Other variant");
9099        };
9100        assert_eq!(unknown.type_, "range");
9101        assert_eq!(unknown.value, json!({ "min": 1, "max": 5 }));
9102    }
9103
9104    #[test]
9105    fn test_session_config_option_value_roundtrip_id() {
9106        let original = SessionConfigOptionValue::id("option-a");
9107        let json = serde_json::to_value(&original).unwrap();
9108        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9109        assert_eq!(original, roundtripped);
9110    }
9111
9112    #[test]
9113    fn test_session_config_option_value_roundtrip_boolean() {
9114        let original = SessionConfigOptionValue::boolean(false);
9115        let json = serde_json::to_value(&original).unwrap();
9116        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9117        assert_eq!(original, roundtripped);
9118    }
9119
9120    #[test]
9121    fn test_session_config_option_value_roundtrip_other() {
9122        let mut fields = BTreeMap::new();
9123        fields.insert("maxLength".to_string(), json!(200));
9124        let original = SessionConfigOptionValue::Other(OtherSessionConfigOptionValue::new(
9125            "text",
9126            json!("freeform input"),
9127            fields,
9128        ));
9129        let json = serde_json::to_value(&original).unwrap();
9130        let roundtripped: SessionConfigOptionValue = serde_json::from_value(json).unwrap();
9131        assert_eq!(original, roundtripped);
9132    }
9133
9134    #[test]
9135    fn test_session_config_option_value_type_mismatch_boolean_with_string() {
9136        let json = json!({ "type": "boolean", "value": "not a bool" });
9137        let result = serde_json::from_value::<SessionConfigOptionValue>(json);
9138        assert!(result.is_err());
9139    }
9140
9141    #[test]
9142    fn test_session_config_option_value_from_impls() {
9143        let from_str: SessionConfigOptionValue = "model-1".into();
9144        assert_eq!(from_str.as_id().unwrap().to_string(), "model-1");
9145
9146        let from_id: SessionConfigOptionValue = SessionConfigValueId::new("model-2").into();
9147        assert_eq!(from_id.as_id().unwrap().to_string(), "model-2");
9148
9149        let from_bool: SessionConfigOptionValue = true.into();
9150        assert_eq!(from_bool.as_bool(), Some(true));
9151    }
9152
9153    #[test]
9154    fn test_set_session_config_option_request_id() {
9155        let req = SetSessionConfigOptionRequest::new("sess_1", "model", "model-1");
9156        let json = serde_json::to_value(&req).unwrap();
9157        assert_eq!(
9158            json,
9159            json!({
9160                "sessionId": "sess_1",
9161                "configId": "model",
9162                "type": "id",
9163                "value": "model-1"
9164            })
9165        );
9166    }
9167
9168    #[test]
9169    fn test_set_session_config_option_request_boolean() {
9170        let req = SetSessionConfigOptionRequest::new("sess_1", "brave_mode", true);
9171        let json = serde_json::to_value(&req).unwrap();
9172        assert_eq!(
9173            json,
9174            json!({
9175                "sessionId": "sess_1",
9176                "configId": "brave_mode",
9177                "type": "boolean",
9178                "value": true
9179            })
9180        );
9181    }
9182
9183    #[test]
9184    fn test_set_session_config_option_request_deserialize_requires_type() {
9185        let json = json!({
9186            "sessionId": "sess_1",
9187            "configId": "model",
9188            "value": "model-1"
9189        });
9190        let result = serde_json::from_value::<SetSessionConfigOptionRequest>(json);
9191        assert!(result.is_err());
9192    }
9193
9194    #[test]
9195    fn test_set_session_config_option_request_deserialize_boolean() {
9196        let json = json!({
9197            "sessionId": "sess_1",
9198            "configId": "brave_mode",
9199            "type": "boolean",
9200            "value": true
9201        });
9202        let req: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9203        assert_eq!(req.value.as_bool(), Some(true));
9204    }
9205
9206    #[test]
9207    fn test_set_session_config_option_request_roundtrip_id() {
9208        let original = SetSessionConfigOptionRequest::new("s", "c", "v");
9209        let json = serde_json::to_value(&original).unwrap();
9210        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9211        assert_eq!(original, roundtripped);
9212    }
9213
9214    #[test]
9215    fn test_set_session_config_option_request_roundtrip_boolean() {
9216        let original = SetSessionConfigOptionRequest::new("s", "c", false);
9217        let json = serde_json::to_value(&original).unwrap();
9218        let roundtripped: SetSessionConfigOptionRequest = serde_json::from_value(json).unwrap();
9219        assert_eq!(original, roundtripped);
9220    }
9221
9222    #[test]
9223    fn test_session_config_boolean_serialization() {
9224        let cfg = SessionConfigBoolean::new(true);
9225        let json = serde_json::to_value(&cfg).unwrap();
9226        assert_eq!(json, json!({ "currentValue": true }));
9227
9228        let deserialized: SessionConfigBoolean = serde_json::from_value(json).unwrap();
9229        assert!(deserialized.current_value);
9230    }
9231
9232    #[test]
9233    fn test_session_config_option_boolean_variant() {
9234        let opt = SessionConfigOption::boolean("brave_mode", "Brave Mode", false)
9235            .description("Skip confirmation prompts")
9236            .meta(test_meta());
9237        assert_eq!(serialized_meta_key_count(&opt), 1);
9238
9239        let json = serde_json::to_value(&opt).unwrap();
9240        assert_eq!(
9241            json,
9242            json!({
9243                "configId": "brave_mode",
9244                "name": "Brave Mode",
9245                "description": "Skip confirmation prompts",
9246                "type": "boolean",
9247                "currentValue": false,
9248                "_meta": {
9249                    "source": "test"
9250                }
9251            })
9252        );
9253
9254        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9255        assert_eq!(deserialized.config_id.to_string(), "brave_mode");
9256        assert_eq!(deserialized.name, "Brave Mode");
9257        match deserialized.kind {
9258            SessionConfigKind::Boolean(ref b) => assert!(!b.current_value),
9259            _ => panic!("Expected Boolean kind"),
9260        }
9261    }
9262
9263    #[test]
9264    fn test_session_config_option_select_still_works() {
9265        // Make sure existing select options are unaffected
9266        let opt = SessionConfigOption::select(
9267            "model",
9268            "Model",
9269            "model-1",
9270            vec![
9271                SessionConfigSelectOption::new("model-1", "Model 1"),
9272                SessionConfigSelectOption::new("model-2", "Model 2"),
9273            ],
9274        )
9275        .meta(test_meta());
9276        assert_eq!(serialized_meta_key_count(&opt), 1);
9277
9278        let json = serde_json::to_value(&opt).unwrap();
9279        assert_eq!(json["type"], "select");
9280        assert_eq!(json["currentValue"], "model-1");
9281        assert_eq!(json["options"].as_array().unwrap().len(), 2);
9282        assert_eq!(json["_meta"]["source"], "test");
9283
9284        let deserialized: SessionConfigOption = serde_json::from_value(json).unwrap();
9285        match deserialized.kind {
9286            SessionConfigKind::Select(ref s) => {
9287                assert_eq!(s.current_value.to_string(), "model-1");
9288            }
9289            _ => panic!("Expected Select kind"),
9290        }
9291    }
9292
9293    #[test]
9294    fn test_session_config_option_unknown_kind_roundtrip() {
9295        let option: SessionConfigOption = serde_json::from_value(json!({
9296            "configId": "verbosity",
9297            "name": "Verbosity",
9298            "type": "_slider",
9299            "currentValue": 3,
9300            "min": 0,
9301            "max": 5,
9302            "_meta": {
9303                "source": "test"
9304            }
9305        }))
9306        .unwrap();
9307
9308        assert_eq!(option.config_id.to_string(), "verbosity");
9309        assert_eq!(option.meta.as_ref().unwrap()["source"], "test");
9310        let SessionConfigKind::Other(unknown) = &option.kind else {
9311            panic!("expected unknown config kind");
9312        };
9313        assert_eq!(unknown.type_, "_slider");
9314        assert_eq!(unknown.fields.get("currentValue"), Some(&json!(3)));
9315        assert!(!unknown.fields.contains_key("_meta"));
9316        assert_eq!(serialized_meta_key_count(&option), 1);
9317
9318        let json = serde_json::to_value(&option).unwrap();
9319        assert_eq!(json["type"], "_slider");
9320        assert_eq!(json["currentValue"], 3);
9321        assert_eq!(json["min"], 0);
9322        assert_eq!(json["max"], 5);
9323        assert_eq!(json["_meta"]["source"], "test");
9324    }
9325
9326    #[test]
9327    fn test_session_config_option_unknown_kind_does_not_duplicate_flattened_meta() {
9328        let mut fields = std::collections::BTreeMap::new();
9329        fields.insert("currentValue".to_string(), json!(3));
9330        fields.insert("_meta".to_string(), json!({ "inner": "ignored" }));
9331
9332        let option = SessionConfigOption::new(
9333            "verbosity",
9334            "Verbosity",
9335            SessionConfigKind::Other(OtherSessionConfigKind::new("_slider", fields)),
9336        )
9337        .meta(test_meta());
9338
9339        let SessionConfigKind::Other(unknown) = &option.kind else {
9340            panic!("expected unknown config kind");
9341        };
9342        assert!(!unknown.fields.contains_key("_meta"));
9343        assert_eq!(serialized_meta_key_count(&option), 1);
9344
9345        let json = serde_json::to_value(&option).unwrap();
9346        assert_eq!(json["type"], "_slider");
9347        assert_eq!(json["currentValue"], 3);
9348        assert_eq!(json["_meta"]["source"], "test");
9349    }
9350
9351    #[test]
9352    fn test_session_config_option_unknown_does_not_hide_malformed_known_kind() {
9353        assert!(
9354            serde_json::from_value::<SessionConfigOption>(json!({
9355                "configId": "model",
9356                "name": "Model",
9357                "type": "select"
9358            }))
9359            .is_err()
9360        );
9361    }
9362
9363    #[cfg(feature = "unstable_llm_providers")]
9364    #[test]
9365    fn test_llm_protocol_known_variants() {
9366        assert_eq!(
9367            serde_json::to_value(&LlmProtocol::Anthropic).unwrap(),
9368            json!("anthropic")
9369        );
9370        assert_eq!(
9371            serde_json::to_value(&LlmProtocol::OpenAi).unwrap(),
9372            json!("openai")
9373        );
9374        assert_eq!(
9375            serde_json::to_value(&LlmProtocol::Azure).unwrap(),
9376            json!("azure")
9377        );
9378        assert_eq!(
9379            serde_json::to_value(&LlmProtocol::Vertex).unwrap(),
9380            json!("vertex")
9381        );
9382        assert_eq!(
9383            serde_json::to_value(&LlmProtocol::Bedrock).unwrap(),
9384            json!("bedrock")
9385        );
9386
9387        assert_eq!(
9388            serde_json::from_str::<LlmProtocol>("\"anthropic\"").unwrap(),
9389            LlmProtocol::Anthropic
9390        );
9391        assert_eq!(
9392            serde_json::from_str::<LlmProtocol>("\"openai\"").unwrap(),
9393            LlmProtocol::OpenAi
9394        );
9395        assert_eq!(
9396            serde_json::from_str::<LlmProtocol>("\"azure\"").unwrap(),
9397            LlmProtocol::Azure
9398        );
9399        assert_eq!(
9400            serde_json::from_str::<LlmProtocol>("\"vertex\"").unwrap(),
9401            LlmProtocol::Vertex
9402        );
9403        assert_eq!(
9404            serde_json::from_str::<LlmProtocol>("\"bedrock\"").unwrap(),
9405            LlmProtocol::Bedrock
9406        );
9407    }
9408
9409    #[cfg(feature = "unstable_llm_providers")]
9410    #[test]
9411    fn test_llm_protocol_unknown_variant() {
9412        let unknown: LlmProtocol = serde_json::from_str("\"cohere\"").unwrap();
9413        assert_eq!(unknown, LlmProtocol::Other("cohere".to_string()));
9414
9415        let json = serde_json::to_value(&unknown).unwrap();
9416        assert_eq!(json, json!("cohere"));
9417    }
9418
9419    #[cfg(feature = "unstable_llm_providers")]
9420    #[test]
9421    fn test_provider_current_config_serialization() {
9422        let config =
9423            ProviderCurrentConfig::new(LlmProtocol::Anthropic, "https://api.anthropic.com");
9424
9425        let json = serde_json::to_value(&config).unwrap();
9426        assert_eq!(
9427            json,
9428            json!({
9429                "apiType": "anthropic",
9430                "baseUrl": "https://api.anthropic.com"
9431            })
9432        );
9433
9434        let deserialized: ProviderCurrentConfig = serde_json::from_value(json).unwrap();
9435        assert_eq!(deserialized.api_type, LlmProtocol::Anthropic);
9436        assert_eq!(deserialized.base_url, "https://api.anthropic.com");
9437    }
9438
9439    #[cfg(feature = "unstable_llm_providers")]
9440    #[test]
9441    fn test_provider_info_with_current_config() {
9442        let info = ProviderInfo::new(
9443            "main",
9444            vec![LlmProtocol::Anthropic, LlmProtocol::OpenAi],
9445            true,
9446            Some(ProviderCurrentConfig::new(
9447                LlmProtocol::Anthropic,
9448                "https://api.anthropic.com",
9449            )),
9450        );
9451
9452        let json = serde_json::to_value(&info).unwrap();
9453        assert_eq!(
9454            json,
9455            json!({
9456                "providerId": "main",
9457                "supported": ["anthropic", "openai"],
9458                "required": true,
9459                "current": {
9460                    "apiType": "anthropic",
9461                    "baseUrl": "https://api.anthropic.com"
9462                }
9463            })
9464        );
9465
9466        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9467        assert_eq!(deserialized.provider_id.to_string(), "main");
9468        assert_eq!(deserialized.supported.len(), 2);
9469        assert!(deserialized.required);
9470        assert!(deserialized.current.is_some());
9471        assert_eq!(
9472            deserialized.current.as_ref().unwrap().api_type,
9473            LlmProtocol::Anthropic
9474        );
9475    }
9476
9477    #[cfg(feature = "unstable_llm_providers")]
9478    #[test]
9479    fn test_provider_info_disabled() {
9480        let info = ProviderInfo::new(
9481            "secondary",
9482            vec![LlmProtocol::OpenAi],
9483            false,
9484            None::<ProviderCurrentConfig>,
9485        );
9486
9487        let json = serde_json::to_value(&info).unwrap();
9488        assert_eq!(
9489            json,
9490            json!({
9491                "providerId": "secondary",
9492                "supported": ["openai"],
9493                "required": false
9494            })
9495        );
9496
9497        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9498        assert_eq!(deserialized.provider_id.to_string(), "secondary");
9499        assert!(!deserialized.required);
9500        assert!(deserialized.current.is_none());
9501    }
9502
9503    #[cfg(feature = "unstable_llm_providers")]
9504    #[test]
9505    fn test_provider_info_missing_current_defaults_to_none() {
9506        // current is optional; omitting it should decode as None
9507        let json = json!({
9508            "providerId": "main",
9509            "supported": ["anthropic"],
9510            "required": true
9511        });
9512        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9513        assert!(deserialized.current.is_none());
9514    }
9515
9516    #[cfg(feature = "unstable_llm_providers")]
9517    #[test]
9518    fn test_provider_info_explicit_null_current_decodes_to_none() {
9519        // current: null and an omitted current are equivalent on the wire;
9520        // both must deserialize into None so the disabled state is preserved
9521        // regardless of which form the peer chose to send.
9522        let json = json!({
9523            "providerId": "main",
9524            "supported": ["anthropic"],
9525            "required": true,
9526            "current": null
9527        });
9528        let deserialized: ProviderInfo = serde_json::from_value(json).unwrap();
9529        assert!(deserialized.current.is_none());
9530    }
9531
9532    #[cfg(feature = "unstable_llm_providers")]
9533    #[test]
9534    fn test_list_providers_response_serialization() {
9535        let response = ListProvidersResponse::new(vec![ProviderInfo::new(
9536            "main",
9537            vec![LlmProtocol::Anthropic],
9538            true,
9539            Some(ProviderCurrentConfig::new(
9540                LlmProtocol::Anthropic,
9541                "https://api.anthropic.com",
9542            )),
9543        )]);
9544
9545        let json = serde_json::to_value(&response).unwrap();
9546        assert_eq!(json["providers"].as_array().unwrap().len(), 1);
9547        assert_eq!(json["providers"][0]["providerId"], "main");
9548
9549        let deserialized: ListProvidersResponse = serde_json::from_value(json).unwrap();
9550        assert_eq!(deserialized.providers.len(), 1);
9551    }
9552
9553    #[cfg(feature = "unstable_llm_providers")]
9554    #[test]
9555    fn test_set_provider_request_serialization() {
9556        use std::collections::HashMap;
9557
9558        let mut headers = HashMap::new();
9559        headers.insert("Authorization".to_string(), "Bearer sk-test".to_string());
9560
9561        let request =
9562            SetProviderRequest::new("main", LlmProtocol::OpenAi, "https://api.openai.com/v1")
9563                .headers(headers);
9564
9565        let json = serde_json::to_value(&request).unwrap();
9566        assert_eq!(
9567            json,
9568            json!({
9569                "providerId": "main",
9570                "apiType": "openai",
9571                "baseUrl": "https://api.openai.com/v1",
9572                "headers": {
9573                    "Authorization": "Bearer sk-test"
9574                }
9575            })
9576        );
9577
9578        let deserialized: SetProviderRequest = serde_json::from_value(json).unwrap();
9579        assert_eq!(deserialized.provider_id.to_string(), "main");
9580        assert_eq!(deserialized.api_type, LlmProtocol::OpenAi);
9581        assert_eq!(deserialized.base_url, "https://api.openai.com/v1");
9582        assert_eq!(deserialized.headers.len(), 1);
9583        assert_eq!(
9584            deserialized.headers.get("Authorization").unwrap(),
9585            "Bearer sk-test"
9586        );
9587    }
9588
9589    #[cfg(feature = "unstable_llm_providers")]
9590    #[test]
9591    fn test_set_provider_request_omits_empty_headers() {
9592        let request =
9593            SetProviderRequest::new("main", LlmProtocol::Anthropic, "https://api.anthropic.com");
9594
9595        let json = serde_json::to_value(&request).unwrap();
9596        // headers should be omitted when empty
9597        assert!(!json.as_object().unwrap().contains_key("headers"));
9598    }
9599
9600    #[cfg(feature = "unstable_llm_providers")]
9601    #[test]
9602    fn test_disable_provider_request_serialization() {
9603        let request = DisableProviderRequest::new("secondary");
9604
9605        let json = serde_json::to_value(&request).unwrap();
9606        assert_eq!(json, json!({ "providerId": "secondary" }));
9607
9608        let deserialized: DisableProviderRequest = serde_json::from_value(json).unwrap();
9609        assert_eq!(deserialized.provider_id.to_string(), "secondary");
9610    }
9611
9612    #[cfg(feature = "unstable_llm_providers")]
9613    #[test]
9614    fn test_providers_capabilities_serialization() {
9615        let caps = ProvidersCapabilities::new();
9616
9617        let json = serde_json::to_value(&caps).unwrap();
9618        assert_eq!(json, json!({}));
9619
9620        let deserialized: ProvidersCapabilities = serde_json::from_value(json).unwrap();
9621        assert!(deserialized.meta.is_none());
9622    }
9623
9624    #[cfg(feature = "unstable_llm_providers")]
9625    #[test]
9626    fn test_agent_capabilities_with_providers() {
9627        let caps = AgentCapabilities::new().providers(ProvidersCapabilities::new());
9628
9629        let json = serde_json::to_value(&caps).unwrap();
9630        assert_eq!(json["providers"], json!({}));
9631
9632        let deserialized: AgentCapabilities = serde_json::from_value(json).unwrap();
9633        assert!(deserialized.providers.is_some());
9634    }
9635
9636    #[test]
9637    fn test_agent_capabilities_session_is_explicit() {
9638        let json = serde_json::to_value(AgentCapabilities::new()).unwrap();
9639        assert!(json.get("session").is_none());
9640
9641        let caps = AgentCapabilities::new().session(
9642            SessionCapabilities::new()
9643                .prompt(PromptCapabilities::new().image(PromptImageCapabilities::new()))
9644                .mcp(McpCapabilities::new().stdio(McpStdioCapabilities::new())),
9645        );
9646
9647        assert_eq!(
9648            serde_json::to_value(&caps).unwrap(),
9649            json!({
9650                "session": {
9651                    "prompt": {
9652                        "image": {}
9653                    },
9654                    "mcp": {
9655                        "stdio": {}
9656                    }
9657                }
9658            })
9659        );
9660
9661        let deserialized: AgentCapabilities = serde_json::from_value(json!({
9662            "session": false
9663        }))
9664        .unwrap();
9665        assert!(deserialized.session.is_none());
9666    }
9667
9668    #[test]
9669    fn test_prompt_capabilities_serialize_supported_content_as_objects() {
9670        let caps = PromptCapabilities::new()
9671            .image(PromptImageCapabilities::new())
9672            .audio(PromptAudioCapabilities::new())
9673            .embedded_context(PromptEmbeddedContextCapabilities::new());
9674
9675        assert_eq!(
9676            serde_json::to_value(&caps).unwrap(),
9677            json!({
9678                "image": {},
9679                "audio": {},
9680                "embeddedContext": {}
9681            })
9682        );
9683
9684        let deserialized: PromptCapabilities = serde_json::from_value(json!({
9685            "image": null,
9686            "audio": false,
9687            "embeddedContext": {}
9688        }))
9689        .unwrap();
9690        assert!(deserialized.image.is_none());
9691        assert!(deserialized.audio.is_none());
9692        assert!(deserialized.embedded_context.is_some());
9693    }
9694
9695    #[test]
9696    fn test_mcp_capabilities_serialize_supported_transports_as_objects() {
9697        let caps = McpCapabilities::new()
9698            .stdio(McpStdioCapabilities::new())
9699            .http(McpHttpCapabilities::new());
9700
9701        assert_eq!(
9702            serde_json::to_value(&caps).unwrap(),
9703            json!({
9704                "stdio": {},
9705                "http": {}
9706            })
9707        );
9708
9709        let deserialized: McpCapabilities = serde_json::from_value(json!({
9710            "stdio": null,
9711            "http": false
9712        }))
9713        .unwrap();
9714        assert!(deserialized.stdio.is_none());
9715        assert!(deserialized.http.is_none());
9716    }
9717
9718    #[cfg(feature = "unstable_mcp_over_acp")]
9719    #[test]
9720    fn test_mcp_capabilities_serialize_acp_support_as_object() {
9721        let caps = McpCapabilities::new().acp(McpAcpCapabilities::new());
9722
9723        assert_eq!(
9724            serde_json::to_value(&caps).unwrap(),
9725            json!({
9726                "acp": {}
9727            })
9728        );
9729    }
9730
9731    #[test]
9732    fn prompt_request_rejects_malformed_content_block() {
9733        use serde_json::json;
9734
9735        assert!(
9736            serde_json::from_value::<PromptRequest>(json!({
9737                "sessionId": "sess-1",
9738                "prompt": [{"type": "text"}]
9739            }))
9740            .is_err()
9741        );
9742    }
9743
9744    #[test]
9745    fn prompt_request_rejects_non_array_prompt() {
9746        use serde_json::json;
9747
9748        assert!(
9749            serde_json::from_value::<PromptRequest>(json!({
9750                "sessionId": "sess-1",
9751                "prompt": "hello"
9752            }))
9753            .is_err()
9754        );
9755    }
9756
9757    fn assert_session_setup_lists_are_strict<T>(base: &serde_json::Value)
9758    where
9759        T: serde::de::DeserializeOwned + PartialEq + std::fmt::Debug,
9760    {
9761        let name = std::any::type_name::<T>();
9762        let with = |field: &str, value: serde_json::Value| {
9763            let mut request = base.clone();
9764            request[field] = value;
9765            serde_json::from_value::<T>(request)
9766        };
9767
9768        for (field, value) in [
9769            // Dropping the malformed server would start the session without it.
9770            (
9771                "mcpServers",
9772                json!([
9773                    {"type": "stdio", "name": "github", "command": "/usr/bin/gh-mcp", "env": [{"name": "GITHUB_TOKEN", "value": null}]},
9774                    {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp"}
9775                ]),
9776            ),
9777            ("mcpServers", json!({"type": "stdio", "name": "fs"})),
9778            ("additionalDirectories", json!(["/repo/lib", 42])),
9779            ("additionalDirectories", json!("/repo/lib")),
9780        ] {
9781            assert!(
9782                with(field, value.clone()).is_err(),
9783                "{name}.{field}: {value}"
9784            );
9785        }
9786
9787        let empty = serde_json::from_value::<T>(base.clone()).unwrap();
9788        for field in ["mcpServers", "additionalDirectories"] {
9789            assert_eq!(
9790                with(field, serde_json::Value::Null).unwrap(),
9791                empty,
9792                "{name}.{field}"
9793            );
9794        }
9795    }
9796
9797    #[test]
9798    fn session_setup_requests_reject_malformed_mcp_servers_and_directories() {
9799        assert_session_setup_lists_are_strict::<NewSessionRequest>(&json!({"cwd": "/repo"}));
9800        assert_session_setup_lists_are_strict::<ResumeSessionRequest>(&json!({
9801            "sessionId": "sess-1",
9802            "cwd": "/repo"
9803        }));
9804        #[cfg(feature = "unstable_session_fork")]
9805        assert_session_setup_lists_are_strict::<ForkSessionRequest>(&json!({
9806            "sessionId": "sess-1",
9807            "cwd": "/repo"
9808        }));
9809    }
9810
9811    #[test]
9812    fn resume_session_request_rejects_malformed_replay_from() {
9813        let request = |replay_from: serde_json::Value| {
9814            serde_json::from_value::<ResumeSessionRequest>(json!({
9815                "sessionId": "sess-1",
9816                "cwd": "/repo",
9817                "replayFrom": replay_from
9818            }))
9819        };
9820
9821        // Treating this as omitted would reconnect without the requested replay.
9822        assert!(request(json!("start")).is_err());
9823        assert_eq!(request(serde_json::Value::Null).unwrap().replay_from, None);
9824        assert!(matches!(
9825            request(json!({"type": "start"})).unwrap().replay_from,
9826            Some(ReplayFrom::Start(_))
9827        ));
9828    }
9829
9830    #[test]
9831    fn mcp_server_lists_treat_null_as_empty() {
9832        let servers: Vec<McpServer> = serde_json::from_value(json!([
9833            {"type": "stdio", "name": "fs", "command": "/usr/bin/fs-mcp", "args": null, "env": null},
9834            {"type": "http", "name": "api", "url": "https://example.com/mcp", "headers": null}
9835        ]))
9836        .unwrap();
9837
9838        assert_eq!(
9839            servers,
9840            vec![
9841                McpServer::Stdio(McpServerStdio::new("fs", "/usr/bin/fs-mcp")),
9842                McpServer::Http(McpServerHttp::new("api", "https://example.com/mcp")),
9843            ]
9844        );
9845    }
9846
9847    #[test]
9848    fn terminal_auth_methods_reject_malformed_launch_config() {
9849        // The client spawns a process from `args` and `env`, so a malformed
9850        // method is dropped from `authMethods` instead of launched with
9851        // altered arguments or preserved as an unknown method.
9852        let response: InitializeResponse = serde_json::from_value(json!({
9853            "protocolVersion": 2,
9854            "info": {"name": "agent", "version": "1.0.0"},
9855            "authMethods": [
9856                {"type": "terminal", "methodId": "bad-args", "name": "Log in", "args": ["auth", "--port", 8123]},
9857                {"type": "terminal", "methodId": "bad-env", "name": "Log in", "env": [{"name": "DEBUG", "value": 1}]},
9858                {"type": "terminal", "methodId": "null-config", "name": "Log in", "args": null, "env": null},
9859                {"type": "agent", "methodId": "agent", "name": "Agent login"}
9860            ]
9861        }))
9862        .unwrap();
9863
9864        assert_eq!(
9865            response.auth_methods,
9866            vec![
9867                AuthMethod::Terminal(AuthMethodTerminal::new("null-config", "Log in")),
9868                AuthMethod::Agent(AuthMethodAgent::new("agent", "Agent login")),
9869            ]
9870        );
9871    }
9872
9873    #[cfg(feature = "unstable_auth_status")]
9874    #[test]
9875    fn auth_status_response_round_trips() {
9876        let response = AuthStatusResponse::new(AuthState::Authenticated).accounts(vec![
9877            AuthAccount::new("Anthropic API key").active(true),
9878            AuthAccount::new("Claude Max").email("user@example.com".to_string()),
9879        ]);
9880        let json = serde_json::to_value(&response).unwrap();
9881        assert_eq!(
9882            json,
9883            json!({
9884                "state": "authenticated",
9885                "accounts": [
9886                    {
9887                        "label": "Anthropic API key",
9888                        "active": true
9889                    },
9890                    {
9891                        "label": "Claude Max",
9892                        "email": "user@example.com"
9893                    }
9894                ]
9895            })
9896        );
9897        assert_eq!(
9898            serde_json::from_value::<AuthStatusResponse>(json).unwrap(),
9899            response
9900        );
9901        assert_eq!(
9902            serde_json::to_value(AuthStatusResponse::new(AuthState::NotRequired)).unwrap(),
9903            json!({"state": "not_required"})
9904        );
9905    }
9906
9907    #[cfg(feature = "unstable_auth_status")]
9908    #[test]
9909    fn auth_status_state_is_strict_and_accounts_are_lenient() {
9910        assert!(serde_json::from_value::<AuthStatusResponse>(json!({})).is_err());
9911        assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": null})).is_err());
9912        assert!(serde_json::from_value::<AuthStatusResponse>(json!({"state": 1})).is_err());
9913
9914        let response: AuthStatusResponse = serde_json::from_value(json!({
9915            "state": "_vendor_state",
9916            "accounts": [
9917                {"active": true},
9918                {
9919                    "label": "Vertex AI",
9920                    "active": "yes",
9921                    "email": ["not", "a", "string"]
9922                }
9923            ]
9924        }))
9925        .unwrap();
9926        assert_eq!(
9927            response,
9928            AuthStatusResponse::new(AuthState::Other("_vendor_state".to_string()))
9929                .accounts(vec![AuthAccount::new("Vertex AI")])
9930        );
9931        assert_eq!(
9932            serde_json::from_value::<AuthStatusResponse>(
9933                json!({"state": "unknown", "accounts": null})
9934            )
9935            .unwrap(),
9936            AuthStatusResponse::new(AuthState::Unknown)
9937        );
9938    }
9939
9940    #[cfg(feature = "unstable_auth_status")]
9941    #[test]
9942    fn auth_status_update_notification_routes() {
9943        use crate::v2::{AgentNotification, CLIENT_METHOD_NAMES};
9944
9945        assert_eq!(AGENT_METHOD_NAMES.auth_status, "auth/status");
9946        assert_eq!(CLIENT_METHOD_NAMES.auth_status_update, "auth/status_update");
9947        let notification = AgentNotification::AuthStatusUpdateNotification(Box::new(
9948            AuthStatusUpdateNotification::new(AuthState::Unauthenticated),
9949        ));
9950        assert_eq!(notification.method(), "auth/status_update");
9951        assert_eq!(
9952            serde_json::to_value(&notification).unwrap(),
9953            json!({"state": "unauthenticated"})
9954        );
9955    }
9956}