Skip to main content

agent_client_protocol_schema/v2/
client.rs

1//! Methods and notifications the client handles/receives.
2//!
3//! This module defines the Client trait and all associated types for implementing
4//! a client that interacts with AI coding agents via the Agent Client Protocol (ACP).
5
6use std::{collections::BTreeMap, sync::Arc};
7
8use derive_more::{Display, From};
9use schemars::{JsonSchema, Schema};
10use serde::{Deserialize, Serialize};
11use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
12
13#[cfg(feature = "unstable_plan_operations")]
14use super::PlanRemoved;
15#[cfg(feature = "unstable_end_turn_token_usage")]
16use super::Usage;
17#[cfg(feature = "unstable_elicitation")]
18use super::{
19    CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse,
20    ElicitationCapabilities,
21};
22use super::{
23    ContentBlock, ExtNotification, ExtRequest, ExtResponse, Meta, PlanUpdate, SessionConfigOption,
24    SessionId, StopReason, ToolCallContentChunk, ToolCallUpdate,
25};
26use crate::{IntoMaybeUndefined, IntoOption, MaybeUndefined, SkipListener};
27
28#[cfg(feature = "unstable_mcp_over_acp")]
29use super::mcp::{
30    ConnectMcpRequest, ConnectMcpResponse, DisconnectMcpRequest, DisconnectMcpResponse,
31    MCP_CONNECT_METHOD_NAME, MCP_DISCONNECT_METHOD_NAME, MCP_MESSAGE_METHOD_NAME,
32    MessageMcpNotification, MessageMcpRequest, MessageMcpResponse,
33};
34
35#[cfg(feature = "unstable_nes")]
36use super::{ClientNesCapabilities, PositionEncodingKind};
37
38// Session updates
39
40/// Notification containing a session update from the agent.
41///
42/// Used to stream real-time progress and results during prompt processing.
43///
44/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-lifecycle#3-agent-reports-output)
45#[serde_as]
46#[skip_serializing_none]
47#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
48#[schemars(extend("x-side" = "client", "x-method" = SESSION_UPDATE_NOTIFICATION))]
49#[serde(rename_all = "camelCase")]
50#[non_exhaustive]
51pub struct UpdateSessionNotification {
52    /// The ID of the session this update pertains to.
53    pub session_id: SessionId,
54    /// The actual update content.
55    pub update: SessionUpdate,
56    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
57    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
58    /// these keys.
59    ///
60    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
61    #[serde_as(deserialize_as = "DefaultOnError")]
62    #[schemars(extend("x-deserialize-default-on-error" = true))]
63    #[serde(default)]
64    #[serde(rename = "_meta")]
65    pub meta: Option<Meta>,
66}
67
68impl UpdateSessionNotification {
69    /// Builds [`SessionNotification`] with the required notification fields set; optional fields start unset or empty.
70    #[must_use]
71    pub fn new(session_id: impl Into<SessionId>, update: SessionUpdate) -> Self {
72        Self {
73            session_id: session_id.into(),
74            update,
75            meta: None,
76        }
77    }
78
79    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
80    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
81    /// these keys.
82    ///
83    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
84    #[must_use]
85    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
86        self.meta = meta.into_option();
87        self
88    }
89}
90
91/// Different types of updates that can be sent during session processing.
92///
93/// These updates provide real-time feedback about the agent's progress.
94///
95/// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-lifecycle#3-agent-reports-output)
96#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
97#[serde(tag = "sessionUpdate", rename_all = "snake_case")]
98#[schemars(extend("discriminator" = {"propertyName": "sessionUpdate"}))]
99#[non_exhaustive]
100pub enum SessionUpdate {
101    /// A chunk of the user's message being streamed.
102    UserMessageChunk(ContentChunk),
103    /// A user message has been created or updated.
104    ///
105    /// Agents can send this when they accept or replay a user message. When a
106    /// client receives another `user_message` update with the same `messageId`,
107    /// fields in the new update patch the previous fields for that message.
108    UserMessage(UserMessage),
109    /// A chunk of the agent's response being streamed.
110    AgentMessageChunk(ContentChunk),
111    /// An agent message has been created or updated.
112    ///
113    /// Agents can send this in addition to streamed chunks. When a client
114    /// receives another `agent_message` update with the same `messageId`,
115    /// fields in the new update patch the previous fields for that message.
116    AgentMessage(AgentMessage),
117    /// A chunk of the agent's internal reasoning being streamed.
118    AgentThoughtChunk(ContentChunk),
119    /// An agent thought or reasoning message has been created or updated.
120    ///
121    /// Agents can send this in addition to streamed chunks. When a client
122    /// receives another `agent_thought` update with the same `messageId`,
123    /// fields in the new update patch the previous fields for that message.
124    AgentThought(AgentThought),
125    /// The agent's session state has changed.
126    ///
127    /// Agents send this to report when work starts, completes, or pauses while
128    /// waiting for user action. Completion of active work is reported here instead
129    /// of in the `session/prompt` response.
130    StateUpdate(StateUpdate),
131    /// A chunk of tool-call content being streamed.
132    ToolCallContentChunk(ToolCallContentChunk),
133    /// A tool call has been created or updated.
134    ToolCallUpdate(ToolCallUpdate),
135    /// A content update for a plan identified by ID.
136    /// See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)
137    PlanUpdate(PlanUpdate),
138    /// **UNSTABLE**
139    ///
140    /// This capability is not part of the spec yet, and may be removed or changed at any point.
141    ///
142    /// Removal notice for a plan identified by ID.
143    #[cfg(feature = "unstable_plan_operations")]
144    PlanRemoved(PlanRemoved),
145    /// Available commands are ready or have changed
146    AvailableCommandsUpdate(AvailableCommandsUpdate),
147    /// Session configuration options have been updated.
148    ConfigOptionUpdate(ConfigOptionUpdate),
149    /// Session metadata has been updated (title, timestamps, custom metadata)
150    SessionInfoUpdate(SessionInfoUpdate),
151    /// Context window and cost update for the session.
152    UsageUpdate(UsageUpdate),
153    /// Custom or future session update.
154    ///
155    /// Values beginning with `_` are reserved for implementation-specific
156    /// extensions. Unknown values that do not begin with `_` are reserved for
157    /// future ACP variants.
158    ///
159    /// Receivers that do not understand this update type should preserve the
160    /// raw payload when storing, replaying, proxying, or forwarding session
161    /// history, and otherwise ignore it or display it generically.
162    #[serde(untagged)]
163    Other(OtherSessionUpdate),
164}
165
166/// Custom or future session update payload.
167///
168/// This preserves the unknown `sessionUpdate` discriminator and the rest of the
169/// update object for clients that store, replay, proxy, or forward session
170/// history.
171#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq)]
172#[schemars(inline)]
173#[schemars(transform = other_session_update_schema)]
174#[serde(rename_all = "camelCase")]
175#[non_exhaustive]
176pub struct OtherSessionUpdate {
177    /// Custom or future session update type.
178    ///
179    /// Values beginning with `_` are reserved for implementation-specific
180    /// extensions. Unknown values that do not begin with `_` are reserved for
181    /// future ACP variants.
182    #[serde(rename = "sessionUpdate")]
183    pub session_update: String,
184    /// Additional fields from the unknown update payload.
185    #[serde(flatten)]
186    pub fields: BTreeMap<String, serde_json::Value>,
187}
188
189impl OtherSessionUpdate {
190    /// Builds [`OtherSessionUpdate`] from an unknown discriminator and preserves the remaining extension fields.
191    #[must_use]
192    pub fn new(
193        session_update: impl Into<String>,
194        mut fields: BTreeMap<String, serde_json::Value>,
195    ) -> Self {
196        fields.remove("sessionUpdate");
197        Self {
198            session_update: session_update.into(),
199            fields,
200        }
201    }
202}
203
204impl<'de> Deserialize<'de> for OtherSessionUpdate {
205    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206    where
207        D: serde::Deserializer<'de>,
208    {
209        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
210        let session_update = fields
211            .remove("sessionUpdate")
212            .ok_or_else(|| serde::de::Error::missing_field("sessionUpdate"))?;
213        let serde_json::Value::String(session_update) = session_update else {
214            return Err(serde::de::Error::custom("`sessionUpdate` must be a string"));
215        };
216
217        if is_known_session_update(&session_update) {
218            return Err(serde::de::Error::custom(format!(
219                "known session update `{session_update}` did not match its schema"
220            )));
221        }
222
223        Ok(Self {
224            session_update,
225            fields,
226        })
227    }
228}
229
230fn is_known_session_update(session_update: &str) -> bool {
231    matches!(
232        session_update,
233        "user_message_chunk"
234            | "user_message"
235            | "agent_message_chunk"
236            | "agent_message"
237            | "agent_thought_chunk"
238            | "agent_thought"
239            | "state_update"
240            | "tool_call_content_chunk"
241            | "tool_call_update"
242            | "plan_update"
243            | "available_commands_update"
244            | "config_option_update"
245            | "session_info_update"
246            | "usage_update"
247    )
248}
249
250fn other_session_update_schema(schema: &mut Schema) {
251    super::schema_util::reject_known_string_discriminators(
252        schema,
253        "sessionUpdate",
254        &[
255            "user_message_chunk",
256            "user_message",
257            "agent_message_chunk",
258            "agent_message",
259            "agent_thought_chunk",
260            "agent_thought",
261            "state_update",
262            "tool_call_content_chunk",
263            "tool_call_update",
264            "plan_update",
265            "available_commands_update",
266            "config_option_update",
267            "session_info_update",
268            #[cfg(feature = "unstable_plan_operations")]
269            "plan_removed",
270            "usage_update",
271        ],
272    );
273}
274
275/// Session configuration options have been updated.
276#[serde_as]
277#[skip_serializing_none]
278#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
279#[serde(rename_all = "camelCase")]
280#[non_exhaustive]
281pub struct ConfigOptionUpdate {
282    /// The full set of configuration options and their current values.
283    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
284    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
285    pub config_options: Vec<SessionConfigOption>,
286    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
287    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
288    /// these keys.
289    ///
290    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
291    #[serde_as(deserialize_as = "DefaultOnError")]
292    #[schemars(extend("x-deserialize-default-on-error" = true))]
293    #[serde(default)]
294    #[serde(rename = "_meta")]
295    pub meta: Option<Meta>,
296}
297
298impl ConfigOptionUpdate {
299    /// Builds [`ConfigOptionUpdate`] with the required fields set; optional fields start unset or empty.
300    #[must_use]
301    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
302        Self {
303            config_options,
304            meta: None,
305        }
306    }
307
308    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
309    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
310    /// these keys.
311    ///
312    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
313    #[must_use]
314    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
315        self.meta = meta.into_option();
316        self
317    }
318}
319
320/// Update to session metadata. All fields are optional to support partial updates.
321///
322/// Agents send this notification to update session information like title or custom metadata.
323/// This allows clients to display dynamic session names and track session state changes.
324#[serde_as]
325#[skip_serializing_none]
326#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
327#[serde(rename_all = "camelCase")]
328#[non_exhaustive]
329pub struct SessionInfoUpdate {
330    /// Human-readable title for the session. Set to null to clear.
331    #[serde_as(deserialize_as = "DefaultOnError")]
332    #[schemars(extend("x-deserialize-default-on-error" = true))]
333    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
334    pub title: MaybeUndefined<String>,
335    /// ISO 8601 timestamp of last activity. Set to null to clear.
336    #[serde_as(deserialize_as = "DefaultOnError")]
337    #[schemars(extend("x-deserialize-default-on-error" = true))]
338    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
339    pub updated_at: MaybeUndefined<String>,
340    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
341    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
342    /// these keys.
343    ///
344    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
345    #[serde_as(deserialize_as = "DefaultOnError")]
346    #[schemars(extend("x-deserialize-default-on-error" = true))]
347    #[serde(default)]
348    #[serde(rename = "_meta")]
349    pub meta: Option<Meta>,
350}
351
352impl SessionInfoUpdate {
353    /// Builds [`SessionInfoUpdate`] with the required fields set; optional fields start unset or empty.
354    #[must_use]
355    pub fn new() -> Self {
356        Self::default()
357    }
358
359    /// Human-readable title for the session. Set to null to clear.
360    #[must_use]
361    pub fn title(mut self, title: impl IntoMaybeUndefined<String>) -> Self {
362        self.title = title.into_maybe_undefined();
363        self
364    }
365
366    /// ISO 8601 timestamp of last activity. Set to null to clear.
367    #[must_use]
368    pub fn updated_at(mut self, updated_at: impl IntoMaybeUndefined<String>) -> Self {
369        self.updated_at = updated_at.into_maybe_undefined();
370        self
371    }
372
373    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
374    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
375    /// these keys.
376    ///
377    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
378    #[must_use]
379    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
380        self.meta = meta.into_option();
381        self
382    }
383}
384
385/// Context window and cost update for a session.
386#[serde_as]
387#[skip_serializing_none]
388#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
389#[serde(rename_all = "camelCase")]
390#[non_exhaustive]
391pub struct UsageUpdate {
392    /// Tokens currently in context.
393    pub used: u64,
394    /// Total context window size in tokens.
395    pub size: u64,
396    /// Cumulative session cost (optional).
397    #[serde_as(deserialize_as = "DefaultOnError")]
398    #[schemars(extend("x-deserialize-default-on-error" = true))]
399    #[serde(default)]
400    pub cost: Option<Cost>,
401    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
402    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
403    /// these keys.
404    ///
405    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
406    #[serde_as(deserialize_as = "DefaultOnError")]
407    #[schemars(extend("x-deserialize-default-on-error" = true))]
408    #[serde(default)]
409    #[serde(rename = "_meta")]
410    pub meta: Option<Meta>,
411}
412
413impl UsageUpdate {
414    /// Builds [`UsageUpdate`] with the required fields set; optional fields start unset or empty.
415    #[must_use]
416    pub fn new(used: u64, size: u64) -> Self {
417        Self {
418            used,
419            size,
420            cost: None,
421            meta: None,
422        }
423    }
424
425    /// Cumulative session cost (optional).
426    #[must_use]
427    pub fn cost(mut self, cost: impl IntoOption<Cost>) -> Self {
428        self.cost = cost.into_option();
429        self
430    }
431
432    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
433    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
434    /// these keys.
435    ///
436    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
437    #[must_use]
438    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
439        self.meta = meta.into_option();
440        self
441    }
442}
443
444/// The agent's session state has changed.
445///
446/// This update is the mechanism for reporting session activity transitions.
447/// A `session/prompt` response only acknowledges that the prompt was accepted;
448/// agents use `state_update` notifications to report that processing has started,
449/// that the session is idle, or that progress is blocked on user action.
450#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
451#[serde(tag = "state", rename_all = "snake_case")]
452#[schemars(extend("discriminator" = {"propertyName": "state"}))]
453#[non_exhaustive]
454pub enum StateUpdate {
455    /// The agent is actively processing work in the session.
456    Running(RunningStateUpdate),
457    /// The agent is not currently processing work in the session.
458    Idle(IdleStateUpdate),
459    /// The agent is waiting on user action before it can continue.
460    RequiresAction(RequiresActionStateUpdate),
461    /// Custom or future session state.
462    ///
463    /// Values beginning with `_` are reserved for implementation-specific
464    /// extensions. Unknown values that do not begin with `_` are reserved for
465    /// future ACP variants.
466    #[serde(untagged)]
467    Other(OtherStateUpdate),
468}
469
470/// The agent is actively processing work in the session.
471#[serde_as]
472#[skip_serializing_none]
473#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
474#[serde(rename_all = "camelCase")]
475#[non_exhaustive]
476pub struct RunningStateUpdate {
477    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
478    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
479    /// these keys.
480    ///
481    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
482    #[serde_as(deserialize_as = "DefaultOnError")]
483    #[schemars(extend("x-deserialize-default-on-error" = true))]
484    #[serde(default)]
485    #[serde(rename = "_meta")]
486    pub meta: Option<Meta>,
487}
488
489impl RunningStateUpdate {
490    /// Builds [`RunningStateUpdate`] with the required fields set; optional fields start unset or empty.
491    #[must_use]
492    pub fn new() -> Self {
493        Self::default()
494    }
495
496    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
497    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
498    /// these keys.
499    ///
500    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
501    #[must_use]
502    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
503        self.meta = meta.into_option();
504        self
505    }
506}
507
508/// The agent is not currently processing work in the session.
509#[serde_as]
510#[skip_serializing_none]
511#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
512#[serde(rename_all = "camelCase")]
513#[non_exhaustive]
514pub struct IdleStateUpdate {
515    /// Indicates why the agent stopped processing active session work.
516    ///
517    /// Optional. Omitted or `null` both mean the agent is not reporting a stop reason.
518    /// Agents SHOULD include this when the idle transition ends active work.
519    #[serde_as(deserialize_as = "DefaultOnError")]
520    #[schemars(extend("x-deserialize-default-on-error" = true))]
521    #[serde(default)]
522    pub stop_reason: Option<StopReason>,
523    /// **UNSTABLE**
524    ///
525    /// This capability is not part of the spec yet, and may be removed or changed at any point.
526    ///
527    /// Token usage for completed session work.
528    ///
529    /// Optional. Omitted or `null` both mean the agent is not reporting token
530    /// usage for this state update.
531    #[cfg(feature = "unstable_end_turn_token_usage")]
532    #[serde_as(deserialize_as = "DefaultOnError")]
533    #[schemars(extend("x-deserialize-default-on-error" = true))]
534    #[serde(default)]
535    pub usage: Option<Usage>,
536    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
537    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
538    /// these keys.
539    ///
540    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
541    #[serde_as(deserialize_as = "DefaultOnError")]
542    #[schemars(extend("x-deserialize-default-on-error" = true))]
543    #[serde(default)]
544    #[serde(rename = "_meta")]
545    pub meta: Option<Meta>,
546}
547
548impl IdleStateUpdate {
549    /// Builds [`IdleStateUpdate`] with the required fields set; optional fields start unset or empty.
550    #[must_use]
551    pub fn new() -> Self {
552        Self::default()
553    }
554
555    /// Indicates why the agent stopped processing active session work.
556    #[must_use]
557    pub fn stop_reason(mut self, stop_reason: impl IntoOption<StopReason>) -> Self {
558        self.stop_reason = stop_reason.into_option();
559        self
560    }
561
562    /// **UNSTABLE**
563    ///
564    /// This capability is not part of the spec yet, and may be removed or changed at any point.
565    ///
566    /// Token usage for completed session work.
567    #[cfg(feature = "unstable_end_turn_token_usage")]
568    #[must_use]
569    pub fn usage(mut self, usage: impl IntoOption<Usage>) -> Self {
570        self.usage = usage.into_option();
571        self
572    }
573
574    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
575    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
576    /// these keys.
577    ///
578    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
579    #[must_use]
580    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
581        self.meta = meta.into_option();
582        self
583    }
584}
585
586/// The agent is waiting on user action before it can continue.
587#[serde_as]
588#[skip_serializing_none]
589#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
590#[serde(rename_all = "camelCase")]
591#[non_exhaustive]
592pub struct RequiresActionStateUpdate {
593    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
594    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
595    /// these keys.
596    ///
597    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
598    #[serde_as(deserialize_as = "DefaultOnError")]
599    #[schemars(extend("x-deserialize-default-on-error" = true))]
600    #[serde(default)]
601    #[serde(rename = "_meta")]
602    pub meta: Option<Meta>,
603}
604
605impl RequiresActionStateUpdate {
606    /// Builds [`RequiresActionStateUpdate`] with the required fields set; optional fields start unset or empty.
607    #[must_use]
608    pub fn new() -> Self {
609        Self::default()
610    }
611
612    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
613    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
614    /// these keys.
615    ///
616    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
617    #[must_use]
618    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
619        self.meta = meta.into_option();
620        self
621    }
622}
623
624/// Custom or future session state payload.
625///
626/// This preserves the unknown `state` discriminator and the rest of the state
627/// object for clients that store, replay, proxy, or forward session history.
628#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq)]
629#[schemars(inline)]
630#[schemars(transform = other_state_update_schema)]
631#[serde(rename_all = "camelCase")]
632#[non_exhaustive]
633pub struct OtherStateUpdate {
634    /// Custom or future session state.
635    ///
636    /// Values beginning with `_` are reserved for implementation-specific
637    /// extensions. Unknown values that do not begin with `_` are reserved for
638    /// future ACP variants.
639    #[serde(rename = "state")]
640    pub state: String,
641    /// Additional fields from the unknown state payload.
642    #[serde(flatten)]
643    pub fields: BTreeMap<String, serde_json::Value>,
644}
645
646impl OtherStateUpdate {
647    /// Builds [`OtherStateUpdate`] from an unknown discriminator and preserves the remaining extension fields.
648    #[must_use]
649    pub fn new(state: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
650        fields.remove("state");
651        Self {
652            state: state.into(),
653            fields,
654        }
655    }
656}
657
658impl<'de> Deserialize<'de> for OtherStateUpdate {
659    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
660    where
661        D: serde::Deserializer<'de>,
662    {
663        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
664        let state = fields
665            .remove("state")
666            .ok_or_else(|| serde::de::Error::missing_field("state"))?;
667        let serde_json::Value::String(state) = state else {
668            return Err(serde::de::Error::custom("`state` must be a string"));
669        };
670
671        if is_known_state_update(&state) {
672            return Err(serde::de::Error::custom(format!(
673                "known state update `{state}` did not match its schema"
674            )));
675        }
676
677        Ok(Self { state, fields })
678    }
679}
680
681fn is_known_state_update(state: &str) -> bool {
682    matches!(state, "running" | "idle" | "requires_action")
683}
684
685fn other_state_update_schema(schema: &mut Schema) {
686    super::schema_util::reject_known_string_discriminators(
687        schema,
688        "state",
689        &["running", "idle", "requires_action"],
690    );
691}
692
693/// Cost information for a session.
694#[serde_as]
695#[skip_serializing_none]
696#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
697#[serde(rename_all = "camelCase")]
698#[non_exhaustive]
699pub struct Cost {
700    /// Total cumulative cost for session.
701    pub amount: f64,
702    /// ISO 4217 currency code (e.g., "USD", "EUR").
703    pub currency: String,
704    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
705    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
706    /// these keys.
707    ///
708    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
709    #[serde_as(deserialize_as = "DefaultOnError")]
710    #[schemars(extend("x-deserialize-default-on-error" = true))]
711    #[serde(default)]
712    #[serde(rename = "_meta")]
713    pub meta: Option<Meta>,
714}
715
716impl Cost {
717    /// Builds [`Cost`] with the required fields set; optional fields start unset or empty.
718    #[must_use]
719    pub fn new(amount: f64, currency: impl Into<String>) -> Self {
720        Self {
721            amount,
722            currency: currency.into(),
723            meta: None,
724        }
725    }
726
727    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
728    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
729    /// these keys.
730    ///
731    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
732    #[must_use]
733    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
734        self.meta = meta.into_option();
735        self
736    }
737}
738
739/// A streamed item of content
740#[serde_as]
741#[skip_serializing_none]
742#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
743#[serde(rename_all = "camelCase")]
744#[non_exhaustive]
745pub struct ContentChunk {
746    /// A unique identifier for the message this chunk belongs to.
747    ///
748    /// All chunks belonging to the same message share the same `messageId`.
749    /// A change in `messageId` indicates a new message has started.
750    pub message_id: MessageId,
751    /// A single item of content
752    pub content: ContentBlock,
753    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
754    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
755    /// these keys.
756    ///
757    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
758    #[serde_as(deserialize_as = "DefaultOnError")]
759    #[schemars(extend("x-deserialize-default-on-error" = true))]
760    #[serde(default)]
761    #[serde(rename = "_meta")]
762    pub meta: Option<Meta>,
763}
764
765impl ContentChunk {
766    /// Builds [`ContentChunk`] with the required fields set; optional fields start unset or empty.
767    #[must_use]
768    pub fn new(content: ContentBlock, message_id: impl Into<MessageId>) -> Self {
769        Self {
770            content,
771            message_id: message_id.into(),
772            meta: None,
773        }
774    }
775
776    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
777    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
778    /// these keys.
779    ///
780    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
781    #[must_use]
782    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
783        self.meta = meta.into_option();
784        self
785    }
786}
787
788/// A user message upsert.
789///
790/// Only [`UserMessage::message_id`] is required. Other fields have patch
791/// semantics: omitted fields leave the existing message value unchanged, `null`
792/// clears or unsets the value, and concrete values replace the previous value.
793/// For a new `messageId`, omitted fields use client defaults. `content` is
794/// replaced as a whole array; send `[]` or `null` to clear it.
795///
796/// Message updates and chunks are applied in the order they are received. When
797/// a `user_message` update includes `content`, that array replaces any content
798/// previously accumulated for the message, including content from earlier
799/// chunks. Later chunks with the same `messageId` append to the current
800/// content.
801#[serde_as]
802#[skip_serializing_none]
803#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
804#[serde(rename_all = "camelCase")]
805#[non_exhaustive]
806pub struct UserMessage {
807    /// A unique identifier for the message.
808    pub message_id: MessageId,
809    /// Complete replacement content for this message.
810    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
811    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
812    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
813    pub content: MaybeUndefined<Vec<ContentBlock>>,
814    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
815    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
816    /// these keys.
817    ///
818    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
819    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
820    #[schemars(extend("x-deserialize-default-on-error" = true))]
821    #[serde(
822        rename = "_meta",
823        default,
824        skip_serializing_if = "MaybeUndefined::is_undefined"
825    )]
826    pub meta: MaybeUndefined<Meta>,
827}
828
829impl UserMessage {
830    /// Builds [`UserMessage`] with the required fields set; optional fields start unset or empty.
831    #[must_use]
832    pub fn new(message_id: impl Into<MessageId>) -> Self {
833        Self {
834            message_id: message_id.into(),
835            content: MaybeUndefined::Undefined,
836            meta: MaybeUndefined::Undefined,
837        }
838    }
839
840    /// Complete replacement content for this message.
841    #[must_use]
842    pub fn content(mut self, content: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
843        self.content = content.into_maybe_undefined();
844        self
845    }
846
847    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
848    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
849    /// these keys.
850    ///
851    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
852    #[must_use]
853    pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
854        self.meta = meta.into_maybe_undefined();
855        self
856    }
857}
858
859/// An agent message upsert.
860///
861/// Only [`AgentMessage::message_id`] is required. Other fields have patch
862/// semantics: omitted fields leave the existing message value unchanged, `null`
863/// clears or unsets the value, and concrete values replace the previous value.
864/// For a new `messageId`, omitted fields use client defaults. `content` is
865/// replaced as a whole array; send `[]` or `null` to clear it.
866///
867/// Message updates and chunks are applied in the order they are received. When
868/// an `agent_message` update includes `content`, that array replaces any
869/// content previously accumulated for the message, including content from
870/// earlier chunks. Later chunks with the same `messageId` append to the current
871/// content.
872#[serde_as]
873#[skip_serializing_none]
874#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
875#[serde(rename_all = "camelCase")]
876#[non_exhaustive]
877pub struct AgentMessage {
878    /// A unique identifier for the message.
879    pub message_id: MessageId,
880    /// Complete replacement content for this message.
881    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
882    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
883    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
884    pub content: MaybeUndefined<Vec<ContentBlock>>,
885    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
886    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
887    /// these keys.
888    ///
889    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
890    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
891    #[schemars(extend("x-deserialize-default-on-error" = true))]
892    #[serde(
893        rename = "_meta",
894        default,
895        skip_serializing_if = "MaybeUndefined::is_undefined"
896    )]
897    pub meta: MaybeUndefined<Meta>,
898}
899
900impl AgentMessage {
901    /// Builds [`AgentMessage`] with the required fields set; optional fields start unset or empty.
902    #[must_use]
903    pub fn new(message_id: impl Into<MessageId>) -> Self {
904        Self {
905            message_id: message_id.into(),
906            content: MaybeUndefined::Undefined,
907            meta: MaybeUndefined::Undefined,
908        }
909    }
910
911    /// Complete replacement content for this message.
912    #[must_use]
913    pub fn content(mut self, content: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
914        self.content = content.into_maybe_undefined();
915        self
916    }
917
918    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
919    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
920    /// these keys.
921    ///
922    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
923    #[must_use]
924    pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
925        self.meta = meta.into_maybe_undefined();
926        self
927    }
928}
929
930/// An agent thought or reasoning message upsert.
931///
932/// Only [`AgentThought::message_id`] is required. Other fields have patch
933/// semantics: omitted fields leave the existing thought value unchanged, `null`
934/// clears or unsets the value, and concrete values replace the previous value.
935/// For a new `messageId`, omitted fields use client defaults. `content` is
936/// replaced as a whole array; send `[]` or `null` to clear it.
937///
938/// Message updates and chunks are applied in the order they are received. When
939/// an `agent_thought` update includes `content`, that array replaces any
940/// content previously accumulated for the thought, including content from
941/// earlier chunks. Later chunks with the same `messageId` append to the current
942/// content.
943#[serde_as]
944#[skip_serializing_none]
945#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
946#[serde(rename_all = "camelCase")]
947#[non_exhaustive]
948pub struct AgentThought {
949    /// A unique identifier for the thought message.
950    pub message_id: MessageId,
951    /// Complete replacement content for this thought message.
952    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
953    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
954    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
955    pub content: MaybeUndefined<Vec<ContentBlock>>,
956    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
957    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
958    /// these keys.
959    ///
960    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
961    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
962    #[schemars(extend("x-deserialize-default-on-error" = true))]
963    #[serde(
964        rename = "_meta",
965        default,
966        skip_serializing_if = "MaybeUndefined::is_undefined"
967    )]
968    pub meta: MaybeUndefined<Meta>,
969}
970
971impl AgentThought {
972    /// Builds [`AgentThought`] with the required fields set; optional fields start unset or empty.
973    #[must_use]
974    pub fn new(message_id: impl Into<MessageId>) -> Self {
975        Self {
976            message_id: message_id.into(),
977            content: MaybeUndefined::Undefined,
978            meta: MaybeUndefined::Undefined,
979        }
980    }
981
982    /// Complete replacement content for this thought message.
983    #[must_use]
984    pub fn content(mut self, content: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
985        self.content = content.into_maybe_undefined();
986        self
987    }
988
989    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
990    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
991    /// these keys.
992    ///
993    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
994    #[must_use]
995    pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
996        self.meta = meta.into_maybe_undefined();
997        self
998    }
999}
1000
1001/// Unique identifier for a message within a session.
1002#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
1003#[serde(transparent)]
1004#[from(Arc<str>, String, &'static str)]
1005#[non_exhaustive]
1006pub struct MessageId(pub Arc<str>);
1007
1008impl MessageId {
1009    /// Wraps a protocol string as a typed [`MessageId`].
1010    #[must_use]
1011    pub fn new(id: impl Into<Arc<str>>) -> Self {
1012        Self(id.into())
1013    }
1014}
1015
1016/// Available commands are ready or have changed
1017#[serde_as]
1018#[skip_serializing_none]
1019#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1020#[serde(rename_all = "camelCase")]
1021#[non_exhaustive]
1022pub struct AvailableCommandsUpdate {
1023    /// Commands the agent can execute
1024    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1025    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1026    pub available_commands: Vec<AvailableCommand>,
1027    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1028    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1029    /// these keys.
1030    ///
1031    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1032    #[serde_as(deserialize_as = "DefaultOnError")]
1033    #[schemars(extend("x-deserialize-default-on-error" = true))]
1034    #[serde(default)]
1035    #[serde(rename = "_meta")]
1036    pub meta: Option<Meta>,
1037}
1038
1039impl AvailableCommandsUpdate {
1040    /// Builds [`AvailableCommandsUpdate`] with the required fields set; optional fields start unset or empty.
1041    #[must_use]
1042    pub fn new(available_commands: Vec<AvailableCommand>) -> Self {
1043        Self {
1044            available_commands,
1045            meta: None,
1046        }
1047    }
1048
1049    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1050    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1051    /// these keys.
1052    ///
1053    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1054    #[must_use]
1055    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1056        self.meta = meta.into_option();
1057        self
1058    }
1059}
1060
1061/// Information about a command.
1062#[serde_as]
1063#[skip_serializing_none]
1064#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1065#[serde(rename_all = "camelCase")]
1066#[non_exhaustive]
1067pub struct AvailableCommand {
1068    /// Command name (e.g., `create_plan`, `research_codebase`).
1069    pub name: String,
1070    /// Human-readable description of what the command does.
1071    pub description: String,
1072    /// Input for the command if required
1073    #[serde_as(deserialize_as = "DefaultOnError")]
1074    #[schemars(extend("x-deserialize-default-on-error" = true))]
1075    #[serde(default)]
1076    pub input: Option<AvailableCommandInput>,
1077    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1078    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1079    /// these keys.
1080    ///
1081    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1082    #[serde_as(deserialize_as = "DefaultOnError")]
1083    #[schemars(extend("x-deserialize-default-on-error" = true))]
1084    #[serde(default)]
1085    #[serde(rename = "_meta")]
1086    pub meta: Option<Meta>,
1087}
1088
1089impl AvailableCommand {
1090    /// Builds [`AvailableCommand`] with the required fields set; optional fields start unset or empty.
1091    #[must_use]
1092    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
1093        Self {
1094            name: name.into(),
1095            description: description.into(),
1096            input: None,
1097            meta: None,
1098        }
1099    }
1100
1101    /// Input for the command if required
1102    #[must_use]
1103    pub fn input(mut self, input: impl IntoOption<AvailableCommandInput>) -> Self {
1104        self.input = input.into_option();
1105        self
1106    }
1107
1108    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1109    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1110    /// these keys.
1111    ///
1112    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1113    #[must_use]
1114    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1115        self.meta = meta.into_option();
1116        self
1117    }
1118}
1119
1120/// The input specification for a command.
1121#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1122#[serde(untagged, rename_all = "camelCase")]
1123#[non_exhaustive]
1124pub enum AvailableCommandInput {
1125    /// All text that was typed after the command name is provided as input.
1126    Unstructured(UnstructuredCommandInput),
1127    /// Custom or future command input specification.
1128    ///
1129    /// Values beginning with `_` are reserved for implementation-specific
1130    /// extensions. Unknown values that do not begin with `_` are reserved for
1131    /// future ACP variants.
1132    ///
1133    /// Clients that do not understand this input type should preserve the raw
1134    /// payload when storing, replaying, proxying, or forwarding command
1135    /// metadata, and otherwise ignore the input specification or display the
1136    /// command without structured input.
1137    Other(OtherAvailableCommandInput),
1138}
1139
1140/// Custom or future command input specification.
1141#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
1142#[schemars(inline)]
1143#[serde(rename_all = "camelCase")]
1144#[non_exhaustive]
1145pub struct OtherAvailableCommandInput {
1146    /// Custom or future command input type.
1147    ///
1148    /// Values beginning with `_` are reserved for implementation-specific
1149    /// extensions. Unknown values that do not begin with `_` are reserved for
1150    /// future ACP variants.
1151    #[serde(rename = "type")]
1152    pub type_: String,
1153    /// Additional fields from the unknown command input payload.
1154    #[serde(flatten)]
1155    pub fields: BTreeMap<String, serde_json::Value>,
1156}
1157
1158impl OtherAvailableCommandInput {
1159    /// Builds [`OtherAvailableCommandInput`] from an unknown discriminator and preserves the remaining extension fields.
1160    #[must_use]
1161    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1162        fields.remove("type");
1163        Self {
1164            type_: type_.into(),
1165            fields,
1166        }
1167    }
1168}
1169
1170impl<'de> Deserialize<'de> for OtherAvailableCommandInput {
1171    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1172    where
1173        D: serde::Deserializer<'de>,
1174    {
1175        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1176        let type_ = fields
1177            .remove("type")
1178            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1179        let serde_json::Value::String(type_) = type_ else {
1180            return Err(serde::de::Error::custom("`type` must be a string"));
1181        };
1182
1183        Ok(Self { type_, fields })
1184    }
1185}
1186
1187/// All text that was typed after the command name is provided as input.
1188#[serde_as]
1189#[skip_serializing_none]
1190#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
1191#[schemars(transform = unstructured_command_input_schema)]
1192#[serde(rename_all = "camelCase")]
1193#[non_exhaustive]
1194pub struct UnstructuredCommandInput {
1195    /// A hint to display when the input hasn't been provided yet
1196    pub hint: String,
1197    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1198    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1199    /// these keys.
1200    ///
1201    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1202    #[serde_as(deserialize_as = "DefaultOnError")]
1203    #[schemars(extend("x-deserialize-default-on-error" = true))]
1204    #[serde(default)]
1205    #[serde(rename = "_meta")]
1206    pub meta: Option<Meta>,
1207}
1208
1209impl UnstructuredCommandInput {
1210    /// Builds [`UnstructuredCommandInput`] with the required fields set; optional fields start unset or empty.
1211    #[must_use]
1212    pub fn new(hint: impl Into<String>) -> Self {
1213        Self {
1214            hint: hint.into(),
1215            meta: None,
1216        }
1217    }
1218
1219    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1220    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1221    /// these keys.
1222    ///
1223    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1224    #[must_use]
1225    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1226        self.meta = meta.into_option();
1227        self
1228    }
1229}
1230
1231impl<'de> Deserialize<'de> for UnstructuredCommandInput {
1232    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1233    where
1234        D: serde::Deserializer<'de>,
1235    {
1236        #[derive(Deserialize)]
1237        #[serde(rename_all = "camelCase")]
1238        struct RawUnstructuredCommandInput {
1239            hint: String,
1240            #[serde(rename = "_meta")]
1241            meta: Option<Meta>,
1242            #[serde(flatten)]
1243            fields: BTreeMap<String, serde_json::Value>,
1244        }
1245
1246        let raw = RawUnstructuredCommandInput::deserialize(deserializer)?;
1247        if raw.fields.contains_key("type") {
1248            return Err(serde::de::Error::custom(
1249                "unstructured command input cannot include a `type` field",
1250            ));
1251        }
1252
1253        Ok(Self {
1254            hint: raw.hint,
1255            meta: raw.meta,
1256        })
1257    }
1258}
1259
1260fn unstructured_command_input_schema(schema: &mut Schema) {
1261    super::schema_util::reject_property(schema, "type");
1262}
1263
1264// Permission
1265
1266/// Request for user permission to execute a tool call.
1267///
1268/// Sent when the agent needs authorization before performing a sensitive operation.
1269///
1270/// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
1271#[serde_as]
1272#[skip_serializing_none]
1273#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
1274#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
1275#[serde(rename_all = "camelCase")]
1276#[non_exhaustive]
1277pub struct RequestPermissionRequest {
1278    /// The session ID for this request.
1279    pub session_id: SessionId,
1280    /// Details about the tool call requiring permission.
1281    pub tool_call: ToolCallUpdate,
1282    /// Available permission options for the user to choose from.
1283    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1284    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1285    pub options: Vec<PermissionOption>,
1286    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1287    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1288    /// these keys.
1289    ///
1290    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1291    #[serde_as(deserialize_as = "DefaultOnError")]
1292    #[schemars(extend("x-deserialize-default-on-error" = true))]
1293    #[serde(default)]
1294    #[serde(rename = "_meta")]
1295    pub meta: Option<Meta>,
1296}
1297
1298impl RequestPermissionRequest {
1299    /// Builds [`RequestPermissionRequest`] with the required request fields set; optional fields start unset or empty.
1300    #[must_use]
1301    pub fn new(
1302        session_id: impl Into<SessionId>,
1303        tool_call: ToolCallUpdate,
1304        options: Vec<PermissionOption>,
1305    ) -> Self {
1306        Self {
1307            session_id: session_id.into(),
1308            tool_call,
1309            options,
1310            meta: None,
1311        }
1312    }
1313
1314    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1315    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1316    /// these keys.
1317    ///
1318    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1319    #[must_use]
1320    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1321        self.meta = meta.into_option();
1322        self
1323    }
1324}
1325
1326/// An option presented to the user when requesting permission.
1327#[serde_as]
1328#[skip_serializing_none]
1329#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1330#[serde(rename_all = "camelCase")]
1331#[non_exhaustive]
1332pub struct PermissionOption {
1333    /// Unique identifier for this permission option.
1334    pub option_id: PermissionOptionId,
1335    /// Human-readable label to display to the user.
1336    pub name: String,
1337    /// Hint about the nature of this permission option.
1338    pub kind: PermissionOptionKind,
1339    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1340    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1341    /// these keys.
1342    ///
1343    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1344    #[serde_as(deserialize_as = "DefaultOnError")]
1345    #[schemars(extend("x-deserialize-default-on-error" = true))]
1346    #[serde(default)]
1347    #[serde(rename = "_meta")]
1348    pub meta: Option<Meta>,
1349}
1350
1351impl PermissionOption {
1352    /// Builds [`PermissionOption`] with the required fields set; optional fields start unset or empty.
1353    #[must_use]
1354    pub fn new(
1355        option_id: impl Into<PermissionOptionId>,
1356        name: impl Into<String>,
1357        kind: PermissionOptionKind,
1358    ) -> Self {
1359        Self {
1360            option_id: option_id.into(),
1361            name: name.into(),
1362            kind,
1363            meta: None,
1364        }
1365    }
1366
1367    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1368    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1369    /// these keys.
1370    ///
1371    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1372    #[must_use]
1373    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1374        self.meta = meta.into_option();
1375        self
1376    }
1377}
1378
1379/// Unique identifier for a permission option.
1380#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
1381#[serde(transparent)]
1382#[from(Arc<str>, String, &'static str)]
1383#[non_exhaustive]
1384pub struct PermissionOptionId(pub Arc<str>);
1385
1386impl PermissionOptionId {
1387    /// Wraps a protocol string as a typed [`PermissionOptionId`].
1388    #[must_use]
1389    pub fn new(id: impl Into<Arc<str>>) -> Self {
1390        Self(id.into())
1391    }
1392}
1393
1394/// The type of permission option being presented to the user.
1395///
1396/// Helps clients choose appropriate icons and UI treatment.
1397#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1398#[serde(rename_all = "snake_case")]
1399#[non_exhaustive]
1400pub enum PermissionOptionKind {
1401    /// Allow this operation only this time.
1402    AllowOnce,
1403    /// Allow this operation and remember the choice.
1404    AllowAlways,
1405    /// Reject this operation only this time.
1406    RejectOnce,
1407    /// Reject this operation and remember the choice.
1408    RejectAlways,
1409    /// Custom or future permission option kind.
1410    ///
1411    /// Values beginning with `_` are reserved for implementation-specific
1412    /// extensions. Unknown values that do not begin with `_` are reserved for
1413    /// future ACP variants.
1414    #[serde(untagged)]
1415    Other(String),
1416}
1417
1418/// Response to a permission request.
1419#[serde_as]
1420#[skip_serializing_none]
1421#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1422#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
1423#[serde(rename_all = "camelCase")]
1424#[non_exhaustive]
1425pub struct RequestPermissionResponse {
1426    /// The user's decision on the permission request.
1427    pub outcome: RequestPermissionOutcome,
1428    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1429    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1430    /// these keys.
1431    ///
1432    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1433    #[serde_as(deserialize_as = "DefaultOnError")]
1434    #[schemars(extend("x-deserialize-default-on-error" = true))]
1435    #[serde(default)]
1436    #[serde(rename = "_meta")]
1437    pub meta: Option<Meta>,
1438}
1439
1440impl RequestPermissionResponse {
1441    /// Builds [`RequestPermissionResponse`] with the required response fields set; optional fields start unset or empty.
1442    #[must_use]
1443    pub fn new(outcome: RequestPermissionOutcome) -> Self {
1444        Self {
1445            outcome,
1446            meta: None,
1447        }
1448    }
1449
1450    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1451    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1452    /// these keys.
1453    ///
1454    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1455    #[must_use]
1456    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1457        self.meta = meta.into_option();
1458        self
1459    }
1460}
1461
1462/// The outcome of a permission request.
1463#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1464#[serde(tag = "outcome", rename_all = "snake_case")]
1465#[schemars(extend("discriminator" = {"propertyName": "outcome"}))]
1466#[non_exhaustive]
1467pub enum RequestPermissionOutcome {
1468    /// Active session work was cancelled before the user responded.
1469    ///
1470    /// When a client sends a `session/cancel` notification to cancel active
1471    /// session work, it MUST respond to all pending `session/request_permission`
1472    /// requests with this `Cancelled` outcome.
1473    ///
1474    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-lifecycle#cancellation)
1475    Cancelled,
1476    /// The user selected one of the provided options.
1477    #[serde(rename_all = "camelCase")]
1478    Selected(SelectedPermissionOutcome),
1479}
1480
1481/// The user selected one of the provided options.
1482#[serde_as]
1483#[skip_serializing_none]
1484#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1485#[serde(rename_all = "camelCase")]
1486#[non_exhaustive]
1487pub struct SelectedPermissionOutcome {
1488    /// The ID of the option the user selected.
1489    pub option_id: PermissionOptionId,
1490    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1491    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1492    /// these keys.
1493    ///
1494    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1495    #[serde_as(deserialize_as = "DefaultOnError")]
1496    #[schemars(extend("x-deserialize-default-on-error" = true))]
1497    #[serde(default)]
1498    #[serde(rename = "_meta")]
1499    pub meta: Option<Meta>,
1500}
1501
1502impl SelectedPermissionOutcome {
1503    /// Builds [`SelectedPermissionOutcome`] with the required fields set; optional fields start unset or empty.
1504    #[must_use]
1505    pub fn new(option_id: impl Into<PermissionOptionId>) -> Self {
1506        Self {
1507            option_id: option_id.into(),
1508            meta: None,
1509        }
1510    }
1511
1512    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1513    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1514    /// these keys.
1515    ///
1516    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1517    #[must_use]
1518    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1519        self.meta = meta.into_option();
1520        self
1521    }
1522}
1523
1524// Capabilities
1525
1526/// Capabilities supported by the client.
1527///
1528/// Advertised during initialization to inform the agent about
1529/// available features and methods.
1530///
1531/// See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/initialization#client-capabilities)
1532#[serde_as]
1533#[skip_serializing_none]
1534#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1535#[serde(rename_all = "camelCase")]
1536#[non_exhaustive]
1537pub struct ClientCapabilities {
1538    /// **UNSTABLE**
1539    ///
1540    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1541    ///
1542    /// Authentication capabilities supported by the client.
1543    /// Determines which authentication method types the agent may include
1544    /// in its `InitializeResponse`.
1545    #[cfg(feature = "unstable_auth_methods")]
1546    #[serde_as(deserialize_as = "DefaultOnError")]
1547    #[schemars(extend("x-deserialize-default-on-error" = true))]
1548    #[serde(default)]
1549    pub auth: Option<AuthCapabilities>,
1550    /// **UNSTABLE**
1551    ///
1552    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1553    ///
1554    /// Elicitation capabilities supported by the client.
1555    /// Determines which elicitation modes the agent may use.
1556    #[cfg(feature = "unstable_elicitation")]
1557    #[serde_as(deserialize_as = "DefaultOnError")]
1558    #[schemars(extend("x-deserialize-default-on-error" = true))]
1559    #[serde(default)]
1560    pub elicitation: Option<ElicitationCapabilities>,
1561    /// **UNSTABLE**
1562    ///
1563    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1564    ///
1565    /// NES (Next Edit Suggestions) capabilities supported by the client.
1566    #[cfg(feature = "unstable_nes")]
1567    #[serde_as(deserialize_as = "DefaultOnError")]
1568    #[schemars(extend("x-deserialize-default-on-error" = true))]
1569    #[serde(default)]
1570    pub nes: Option<ClientNesCapabilities>,
1571    /// **UNSTABLE**
1572    ///
1573    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1574    ///
1575    /// The position encodings supported by the client, in order of preference.
1576    #[cfg(feature = "unstable_nes")]
1577    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1578    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1579    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1580    pub position_encodings: Vec<PositionEncodingKind>,
1581
1582    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1583    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1584    /// these keys.
1585    ///
1586    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1587    #[serde_as(deserialize_as = "DefaultOnError")]
1588    #[schemars(extend("x-deserialize-default-on-error" = true))]
1589    #[serde(default)]
1590    #[serde(rename = "_meta")]
1591    pub meta: Option<Meta>,
1592}
1593
1594impl ClientCapabilities {
1595    /// Builds an empty [`ClientCapabilities`]; use builder methods to advertise supported sub-capabilities.
1596    #[must_use]
1597    pub fn new() -> Self {
1598        Self::default()
1599    }
1600
1601    /// **UNSTABLE**
1602    ///
1603    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1604    ///
1605    /// Authentication capabilities supported by the client.
1606    /// Determines which authentication method types the agent may include
1607    /// in its `InitializeResponse`.
1608    #[cfg(feature = "unstable_auth_methods")]
1609    #[must_use]
1610    pub fn auth(mut self, auth: impl IntoOption<AuthCapabilities>) -> Self {
1611        self.auth = auth.into_option();
1612        self
1613    }
1614
1615    /// **UNSTABLE**
1616    ///
1617    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1618    ///
1619    /// Elicitation capabilities supported by the client.
1620    /// Determines which elicitation modes the agent may use.
1621    #[cfg(feature = "unstable_elicitation")]
1622    #[must_use]
1623    pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
1624        self.elicitation = elicitation.into_option();
1625        self
1626    }
1627
1628    /// **UNSTABLE**
1629    ///
1630    /// NES (Next Edit Suggestions) capabilities supported by the client.
1631    #[cfg(feature = "unstable_nes")]
1632    #[must_use]
1633    pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
1634        self.nes = nes.into_option();
1635        self
1636    }
1637
1638    /// **UNSTABLE**
1639    ///
1640    /// The position encodings supported by the client, in order of preference.
1641    #[cfg(feature = "unstable_nes")]
1642    #[must_use]
1643    pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
1644        self.position_encodings = position_encodings;
1645        self
1646    }
1647
1648    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1649    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1650    /// these keys.
1651    ///
1652    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1653    #[must_use]
1654    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1655        self.meta = meta.into_option();
1656        self
1657    }
1658}
1659
1660/// **UNSTABLE**
1661///
1662/// This capability is not part of the spec yet, and may be removed or changed at any point.
1663///
1664/// Authentication capabilities supported by the client.
1665///
1666/// Advertised during initialization to inform the agent which authentication
1667/// method types the client can handle. This governs opt-in types that require
1668/// additional client-side support.
1669#[cfg(feature = "unstable_auth_methods")]
1670#[serde_as]
1671#[skip_serializing_none]
1672#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1673#[serde(rename_all = "camelCase")]
1674#[non_exhaustive]
1675pub struct AuthCapabilities {
1676    /// Whether the client supports `terminal` authentication methods.
1677    ///
1678    /// Optional. Omitted or `null` both mean the client does not advertise support.
1679    /// Supplying `{}` means the agent may include `terminal` entries in its authentication methods.
1680    #[serde_as(deserialize_as = "DefaultOnError")]
1681    #[schemars(extend("x-deserialize-default-on-error" = true))]
1682    #[serde(default)]
1683    pub terminal: Option<TerminalAuthCapabilities>,
1684    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1685    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1686    /// these keys.
1687    ///
1688    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1689    #[serde_as(deserialize_as = "DefaultOnError")]
1690    #[schemars(extend("x-deserialize-default-on-error" = true))]
1691    #[serde(default)]
1692    #[serde(rename = "_meta")]
1693    pub meta: Option<Meta>,
1694}
1695
1696#[cfg(feature = "unstable_auth_methods")]
1697impl AuthCapabilities {
1698    /// Builds an empty [`AuthCapabilities`]; use builder methods to advertise supported sub-capabilities.
1699    #[must_use]
1700    pub fn new() -> Self {
1701        Self::default()
1702    }
1703
1704    /// Whether the client supports `terminal` authentication methods.
1705    ///
1706    /// Omitted or `null` both mean the client does not advertise support.
1707    /// Supplying `{}` means the agent may include `AuthMethod::Terminal` entries in its authentication methods.
1708    #[must_use]
1709    pub fn terminal(mut self, terminal: impl IntoOption<TerminalAuthCapabilities>) -> Self {
1710        self.terminal = terminal.into_option();
1711        self
1712    }
1713
1714    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1715    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1716    /// these keys.
1717    ///
1718    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1719    #[must_use]
1720    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1721        self.meta = meta.into_option();
1722        self
1723    }
1724}
1725
1726/// **UNSTABLE**
1727///
1728/// This capability is not part of the spec yet, and may be removed or changed at any point.
1729///
1730/// Capabilities for terminal authentication methods.
1731///
1732/// Supplying `{}` means the client supports terminal authentication methods.
1733#[cfg(feature = "unstable_auth_methods")]
1734#[serde_as]
1735#[skip_serializing_none]
1736#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1737#[non_exhaustive]
1738pub struct TerminalAuthCapabilities {
1739    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1740    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1741    /// these keys.
1742    ///
1743    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1744    #[serde_as(deserialize_as = "DefaultOnError")]
1745    #[schemars(extend("x-deserialize-default-on-error" = true))]
1746    #[serde(default)]
1747    #[serde(rename = "_meta")]
1748    pub meta: Option<Meta>,
1749}
1750
1751#[cfg(feature = "unstable_auth_methods")]
1752impl TerminalAuthCapabilities {
1753    /// Builds an empty [`TerminalAuthCapabilities`]; use builder methods to advertise supported sub-capabilities.
1754    #[must_use]
1755    pub fn new() -> Self {
1756        Self::default()
1757    }
1758
1759    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1760    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1761    /// these keys.
1762    ///
1763    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1764    #[must_use]
1765    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1766        self.meta = meta.into_option();
1767        self
1768    }
1769}
1770
1771// Method schema
1772
1773/// Names of all methods that clients handle.
1774///
1775/// Provides a centralized definition of method names used in the protocol.
1776#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1777#[non_exhaustive]
1778pub struct ClientMethodNames {
1779    /// Method for requesting permission from the user.
1780    pub session_request_permission: &'static str,
1781    /// Notification for session updates.
1782    pub session_update: &'static str,
1783    /// Method for opening an MCP-over-ACP connection.
1784    #[cfg(feature = "unstable_mcp_over_acp")]
1785    pub mcp_connect: &'static str,
1786    /// Method for exchanging MCP-over-ACP messages.
1787    #[cfg(feature = "unstable_mcp_over_acp")]
1788    pub mcp_message: &'static str,
1789    /// Method for closing an MCP-over-ACP connection.
1790    #[cfg(feature = "unstable_mcp_over_acp")]
1791    pub mcp_disconnect: &'static str,
1792    /// Method for elicitation.
1793    #[cfg(feature = "unstable_elicitation")]
1794    pub elicitation_create: &'static str,
1795    /// Notification for elicitation completion.
1796    #[cfg(feature = "unstable_elicitation")]
1797    pub elicitation_complete: &'static str,
1798}
1799
1800/// Constant containing all client method names.
1801pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
1802    session_update: SESSION_UPDATE_NOTIFICATION,
1803    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
1804    #[cfg(feature = "unstable_mcp_over_acp")]
1805    mcp_connect: MCP_CONNECT_METHOD_NAME,
1806    #[cfg(feature = "unstable_mcp_over_acp")]
1807    mcp_message: MCP_MESSAGE_METHOD_NAME,
1808    #[cfg(feature = "unstable_mcp_over_acp")]
1809    mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
1810    #[cfg(feature = "unstable_elicitation")]
1811    elicitation_create: ELICITATION_CREATE_METHOD_NAME,
1812    #[cfg(feature = "unstable_elicitation")]
1813    elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
1814};
1815
1816/// Notification name for session updates.
1817pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
1818/// Method name for requesting user permission.
1819pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
1820/// Method name for elicitation.
1821#[cfg(feature = "unstable_elicitation")]
1822pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
1823/// Notification name for elicitation completion.
1824#[cfg(feature = "unstable_elicitation")]
1825pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
1826
1827/// All possible requests that an agent can send to a client.
1828///
1829/// This enum is used internally for routing RPC requests. You typically won't need
1830/// to use this directly.
1831///
1832/// This enum encompasses all method calls from agent to client.
1833#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1834#[serde(untagged)]
1835#[schemars(inline)]
1836#[non_exhaustive]
1837pub enum AgentRequest {
1838    /// Requests permission from the user for a tool call operation.
1839    ///
1840    /// Called by the agent when it needs user authorization before executing
1841    /// a potentially sensitive operation. The client should present the options
1842    /// to the user and return their decision.
1843    ///
1844    /// If the client cancels active session work via `session/cancel`, it MUST
1845    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
1846    ///
1847    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
1848    RequestPermissionRequest(Box<RequestPermissionRequest>),
1849    /// **UNSTABLE**
1850    ///
1851    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1852    ///
1853    /// Requests structured user input via a form or URL.
1854    #[cfg(feature = "unstable_elicitation")]
1855    CreateElicitationRequest(CreateElicitationRequest),
1856    /// **UNSTABLE**
1857    ///
1858    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1859    ///
1860    /// Opens an MCP-over-ACP connection.
1861    #[cfg(feature = "unstable_mcp_over_acp")]
1862    ConnectMcpRequest(ConnectMcpRequest),
1863    /// **UNSTABLE**
1864    ///
1865    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1866    ///
1867    /// Exchanges an MCP-over-ACP message.
1868    #[cfg(feature = "unstable_mcp_over_acp")]
1869    MessageMcpRequest(MessageMcpRequest),
1870    /// **UNSTABLE**
1871    ///
1872    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1873    ///
1874    /// Closes an MCP-over-ACP connection.
1875    #[cfg(feature = "unstable_mcp_over_acp")]
1876    DisconnectMcpRequest(DisconnectMcpRequest),
1877    /// Handles extension method requests from the agent.
1878    ///
1879    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
1880    /// Extension methods provide a way to add custom functionality while maintaining
1881    /// protocol compatibility.
1882    ///
1883    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1884    ExtMethodRequest(ExtRequest),
1885}
1886
1887impl AgentRequest {
1888    /// Returns the corresponding method name of the request.
1889    #[must_use]
1890    pub fn method(&self) -> &str {
1891        match self {
1892            Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
1893            #[cfg(feature = "unstable_elicitation")]
1894            Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
1895            #[cfg(feature = "unstable_mcp_over_acp")]
1896            Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
1897            #[cfg(feature = "unstable_mcp_over_acp")]
1898            Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
1899            #[cfg(feature = "unstable_mcp_over_acp")]
1900            Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
1901            Self::ExtMethodRequest(ext_request) => &ext_request.method,
1902        }
1903    }
1904}
1905
1906/// All possible responses that a client can send to an agent.
1907///
1908/// This enum is used internally for routing RPC responses. You typically won't need
1909/// to use this directly - the responses are handled automatically by the connection.
1910///
1911/// These are responses to the corresponding `AgentRequest` variants.
1912#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1913#[serde(untagged)]
1914#[schemars(inline)]
1915#[non_exhaustive]
1916pub enum ClientResponse {
1917    /// Successful result returned for a `session/request_permission` request.
1918    RequestPermissionResponse(RequestPermissionResponse),
1919    /// Successful result returned for a `elicitation/create` request.
1920    #[cfg(feature = "unstable_elicitation")]
1921    CreateElicitationResponse(CreateElicitationResponse),
1922    /// Successful result returned for a `mcp/connect` request.
1923    #[cfg(feature = "unstable_mcp_over_acp")]
1924    ConnectMcpResponse(ConnectMcpResponse),
1925    /// Successful result returned for a `mcp/disconnect` request.
1926    #[cfg(feature = "unstable_mcp_over_acp")]
1927    DisconnectMcpResponse(#[serde(default)] DisconnectMcpResponse),
1928    /// Successful result returned by an MCP-over-ACP `mcp/message` request.
1929    #[cfg(feature = "unstable_mcp_over_acp")]
1930    MessageMcpResponse(MessageMcpResponse),
1931    /// Successful result returned by an extension method outside the core ACP method set.
1932    ExtMethodResponse(ExtResponse),
1933}
1934
1935/// All possible notifications that an agent can send to a client.
1936///
1937/// This enum is used internally for routing RPC notifications. You typically won't need
1938/// to use this directly.
1939///
1940/// Notifications do not expect a response.
1941#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
1942#[serde(untagged)]
1943#[schemars(inline)]
1944#[non_exhaustive]
1945pub enum AgentNotification {
1946    /// Handles session update notifications from the agent.
1947    ///
1948    /// This is a notification endpoint (no response expected) that receives
1949    /// real-time updates about session progress, including message updates,
1950    /// message chunks, tool calls, and execution plans.
1951    ///
1952    /// Note: Clients SHOULD continue accepting tool call updates even after
1953    /// sending a `session/cancel` notification, as the agent may send final
1954    /// updates before reporting an idle `state_update` with the cancelled
1955    /// stop reason.
1956    ///
1957    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-lifecycle#3-agent-reports-output)
1958    UpdateSessionNotification(Box<UpdateSessionNotification>),
1959    /// **UNSTABLE**
1960    ///
1961    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1962    ///
1963    /// Notification that a URL-based elicitation has completed.
1964    #[cfg(feature = "unstable_elicitation")]
1965    CompleteElicitationNotification(CompleteElicitationNotification),
1966    /// **UNSTABLE**
1967    ///
1968    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1969    ///
1970    /// Receives an MCP-over-ACP notification.
1971    #[cfg(feature = "unstable_mcp_over_acp")]
1972    MessageMcpNotification(MessageMcpNotification),
1973    /// Handles extension notifications from the agent.
1974    ///
1975    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
1976    /// Extension notifications provide a way to send one-way messages for custom functionality
1977    /// while maintaining protocol compatibility.
1978    ///
1979    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1980    ExtNotification(ExtNotification),
1981}
1982
1983impl AgentNotification {
1984    /// Returns the corresponding method name of the notification.
1985    #[must_use]
1986    pub fn method(&self) -> &str {
1987        match self {
1988            Self::UpdateSessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
1989            #[cfg(feature = "unstable_elicitation")]
1990            Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
1991            #[cfg(feature = "unstable_mcp_over_acp")]
1992            Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
1993            Self::ExtNotification(ext_notification) => &ext_notification.method,
1994        }
1995    }
1996}
1997
1998#[cfg(test)]
1999mod tests {
2000    use super::*;
2001
2002    #[cfg(feature = "unstable_auth_methods")]
2003    #[test]
2004    fn test_client_capabilities_auth_defaults_on_malformed_value() {
2005        use serde_json::json;
2006
2007        let capabilities: ClientCapabilities = serde_json::from_value(json!({
2008            "auth": false
2009        }))
2010        .unwrap();
2011
2012        assert_eq!(capabilities.auth, None);
2013    }
2014
2015    #[test]
2016    fn test_serialization_behavior() {
2017        use serde_json::json;
2018
2019        assert_eq!(
2020            serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
2021            SessionInfoUpdate {
2022                title: MaybeUndefined::Undefined,
2023                updated_at: MaybeUndefined::Undefined,
2024                meta: None
2025            }
2026        );
2027        assert_eq!(
2028            serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
2029                .unwrap(),
2030            SessionInfoUpdate {
2031                title: MaybeUndefined::Null,
2032                updated_at: MaybeUndefined::Null,
2033                meta: None
2034            }
2035        );
2036        assert_eq!(
2037            serde_json::from_value::<SessionInfoUpdate>(
2038                json!({"title": "title", "updatedAt": "timestamp"})
2039            )
2040            .unwrap(),
2041            SessionInfoUpdate {
2042                title: MaybeUndefined::Value("title".to_string()),
2043                updated_at: MaybeUndefined::Value("timestamp".to_string()),
2044                meta: None
2045            }
2046        );
2047
2048        assert_eq!(
2049            serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
2050            json!({})
2051        );
2052        assert_eq!(
2053            serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
2054            json!({"title": "title"})
2055        );
2056        assert_eq!(
2057            serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
2058            json!({"title": null})
2059        );
2060        assert_eq!(
2061            serde_json::to_value(
2062                SessionInfoUpdate::new()
2063                    .title("title")
2064                    .title(MaybeUndefined::Undefined)
2065            )
2066            .unwrap(),
2067            json!({})
2068        );
2069    }
2070
2071    #[test]
2072    fn test_content_chunk_message_id_serialization() {
2073        use serde_json::json;
2074
2075        assert_eq!(
2076            serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
2077                ContentBlock::Text(crate::v2::TextContent::new("Hello")),
2078                "msg_agent_c42b9",
2079            )))
2080            .unwrap(),
2081            json!({
2082                "sessionUpdate": "agent_message_chunk",
2083                "messageId": "msg_agent_c42b9",
2084                "content": {
2085                    "type": "text",
2086                    "text": "Hello"
2087                }
2088            })
2089        );
2090
2091        let err = serde_json::from_value::<ContentChunk>(json!({
2092            "content": {
2093                "type": "text",
2094                "text": "Hello"
2095            }
2096        }))
2097        .unwrap_err();
2098
2099        assert!(err.to_string().contains("messageId"), "{err}");
2100    }
2101
2102    #[test]
2103    fn test_tool_call_content_chunk_serialization() {
2104        use serde_json::json;
2105
2106        assert_eq!(
2107            serde_json::to_value(SessionUpdate::ToolCallContentChunk(
2108                ToolCallContentChunk::new(
2109                    "call_001",
2110                    crate::v2::ContentBlock::Text(crate::v2::TextContent::new("partial output")),
2111                )
2112            ))
2113            .unwrap(),
2114            json!({
2115                "sessionUpdate": "tool_call_content_chunk",
2116                "toolCallId": "call_001",
2117                "content": {
2118                    "type": "content",
2119                    "content": {
2120                        "type": "text",
2121                        "text": "partial output"
2122                    }
2123                }
2124            })
2125        );
2126
2127        let err = serde_json::from_value::<ToolCallContentChunk>(json!({
2128            "content": {
2129                "type": "content",
2130                "content": {
2131                    "type": "text",
2132                    "text": "partial output"
2133                }
2134            }
2135        }))
2136        .unwrap_err();
2137
2138        assert!(err.to_string().contains("toolCallId"), "{err}");
2139    }
2140
2141    #[test]
2142    fn test_full_message_serialization() {
2143        use serde_json::json;
2144
2145        assert_eq!(
2146            serde_json::to_value(SessionUpdate::UserMessage(
2147                UserMessage::new("msg_user_8f7a1").content(vec![ContentBlock::Text(
2148                    crate::v2::TextContent::new("Hello")
2149                )])
2150            ))
2151            .unwrap(),
2152            json!({
2153                "sessionUpdate": "user_message",
2154                "messageId": "msg_user_8f7a1",
2155                "content": [
2156                    {
2157                        "type": "text",
2158                        "text": "Hello"
2159                    }
2160                ]
2161            })
2162        );
2163
2164        assert_eq!(
2165            serde_json::to_value(SessionUpdate::AgentMessage(
2166                AgentMessage::new("msg_agent_c42b9").content(vec![ContentBlock::Text(
2167                    crate::v2::TextContent::new("Hello")
2168                )])
2169            ))
2170            .unwrap(),
2171            json!({
2172                "sessionUpdate": "agent_message",
2173                "messageId": "msg_agent_c42b9",
2174                "content": [
2175                    {
2176                        "type": "text",
2177                        "text": "Hello"
2178                    }
2179                ]
2180            })
2181        );
2182
2183        assert_eq!(
2184            serde_json::to_value(SessionUpdate::AgentThought(
2185                AgentThought::new("msg_thought_a12").content(vec![ContentBlock::Text(
2186                    crate::v2::TextContent::new("Need to inspect the call sites first.")
2187                )])
2188            ))
2189            .unwrap(),
2190            json!({
2191                "sessionUpdate": "agent_thought",
2192                "messageId": "msg_thought_a12",
2193                "content": [
2194                    {
2195                        "type": "text",
2196                        "text": "Need to inspect the call sites first."
2197                    }
2198                ]
2199            })
2200        );
2201    }
2202
2203    #[test]
2204    fn test_message_upsert_serialization() {
2205        use serde_json::json;
2206
2207        assert_eq!(
2208            serde_json::to_value(SessionUpdate::UserMessage(
2209                UserMessage::new("msg_empty").content(Vec::<ContentBlock>::new())
2210            ))
2211            .unwrap(),
2212            json!({
2213                "sessionUpdate": "user_message",
2214                "messageId": "msg_empty",
2215                "content": []
2216            })
2217        );
2218
2219        let empty = serde_json::from_value::<UserMessage>(json!({
2220            "messageId": "msg_empty",
2221            "content": []
2222        }))
2223        .unwrap();
2224        assert!(matches!(
2225            empty.content,
2226            MaybeUndefined::Value(ref content) if content.is_empty()
2227        ));
2228
2229        let patch = serde_json::from_value::<AgentMessage>(json!({
2230            "messageId": "msg_agent_c42b9"
2231        }))
2232        .unwrap();
2233        assert_eq!(patch.content, MaybeUndefined::Undefined);
2234        assert_eq!(patch.meta, MaybeUndefined::Undefined);
2235
2236        let malformed_meta = serde_json::from_value::<AgentMessage>(json!({
2237            "messageId": "msg_agent_c42b9",
2238            "_meta": false
2239        }))
2240        .unwrap();
2241        assert_eq!(malformed_meta.meta, MaybeUndefined::Undefined);
2242
2243        let patch = serde_json::from_value::<AgentThought>(json!({
2244            "messageId": "msg_thought_a12"
2245        }))
2246        .unwrap();
2247        assert_eq!(patch.content, MaybeUndefined::Undefined);
2248
2249        let clear = serde_json::from_value::<UserMessage>(json!({
2250            "messageId": "msg_user_8f7a1",
2251            "content": null
2252        }))
2253        .unwrap();
2254        assert_eq!(clear.content, MaybeUndefined::Null);
2255
2256        let clear_meta = serde_json::from_value::<UserMessage>(json!({
2257            "messageId": "msg_user_8f7a1",
2258            "_meta": null
2259        }))
2260        .unwrap();
2261        assert_eq!(clear_meta.meta, MaybeUndefined::Null);
2262
2263        let mut meta = Meta::new();
2264        meta.insert("source".to_string(), json!("replay"));
2265
2266        assert_eq!(
2267            serde_json::to_value(SessionUpdate::UserMessage(
2268                UserMessage::new("msg_user_8f7a1").meta(meta)
2269            ))
2270            .unwrap(),
2271            json!({
2272                "sessionUpdate": "user_message",
2273                "messageId": "msg_user_8f7a1",
2274                "_meta": {
2275                    "source": "replay"
2276                }
2277            })
2278        );
2279
2280        assert_eq!(
2281            serde_json::to_value(SessionUpdate::UserMessage(
2282                UserMessage::new("msg_user_8f7a1").meta(None::<Meta>)
2283            ))
2284            .unwrap(),
2285            json!({
2286                "sessionUpdate": "user_message",
2287                "messageId": "msg_user_8f7a1",
2288                "_meta": null
2289            })
2290        );
2291    }
2292
2293    #[test]
2294    fn test_usage_update_serialization() {
2295        use serde_json::json;
2296
2297        assert_eq!(
2298            serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
2299                53_000, 200_000
2300            )))
2301            .unwrap(),
2302            json!({
2303                "sessionUpdate": "usage_update",
2304                "used": 53000,
2305                "size": 200_000
2306            })
2307        );
2308
2309        assert_eq!(
2310            serde_json::to_value(SessionUpdate::UsageUpdate(
2311                UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
2312            ))
2313            .unwrap(),
2314            json!({
2315                "sessionUpdate": "usage_update",
2316                "used": 53000,
2317                "size": 200_000,
2318                "cost": {
2319                    "amount": 0.045,
2320                    "currency": "USD"
2321                }
2322            })
2323        );
2324
2325        let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
2326            "sessionUpdate": "usage_update",
2327            "used": 53000,
2328            "size": 200_000,
2329            "cost": null
2330        }))
2331        .unwrap() else {
2332            panic!("expected usage update");
2333        };
2334
2335        assert_eq!(update.cost, None);
2336    }
2337
2338    #[test]
2339    fn test_state_update_serialization() {
2340        use serde_json::json;
2341
2342        assert_eq!(
2343            serde_json::to_value(SessionUpdate::StateUpdate(StateUpdate::Running(
2344                RunningStateUpdate::new()
2345            )))
2346            .unwrap(),
2347            json!({
2348                "sessionUpdate": "state_update",
2349                "state": "running"
2350            })
2351        );
2352
2353        assert_eq!(
2354            serde_json::to_value(SessionUpdate::StateUpdate(StateUpdate::Idle(
2355                IdleStateUpdate::new().stop_reason(StopReason::EndTurn)
2356            )))
2357            .unwrap(),
2358            json!({
2359                "sessionUpdate": "state_update",
2360                "state": "idle",
2361                "stopReason": "end_turn"
2362            })
2363        );
2364
2365        let SessionUpdate::StateUpdate(update) = serde_json::from_value(json!({
2366            "sessionUpdate": "state_update",
2367            "state": "requires_action"
2368        }))
2369        .unwrap() else {
2370            panic!("expected state update");
2371        };
2372
2373        assert!(matches!(update, StateUpdate::RequiresAction(_)));
2374
2375        let SessionUpdate::StateUpdate(StateUpdate::Idle(update)) = serde_json::from_value(json!({
2376            "sessionUpdate": "state_update",
2377            "state": "idle",
2378            "stopReason": null
2379        }))
2380        .unwrap() else {
2381            panic!("expected idle state update");
2382        };
2383
2384        assert_eq!(update.stop_reason, None);
2385
2386        let SessionUpdate::StateUpdate(StateUpdate::Other(update)) =
2387            serde_json::from_value(json!({
2388                "sessionUpdate": "state_update",
2389                "state": "_paused",
2390                "label": "Paused"
2391            }))
2392            .unwrap()
2393        else {
2394            panic!("expected unknown state update");
2395        };
2396
2397        assert_eq!(update.state, "_paused");
2398        assert_eq!(update.fields["label"], json!("Paused"));
2399    }
2400
2401    #[test]
2402    fn session_update_preserves_unknown_variant() {
2403        use serde_json::json;
2404
2405        let update: SessionUpdate = serde_json::from_value(json!({
2406            "sessionUpdate": "_status_badge",
2407            "label": "Indexing",
2408            "progress": 0.5
2409        }))
2410        .unwrap();
2411
2412        let SessionUpdate::Other(unknown) = update else {
2413            panic!("expected unknown session update");
2414        };
2415
2416        assert_eq!(unknown.session_update, "_status_badge");
2417        assert_eq!(unknown.fields.get("label"), Some(&json!("Indexing")));
2418        assert_eq!(unknown.fields.get("progress"), Some(&json!(0.5)));
2419
2420        assert_eq!(
2421            serde_json::to_value(SessionUpdate::Other(unknown)).unwrap(),
2422            json!({
2423                "sessionUpdate": "_status_badge",
2424                "label": "Indexing",
2425                "progress": 0.5
2426            })
2427        );
2428    }
2429
2430    #[test]
2431    fn test_plan_update_serialization() {
2432        use serde_json::json;
2433
2434        let plan_update =
2435            SessionUpdate::PlanUpdate(PlanUpdate::new(crate::v2::PlanUpdateContent::items(
2436                "plan-1",
2437                vec![crate::v2::PlanEntry::new(
2438                    "Step 1",
2439                    crate::v2::PlanEntryPriority::High,
2440                    crate::v2::PlanEntryStatus::Pending,
2441                )],
2442            )));
2443
2444        assert_eq!(
2445            serde_json::to_value(plan_update).unwrap(),
2446            json!({
2447                "sessionUpdate": "plan_update",
2448                "plan": {
2449                    "type": "items",
2450                    "id": "plan-1",
2451                    "entries": [
2452                        {
2453                            "content": "Step 1",
2454                            "priority": "high",
2455                            "status": "pending"
2456                        }
2457                    ]
2458                }
2459            })
2460        );
2461    }
2462
2463    #[cfg(feature = "unstable_plan_operations")]
2464    #[test]
2465    fn test_plan_removed_serialization() {
2466        use serde_json::json;
2467
2468        assert_eq!(
2469            serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
2470            json!({
2471                "sessionUpdate": "plan_removed",
2472                "id": "plan-1"
2473            })
2474        );
2475    }
2476
2477    #[test]
2478    fn available_command_input_preserves_unknown_typed_variant() {
2479        use serde_json::json;
2480
2481        let input: AvailableCommandInput = serde_json::from_value(json!({
2482            "type": "_choices",
2483            "hint": "Pick one",
2484            "options": ["fast", "careful"]
2485        }))
2486        .unwrap();
2487
2488        let AvailableCommandInput::Other(unknown) = input else {
2489            panic!("expected unknown command input");
2490        };
2491
2492        assert_eq!(unknown.type_, "_choices");
2493        assert_eq!(unknown.fields.get("hint"), Some(&json!("Pick one")));
2494        assert_eq!(
2495            unknown.fields.get("options"),
2496            Some(&json!(["fast", "careful"]))
2497        );
2498        assert_eq!(
2499            serde_json::to_value(AvailableCommandInput::Other(unknown)).unwrap(),
2500            json!({
2501                "type": "_choices",
2502                "hint": "Pick one",
2503                "options": ["fast", "careful"]
2504            })
2505        );
2506    }
2507
2508    #[test]
2509    fn available_command_input_unknown_does_not_hide_malformed_unstructured_variant() {
2510        use serde_json::json;
2511
2512        assert!(serde_json::from_value::<AvailableCommandInput>(json!({})).is_err());
2513        assert!(
2514            serde_json::from_value::<AvailableCommandInput>(json!({
2515                "type": 1,
2516                "hint": "Pick one"
2517            }))
2518            .is_err()
2519        );
2520    }
2521
2522    #[cfg(feature = "unstable_nes")]
2523    #[test]
2524    fn test_client_capabilities_position_encodings_serialization() {
2525        use serde_json::json;
2526
2527        let capabilities = ClientCapabilities::new().position_encodings(vec![
2528            PositionEncodingKind::Utf32,
2529            PositionEncodingKind::Utf16,
2530        ]);
2531        let json = serde_json::to_value(&capabilities).unwrap();
2532
2533        assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
2534    }
2535
2536    #[cfg(feature = "unstable_mcp_over_acp")]
2537    #[test]
2538    fn test_agent_mcp_request_method_names() {
2539        use serde_json::json;
2540
2541        let params: serde_json::Map<String, serde_json::Value> =
2542            [("cursor".to_string(), json!("abc"))].into_iter().collect();
2543
2544        assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
2545        assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
2546        assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
2547
2548        assert_eq!(
2549            AgentRequest::ConnectMcpRequest(ConnectMcpRequest::new("server-1")).method(),
2550            "mcp/connect"
2551        );
2552        assert_eq!(
2553            AgentRequest::MessageMcpRequest(MessageMcpRequest::new("conn-1", "tools/list"))
2554                .method(),
2555            "mcp/message"
2556        );
2557        assert_eq!(
2558            AgentRequest::DisconnectMcpRequest(DisconnectMcpRequest::new("conn-1")).method(),
2559            "mcp/disconnect"
2560        );
2561        assert_eq!(
2562            AgentNotification::MessageMcpNotification(MessageMcpNotification::new(
2563                "conn-1",
2564                "notifications/progress"
2565            ))
2566            .method(),
2567            "mcp/message"
2568        );
2569
2570        assert_eq!(
2571            serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
2572            json!({ "acpId": "server-1" })
2573        );
2574        assert_eq!(
2575            serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
2576            json!({ "connectionId": "conn-1" })
2577        );
2578        assert_eq!(
2579            serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
2580                .unwrap(),
2581            json!({
2582                "connectionId": "conn-1",
2583                "method": "tools/list",
2584                "params": { "cursor": "abc" }
2585            })
2586        );
2587        assert_eq!(
2588            serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
2589            json!({ "connectionId": "conn-1" })
2590        );
2591        assert_eq!(
2592            serde_json::to_value(MessageMcpNotification::new(
2593                "conn-1",
2594                "notifications/progress"
2595            ))
2596            .unwrap(),
2597            json!({
2598                "connectionId": "conn-1",
2599                "method": "notifications/progress"
2600            })
2601        );
2602
2603        let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
2604            "connectionId": "conn-1",
2605            "method": "tools/list",
2606            "params": null
2607        }))
2608        .unwrap();
2609        assert_eq!(request_with_null_params.params, None);
2610    }
2611
2612    #[cfg(feature = "unstable_auth_methods")]
2613    #[test]
2614    fn test_auth_capabilities_serialize_terminal_support_as_object() {
2615        use serde_json::json;
2616
2617        let capabilities = AuthCapabilities::new().terminal(TerminalAuthCapabilities::new());
2618
2619        assert_eq!(
2620            serde_json::to_value(&capabilities).unwrap(),
2621            json!({
2622                "terminal": {}
2623            })
2624        );
2625
2626        let deserialized: AuthCapabilities = serde_json::from_value(json!({
2627            "terminal": false
2628        }))
2629        .unwrap();
2630        assert!(deserialized.terminal.is_none());
2631    }
2632}