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}