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