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