Skip to main content

agent_client_protocol_schema/v1/
plan.rs

1//! Execution plans for complex tasks that require multiple steps.
2//!
3//! Plans are strategies that agents share with clients through session updates,
4//! providing real-time visibility into their thinking and progress.
5//!
6//! See: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)
7
8#[cfg(feature = "unstable_plan_operations")]
9use std::sync::Arc;
10
11#[cfg(feature = "unstable_plan_operations")]
12use derive_more::{Display, From};
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
16
17use crate::{IntoOption, SkipListener};
18
19use super::Meta;
20
21/// An execution plan for accomplishing complex tasks.
22///
23/// Plans consist of multiple entries representing individual tasks or goals.
24/// Agents report plans to clients to provide visibility into their execution strategy.
25/// Plans can evolve during execution as the agent discovers new requirements or completes tasks.
26///
27/// See protocol docs: [Agent Plan](https://agentclientprotocol.com/protocol/agent-plan)
28#[serde_as]
29#[skip_serializing_none]
30#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
31#[serde(rename_all = "camelCase")]
32#[non_exhaustive]
33pub struct Plan {
34    /// The list of tasks to be accomplished.
35    ///
36    /// When updating a plan, the agent must send a complete list of all entries
37    /// with their current status. The client replaces the entire plan with each update.
38    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
39    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
40    pub entries: Vec<PlanEntry>,
41    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
42    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
43    /// these keys.
44    ///
45    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
46    #[serde(rename = "_meta")]
47    pub meta: Option<Meta>,
48}
49
50impl Plan {
51    /// Builds [`Plan`] with the required fields set; optional fields start unset or empty.
52    #[must_use]
53    pub fn new(entries: Vec<PlanEntry>) -> Self {
54        Self {
55            entries,
56            meta: None,
57        }
58    }
59
60    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
61    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
62    /// these keys.
63    ///
64    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
65    #[must_use]
66    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
67        self.meta = meta.into_option();
68        self
69    }
70}
71
72/// **UNSTABLE**
73///
74/// This capability is not part of the spec yet, and may be removed or changed at any point.
75///
76/// Unique identifier for a plan within a session.
77#[cfg(feature = "unstable_plan_operations")]
78#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)]
79#[serde(transparent)]
80#[from(Arc<str>, String, &'static str)]
81#[non_exhaustive]
82pub struct PlanId(pub Arc<str>);
83
84#[cfg(feature = "unstable_plan_operations")]
85impl PlanId {
86    /// Wraps a protocol string as a typed [`PlanId`].
87    #[must_use]
88    pub fn new(id: impl Into<Arc<str>>) -> Self {
89        Self(id.into())
90    }
91}
92
93/// **UNSTABLE**
94///
95/// This capability is not part of the spec yet, and may be removed or changed at any point.
96///
97/// A content update for a plan identified by ID.
98#[cfg(feature = "unstable_plan_operations")]
99#[skip_serializing_none]
100#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
101#[serde(rename_all = "camelCase")]
102#[non_exhaustive]
103pub struct PlanUpdate {
104    /// The updated plan content.
105    pub plan: PlanUpdateContent,
106    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
107    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
108    /// these keys.
109    ///
110    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
111    #[serde(rename = "_meta")]
112    pub meta: Option<Meta>,
113}
114
115#[cfg(feature = "unstable_plan_operations")]
116impl PlanUpdate {
117    /// Builds [`PlanUpdate`] with the required fields set; optional fields start unset or empty.
118    #[must_use]
119    pub fn new(plan: PlanUpdateContent) -> Self {
120        Self { plan, meta: None }
121    }
122
123    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
124    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
125    /// these keys.
126    ///
127    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
128    #[must_use]
129    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
130        self.meta = meta.into_option();
131        self
132    }
133}
134
135/// **UNSTABLE**
136///
137/// This capability is not part of the spec yet, and may be removed or changed at any point.
138///
139/// Updated content for a plan.
140#[cfg(feature = "unstable_plan_operations")]
141#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
142#[serde(tag = "type", rename_all = "snake_case")]
143#[schemars(extend("discriminator" = {"propertyName": "type"}))]
144#[non_exhaustive]
145pub enum PlanUpdateContent {
146    /// Structured plan entries.
147    Items(PlanItems),
148    /// A URI pointing to a file containing the plan.
149    File(PlanFile),
150    /// Raw markdown content for the plan.
151    Markdown(PlanMarkdown),
152}
153
154#[cfg(feature = "unstable_plan_operations")]
155impl PlanUpdateContent {
156    /// Builds a plan update that replaces the itemized entries for a plan.
157    #[must_use]
158    pub fn items(id: impl Into<PlanId>, entries: Vec<PlanEntry>) -> Self {
159        Self::Items(PlanItems::new(id, entries))
160    }
161
162    /// Builds a plan update that points clients at an external plan file URI.
163    #[must_use]
164    pub fn file(id: impl Into<PlanId>, uri: impl Into<String>) -> Self {
165        Self::File(PlanFile::new(id, uri))
166    }
167
168    /// Builds a plan update whose plan content is inline Markdown.
169    #[must_use]
170    pub fn markdown(id: impl Into<PlanId>, content: impl Into<String>) -> Self {
171        Self::Markdown(PlanMarkdown::new(id, content))
172    }
173}
174
175/// **UNSTABLE**
176///
177/// This capability is not part of the spec yet, and may be removed or changed at any point.
178///
179/// A plan represented as structured entries.
180#[cfg(feature = "unstable_plan_operations")]
181#[serde_as]
182#[skip_serializing_none]
183#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
184#[serde(rename_all = "camelCase")]
185#[non_exhaustive]
186pub struct PlanItems {
187    /// The plan ID to update.
188    pub id: PlanId,
189    /// The list of tasks to be accomplished.
190    ///
191    /// When updating an item-based plan, the agent must send a complete list of all entries
192    /// with their current status. The client replaces that plan with each update.
193    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
194    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
195    pub entries: Vec<PlanEntry>,
196    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
197    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
198    /// these keys.
199    ///
200    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
201    #[serde(rename = "_meta")]
202    pub meta: Option<Meta>,
203}
204
205#[cfg(feature = "unstable_plan_operations")]
206impl PlanItems {
207    /// Builds [`PlanItems`] with the required fields set; optional fields start unset or empty.
208    #[must_use]
209    pub fn new(id: impl Into<PlanId>, entries: Vec<PlanEntry>) -> Self {
210        Self {
211            id: id.into(),
212            entries,
213            meta: None,
214        }
215    }
216
217    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
218    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
219    /// these keys.
220    ///
221    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
222    #[must_use]
223    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
224        self.meta = meta.into_option();
225        self
226    }
227}
228
229/// **UNSTABLE**
230///
231/// This capability is not part of the spec yet, and may be removed or changed at any point.
232///
233/// A plan represented by a file URI.
234#[cfg(feature = "unstable_plan_operations")]
235#[skip_serializing_none]
236#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
237#[serde(rename_all = "camelCase")]
238#[non_exhaustive]
239pub struct PlanFile {
240    /// The plan ID to update.
241    pub id: PlanId,
242    /// The URI of the file containing the plan.
243    pub uri: String,
244    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
245    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
246    /// these keys.
247    ///
248    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
249    #[serde(rename = "_meta")]
250    pub meta: Option<Meta>,
251}
252
253#[cfg(feature = "unstable_plan_operations")]
254impl PlanFile {
255    /// Builds [`PlanFile`] with the required fields set; optional fields start unset or empty.
256    #[must_use]
257    pub fn new(id: impl Into<PlanId>, uri: impl Into<String>) -> Self {
258        Self {
259            id: id.into(),
260            uri: uri.into(),
261            meta: None,
262        }
263    }
264
265    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
266    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
267    /// these keys.
268    ///
269    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
270    #[must_use]
271    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
272        self.meta = meta.into_option();
273        self
274    }
275}
276
277/// **UNSTABLE**
278///
279/// This capability is not part of the spec yet, and may be removed or changed at any point.
280///
281/// A plan represented as raw markdown content.
282#[cfg(feature = "unstable_plan_operations")]
283#[skip_serializing_none]
284#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
285#[serde(rename_all = "camelCase")]
286#[non_exhaustive]
287pub struct PlanMarkdown {
288    /// The plan ID to update.
289    pub id: PlanId,
290    /// Markdown content for the plan.
291    pub content: String,
292    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
293    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
294    /// these keys.
295    ///
296    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
297    #[serde(rename = "_meta")]
298    pub meta: Option<Meta>,
299}
300
301#[cfg(feature = "unstable_plan_operations")]
302impl PlanMarkdown {
303    /// Builds [`PlanMarkdown`] with the required fields set; optional fields start unset or empty.
304    #[must_use]
305    pub fn new(id: impl Into<PlanId>, content: impl Into<String>) -> Self {
306        Self {
307            id: id.into(),
308            content: content.into(),
309            meta: None,
310        }
311    }
312
313    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
314    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
315    /// these keys.
316    ///
317    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
318    #[must_use]
319    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
320        self.meta = meta.into_option();
321        self
322    }
323}
324
325/// **UNSTABLE**
326///
327/// This capability is not part of the spec yet, and may be removed or changed at any point.
328///
329/// Removal notice for a plan identified by ID.
330#[cfg(feature = "unstable_plan_operations")]
331#[skip_serializing_none]
332#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
333#[serde(rename_all = "camelCase")]
334#[non_exhaustive]
335pub struct PlanRemoved {
336    /// The plan ID to remove.
337    pub id: PlanId,
338    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
339    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
340    /// these keys.
341    ///
342    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
343    #[serde(rename = "_meta")]
344    pub meta: Option<Meta>,
345}
346
347#[cfg(feature = "unstable_plan_operations")]
348impl PlanRemoved {
349    /// Builds [`PlanRemoved`] with the required fields set; optional fields start unset or empty.
350    #[must_use]
351    pub fn new(id: impl Into<PlanId>) -> Self {
352        Self {
353            id: id.into(),
354            meta: None,
355        }
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/// **UNSTABLE**
371///
372/// This capability is not part of the spec yet, and may be removed or changed at any point.
373///
374/// Capabilities for receiving `plan_update` and `plan_removed` session updates.
375#[cfg(feature = "unstable_plan_operations")]
376#[skip_serializing_none]
377#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
378#[serde(rename_all = "camelCase")]
379#[non_exhaustive]
380pub struct PlanCapabilities {
381    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
382    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
383    /// these keys.
384    ///
385    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
386    #[serde(rename = "_meta")]
387    pub meta: Option<Meta>,
388}
389
390#[cfg(feature = "unstable_plan_operations")]
391impl PlanCapabilities {
392    /// Builds an empty [`PlanCapabilities`]; use builder methods to advertise supported sub-capabilities.
393    #[must_use]
394    pub fn new() -> Self {
395        Self::default()
396    }
397
398    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
399    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
400    /// these keys.
401    ///
402    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
403    #[must_use]
404    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
405        self.meta = meta.into_option();
406        self
407    }
408}
409
410/// A single entry in the execution plan.
411///
412/// Represents a task or goal that the assistant intends to accomplish
413/// as part of fulfilling the user's request.
414/// See protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/agent-plan#plan-entries)
415#[skip_serializing_none]
416#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
417#[serde(rename_all = "camelCase")]
418#[non_exhaustive]
419pub struct PlanEntry {
420    /// Human-readable description of what this task aims to accomplish.
421    pub content: String,
422    /// The relative importance of this task.
423    /// Used to indicate which tasks are most critical to the overall goal.
424    pub priority: PlanEntryPriority,
425    /// Current execution status of this task.
426    pub status: PlanEntryStatus,
427    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
428    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
429    /// these keys.
430    ///
431    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
432    #[serde(rename = "_meta")]
433    pub meta: Option<Meta>,
434}
435
436impl PlanEntry {
437    /// Builds [`PlanEntry`] with the required fields set; optional fields start unset or empty.
438    #[must_use]
439    pub fn new(
440        content: impl Into<String>,
441        priority: PlanEntryPriority,
442        status: PlanEntryStatus,
443    ) -> Self {
444        Self {
445            content: content.into(),
446            priority,
447            status,
448            meta: None,
449        }
450    }
451
452    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
453    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
454    /// these keys.
455    ///
456    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
457    #[must_use]
458    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
459        self.meta = meta.into_option();
460        self
461    }
462}
463
464/// Priority levels for plan entries.
465///
466/// Used to indicate the relative importance or urgency of different
467/// tasks in the execution plan.
468/// See protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/agent-plan#plan-entries)
469#[derive(Deserialize, Serialize, JsonSchema, Debug, Clone, PartialEq, Eq)]
470#[serde(rename_all = "snake_case")]
471#[non_exhaustive]
472pub enum PlanEntryPriority {
473    /// High priority task - critical to the overall goal.
474    High,
475    /// Medium priority task - important but not critical.
476    Medium,
477    /// Low priority task - nice to have but not essential.
478    Low,
479}
480
481/// Status of a plan entry in the execution flow.
482///
483/// Tracks the lifecycle of each task from planning through completion.
484/// See protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/agent-plan#plan-entries)
485#[derive(Deserialize, Serialize, JsonSchema, Debug, Clone, PartialEq, Eq)]
486#[serde(rename_all = "snake_case")]
487#[non_exhaustive]
488pub enum PlanEntryStatus {
489    /// The task has not started yet.
490    Pending,
491    /// The task is currently being worked on.
492    InProgress,
493    /// The task has been successfully completed.
494    Completed,
495}