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}