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