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