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