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