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    #[cfg(feature = "unstable_plan_operations")]
232    if session_update == "plan_removed" {
233        return true;
234    }
235    matches!(
236        session_update,
237        "user_message_chunk"
238            | "user_message"
239            | "agent_message_chunk"
240            | "agent_message"
241            | "agent_thought_chunk"
242            | "agent_thought"
243            | "state_update"
244            | "tool_call_content_chunk"
245            | "tool_call_update"
246            | "plan_update"
247            | "available_commands_update"
248            | "config_option_update"
249            | "session_info_update"
250            | "usage_update"
251    )
252}
253
254fn other_session_update_schema(schema: &mut Schema) {
255    super::schema_util::reject_known_string_discriminators(
256        schema,
257        "sessionUpdate",
258        &[
259            "user_message_chunk",
260            "user_message",
261            "agent_message_chunk",
262            "agent_message",
263            "agent_thought_chunk",
264            "agent_thought",
265            "state_update",
266            "tool_call_content_chunk",
267            "tool_call_update",
268            "plan_update",
269            "available_commands_update",
270            "config_option_update",
271            "session_info_update",
272            #[cfg(feature = "unstable_plan_operations")]
273            "plan_removed",
274            "usage_update",
275        ],
276    );
277}
278
279/// Session configuration options have been updated.
280#[serde_as]
281#[skip_serializing_none]
282#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
283#[serde(rename_all = "camelCase")]
284#[non_exhaustive]
285pub struct ConfigOptionUpdate {
286    /// The full set of configuration options and their current values.
287    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
288    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
289    pub config_options: Vec<SessionConfigOption>,
290    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
291    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
292    /// these keys.
293    ///
294    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
295    #[serde_as(deserialize_as = "DefaultOnError")]
296    #[schemars(extend("x-deserialize-default-on-error" = true))]
297    #[serde(default)]
298    #[serde(rename = "_meta")]
299    pub meta: Option<Meta>,
300}
301
302impl ConfigOptionUpdate {
303    /// Builds [`ConfigOptionUpdate`] with the required fields set; optional fields start unset or empty.
304    #[must_use]
305    pub fn new(config_options: Vec<SessionConfigOption>) -> Self {
306        Self {
307            config_options,
308            meta: None,
309        }
310    }
311
312    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
313    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
314    /// these keys.
315    ///
316    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
317    #[must_use]
318    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
319        self.meta = meta.into_option();
320        self
321    }
322}
323
324/// Update to session metadata. All fields are optional to support partial updates.
325///
326/// Agents send this notification to update session information like title or custom metadata.
327/// This allows clients to display dynamic session names and track session state changes.
328///
329/// Omitted fields leave the existing session info unchanged. `null` clears the
330/// corresponding value.
331#[serde_as]
332#[skip_serializing_none]
333#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
334#[serde(rename_all = "camelCase")]
335#[non_exhaustive]
336pub struct SessionInfoUpdate {
337    /// Human-readable title for the session. Set to null to clear.
338    #[serde_as(deserialize_as = "DefaultOnError")]
339    #[schemars(extend("x-deserialize-default-on-error" = true))]
340    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
341    pub title: MaybeUndefined<String>,
342    /// ISO 8601 timestamp of last activity. Set to null to clear.
343    #[serde_as(deserialize_as = "DefaultOnError")]
344    #[schemars(extend("x-deserialize-default-on-error" = true, "format" = "date-time"))]
345    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
346    pub updated_at: MaybeUndefined<String>,
347    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
348    /// metadata to their interactions. Omitted means no metadata update; `null` is an
349    /// explicit clear signal. Implementations MUST NOT make assumptions about values at these keys.
350    ///
351    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
352    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
353    #[schemars(extend("x-deserialize-default-on-error" = true))]
354    #[serde(
355        rename = "_meta",
356        default,
357        skip_serializing_if = "MaybeUndefined::is_undefined"
358    )]
359    pub meta: MaybeUndefined<Meta>,
360}
361
362impl SessionInfoUpdate {
363    /// Builds [`SessionInfoUpdate`] with the required fields set; optional fields start unset or empty.
364    #[must_use]
365    pub fn new() -> Self {
366        Self::default()
367    }
368
369    /// Human-readable title for the session. Set to null to clear.
370    #[must_use]
371    pub fn title(mut self, title: impl IntoMaybeUndefined<String>) -> Self {
372        self.title = title.into_maybe_undefined();
373        self
374    }
375
376    /// ISO 8601 timestamp of last activity. Set to null to clear.
377    #[must_use]
378    pub fn updated_at(mut self, updated_at: impl IntoMaybeUndefined<String>) -> Self {
379        self.updated_at = updated_at.into_maybe_undefined();
380        self
381    }
382
383    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
384    /// metadata to their interactions. Omitted means no metadata update; `null` is an
385    /// explicit clear signal. Implementations MUST NOT make assumptions about values at these keys.
386    ///
387    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
388    #[must_use]
389    pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
390        self.meta = meta.into_maybe_undefined();
391        self
392    }
393}
394
395/// Context window and cost update for a session.
396#[serde_as]
397#[skip_serializing_none]
398#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
399#[serde(rename_all = "camelCase")]
400#[non_exhaustive]
401pub struct UsageUpdate {
402    /// Tokens currently in context.
403    pub used: u64,
404    /// Total context window size in tokens.
405    pub size: u64,
406    /// Cumulative session cost (optional).
407    #[serde_as(deserialize_as = "DefaultOnError")]
408    #[schemars(extend("x-deserialize-default-on-error" = true))]
409    #[serde(default)]
410    pub cost: Option<Cost>,
411    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
412    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
413    /// these keys.
414    ///
415    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
416    #[serde_as(deserialize_as = "DefaultOnError")]
417    #[schemars(extend("x-deserialize-default-on-error" = true))]
418    #[serde(default)]
419    #[serde(rename = "_meta")]
420    pub meta: Option<Meta>,
421}
422
423impl UsageUpdate {
424    /// Builds [`UsageUpdate`] with the required fields set; optional fields start unset or empty.
425    #[must_use]
426    pub fn new(used: u64, size: u64) -> Self {
427        Self {
428            used,
429            size,
430            cost: None,
431            meta: None,
432        }
433    }
434
435    /// Cumulative session cost (optional).
436    #[must_use]
437    pub fn cost(mut self, cost: impl IntoOption<Cost>) -> Self {
438        self.cost = cost.into_option();
439        self
440    }
441
442    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
443    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
444    /// these keys.
445    ///
446    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
447    #[must_use]
448    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
449        self.meta = meta.into_option();
450        self
451    }
452}
453
454/// The agent's session state has changed.
455///
456/// This update is the mechanism for reporting session activity transitions.
457/// A `session/prompt` response only acknowledges that the prompt was accepted;
458/// agents use `state_update` notifications to report that processing has started,
459/// that the session is idle, or that progress is blocked on user action.
460#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
461#[serde(tag = "state", rename_all = "snake_case")]
462#[schemars(extend("discriminator" = {"propertyName": "state"}))]
463#[non_exhaustive]
464pub enum StateUpdate {
465    /// The agent is actively processing work in the session.
466    Running(RunningStateUpdate),
467    /// The agent is not currently processing work in the session.
468    Idle(IdleStateUpdate),
469    /// The agent is waiting on user action before it can continue.
470    RequiresAction(RequiresActionStateUpdate),
471    /// Custom or future session state.
472    ///
473    /// Values beginning with `_` are reserved for implementation-specific
474    /// extensions. Unknown values that do not begin with `_` are reserved for
475    /// future ACP variants.
476    #[serde(untagged)]
477    Other(OtherStateUpdate),
478}
479
480/// The agent is actively processing work in the session.
481#[serde_as]
482#[skip_serializing_none]
483#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
484#[serde(rename_all = "camelCase")]
485#[non_exhaustive]
486pub struct RunningStateUpdate {
487    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
488    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
489    /// these keys.
490    ///
491    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
492    #[serde_as(deserialize_as = "DefaultOnError")]
493    #[schemars(extend("x-deserialize-default-on-error" = true))]
494    #[serde(default)]
495    #[serde(rename = "_meta")]
496    pub meta: Option<Meta>,
497}
498
499impl RunningStateUpdate {
500    /// Builds [`RunningStateUpdate`] with the required fields set; optional fields start unset or empty.
501    #[must_use]
502    pub fn new() -> Self {
503        Self::default()
504    }
505
506    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
507    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
508    /// these keys.
509    ///
510    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
511    #[must_use]
512    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
513        self.meta = meta.into_option();
514        self
515    }
516}
517
518/// The agent is not currently processing work in the session.
519#[serde_as]
520#[skip_serializing_none]
521#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
522#[serde(rename_all = "camelCase")]
523#[non_exhaustive]
524pub struct IdleStateUpdate {
525    /// Indicates why the agent stopped processing active session work.
526    ///
527    /// Optional. Omitted or `null` both mean the agent is not reporting a stop reason.
528    /// Agents SHOULD include this when the idle transition ends active work.
529    #[serde_as(deserialize_as = "DefaultOnError")]
530    #[schemars(extend("x-deserialize-default-on-error" = true))]
531    #[serde(default)]
532    pub stop_reason: Option<StopReason>,
533    /// **UNSTABLE**
534    ///
535    /// This capability is not part of the spec yet, and may be removed or changed at any point.
536    ///
537    /// Token usage for completed session work.
538    ///
539    /// Optional. Omitted or `null` both mean the agent is not reporting token
540    /// usage for this state update.
541    #[cfg(feature = "unstable_end_turn_token_usage")]
542    #[serde_as(deserialize_as = "DefaultOnError")]
543    #[schemars(extend("x-deserialize-default-on-error" = true))]
544    #[serde(default)]
545    pub usage: Option<Usage>,
546    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
547    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
548    /// these keys.
549    ///
550    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
551    #[serde_as(deserialize_as = "DefaultOnError")]
552    #[schemars(extend("x-deserialize-default-on-error" = true))]
553    #[serde(default)]
554    #[serde(rename = "_meta")]
555    pub meta: Option<Meta>,
556}
557
558impl IdleStateUpdate {
559    /// Builds [`IdleStateUpdate`] with the required fields set; optional fields start unset or empty.
560    #[must_use]
561    pub fn new() -> Self {
562        Self::default()
563    }
564
565    /// Indicates why the agent stopped processing active session work.
566    #[must_use]
567    pub fn stop_reason(mut self, stop_reason: impl IntoOption<StopReason>) -> Self {
568        self.stop_reason = stop_reason.into_option();
569        self
570    }
571
572    /// **UNSTABLE**
573    ///
574    /// This capability is not part of the spec yet, and may be removed or changed at any point.
575    ///
576    /// Token usage for completed session work.
577    #[cfg(feature = "unstable_end_turn_token_usage")]
578    #[must_use]
579    pub fn usage(mut self, usage: impl IntoOption<Usage>) -> Self {
580        self.usage = usage.into_option();
581        self
582    }
583
584    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
585    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
586    /// these keys.
587    ///
588    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
589    #[must_use]
590    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
591        self.meta = meta.into_option();
592        self
593    }
594}
595
596/// The agent is waiting on user action before it can continue.
597#[serde_as]
598#[skip_serializing_none]
599#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
600#[serde(rename_all = "camelCase")]
601#[non_exhaustive]
602pub struct RequiresActionStateUpdate {
603    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
604    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
605    /// these keys.
606    ///
607    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
608    #[serde_as(deserialize_as = "DefaultOnError")]
609    #[schemars(extend("x-deserialize-default-on-error" = true))]
610    #[serde(default)]
611    #[serde(rename = "_meta")]
612    pub meta: Option<Meta>,
613}
614
615impl RequiresActionStateUpdate {
616    /// Builds [`RequiresActionStateUpdate`] with the required fields set; optional fields start unset or empty.
617    #[must_use]
618    pub fn new() -> Self {
619        Self::default()
620    }
621
622    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
623    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
624    /// these keys.
625    ///
626    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
627    #[must_use]
628    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
629        self.meta = meta.into_option();
630        self
631    }
632}
633
634/// Custom or future session state payload.
635///
636/// This preserves the unknown `state` discriminator and the rest of the state
637/// object for clients that store, replay, proxy, or forward session history.
638#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq)]
639#[schemars(inline)]
640#[schemars(transform = other_state_update_schema)]
641#[serde(rename_all = "camelCase")]
642#[non_exhaustive]
643pub struct OtherStateUpdate {
644    /// Custom or future session state.
645    ///
646    /// Values beginning with `_` are reserved for implementation-specific
647    /// extensions. Unknown values that do not begin with `_` are reserved for
648    /// future ACP variants.
649    #[serde(rename = "state")]
650    pub state: String,
651    /// Additional fields from the unknown state payload.
652    #[serde(flatten)]
653    pub fields: BTreeMap<String, serde_json::Value>,
654}
655
656impl OtherStateUpdate {
657    /// Builds [`OtherStateUpdate`] from an unknown discriminator and preserves the remaining extension fields.
658    #[must_use]
659    pub fn new(state: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
660        fields.remove("state");
661        Self {
662            state: state.into(),
663            fields,
664        }
665    }
666}
667
668impl<'de> Deserialize<'de> for OtherStateUpdate {
669    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
670    where
671        D: serde::Deserializer<'de>,
672    {
673        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
674        let state = fields
675            .remove("state")
676            .ok_or_else(|| serde::de::Error::missing_field("state"))?;
677        let serde_json::Value::String(state) = state else {
678            return Err(serde::de::Error::custom("`state` must be a string"));
679        };
680
681        if is_known_state_update(&state) {
682            return Err(serde::de::Error::custom(format!(
683                "known state update `{state}` did not match its schema"
684            )));
685        }
686
687        Ok(Self { state, fields })
688    }
689}
690
691fn is_known_state_update(state: &str) -> bool {
692    matches!(state, "running" | "idle" | "requires_action")
693}
694
695fn other_state_update_schema(schema: &mut Schema) {
696    super::schema_util::reject_known_string_discriminators(
697        schema,
698        "state",
699        &["running", "idle", "requires_action"],
700    );
701}
702
703/// Cost information for a session.
704#[serde_as]
705#[skip_serializing_none]
706#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
707#[serde(rename_all = "camelCase")]
708#[non_exhaustive]
709pub struct Cost {
710    /// Total cumulative cost for session.
711    pub amount: f64,
712    /// ISO 4217 currency code (e.g., "USD", "EUR").
713    #[schemars(pattern(r"^[A-Z]{3}$"))]
714    pub currency: String,
715    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
716    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
717    /// these keys.
718    ///
719    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
720    #[serde_as(deserialize_as = "DefaultOnError")]
721    #[schemars(extend("x-deserialize-default-on-error" = true))]
722    #[serde(default)]
723    #[serde(rename = "_meta")]
724    pub meta: Option<Meta>,
725}
726
727impl Cost {
728    /// Builds [`Cost`] with the required fields set; optional fields start unset or empty.
729    #[must_use]
730    pub fn new(amount: f64, currency: impl Into<String>) -> Self {
731        Self {
732            amount,
733            currency: currency.into(),
734            meta: None,
735        }
736    }
737
738    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
739    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
740    /// these keys.
741    ///
742    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
743    #[must_use]
744    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
745        self.meta = meta.into_option();
746        self
747    }
748}
749
750/// A streamed item of message content.
751#[serde_as]
752#[skip_serializing_none]
753#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
754#[serde(rename_all = "camelCase")]
755#[non_exhaustive]
756pub struct ContentChunk {
757    /// A unique identifier for the message this chunk belongs to.
758    ///
759    /// All chunks belonging to the same message share the same `messageId`.
760    /// A change in `messageId` indicates a new message has started.
761    pub message_id: MessageId,
762    /// A single item of content
763    pub content: ContentBlock,
764    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
765    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
766    /// these keys. This field is chunk-scoped.
767    ///
768    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
769    #[serde_as(deserialize_as = "DefaultOnError")]
770    #[schemars(extend("x-deserialize-default-on-error" = true))]
771    #[serde(default)]
772    #[serde(rename = "_meta")]
773    pub meta: Option<Meta>,
774}
775
776impl ContentChunk {
777    /// Builds [`ContentChunk`] with the required fields set; optional fields start unset or empty.
778    #[must_use]
779    pub fn new(content: ContentBlock, message_id: impl Into<MessageId>) -> Self {
780        Self {
781            content,
782            message_id: message_id.into(),
783            meta: None,
784        }
785    }
786
787    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
788    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
789    /// these keys. This field is chunk-scoped.
790    ///
791    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
792    #[must_use]
793    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
794        self.meta = meta.into_option();
795        self
796    }
797}
798
799/// A user message upsert.
800///
801/// Only [`UserMessage::message_id`] is required. `content` has patch semantics:
802/// an omitted field leaves existing message content unchanged, `null` clears the
803/// value, and a concrete array replaces the previous value. For a new
804/// `messageId`, omitted fields use client defaults. `content` is replaced as a
805/// whole array; send `[]` or `null` to clear it.
806///
807/// Message updates and chunks are applied in the order they are received. When
808/// a `user_message` update includes `content`, that array replaces any content
809/// previously accumulated for the message, including content from earlier
810/// chunks. Later chunks with the same `messageId` append to the current
811/// content.
812#[serde_as]
813#[skip_serializing_none]
814#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
815#[serde(rename_all = "camelCase")]
816#[non_exhaustive]
817pub struct UserMessage {
818    /// A unique identifier for the message.
819    pub message_id: MessageId,
820    /// Complete replacement content for this message.
821    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
822    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
823    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
824    pub content: MaybeUndefined<Vec<ContentBlock>>,
825    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
826    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
827    /// these keys. Omitted means no metadata update; `null` is an explicit clear signal.
828    ///
829    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
830    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
831    #[schemars(extend("x-deserialize-default-on-error" = true))]
832    #[serde(
833        rename = "_meta",
834        default,
835        skip_serializing_if = "MaybeUndefined::is_undefined"
836    )]
837    pub meta: MaybeUndefined<Meta>,
838}
839
840impl UserMessage {
841    /// Builds [`UserMessage`] with the required fields set; optional fields start unset or empty.
842    #[must_use]
843    pub fn new(message_id: impl Into<MessageId>) -> Self {
844        Self {
845            message_id: message_id.into(),
846            content: MaybeUndefined::Undefined,
847            meta: MaybeUndefined::Undefined,
848        }
849    }
850
851    /// Complete replacement content for this message.
852    #[must_use]
853    pub fn content(mut self, content: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
854        self.content = content.into_maybe_undefined();
855        self
856    }
857
858    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
859    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
860    /// these keys.
861    ///
862    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
863    #[must_use]
864    pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
865        self.meta = meta.into_maybe_undefined();
866        self
867    }
868}
869
870/// An agent message upsert.
871///
872/// Only [`AgentMessage::message_id`] is required. `content` has patch semantics:
873/// an omitted field leaves existing message content unchanged, `null` clears the
874/// value, and a concrete array replaces the previous value. For a new
875/// `messageId`, omitted fields use client defaults. `content` is replaced as a
876/// whole array; send `[]` or `null` to clear it.
877///
878/// Message updates and chunks are applied in the order they are received. When
879/// an `agent_message` update includes `content`, that array replaces any
880/// content previously accumulated for the message, including content from
881/// earlier chunks. Later chunks with the same `messageId` append to the current
882/// content.
883#[serde_as]
884#[skip_serializing_none]
885#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
886#[serde(rename_all = "camelCase")]
887#[non_exhaustive]
888pub struct AgentMessage {
889    /// A unique identifier for the message.
890    pub message_id: MessageId,
891    /// Complete replacement content for this message.
892    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
893    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
894    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
895    pub content: MaybeUndefined<Vec<ContentBlock>>,
896    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
897    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
898    /// these keys. Omitted means no metadata update; `null` is an explicit clear signal.
899    ///
900    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
901    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
902    #[schemars(extend("x-deserialize-default-on-error" = true))]
903    #[serde(
904        rename = "_meta",
905        default,
906        skip_serializing_if = "MaybeUndefined::is_undefined"
907    )]
908    pub meta: MaybeUndefined<Meta>,
909}
910
911impl AgentMessage {
912    /// Builds [`AgentMessage`] with the required fields set; optional fields start unset or empty.
913    #[must_use]
914    pub fn new(message_id: impl Into<MessageId>) -> Self {
915        Self {
916            message_id: message_id.into(),
917            content: MaybeUndefined::Undefined,
918            meta: MaybeUndefined::Undefined,
919        }
920    }
921
922    /// Complete replacement content for this message.
923    #[must_use]
924    pub fn content(mut self, content: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
925        self.content = content.into_maybe_undefined();
926        self
927    }
928
929    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
930    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
931    /// these keys.
932    ///
933    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
934    #[must_use]
935    pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
936        self.meta = meta.into_maybe_undefined();
937        self
938    }
939}
940
941/// An agent thought or reasoning message upsert.
942///
943/// Only [`AgentThought::message_id`] is required. `content` has patch semantics:
944/// an omitted field leaves existing thought content unchanged, `null` clears the
945/// value, and a concrete array replaces the previous value. For a new
946/// `messageId`, omitted fields use client defaults. `content` is replaced as a
947/// whole array; send `[]` or `null` to clear it.
948///
949/// Message updates and chunks are applied in the order they are received. When
950/// an `agent_thought` update includes `content`, that array replaces any
951/// content previously accumulated for the thought, including content from
952/// earlier chunks. Later chunks with the same `messageId` append to the current
953/// content.
954#[serde_as]
955#[skip_serializing_none]
956#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
957#[serde(rename_all = "camelCase")]
958#[non_exhaustive]
959pub struct AgentThought {
960    /// A unique identifier for the thought message.
961    pub message_id: MessageId,
962    /// Complete replacement content for this thought message.
963    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<VecSkipError<_, SkipListener>>>")]
964    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
965    #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
966    pub content: MaybeUndefined<Vec<ContentBlock>>,
967    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
968    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
969    /// these keys. Omitted means no metadata update; `null` is an explicit clear signal.
970    ///
971    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
972    #[serde_as(deserialize_as = "DefaultOnError<MaybeUndefined<_>>")]
973    #[schemars(extend("x-deserialize-default-on-error" = true))]
974    #[serde(
975        rename = "_meta",
976        default,
977        skip_serializing_if = "MaybeUndefined::is_undefined"
978    )]
979    pub meta: MaybeUndefined<Meta>,
980}
981
982impl AgentThought {
983    /// Builds [`AgentThought`] with the required fields set; optional fields start unset or empty.
984    #[must_use]
985    pub fn new(message_id: impl Into<MessageId>) -> Self {
986        Self {
987            message_id: message_id.into(),
988            content: MaybeUndefined::Undefined,
989            meta: MaybeUndefined::Undefined,
990        }
991    }
992
993    /// Complete replacement content for this thought message.
994    #[must_use]
995    pub fn content(mut self, content: impl IntoMaybeUndefined<Vec<ContentBlock>>) -> Self {
996        self.content = content.into_maybe_undefined();
997        self
998    }
999
1000    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1001    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1002    /// these keys.
1003    ///
1004    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1005    #[must_use]
1006    pub fn meta(mut self, meta: impl IntoMaybeUndefined<Meta>) -> Self {
1007        self.meta = meta.into_maybe_undefined();
1008        self
1009    }
1010}
1011
1012/// Unique identifier for a message within a session.
1013#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
1014#[serde(transparent)]
1015#[from(Arc<str>, String, &'static str)]
1016#[non_exhaustive]
1017pub struct MessageId(pub Arc<str>);
1018
1019impl MessageId {
1020    /// Wraps a protocol string as a typed [`MessageId`].
1021    #[must_use]
1022    pub fn new(id: impl Into<Arc<str>>) -> Self {
1023        Self(id.into())
1024    }
1025}
1026
1027/// Available commands are ready or have changed
1028#[serde_as]
1029#[skip_serializing_none]
1030#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1031#[serde(rename_all = "camelCase")]
1032#[non_exhaustive]
1033pub struct AvailableCommandsUpdate {
1034    /// Commands the agent can execute.
1035    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1036    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1037    pub available_commands: Vec<AvailableCommand>,
1038    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1039    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1040    /// these keys.
1041    ///
1042    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1043    #[serde_as(deserialize_as = "DefaultOnError")]
1044    #[schemars(extend("x-deserialize-default-on-error" = true))]
1045    #[serde(default)]
1046    #[serde(rename = "_meta")]
1047    pub meta: Option<Meta>,
1048}
1049
1050impl AvailableCommandsUpdate {
1051    /// Builds [`AvailableCommandsUpdate`] with the required fields set; optional fields start unset or empty.
1052    #[must_use]
1053    pub fn new(available_commands: Vec<AvailableCommand>) -> Self {
1054        Self {
1055            available_commands,
1056            meta: None,
1057        }
1058    }
1059
1060    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1061    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1062    /// these keys.
1063    ///
1064    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1065    #[must_use]
1066    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1067        self.meta = meta.into_option();
1068        self
1069    }
1070}
1071
1072/// Information about a command.
1073#[serde_as]
1074#[skip_serializing_none]
1075#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1076#[serde(rename_all = "camelCase")]
1077#[non_exhaustive]
1078pub struct AvailableCommand {
1079    /// Command name (e.g., `create_plan`, `research_codebase`).
1080    pub name: String,
1081    /// Human-readable description of what the command does.
1082    pub description: String,
1083    /// Input for the command if required
1084    #[serde_as(deserialize_as = "DefaultOnError")]
1085    #[schemars(extend("x-deserialize-default-on-error" = true))]
1086    #[serde(default)]
1087    pub input: Option<AvailableCommandInput>,
1088    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1089    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1090    /// these keys.
1091    ///
1092    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1093    #[serde_as(deserialize_as = "DefaultOnError")]
1094    #[schemars(extend("x-deserialize-default-on-error" = true))]
1095    #[serde(default)]
1096    #[serde(rename = "_meta")]
1097    pub meta: Option<Meta>,
1098}
1099
1100impl AvailableCommand {
1101    /// Builds [`AvailableCommand`] with the required fields set; optional fields start unset or empty.
1102    #[must_use]
1103    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
1104        Self {
1105            name: name.into(),
1106            description: description.into(),
1107            input: None,
1108            meta: None,
1109        }
1110    }
1111
1112    /// Input for the command if required
1113    #[must_use]
1114    pub fn input(mut self, input: impl IntoOption<AvailableCommandInput>) -> Self {
1115        self.input = input.into_option();
1116        self
1117    }
1118
1119    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1120    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1121    /// these keys.
1122    ///
1123    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1124    #[must_use]
1125    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1126        self.meta = meta.into_option();
1127        self
1128    }
1129}
1130
1131/// The input specification for a command.
1132#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1133#[serde(tag = "type", rename_all = "snake_case")]
1134#[schemars(extend("discriminator" = {"propertyName": "type"}))]
1135#[non_exhaustive]
1136pub enum AvailableCommandInput {
1137    /// All text that was typed after the command name is provided as input.
1138    #[serde(rename = "text")]
1139    Text(TextCommandInput),
1140    /// Custom or future command input specification.
1141    ///
1142    /// Values beginning with `_` are reserved for implementation-specific
1143    /// extensions. Unknown values that do not begin with `_` are reserved for
1144    /// future ACP variants.
1145    ///
1146    /// Clients that do not understand this input type should preserve the raw
1147    /// payload when storing, replaying, proxying, or forwarding command
1148    /// metadata, and otherwise ignore the input specification or display the
1149    /// command without structured input.
1150    #[serde(untagged)]
1151    Other(OtherAvailableCommandInput),
1152}
1153
1154/// Custom or future command input specification.
1155#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
1156#[schemars(inline)]
1157#[schemars(transform = other_available_command_input_schema)]
1158#[serde(rename_all = "camelCase")]
1159#[non_exhaustive]
1160pub struct OtherAvailableCommandInput {
1161    /// Custom or future command input type.
1162    ///
1163    /// Values beginning with `_` are reserved for implementation-specific
1164    /// extensions. Unknown values that do not begin with `_` are reserved for
1165    /// future ACP variants.
1166    #[serde(rename = "type")]
1167    pub type_: String,
1168    /// Additional fields from the unknown command input payload.
1169    #[serde(flatten)]
1170    pub fields: BTreeMap<String, serde_json::Value>,
1171}
1172
1173impl OtherAvailableCommandInput {
1174    /// Builds [`OtherAvailableCommandInput`] from an unknown discriminator and preserves the remaining extension fields.
1175    #[must_use]
1176    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1177        fields.remove("type");
1178        Self {
1179            type_: type_.into(),
1180            fields,
1181        }
1182    }
1183}
1184
1185impl<'de> Deserialize<'de> for OtherAvailableCommandInput {
1186    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1187    where
1188        D: serde::Deserializer<'de>,
1189    {
1190        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1191        let type_ = fields
1192            .remove("type")
1193            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1194        let serde_json::Value::String(type_) = type_ else {
1195            return Err(serde::de::Error::custom("`type` must be a string"));
1196        };
1197
1198        if is_known_available_command_input_type(&type_) {
1199            return Err(serde::de::Error::custom(format!(
1200                "known available command input type `{type_}` did not match its schema"
1201            )));
1202        }
1203
1204        Ok(Self { type_, fields })
1205    }
1206}
1207
1208const KNOWN_AVAILABLE_COMMAND_INPUT_TYPES: &[&str] = &["text"];
1209
1210fn is_known_available_command_input_type(type_: &str) -> bool {
1211    KNOWN_AVAILABLE_COMMAND_INPUT_TYPES.contains(&type_)
1212}
1213
1214fn other_available_command_input_schema(schema: &mut Schema) {
1215    super::schema_util::reject_known_string_discriminators(
1216        schema,
1217        "type",
1218        KNOWN_AVAILABLE_COMMAND_INPUT_TYPES,
1219    );
1220}
1221
1222/// All text that was typed after the command name is provided as input.
1223#[serde_as]
1224#[skip_serializing_none]
1225#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1226#[serde(rename_all = "camelCase")]
1227#[non_exhaustive]
1228pub struct TextCommandInput {
1229    /// A hint to display when the input hasn't been provided yet
1230    pub hint: String,
1231    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1232    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1233    /// these keys.
1234    ///
1235    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1236    #[serde_as(deserialize_as = "DefaultOnError")]
1237    #[schemars(extend("x-deserialize-default-on-error" = true))]
1238    #[serde(default)]
1239    #[serde(rename = "_meta")]
1240    pub meta: Option<Meta>,
1241}
1242
1243impl TextCommandInput {
1244    /// Builds [`TextCommandInput`] with the required fields set; optional fields start unset or empty.
1245    #[must_use]
1246    pub fn new(hint: impl Into<String>) -> Self {
1247        Self {
1248            hint: hint.into(),
1249            meta: None,
1250        }
1251    }
1252
1253    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1254    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1255    /// these keys.
1256    ///
1257    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1258    #[must_use]
1259    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1260        self.meta = meta.into_option();
1261        self
1262    }
1263}
1264
1265// Permission
1266
1267/// Request for user permission to proceed with an operation.
1268///
1269/// Sent when the agent needs authorization before performing a sensitive operation.
1270///
1271/// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
1272#[serde_as]
1273#[skip_serializing_none]
1274#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
1275#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
1276#[serde(rename_all = "camelCase")]
1277#[non_exhaustive]
1278pub struct RequestPermissionRequest {
1279    /// The session ID for this request.
1280    pub session_id: SessionId,
1281    /// Human-readable title for the permission prompt.
1282    ///
1283    /// This title is specific to the permission prompt and does not update any
1284    /// subject's displayed title.
1285    pub title: String,
1286    /// Optional human-readable explanation of why permission is needed.
1287    ///
1288    /// This text is specific to the permission prompt and does not update any
1289    /// subject's displayed content. Omitted or `null` both mean no separate
1290    /// permission description was provided.
1291    #[serde_as(deserialize_as = "DefaultOnError")]
1292    #[schemars(extend("x-deserialize-default-on-error" = true))]
1293    #[serde(default)]
1294    pub description: Option<String>,
1295    /// Optional structured context about the operation requiring permission.
1296    ///
1297    /// Omitted or `null` both mean no structured subject was provided.
1298    #[serde(default)]
1299    pub subject: Option<RequestPermissionSubject>,
1300    /// Available permission options for the user to choose from.
1301    /// Must contain at least one option.
1302    #[schemars(length(min = 1))]
1303    pub options: Vec<PermissionOption>,
1304    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1305    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1306    /// these keys.
1307    ///
1308    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1309    #[serde_as(deserialize_as = "DefaultOnError")]
1310    #[schemars(extend("x-deserialize-default-on-error" = true))]
1311    #[serde(default)]
1312    #[serde(rename = "_meta")]
1313    pub meta: Option<Meta>,
1314}
1315
1316impl RequestPermissionRequest {
1317    /// Builds [`RequestPermissionRequest`] with the required request fields set; optional fields start unset or empty.
1318    #[must_use]
1319    pub fn new(
1320        session_id: impl Into<SessionId>,
1321        title: impl Into<String>,
1322        options: Vec<PermissionOption>,
1323    ) -> Self {
1324        Self {
1325            session_id: session_id.into(),
1326            title: title.into(),
1327            description: None,
1328            subject: None,
1329            options,
1330            meta: None,
1331        }
1332    }
1333
1334    /// Sets or clears the optional `description` field.
1335    #[must_use]
1336    pub fn description(mut self, description: impl IntoOption<String>) -> Self {
1337        self.description = description.into_option();
1338        self
1339    }
1340
1341    /// Sets or clears the optional `subject` field.
1342    #[must_use]
1343    pub fn subject(mut self, subject: impl IntoOption<RequestPermissionSubject>) -> Self {
1344        self.subject = subject.into_option();
1345        self
1346    }
1347
1348    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1349    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1350    /// these keys.
1351    ///
1352    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1353    #[must_use]
1354    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1355        self.meta = meta.into_option();
1356        self
1357    }
1358}
1359
1360/// The operation requiring permission.
1361#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
1362#[serde(tag = "type", rename_all = "snake_case")]
1363#[schemars(extend("discriminator" = {"propertyName": "type"}))]
1364#[non_exhaustive]
1365pub enum RequestPermissionSubject {
1366    /// Permission is requested before executing a tool call.
1367    ToolCall(Box<ToolCallPermissionSubject>),
1368    /// Custom or future permission subject.
1369    ///
1370    /// Values beginning with `_` are reserved for implementation-specific
1371    /// extensions. Unknown values that do not begin with `_` are reserved for
1372    /// future ACP variants.
1373    ///
1374    /// Clients that do not understand this subject type should preserve the raw
1375    /// payload when storing, replaying, proxying, or forwarding permission
1376    /// requests, and otherwise display a generic permission prompt or decline it
1377    /// according to policy.
1378    #[serde(untagged)]
1379    Other(OtherRequestPermissionSubject),
1380}
1381
1382impl From<ToolCallPermissionSubject> for RequestPermissionSubject {
1383    fn from(subject: ToolCallPermissionSubject) -> Self {
1384        Self::ToolCall(Box::new(subject))
1385    }
1386}
1387
1388impl From<ToolCallUpdate> for RequestPermissionSubject {
1389    fn from(tool_call: ToolCallUpdate) -> Self {
1390        ToolCallPermissionSubject::new(tool_call).into()
1391    }
1392}
1393
1394/// Permission request details for a tool call.
1395#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq)]
1396#[serde(rename_all = "camelCase")]
1397#[non_exhaustive]
1398pub struct ToolCallPermissionSubject {
1399    /// Details about the tool call requiring permission.
1400    pub tool_call: ToolCallUpdate,
1401}
1402
1403impl ToolCallPermissionSubject {
1404    /// Builds [`ToolCallPermissionSubject`] with the required fields set.
1405    #[must_use]
1406    pub fn new(tool_call: ToolCallUpdate) -> Self {
1407        Self { tool_call }
1408    }
1409}
1410
1411/// Custom or future permission subject payload.
1412#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq)]
1413#[schemars(inline)]
1414#[schemars(transform = other_request_permission_subject_schema)]
1415#[serde(rename_all = "camelCase")]
1416#[non_exhaustive]
1417pub struct OtherRequestPermissionSubject {
1418    /// Custom or future permission subject type.
1419    ///
1420    /// Values beginning with `_` are reserved for implementation-specific
1421    /// extensions. Unknown values that do not begin with `_` are reserved for
1422    /// future ACP variants.
1423    #[serde(rename = "type")]
1424    pub type_: String,
1425    /// Additional fields from the unknown permission subject payload.
1426    #[serde(flatten)]
1427    pub fields: BTreeMap<String, serde_json::Value>,
1428}
1429
1430impl OtherRequestPermissionSubject {
1431    /// Builds [`OtherRequestPermissionSubject`] from an unknown discriminator and preserves the remaining extension fields.
1432    #[must_use]
1433    pub fn new(type_: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
1434        fields.remove("type");
1435        Self {
1436            type_: type_.into(),
1437            fields,
1438        }
1439    }
1440}
1441
1442impl<'de> Deserialize<'de> for OtherRequestPermissionSubject {
1443    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1444    where
1445        D: serde::Deserializer<'de>,
1446    {
1447        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1448        let type_ = fields
1449            .remove("type")
1450            .ok_or_else(|| serde::de::Error::missing_field("type"))?;
1451        let serde_json::Value::String(type_) = type_ else {
1452            return Err(serde::de::Error::custom("`type` must be a string"));
1453        };
1454
1455        if is_known_request_permission_subject_type(&type_) {
1456            return Err(serde::de::Error::custom(format!(
1457                "known request permission subject `{type_}` did not match its schema"
1458            )));
1459        }
1460
1461        Ok(Self { type_, fields })
1462    }
1463}
1464
1465fn is_known_request_permission_subject_type(type_: &str) -> bool {
1466    matches!(type_, "tool_call")
1467}
1468
1469fn other_request_permission_subject_schema(schema: &mut Schema) {
1470    super::schema_util::reject_known_string_discriminators(schema, "type", &["tool_call"]);
1471}
1472
1473/// An option presented to the user when requesting permission.
1474#[serde_as]
1475#[skip_serializing_none]
1476#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1477#[serde(rename_all = "camelCase")]
1478#[non_exhaustive]
1479pub struct PermissionOption {
1480    /// Unique identifier for this permission option.
1481    pub option_id: PermissionOptionId,
1482    /// Human-readable label to display to the user.
1483    pub name: String,
1484    /// Hint about the nature of this permission option.
1485    pub kind: PermissionOptionKind,
1486    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1487    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1488    /// these keys.
1489    ///
1490    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1491    #[serde_as(deserialize_as = "DefaultOnError")]
1492    #[schemars(extend("x-deserialize-default-on-error" = true))]
1493    #[serde(default)]
1494    #[serde(rename = "_meta")]
1495    pub meta: Option<Meta>,
1496}
1497
1498impl PermissionOption {
1499    /// Builds [`PermissionOption`] with the required fields set; optional fields start unset or empty.
1500    #[must_use]
1501    pub fn new(
1502        option_id: impl Into<PermissionOptionId>,
1503        name: impl Into<String>,
1504        kind: PermissionOptionKind,
1505    ) -> Self {
1506        Self {
1507            option_id: option_id.into(),
1508            name: name.into(),
1509            kind,
1510            meta: None,
1511        }
1512    }
1513
1514    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1515    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1516    /// these keys.
1517    ///
1518    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1519    #[must_use]
1520    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1521        self.meta = meta.into_option();
1522        self
1523    }
1524}
1525
1526/// Unique identifier for a permission option.
1527#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
1528#[serde(transparent)]
1529#[from(Arc<str>, String, &'static str)]
1530#[non_exhaustive]
1531pub struct PermissionOptionId(pub Arc<str>);
1532
1533impl PermissionOptionId {
1534    /// Wraps a protocol string as a typed [`PermissionOptionId`].
1535    #[must_use]
1536    pub fn new(id: impl Into<Arc<str>>) -> Self {
1537        Self(id.into())
1538    }
1539}
1540
1541/// The type of permission option being presented to the user.
1542///
1543/// Helps clients choose appropriate icons and UI treatment.
1544#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1545#[serde(rename_all = "snake_case")]
1546#[non_exhaustive]
1547pub enum PermissionOptionKind {
1548    /// Allow this operation only this time.
1549    AllowOnce,
1550    /// Allow this operation and remember the choice.
1551    AllowAlways,
1552    /// Reject this operation only this time.
1553    RejectOnce,
1554    /// Reject this operation and remember the choice.
1555    RejectAlways,
1556    /// Custom or future permission option kind.
1557    ///
1558    /// Values beginning with `_` are reserved for implementation-specific
1559    /// extensions. Unknown values that do not begin with `_` are reserved for
1560    /// future ACP variants.
1561    #[serde(untagged)]
1562    Other(String),
1563}
1564
1565/// Response to a permission request.
1566#[serde_as]
1567#[skip_serializing_none]
1568#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1569#[schemars(extend("x-side" = "client", "x-method" = SESSION_REQUEST_PERMISSION_METHOD_NAME))]
1570#[serde(rename_all = "camelCase")]
1571#[non_exhaustive]
1572pub struct RequestPermissionResponse {
1573    /// The user's decision on the permission request.
1574    pub outcome: RequestPermissionOutcome,
1575    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1576    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1577    /// these keys.
1578    ///
1579    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1580    #[serde_as(deserialize_as = "DefaultOnError")]
1581    #[schemars(extend("x-deserialize-default-on-error" = true))]
1582    #[serde(default)]
1583    #[serde(rename = "_meta")]
1584    pub meta: Option<Meta>,
1585}
1586
1587impl RequestPermissionResponse {
1588    /// Builds [`RequestPermissionResponse`] with the required response fields set; optional fields start unset or empty.
1589    #[must_use]
1590    pub fn new(outcome: RequestPermissionOutcome) -> Self {
1591        Self {
1592            outcome,
1593            meta: None,
1594        }
1595    }
1596
1597    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1598    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1599    /// these keys.
1600    ///
1601    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1602    #[must_use]
1603    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1604        self.meta = meta.into_option();
1605        self
1606    }
1607}
1608
1609/// The outcome of a permission request.
1610#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1611#[serde(tag = "outcome", rename_all = "snake_case")]
1612#[schemars(extend("discriminator" = {"propertyName": "outcome"}))]
1613#[non_exhaustive]
1614pub enum RequestPermissionOutcome {
1615    /// Active session work was cancelled before the user responded.
1616    ///
1617    /// When a client sends a `session/cancel` notification to cancel active
1618    /// session work, it MUST respond to all pending `session/request_permission`
1619    /// requests with this `Cancelled` outcome.
1620    ///
1621    /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-lifecycle#cancellation)
1622    Cancelled,
1623    /// The user selected one of the provided options.
1624    #[serde(rename_all = "camelCase")]
1625    Selected(SelectedPermissionOutcome),
1626    /// Custom or future permission outcome.
1627    ///
1628    /// Values beginning with `_` are reserved for implementation-specific
1629    /// extensions. Unknown values that do not begin with `_` are reserved for
1630    /// future ACP variants.
1631    ///
1632    /// Agents that do not understand this outcome MUST NOT treat it as approval.
1633    /// They should preserve the raw payload when storing, replaying, proxying, or
1634    /// forwarding permission responses, and otherwise fail or decline the
1635    /// permission request according to policy.
1636    #[serde(untagged)]
1637    Other(OtherRequestPermissionOutcome),
1638}
1639
1640/// Custom or future permission outcome payload.
1641///
1642/// This preserves the unknown `outcome` discriminator and the rest of the
1643/// outcome object for agents that store, replay, proxy, or forward permission
1644/// responses.
1645#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
1646#[schemars(inline)]
1647#[schemars(transform = other_request_permission_outcome_schema)]
1648#[serde(rename_all = "camelCase")]
1649#[non_exhaustive]
1650pub struct OtherRequestPermissionOutcome {
1651    /// Custom or future permission outcome.
1652    ///
1653    /// Values beginning with `_` are reserved for implementation-specific
1654    /// extensions. Unknown values that do not begin with `_` are reserved for
1655    /// future ACP variants.
1656    pub outcome: String,
1657    /// Additional fields from the unknown permission outcome payload.
1658    #[serde(flatten)]
1659    pub fields: BTreeMap<String, serde_json::Value>,
1660}
1661
1662impl OtherRequestPermissionOutcome {
1663    /// Builds [`OtherRequestPermissionOutcome`] from an unknown discriminator and preserves the remaining extension fields.
1664    #[must_use]
1665    pub fn new(
1666        outcome: impl Into<String>,
1667        mut fields: BTreeMap<String, serde_json::Value>,
1668    ) -> Self {
1669        fields.remove("outcome");
1670        Self {
1671            outcome: outcome.into(),
1672            fields,
1673        }
1674    }
1675}
1676
1677impl<'de> Deserialize<'de> for OtherRequestPermissionOutcome {
1678    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1679    where
1680        D: serde::Deserializer<'de>,
1681    {
1682        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
1683        let outcome = fields
1684            .remove("outcome")
1685            .ok_or_else(|| serde::de::Error::missing_field("outcome"))?;
1686        let serde_json::Value::String(outcome) = outcome else {
1687            return Err(serde::de::Error::custom("`outcome` must be a string"));
1688        };
1689
1690        if is_known_request_permission_outcome(&outcome) {
1691            return Err(serde::de::Error::custom(format!(
1692                "known request permission outcome `{outcome}` did not match its schema"
1693            )));
1694        }
1695
1696        Ok(Self { outcome, fields })
1697    }
1698}
1699
1700fn is_known_request_permission_outcome(outcome: &str) -> bool {
1701    matches!(outcome, "cancelled" | "selected")
1702}
1703
1704fn other_request_permission_outcome_schema(schema: &mut Schema) {
1705    super::schema_util::reject_known_string_discriminators(
1706        schema,
1707        "outcome",
1708        &["cancelled", "selected"],
1709    );
1710}
1711
1712/// The user selected one of the provided options.
1713#[serde_as]
1714#[skip_serializing_none]
1715#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1716#[serde(rename_all = "camelCase")]
1717#[non_exhaustive]
1718pub struct SelectedPermissionOutcome {
1719    /// The ID of the option the user selected.
1720    pub option_id: PermissionOptionId,
1721    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1722    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1723    /// these keys.
1724    ///
1725    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1726    #[serde_as(deserialize_as = "DefaultOnError")]
1727    #[schemars(extend("x-deserialize-default-on-error" = true))]
1728    #[serde(default)]
1729    #[serde(rename = "_meta")]
1730    pub meta: Option<Meta>,
1731}
1732
1733impl SelectedPermissionOutcome {
1734    /// Builds [`SelectedPermissionOutcome`] with the required fields set; optional fields start unset or empty.
1735    #[must_use]
1736    pub fn new(option_id: impl Into<PermissionOptionId>) -> Self {
1737        Self {
1738            option_id: option_id.into(),
1739            meta: None,
1740        }
1741    }
1742
1743    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1744    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1745    /// these keys.
1746    ///
1747    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1748    #[must_use]
1749    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1750        self.meta = meta.into_option();
1751        self
1752    }
1753}
1754
1755// Capabilities
1756
1757/// Capabilities supported by the client.
1758///
1759/// Advertised during initialization to inform the agent about
1760/// available features and methods.
1761///
1762/// See protocol docs: [Client Capabilities](https://agentclientprotocol.com/protocol/initialization#client-capabilities)
1763#[serde_as]
1764#[skip_serializing_none]
1765#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1766#[serde(rename_all = "camelCase")]
1767#[non_exhaustive]
1768pub struct ClientCapabilities {
1769    /// **UNSTABLE**
1770    ///
1771    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1772    ///
1773    /// Authentication capabilities supported by the client.
1774    /// Determines which authentication method types the agent may include
1775    /// in its `InitializeResponse`.
1776    ///
1777    /// Optional. Omitted or `null` both mean the client does not advertise any
1778    /// authentication-method extensions.
1779    #[cfg(feature = "unstable_auth_methods")]
1780    #[serde_as(deserialize_as = "DefaultOnError")]
1781    #[schemars(extend("x-deserialize-default-on-error" = true))]
1782    #[serde(default)]
1783    pub auth: Option<AuthCapabilities>,
1784    /// **UNSTABLE**
1785    ///
1786    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1787    ///
1788    /// Elicitation capabilities supported by the client.
1789    /// Determines which elicitation modes the agent may use.
1790    ///
1791    /// Optional. Omitted or `null` both mean the client does not advertise
1792    /// elicitation support.
1793    #[cfg(feature = "unstable_elicitation")]
1794    #[serde_as(deserialize_as = "DefaultOnError")]
1795    #[schemars(extend("x-deserialize-default-on-error" = true))]
1796    #[serde(default)]
1797    pub elicitation: Option<ElicitationCapabilities>,
1798    /// **UNSTABLE**
1799    ///
1800    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1801    ///
1802    /// NES (Next Edit Suggestions) capabilities supported by the client.
1803    ///
1804    /// Optional. Omitted or `null` both mean the client does not advertise any
1805    /// NES suggestion-kind extensions.
1806    #[cfg(feature = "unstable_nes")]
1807    #[serde_as(deserialize_as = "DefaultOnError")]
1808    #[schemars(extend("x-deserialize-default-on-error" = true))]
1809    #[serde(default)]
1810    pub nes: Option<ClientNesCapabilities>,
1811    /// **UNSTABLE**
1812    ///
1813    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1814    ///
1815    /// The position encodings supported by the client, in order of preference.
1816    #[cfg(feature = "unstable_nes")]
1817    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
1818    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1819    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1820    pub position_encodings: Vec<PositionEncodingKind>,
1821
1822    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1823    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1824    /// these keys.
1825    ///
1826    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1827    #[serde_as(deserialize_as = "DefaultOnError")]
1828    #[schemars(extend("x-deserialize-default-on-error" = true))]
1829    #[serde(default)]
1830    #[serde(rename = "_meta")]
1831    pub meta: Option<Meta>,
1832}
1833
1834impl ClientCapabilities {
1835    /// Builds an empty [`ClientCapabilities`]; use builder methods to advertise supported sub-capabilities.
1836    #[must_use]
1837    pub fn new() -> Self {
1838        Self::default()
1839    }
1840
1841    /// **UNSTABLE**
1842    ///
1843    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1844    ///
1845    /// Authentication capabilities supported by the client.
1846    /// Determines which authentication method types the agent may include
1847    /// in its `InitializeResponse`.
1848    #[cfg(feature = "unstable_auth_methods")]
1849    #[must_use]
1850    pub fn auth(mut self, auth: impl IntoOption<AuthCapabilities>) -> Self {
1851        self.auth = auth.into_option();
1852        self
1853    }
1854
1855    /// **UNSTABLE**
1856    ///
1857    /// This capability is not part of the spec yet, and may be removed or changed at any point.
1858    ///
1859    /// Elicitation capabilities supported by the client.
1860    /// Determines which elicitation modes the agent may use.
1861    #[cfg(feature = "unstable_elicitation")]
1862    #[must_use]
1863    pub fn elicitation(mut self, elicitation: impl IntoOption<ElicitationCapabilities>) -> Self {
1864        self.elicitation = elicitation.into_option();
1865        self
1866    }
1867
1868    /// **UNSTABLE**
1869    ///
1870    /// NES (Next Edit Suggestions) capabilities supported by the client.
1871    #[cfg(feature = "unstable_nes")]
1872    #[must_use]
1873    pub fn nes(mut self, nes: impl IntoOption<ClientNesCapabilities>) -> Self {
1874        self.nes = nes.into_option();
1875        self
1876    }
1877
1878    /// **UNSTABLE**
1879    ///
1880    /// The position encodings supported by the client, in order of preference.
1881    #[cfg(feature = "unstable_nes")]
1882    #[must_use]
1883    pub fn position_encodings(mut self, position_encodings: Vec<PositionEncodingKind>) -> Self {
1884        self.position_encodings = position_encodings;
1885        self
1886    }
1887
1888    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1889    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1890    /// these keys.
1891    ///
1892    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1893    #[must_use]
1894    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1895        self.meta = meta.into_option();
1896        self
1897    }
1898}
1899
1900/// **UNSTABLE**
1901///
1902/// This capability is not part of the spec yet, and may be removed or changed at any point.
1903///
1904/// Authentication capabilities supported by the client.
1905///
1906/// Advertised during initialization to inform the agent which authentication
1907/// method types the client can handle. This governs opt-in types that require
1908/// additional client-side support.
1909#[cfg(feature = "unstable_auth_methods")]
1910#[serde_as]
1911#[skip_serializing_none]
1912#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1913#[serde(rename_all = "camelCase")]
1914#[non_exhaustive]
1915pub struct AuthCapabilities {
1916    /// Whether the client supports `terminal` authentication methods.
1917    ///
1918    /// Optional. Omitted or `null` both mean the client does not advertise support.
1919    /// Supplying `{}` means the agent may include `terminal` entries in its authentication methods.
1920    #[serde_as(deserialize_as = "DefaultOnError")]
1921    #[schemars(extend("x-deserialize-default-on-error" = true))]
1922    #[serde(default)]
1923    pub terminal: Option<TerminalAuthCapabilities>,
1924    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1925    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1926    /// these keys.
1927    ///
1928    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1929    #[serde_as(deserialize_as = "DefaultOnError")]
1930    #[schemars(extend("x-deserialize-default-on-error" = true))]
1931    #[serde(default)]
1932    #[serde(rename = "_meta")]
1933    pub meta: Option<Meta>,
1934}
1935
1936#[cfg(feature = "unstable_auth_methods")]
1937impl AuthCapabilities {
1938    /// Builds an empty [`AuthCapabilities`]; use builder methods to advertise supported sub-capabilities.
1939    #[must_use]
1940    pub fn new() -> Self {
1941        Self::default()
1942    }
1943
1944    /// Whether the client supports `terminal` authentication methods.
1945    ///
1946    /// Omitted or `null` both mean the client does not advertise support.
1947    /// Supplying `{}` means the agent may include `AuthMethod::Terminal` entries in its authentication methods.
1948    #[must_use]
1949    pub fn terminal(mut self, terminal: impl IntoOption<TerminalAuthCapabilities>) -> Self {
1950        self.terminal = terminal.into_option();
1951        self
1952    }
1953
1954    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1955    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1956    /// these keys.
1957    ///
1958    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1959    #[must_use]
1960    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1961        self.meta = meta.into_option();
1962        self
1963    }
1964}
1965
1966/// **UNSTABLE**
1967///
1968/// This capability is not part of the spec yet, and may be removed or changed at any point.
1969///
1970/// Capabilities for terminal authentication methods.
1971///
1972/// Supplying `{}` means the client supports terminal authentication methods.
1973#[cfg(feature = "unstable_auth_methods")]
1974#[serde_as]
1975#[skip_serializing_none]
1976#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1977#[non_exhaustive]
1978pub struct TerminalAuthCapabilities {
1979    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1980    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1981    /// these keys.
1982    ///
1983    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1984    #[serde_as(deserialize_as = "DefaultOnError")]
1985    #[schemars(extend("x-deserialize-default-on-error" = true))]
1986    #[serde(default)]
1987    #[serde(rename = "_meta")]
1988    pub meta: Option<Meta>,
1989}
1990
1991#[cfg(feature = "unstable_auth_methods")]
1992impl TerminalAuthCapabilities {
1993    /// Builds an empty [`TerminalAuthCapabilities`]; use builder methods to advertise supported sub-capabilities.
1994    #[must_use]
1995    pub fn new() -> Self {
1996        Self::default()
1997    }
1998
1999    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2000    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2001    /// these keys.
2002    ///
2003    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2004    #[must_use]
2005    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2006        self.meta = meta.into_option();
2007        self
2008    }
2009}
2010
2011// Method schema
2012
2013/// Names of all methods that clients handle.
2014///
2015/// Provides a centralized definition of method names used in the protocol.
2016#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2017#[non_exhaustive]
2018pub struct ClientMethodNames {
2019    /// Method for requesting permission from the user.
2020    pub session_request_permission: &'static str,
2021    /// Notification for session updates.
2022    pub session_update: &'static str,
2023    /// Method for opening an MCP-over-ACP connection.
2024    #[cfg(feature = "unstable_mcp_over_acp")]
2025    pub mcp_connect: &'static str,
2026    /// Method for exchanging MCP-over-ACP messages.
2027    #[cfg(feature = "unstable_mcp_over_acp")]
2028    pub mcp_message: &'static str,
2029    /// Method for closing an MCP-over-ACP connection.
2030    #[cfg(feature = "unstable_mcp_over_acp")]
2031    pub mcp_disconnect: &'static str,
2032    /// Method for elicitation.
2033    #[cfg(feature = "unstable_elicitation")]
2034    pub elicitation_create: &'static str,
2035    /// Notification for elicitation completion.
2036    #[cfg(feature = "unstable_elicitation")]
2037    pub elicitation_complete: &'static str,
2038}
2039
2040/// Constant containing all client method names.
2041pub const CLIENT_METHOD_NAMES: ClientMethodNames = ClientMethodNames {
2042    session_update: SESSION_UPDATE_NOTIFICATION,
2043    session_request_permission: SESSION_REQUEST_PERMISSION_METHOD_NAME,
2044    #[cfg(feature = "unstable_mcp_over_acp")]
2045    mcp_connect: MCP_CONNECT_METHOD_NAME,
2046    #[cfg(feature = "unstable_mcp_over_acp")]
2047    mcp_message: MCP_MESSAGE_METHOD_NAME,
2048    #[cfg(feature = "unstable_mcp_over_acp")]
2049    mcp_disconnect: MCP_DISCONNECT_METHOD_NAME,
2050    #[cfg(feature = "unstable_elicitation")]
2051    elicitation_create: ELICITATION_CREATE_METHOD_NAME,
2052    #[cfg(feature = "unstable_elicitation")]
2053    elicitation_complete: ELICITATION_COMPLETE_NOTIFICATION,
2054};
2055
2056/// Notification name for session updates.
2057pub(crate) const SESSION_UPDATE_NOTIFICATION: &str = "session/update";
2058/// Method name for requesting user permission.
2059pub(crate) const SESSION_REQUEST_PERMISSION_METHOD_NAME: &str = "session/request_permission";
2060/// Method name for elicitation.
2061#[cfg(feature = "unstable_elicitation")]
2062pub(crate) const ELICITATION_CREATE_METHOD_NAME: &str = "elicitation/create";
2063/// Notification name for elicitation completion.
2064#[cfg(feature = "unstable_elicitation")]
2065pub(crate) const ELICITATION_COMPLETE_NOTIFICATION: &str = "elicitation/complete";
2066
2067/// All possible requests that an agent can send to a client.
2068///
2069/// This enum is used internally for routing RPC requests. You typically won't need
2070/// to use this directly.
2071///
2072/// This enum encompasses all method calls from agent to client.
2073#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
2074#[serde(untagged)]
2075#[schemars(inline)]
2076#[non_exhaustive]
2077pub enum AgentRequest {
2078    /// Requests permission from the user for an operation.
2079    ///
2080    /// Called by the agent when it needs user authorization before executing
2081    /// a potentially sensitive operation. The client should present the options
2082    /// to the user and return their decision.
2083    ///
2084    /// If the client cancels active session work via `session/cancel`, it MUST
2085    /// respond to this request with `RequestPermissionOutcome::Cancelled`.
2086    ///
2087    /// See protocol docs: [Requesting Permission](https://agentclientprotocol.com/protocol/tool-calls#requesting-permission)
2088    RequestPermissionRequest(Box<RequestPermissionRequest>),
2089    /// **UNSTABLE**
2090    ///
2091    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2092    ///
2093    /// Requests structured user input via a form or URL.
2094    #[cfg(feature = "unstable_elicitation")]
2095    CreateElicitationRequest(Box<CreateElicitationRequest>),
2096    /// **UNSTABLE**
2097    ///
2098    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2099    ///
2100    /// Opens an MCP-over-ACP connection.
2101    #[cfg(feature = "unstable_mcp_over_acp")]
2102    ConnectMcpRequest(Box<ConnectMcpRequest>),
2103    /// **UNSTABLE**
2104    ///
2105    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2106    ///
2107    /// Exchanges an MCP-over-ACP message.
2108    #[cfg(feature = "unstable_mcp_over_acp")]
2109    MessageMcpRequest(Box<MessageMcpRequest>),
2110    /// **UNSTABLE**
2111    ///
2112    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2113    ///
2114    /// Closes an MCP-over-ACP connection.
2115    #[cfg(feature = "unstable_mcp_over_acp")]
2116    DisconnectMcpRequest(Box<DisconnectMcpRequest>),
2117    /// Handles extension method requests from the agent.
2118    ///
2119    /// Allows the Agent to send an arbitrary request that is not part of the ACP spec.
2120    /// Extension methods provide a way to add custom functionality while maintaining
2121    /// protocol compatibility.
2122    ///
2123    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2124    ExtMethodRequest(Box<ExtRequest>),
2125}
2126
2127impl AgentRequest {
2128    /// Returns the corresponding method name of the request.
2129    #[must_use]
2130    pub fn method(&self) -> &str {
2131        match self {
2132            Self::RequestPermissionRequest(_) => CLIENT_METHOD_NAMES.session_request_permission,
2133            #[cfg(feature = "unstable_elicitation")]
2134            Self::CreateElicitationRequest(_) => CLIENT_METHOD_NAMES.elicitation_create,
2135            #[cfg(feature = "unstable_mcp_over_acp")]
2136            Self::ConnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_connect,
2137            #[cfg(feature = "unstable_mcp_over_acp")]
2138            Self::MessageMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_message,
2139            #[cfg(feature = "unstable_mcp_over_acp")]
2140            Self::DisconnectMcpRequest(_) => CLIENT_METHOD_NAMES.mcp_disconnect,
2141            Self::ExtMethodRequest(ext_request) => &ext_request.method,
2142        }
2143    }
2144}
2145
2146/// All possible responses that a client can send to an agent.
2147///
2148/// This enum is used internally for routing RPC responses. You typically won't need
2149/// to use this directly - the responses are handled automatically by the connection.
2150///
2151/// These are responses to the corresponding `AgentRequest` variants.
2152#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
2153#[serde(untagged)]
2154#[schemars(inline)]
2155#[non_exhaustive]
2156pub enum ClientResponse {
2157    /// Successful result returned for a `session/request_permission` request.
2158    RequestPermissionResponse(Box<RequestPermissionResponse>),
2159    /// Successful result returned for a `elicitation/create` request.
2160    #[cfg(feature = "unstable_elicitation")]
2161    CreateElicitationResponse(Box<CreateElicitationResponse>),
2162    /// Successful result returned for a `mcp/connect` request.
2163    #[cfg(feature = "unstable_mcp_over_acp")]
2164    ConnectMcpResponse(Box<ConnectMcpResponse>),
2165    /// Successful result returned for a `mcp/disconnect` request.
2166    #[cfg(feature = "unstable_mcp_over_acp")]
2167    DisconnectMcpResponse(#[serde(default)] Box<DisconnectMcpResponse>),
2168    /// Successful result returned by an MCP-over-ACP `mcp/message` request.
2169    #[cfg(feature = "unstable_mcp_over_acp")]
2170    MessageMcpResponse(Box<MessageMcpResponse>),
2171    /// Successful result returned by an extension method outside the core ACP method set.
2172    ExtMethodResponse(Box<ExtResponse>),
2173}
2174
2175/// All possible notifications that an agent can send to a client.
2176///
2177/// This enum is used internally for routing RPC notifications. You typically won't need
2178/// to use this directly.
2179///
2180/// Notifications do not expect a response.
2181#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
2182#[serde(untagged)]
2183#[schemars(inline)]
2184#[non_exhaustive]
2185pub enum AgentNotification {
2186    /// Handles session update notifications from the agent.
2187    ///
2188    /// This is a notification endpoint (no response expected) that receives
2189    /// real-time updates about session progress, including message updates,
2190    /// message chunks, tool calls, and execution plans.
2191    ///
2192    /// Note: Clients SHOULD continue accepting tool call updates even after
2193    /// sending a `session/cancel` notification, as the agent may send final
2194    /// updates before reporting an idle `state_update` with the cancelled
2195    /// stop reason.
2196    ///
2197    /// See protocol docs: [Agent Reports Output](https://agentclientprotocol.com/protocol/prompt-lifecycle#3-agent-reports-output)
2198    UpdateSessionNotification(Box<UpdateSessionNotification>),
2199    /// **UNSTABLE**
2200    ///
2201    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2202    ///
2203    /// Notification that a URL-based elicitation has completed.
2204    #[cfg(feature = "unstable_elicitation")]
2205    CompleteElicitationNotification(Box<CompleteElicitationNotification>),
2206    /// **UNSTABLE**
2207    ///
2208    /// This capability is not part of the spec yet, and may be removed or changed at any point.
2209    ///
2210    /// Receives an MCP-over-ACP notification.
2211    #[cfg(feature = "unstable_mcp_over_acp")]
2212    MessageMcpNotification(Box<MessageMcpNotification>),
2213    /// Handles extension notifications from the agent.
2214    ///
2215    /// Allows the Agent to send an arbitrary notification that is not part of the ACP spec.
2216    /// Extension notifications provide a way to send one-way messages for custom functionality
2217    /// while maintaining protocol compatibility.
2218    ///
2219    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2220    ExtNotification(Box<ExtNotification>),
2221}
2222
2223impl AgentNotification {
2224    /// Returns the corresponding method name of the notification.
2225    #[must_use]
2226    pub fn method(&self) -> &str {
2227        match self {
2228            Self::UpdateSessionNotification(_) => CLIENT_METHOD_NAMES.session_update,
2229            #[cfg(feature = "unstable_elicitation")]
2230            Self::CompleteElicitationNotification(_) => CLIENT_METHOD_NAMES.elicitation_complete,
2231            #[cfg(feature = "unstable_mcp_over_acp")]
2232            Self::MessageMcpNotification(_) => CLIENT_METHOD_NAMES.mcp_message,
2233            Self::ExtNotification(ext_notification) => &ext_notification.method,
2234        }
2235    }
2236}
2237
2238#[cfg(test)]
2239mod tests {
2240    use super::*;
2241
2242    #[cfg(feature = "unstable_auth_methods")]
2243    #[test]
2244    fn test_client_capabilities_auth_defaults_on_malformed_value() {
2245        use serde_json::json;
2246
2247        let capabilities: ClientCapabilities = serde_json::from_value(json!({
2248            "auth": false
2249        }))
2250        .unwrap();
2251
2252        assert_eq!(capabilities.auth, None);
2253    }
2254
2255    #[test]
2256    fn test_serialization_behavior() {
2257        use serde_json::json;
2258
2259        assert_eq!(
2260            serde_json::from_value::<SessionInfoUpdate>(json!({})).unwrap(),
2261            SessionInfoUpdate {
2262                title: MaybeUndefined::Undefined,
2263                updated_at: MaybeUndefined::Undefined,
2264                meta: MaybeUndefined::Undefined
2265            }
2266        );
2267        assert_eq!(
2268            serde_json::from_value::<SessionInfoUpdate>(json!({"title": null, "updatedAt": null}))
2269                .unwrap(),
2270            SessionInfoUpdate {
2271                title: MaybeUndefined::Null,
2272                updated_at: MaybeUndefined::Null,
2273                meta: MaybeUndefined::Undefined
2274            }
2275        );
2276        assert_eq!(
2277            serde_json::from_value::<SessionInfoUpdate>(
2278                json!({"title": "title", "updatedAt": "timestamp"})
2279            )
2280            .unwrap(),
2281            SessionInfoUpdate {
2282                title: MaybeUndefined::Value("title".to_string()),
2283                updated_at: MaybeUndefined::Value("timestamp".to_string()),
2284                meta: MaybeUndefined::Undefined
2285            }
2286        );
2287
2288        let clear_meta =
2289            serde_json::from_value::<SessionInfoUpdate>(json!({"_meta": null})).unwrap();
2290        assert_eq!(clear_meta.meta, MaybeUndefined::Null);
2291
2292        let mut meta = Meta::new();
2293        meta.insert("source".to_string(), json!("session-info"));
2294
2295        assert_eq!(
2296            serde_json::from_value::<SessionInfoUpdate>(json!({"_meta": {
2297                "source": "session-info"
2298            }}))
2299            .unwrap()
2300            .meta,
2301            MaybeUndefined::Value(meta.clone())
2302        );
2303
2304        assert_eq!(
2305            serde_json::to_value(SessionInfoUpdate::new()).unwrap(),
2306            json!({})
2307        );
2308
2309        assert_eq!(
2310            serde_json::to_value(SessionInfoUpdate::new().meta(None::<Meta>)).unwrap(),
2311            json!({"_meta": null})
2312        );
2313
2314        assert_eq!(
2315            serde_json::to_value(SessionInfoUpdate::new().meta(meta)).unwrap(),
2316            json!({"_meta": {
2317                "source": "session-info"
2318            }})
2319        );
2320        assert_eq!(
2321            serde_json::to_value(SessionInfoUpdate::new().title("title")).unwrap(),
2322            json!({"title": "title"})
2323        );
2324        assert_eq!(
2325            serde_json::to_value(SessionInfoUpdate::new().title(None)).unwrap(),
2326            json!({"title": null})
2327        );
2328        assert_eq!(
2329            serde_json::to_value(
2330                SessionInfoUpdate::new()
2331                    .title("title")
2332                    .title(MaybeUndefined::Undefined)
2333            )
2334            .unwrap(),
2335            json!({})
2336        );
2337    }
2338
2339    #[test]
2340    fn test_content_chunk_message_id_serialization() {
2341        use serde_json::json;
2342
2343        assert_eq!(
2344            serde_json::to_value(SessionUpdate::AgentMessageChunk(ContentChunk::new(
2345                ContentBlock::Text(crate::v2::TextContent::new("Hello")),
2346                "msg_agent_c42b9",
2347            )))
2348            .unwrap(),
2349            json!({
2350                "sessionUpdate": "agent_message_chunk",
2351                "messageId": "msg_agent_c42b9",
2352                "content": {
2353                    "type": "text",
2354                    "text": "Hello"
2355                }
2356            })
2357        );
2358
2359        let err = serde_json::from_value::<ContentChunk>(json!({
2360            "content": {
2361                "type": "text",
2362                "text": "Hello"
2363            }
2364        }))
2365        .unwrap_err();
2366
2367        assert!(err.to_string().contains("messageId"), "{err}");
2368    }
2369
2370    #[test]
2371    fn test_tool_call_content_chunk_serialization() {
2372        use serde_json::json;
2373
2374        assert_eq!(
2375            serde_json::to_value(SessionUpdate::ToolCallContentChunk(
2376                ToolCallContentChunk::new(
2377                    "call_001",
2378                    crate::v2::ContentBlock::Text(crate::v2::TextContent::new("partial output")),
2379                )
2380            ))
2381            .unwrap(),
2382            json!({
2383                "sessionUpdate": "tool_call_content_chunk",
2384                "toolCallId": "call_001",
2385                "content": {
2386                    "type": "content",
2387                    "content": {
2388                        "type": "text",
2389                        "text": "partial output"
2390                    }
2391                }
2392            })
2393        );
2394
2395        let err = serde_json::from_value::<ToolCallContentChunk>(json!({
2396            "content": {
2397                "type": "content",
2398                "content": {
2399                    "type": "text",
2400                    "text": "partial output"
2401                }
2402            }
2403        }))
2404        .unwrap_err();
2405
2406        assert!(err.to_string().contains("toolCallId"), "{err}");
2407    }
2408
2409    #[test]
2410    fn test_full_message_serialization() {
2411        use serde_json::json;
2412
2413        assert_eq!(
2414            serde_json::to_value(SessionUpdate::UserMessage(
2415                UserMessage::new("msg_user_8f7a1").content(vec![ContentBlock::Text(
2416                    crate::v2::TextContent::new("Hello")
2417                )])
2418            ))
2419            .unwrap(),
2420            json!({
2421                "sessionUpdate": "user_message",
2422                "messageId": "msg_user_8f7a1",
2423                "content": [
2424                    {
2425                        "type": "text",
2426                        "text": "Hello"
2427                    }
2428                ]
2429            })
2430        );
2431
2432        assert_eq!(
2433            serde_json::to_value(SessionUpdate::AgentMessage(
2434                AgentMessage::new("msg_agent_c42b9").content(vec![ContentBlock::Text(
2435                    crate::v2::TextContent::new("Hello")
2436                )])
2437            ))
2438            .unwrap(),
2439            json!({
2440                "sessionUpdate": "agent_message",
2441                "messageId": "msg_agent_c42b9",
2442                "content": [
2443                    {
2444                        "type": "text",
2445                        "text": "Hello"
2446                    }
2447                ]
2448            })
2449        );
2450
2451        assert_eq!(
2452            serde_json::to_value(SessionUpdate::AgentThought(
2453                AgentThought::new("msg_thought_a12").content(vec![ContentBlock::Text(
2454                    crate::v2::TextContent::new("Need to inspect the call sites first.")
2455                )])
2456            ))
2457            .unwrap(),
2458            json!({
2459                "sessionUpdate": "agent_thought",
2460                "messageId": "msg_thought_a12",
2461                "content": [
2462                    {
2463                        "type": "text",
2464                        "text": "Need to inspect the call sites first."
2465                    }
2466                ]
2467            })
2468        );
2469    }
2470
2471    #[test]
2472    fn test_message_upsert_serialization() {
2473        use serde_json::json;
2474
2475        assert_eq!(
2476            serde_json::to_value(SessionUpdate::UserMessage(
2477                UserMessage::new("msg_empty").content(Vec::<ContentBlock>::new())
2478            ))
2479            .unwrap(),
2480            json!({
2481                "sessionUpdate": "user_message",
2482                "messageId": "msg_empty",
2483                "content": []
2484            })
2485        );
2486
2487        let empty = serde_json::from_value::<UserMessage>(json!({
2488            "messageId": "msg_empty",
2489            "content": []
2490        }))
2491        .unwrap();
2492        assert!(matches!(
2493            empty.content,
2494            MaybeUndefined::Value(ref content) if content.is_empty()
2495        ));
2496
2497        let patch = serde_json::from_value::<AgentMessage>(json!({
2498            "messageId": "msg_agent_c42b9"
2499        }))
2500        .unwrap();
2501        assert_eq!(patch.content, MaybeUndefined::Undefined);
2502        assert_eq!(patch.meta, MaybeUndefined::Undefined);
2503
2504        let malformed_meta = serde_json::from_value::<AgentMessage>(json!({
2505            "messageId": "msg_agent_c42b9",
2506            "_meta": false
2507        }))
2508        .unwrap();
2509        assert_eq!(malformed_meta.meta, MaybeUndefined::Undefined);
2510
2511        let patch = serde_json::from_value::<AgentThought>(json!({
2512            "messageId": "msg_thought_a12"
2513        }))
2514        .unwrap();
2515        assert_eq!(patch.content, MaybeUndefined::Undefined);
2516
2517        let clear = serde_json::from_value::<UserMessage>(json!({
2518            "messageId": "msg_user_8f7a1",
2519            "content": null
2520        }))
2521        .unwrap();
2522        assert_eq!(clear.content, MaybeUndefined::Null);
2523
2524        let clear_meta = serde_json::from_value::<UserMessage>(json!({
2525            "messageId": "msg_user_8f7a1",
2526            "_meta": null
2527        }))
2528        .unwrap();
2529        assert_eq!(clear_meta.meta, MaybeUndefined::Null);
2530
2531        let mut meta = Meta::new();
2532        meta.insert("source".to_string(), json!("replay"));
2533
2534        assert_eq!(
2535            serde_json::to_value(SessionUpdate::UserMessage(
2536                UserMessage::new("msg_user_8f7a1").meta(meta)
2537            ))
2538            .unwrap(),
2539            json!({
2540                "sessionUpdate": "user_message",
2541                "messageId": "msg_user_8f7a1",
2542                "_meta": {
2543                    "source": "replay"
2544                }
2545            })
2546        );
2547
2548        assert_eq!(
2549            serde_json::to_value(SessionUpdate::UserMessage(
2550                UserMessage::new("msg_user_8f7a1").meta(None::<Meta>)
2551            ))
2552            .unwrap(),
2553            json!({
2554                "sessionUpdate": "user_message",
2555                "messageId": "msg_user_8f7a1",
2556                "_meta": null
2557            })
2558        );
2559    }
2560
2561    #[test]
2562    fn test_usage_update_serialization() {
2563        use serde_json::json;
2564
2565        assert_eq!(
2566            serde_json::to_value(SessionUpdate::UsageUpdate(UsageUpdate::new(
2567                53_000, 200_000
2568            )))
2569            .unwrap(),
2570            json!({
2571                "sessionUpdate": "usage_update",
2572                "used": 53000,
2573                "size": 200_000
2574            })
2575        );
2576
2577        assert_eq!(
2578            serde_json::to_value(SessionUpdate::UsageUpdate(
2579                UsageUpdate::new(53_000, 200_000).cost(Cost::new(0.045, "USD"))
2580            ))
2581            .unwrap(),
2582            json!({
2583                "sessionUpdate": "usage_update",
2584                "used": 53000,
2585                "size": 200_000,
2586                "cost": {
2587                    "amount": 0.045,
2588                    "currency": "USD"
2589                }
2590            })
2591        );
2592
2593        let SessionUpdate::UsageUpdate(update) = serde_json::from_value(json!({
2594            "sessionUpdate": "usage_update",
2595            "used": 53000,
2596            "size": 200_000,
2597            "cost": null
2598        }))
2599        .unwrap() else {
2600            panic!("expected usage update");
2601        };
2602
2603        assert_eq!(update.cost, None);
2604    }
2605
2606    #[test]
2607    fn test_state_update_serialization() {
2608        use serde_json::json;
2609
2610        assert_eq!(
2611            serde_json::to_value(SessionUpdate::StateUpdate(StateUpdate::Running(
2612                RunningStateUpdate::new()
2613            )))
2614            .unwrap(),
2615            json!({
2616                "sessionUpdate": "state_update",
2617                "state": "running"
2618            })
2619        );
2620
2621        assert_eq!(
2622            serde_json::to_value(SessionUpdate::StateUpdate(StateUpdate::Idle(
2623                IdleStateUpdate::new().stop_reason(StopReason::EndTurn)
2624            )))
2625            .unwrap(),
2626            json!({
2627                "sessionUpdate": "state_update",
2628                "state": "idle",
2629                "stopReason": "end_turn"
2630            })
2631        );
2632
2633        let SessionUpdate::StateUpdate(update) = serde_json::from_value(json!({
2634            "sessionUpdate": "state_update",
2635            "state": "requires_action"
2636        }))
2637        .unwrap() else {
2638            panic!("expected state update");
2639        };
2640
2641        assert!(matches!(update, StateUpdate::RequiresAction(_)));
2642
2643        let SessionUpdate::StateUpdate(StateUpdate::Idle(update)) = serde_json::from_value(json!({
2644            "sessionUpdate": "state_update",
2645            "state": "idle",
2646            "stopReason": null
2647        }))
2648        .unwrap() else {
2649            panic!("expected idle state update");
2650        };
2651
2652        assert_eq!(update.stop_reason, None);
2653
2654        let SessionUpdate::StateUpdate(StateUpdate::Other(update)) =
2655            serde_json::from_value(json!({
2656                "sessionUpdate": "state_update",
2657                "state": "_paused",
2658                "label": "Paused"
2659            }))
2660            .unwrap()
2661        else {
2662            panic!("expected unknown state update");
2663        };
2664
2665        assert_eq!(update.state, "_paused");
2666        assert_eq!(update.fields["label"], json!("Paused"));
2667    }
2668
2669    #[test]
2670    fn session_update_preserves_unknown_variant() {
2671        use serde_json::json;
2672
2673        let update: SessionUpdate = serde_json::from_value(json!({
2674            "sessionUpdate": "_status_badge",
2675            "label": "Indexing",
2676            "progress": 0.5
2677        }))
2678        .unwrap();
2679
2680        let SessionUpdate::Other(unknown) = update else {
2681            panic!("expected unknown session update");
2682        };
2683
2684        assert_eq!(unknown.session_update, "_status_badge");
2685        assert_eq!(unknown.fields.get("label"), Some(&json!("Indexing")));
2686        assert_eq!(unknown.fields.get("progress"), Some(&json!(0.5)));
2687
2688        assert_eq!(
2689            serde_json::to_value(SessionUpdate::Other(unknown)).unwrap(),
2690            json!({
2691                "sessionUpdate": "_status_badge",
2692                "label": "Indexing",
2693                "progress": 0.5
2694            })
2695        );
2696    }
2697
2698    #[test]
2699    fn test_plan_update_serialization() {
2700        use serde_json::json;
2701
2702        let plan_update =
2703            SessionUpdate::PlanUpdate(PlanUpdate::new(crate::v2::PlanUpdateContent::items(
2704                "plan-1",
2705                vec![crate::v2::PlanEntry::new(
2706                    "Step 1",
2707                    crate::v2::PlanEntryPriority::High,
2708                    crate::v2::PlanEntryStatus::Pending,
2709                )],
2710            )));
2711
2712        assert_eq!(
2713            serde_json::to_value(plan_update).unwrap(),
2714            json!({
2715                "sessionUpdate": "plan_update",
2716                "plan": {
2717                    "type": "items",
2718                    "planId": "plan-1",
2719                    "entries": [
2720                        {
2721                            "content": "Step 1",
2722                            "priority": "high",
2723                            "status": "pending"
2724                        }
2725                    ]
2726                }
2727            })
2728        );
2729    }
2730
2731    #[cfg(feature = "unstable_plan_operations")]
2732    #[test]
2733    fn test_plan_removed_serialization() {
2734        use serde_json::json;
2735
2736        assert_eq!(
2737            serde_json::to_value(SessionUpdate::PlanRemoved(PlanRemoved::new("plan-1"))).unwrap(),
2738            json!({
2739                "sessionUpdate": "plan_removed",
2740                "planId": "plan-1"
2741            })
2742        );
2743    }
2744
2745    #[test]
2746    fn available_command_input_preserves_unknown_typed_variant() {
2747        use serde_json::json;
2748
2749        let input: AvailableCommandInput = serde_json::from_value(json!({
2750            "type": "_choices",
2751            "hint": "Pick one",
2752            "options": ["fast", "careful"]
2753        }))
2754        .unwrap();
2755
2756        let AvailableCommandInput::Other(unknown) = input else {
2757            panic!("expected unknown command input");
2758        };
2759
2760        assert_eq!(unknown.type_, "_choices");
2761        assert_eq!(unknown.fields.get("hint"), Some(&json!("Pick one")));
2762        assert_eq!(
2763            unknown.fields.get("options"),
2764            Some(&json!(["fast", "careful"]))
2765        );
2766        assert_eq!(
2767            serde_json::to_value(AvailableCommandInput::Other(unknown)).unwrap(),
2768            json!({
2769                "type": "_choices",
2770                "hint": "Pick one",
2771                "options": ["fast", "careful"]
2772            })
2773        );
2774    }
2775
2776    #[test]
2777    fn available_command_input_text_uses_type_discriminator() {
2778        use serde_json::json;
2779
2780        let input = AvailableCommandInput::Text(TextCommandInput::new("Describe changes"));
2781
2782        let json = serde_json::to_value(&input).unwrap();
2783        assert_eq!(
2784            json,
2785            json!({
2786                "type": "text",
2787                "hint": "Describe changes"
2788            })
2789        );
2790
2791        let roundtripped: AvailableCommandInput = serde_json::from_value(json).unwrap();
2792        assert!(matches!(roundtripped, AvailableCommandInput::Text(_)));
2793    }
2794
2795    #[test]
2796    fn request_permission_subject_tool_call_uses_type_discriminator() {
2797        use serde_json::json;
2798
2799        let subject = RequestPermissionSubject::from(ToolCallUpdate::new("call_001"));
2800
2801        let json = serde_json::to_value(&subject).unwrap();
2802        assert_eq!(
2803            json,
2804            json!({
2805                "type": "tool_call",
2806                "toolCall": {
2807                    "toolCallId": "call_001"
2808                }
2809            })
2810        );
2811
2812        let roundtripped: RequestPermissionSubject = serde_json::from_value(json).unwrap();
2813        assert!(matches!(
2814            roundtripped,
2815            RequestPermissionSubject::ToolCall(_)
2816        ));
2817    }
2818
2819    #[test]
2820    fn request_permission_subject_preserves_unknown_variant() {
2821        use serde_json::json;
2822
2823        let subject: RequestPermissionSubject = serde_json::from_value(json!({
2824            "type": "_review",
2825            "reason": "needs-review",
2826            "retryAfterSeconds": 30
2827        }))
2828        .unwrap();
2829
2830        let RequestPermissionSubject::Other(unknown) = subject else {
2831            panic!("expected unknown permission subject");
2832        };
2833
2834        assert_eq!(unknown.type_, "_review");
2835        assert_eq!(unknown.fields.get("reason"), Some(&json!("needs-review")));
2836        assert_eq!(unknown.fields.get("retryAfterSeconds"), Some(&json!(30)));
2837        assert_eq!(
2838            serde_json::to_value(RequestPermissionSubject::Other(unknown)).unwrap(),
2839            json!({
2840                "type": "_review",
2841                "reason": "needs-review",
2842                "retryAfterSeconds": 30
2843            })
2844        );
2845    }
2846
2847    #[test]
2848    fn request_permission_subject_unknown_does_not_hide_malformed_known_variant() {
2849        use serde_json::json;
2850
2851        assert!(
2852            serde_json::from_value::<RequestPermissionSubject>(json!({
2853                "type": "tool_call"
2854            }))
2855            .is_err()
2856        );
2857        assert!(
2858            serde_json::from_value::<RequestPermissionSubject>(json!({
2859                "type": 1
2860            }))
2861            .is_err()
2862        );
2863    }
2864
2865    #[test]
2866    fn request_permission_title_and_description_are_separate_from_tool_call_content() {
2867        use serde_json::json;
2868
2869        let request =
2870            RequestPermissionRequest::new("sess_abc123def456", "Approve file edit?", Vec::new())
2871                .description("Allow this tool to edit src/main.rs?")
2872                .subject(RequestPermissionSubject::from(ToolCallUpdate::new(
2873                    "call_001",
2874                )));
2875
2876        assert_eq!(
2877            serde_json::to_value(request).unwrap(),
2878            json!({
2879                "sessionId": "sess_abc123def456",
2880                "title": "Approve file edit?",
2881                "description": "Allow this tool to edit src/main.rs?",
2882                "subject": {
2883                    "type": "tool_call",
2884                    "toolCall": {
2885                        "toolCallId": "call_001"
2886                    }
2887                },
2888                "options": []
2889            })
2890        );
2891    }
2892
2893    #[test]
2894    fn request_permission_requires_title_and_allows_missing_subject() {
2895        use serde_json::json;
2896
2897        let request = RequestPermissionRequest::new(
2898            "sess_abc123def456",
2899            "Approve elevated permissions?",
2900            Vec::new(),
2901        );
2902
2903        assert_eq!(
2904            serde_json::to_value(request).unwrap(),
2905            json!({
2906                "sessionId": "sess_abc123def456",
2907                "title": "Approve elevated permissions?",
2908                "options": []
2909            })
2910        );
2911
2912        let missing_subject: RequestPermissionRequest = serde_json::from_value(json!({
2913            "sessionId": "sess_abc123def456",
2914            "title": "Approve elevated permissions?",
2915            "options": []
2916        }))
2917        .unwrap();
2918        assert!(missing_subject.subject.is_none());
2919
2920        let null_subject: RequestPermissionRequest = serde_json::from_value(json!({
2921            "sessionId": "sess_abc123def456",
2922            "title": "Approve elevated permissions?",
2923            "subject": null,
2924            "options": []
2925        }))
2926        .unwrap();
2927        assert!(null_subject.subject.is_none());
2928
2929        assert!(
2930            serde_json::from_value::<RequestPermissionRequest>(json!({
2931                "sessionId": "sess_abc123def456",
2932                "options": []
2933            }))
2934            .is_err()
2935        );
2936    }
2937
2938    #[test]
2939    fn request_permission_outcome_preserves_unknown_variant() {
2940        use serde_json::json;
2941
2942        let outcome: RequestPermissionOutcome = serde_json::from_value(json!({
2943            "outcome": "_defer",
2944            "reason": "needs-review",
2945            "retryAfterSeconds": 30
2946        }))
2947        .unwrap();
2948
2949        let RequestPermissionOutcome::Other(unknown) = outcome else {
2950            panic!("expected unknown permission outcome");
2951        };
2952
2953        assert_eq!(unknown.outcome, "_defer");
2954        assert_eq!(unknown.fields.get("reason"), Some(&json!("needs-review")));
2955        assert_eq!(unknown.fields.get("retryAfterSeconds"), Some(&json!(30)));
2956        assert_eq!(
2957            serde_json::to_value(RequestPermissionOutcome::Other(unknown)).unwrap(),
2958            json!({
2959                "outcome": "_defer",
2960                "reason": "needs-review",
2961                "retryAfterSeconds": 30
2962            })
2963        );
2964    }
2965
2966    #[test]
2967    fn request_permission_outcome_unknown_does_not_hide_malformed_known_variant() {
2968        use serde_json::json;
2969
2970        assert!(
2971            serde_json::from_value::<RequestPermissionOutcome>(json!({
2972                "outcome": "selected"
2973            }))
2974            .is_err()
2975        );
2976        assert!(
2977            serde_json::from_value::<RequestPermissionOutcome>(json!({
2978                "outcome": 1
2979            }))
2980            .is_err()
2981        );
2982    }
2983
2984    #[test]
2985    fn available_command_input_unknown_does_not_hide_malformed_text_variant() {
2986        use serde_json::json;
2987
2988        assert!(serde_json::from_value::<AvailableCommandInput>(json!({})).is_err());
2989        assert!(
2990            serde_json::from_value::<AvailableCommandInput>(json!({
2991                "hint": "Pick one"
2992            }))
2993            .is_err()
2994        );
2995        assert!(
2996            serde_json::from_value::<AvailableCommandInput>(json!({
2997                "type": 1,
2998                "hint": "Pick one"
2999            }))
3000            .is_err()
3001        );
3002        assert!(
3003            serde_json::from_value::<OtherAvailableCommandInput>(json!({
3004                "type": "text",
3005                "hint": "Pick one"
3006            }))
3007            .is_err()
3008        );
3009    }
3010
3011    #[cfg(feature = "unstable_nes")]
3012    #[test]
3013    fn test_client_capabilities_position_encodings_serialization() {
3014        use serde_json::json;
3015
3016        let capabilities = ClientCapabilities::new().position_encodings(vec![
3017            PositionEncodingKind::Utf32,
3018            PositionEncodingKind::Utf16,
3019        ]);
3020        let json = serde_json::to_value(&capabilities).unwrap();
3021
3022        assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"]));
3023    }
3024
3025    #[cfg(feature = "unstable_mcp_over_acp")]
3026    #[test]
3027    fn test_agent_mcp_request_method_names() {
3028        use serde_json::json;
3029
3030        let params: serde_json::Map<String, serde_json::Value> =
3031            [("cursor".to_string(), json!("abc"))].into_iter().collect();
3032
3033        assert_eq!(CLIENT_METHOD_NAMES.mcp_connect, "mcp/connect");
3034        assert_eq!(CLIENT_METHOD_NAMES.mcp_message, "mcp/message");
3035        assert_eq!(CLIENT_METHOD_NAMES.mcp_disconnect, "mcp/disconnect");
3036
3037        assert_eq!(
3038            AgentRequest::ConnectMcpRequest(Box::new(ConnectMcpRequest::new("server-1"))).method(),
3039            "mcp/connect"
3040        );
3041        assert_eq!(
3042            AgentRequest::MessageMcpRequest(Box::new(MessageMcpRequest::new(
3043                "conn-1",
3044                "tools/list"
3045            )))
3046            .method(),
3047            "mcp/message"
3048        );
3049        assert_eq!(
3050            AgentRequest::DisconnectMcpRequest(Box::new(DisconnectMcpRequest::new("conn-1")))
3051                .method(),
3052            "mcp/disconnect"
3053        );
3054        assert_eq!(
3055            AgentNotification::MessageMcpNotification(Box::new(MessageMcpNotification::new(
3056                "conn-1",
3057                "notifications/progress"
3058            )))
3059            .method(),
3060            "mcp/message"
3061        );
3062
3063        assert_eq!(
3064            serde_json::to_value(ConnectMcpRequest::new("server-1")).unwrap(),
3065            json!({ "serverId": "server-1" })
3066        );
3067        assert_eq!(
3068            serde_json::to_value(ConnectMcpResponse::new("conn-1")).unwrap(),
3069            json!({ "connectionId": "conn-1" })
3070        );
3071        assert_eq!(
3072            serde_json::to_value(MessageMcpRequest::new("conn-1", "tools/list").params(params))
3073                .unwrap(),
3074            json!({
3075                "connectionId": "conn-1",
3076                "method": "tools/list",
3077                "params": { "cursor": "abc" }
3078            })
3079        );
3080        assert_eq!(
3081            serde_json::to_value(DisconnectMcpRequest::new("conn-1")).unwrap(),
3082            json!({ "connectionId": "conn-1" })
3083        );
3084        assert_eq!(
3085            serde_json::to_value(MessageMcpNotification::new(
3086                "conn-1",
3087                "notifications/progress"
3088            ))
3089            .unwrap(),
3090            json!({
3091                "connectionId": "conn-1",
3092                "method": "notifications/progress"
3093            })
3094        );
3095
3096        let request_with_null_params: MessageMcpRequest = serde_json::from_value(json!({
3097            "connectionId": "conn-1",
3098            "method": "tools/list",
3099            "params": null
3100        }))
3101        .unwrap();
3102        assert_eq!(request_with_null_params.params, None);
3103    }
3104
3105    #[cfg(feature = "unstable_auth_methods")]
3106    #[test]
3107    fn test_auth_capabilities_serialize_terminal_support_as_object() {
3108        use serde_json::json;
3109
3110        let capabilities = AuthCapabilities::new().terminal(TerminalAuthCapabilities::new());
3111
3112        assert_eq!(
3113            serde_json::to_value(&capabilities).unwrap(),
3114            json!({
3115                "terminal": {}
3116            })
3117        );
3118
3119        let deserialized: AuthCapabilities = serde_json::from_value(json!({
3120            "terminal": false
3121        }))
3122        .unwrap();
3123        assert!(deserialized.terminal.is_none());
3124    }
3125
3126    #[test]
3127    fn request_permission_request_rejects_malformed_options() {
3128        use serde_json::json;
3129
3130        assert!(
3131            serde_json::from_value::<RequestPermissionRequest>(json!({
3132                "sessionId": "sess-1",
3133                "title": "Run tool?",
3134                "options": "not-an-array"
3135            }))
3136            .is_err()
3137        );
3138        assert!(
3139            serde_json::from_value::<RequestPermissionRequest>(json!({
3140                "sessionId": "sess-1",
3141                "title": "Run tool?",
3142                "options": [{"optionId": "allow"}]
3143            }))
3144            .is_err()
3145        );
3146    }
3147
3148    #[cfg(feature = "unstable_plan_operations")]
3149    #[test]
3150    fn malformed_plan_removed_is_not_hidden_as_unknown_update() {
3151        use serde_json::json;
3152
3153        assert!(
3154            serde_json::from_value::<SessionUpdate>(json!({
3155                "sessionUpdate": "plan_removed"
3156            }))
3157            .is_err()
3158        );
3159    }
3160}