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