mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
Rename update_plan tool to todo_write (keep update_plan alias)
This commit is contained in:
@@ -2256,6 +2256,7 @@ pub struct TurnDiffUpdatedNotification {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
/// Todo list update from the todo_write tool. The plan name is legacy.
|
||||
pub struct TurnPlanUpdatedNotification {
|
||||
pub thread_id: String,
|
||||
pub turn_id: String,
|
||||
|
||||
@@ -433,7 +433,7 @@ The app-server streams JSON-RPC notifications while a turn is running. Each turn
|
||||
- `turn/started` — `{ turn }` with the turn id, empty `items`, and `status: "inProgress"`.
|
||||
- `turn/completed` — `{ turn }` where `turn.status` is `completed`, `interrupted`, or `failed`; failures carry `{ error: { message, codexErrorInfo?, additionalDetails? } }`.
|
||||
- `turn/diff/updated` — `{ threadId, turnId, diff }` represents the up-to-date snapshot of the turn-level unified diff, emitted after every FileChange item. `diff` is the latest aggregated unified diff across every file change in the turn. UIs can render this to show the full "what changed" view without stitching individual `fileChange` items.
|
||||
- `turn/plan/updated` — `{ turnId, explanation?, plan }` whenever the agent shares or changes its plan; each `plan` entry is `{ step, status }` with `status` in `pending`, `inProgress`, or `completed`.
|
||||
- `turn/plan/updated` — `{ turnId, explanation?, plan }` whenever the agent shares or changes its todo list; each `plan` entry is `{ step, status }` with `status` in `pending`, `inProgress`, or `completed`. (The plan-named event and field are legacy and are unrelated to Plan mode.)
|
||||
|
||||
Today both notifications carry an empty `items` array even when item events were streamed; rely on `item/*` notifications for the canonical item list until this is fixed.
|
||||
|
||||
|
||||
@@ -1106,11 +1106,11 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
EventMsg::PlanUpdate(plan_update_event) => {
|
||||
handle_turn_plan_update(
|
||||
EventMsg::PlanUpdate(todo_update_event) => {
|
||||
handle_turn_todo_update(
|
||||
conversation_id,
|
||||
&event_turn_id,
|
||||
plan_update_event,
|
||||
todo_update_event,
|
||||
api_version,
|
||||
outgoing.as_ref(),
|
||||
)
|
||||
@@ -1140,19 +1140,20 @@ async fn handle_turn_diff(
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_turn_plan_update(
|
||||
async fn handle_turn_todo_update(
|
||||
conversation_id: ThreadId,
|
||||
event_turn_id: &str,
|
||||
plan_update_event: UpdatePlanArgs,
|
||||
todo_update_event: UpdatePlanArgs,
|
||||
api_version: ApiVersion,
|
||||
outgoing: &OutgoingMessageSender,
|
||||
) {
|
||||
if let ApiVersion::V2 = api_version {
|
||||
// NOTE: The protocol keeps plan-named fields/events for compatibility; this is the todo list update.
|
||||
let notification = TurnPlanUpdatedNotification {
|
||||
thread_id: conversation_id.to_string(),
|
||||
turn_id: event_turn_id.to_string(),
|
||||
explanation: plan_update_event.explanation,
|
||||
plan: plan_update_event
|
||||
explanation: todo_update_event.explanation,
|
||||
plan: todo_update_event
|
||||
.plan
|
||||
.into_iter()
|
||||
.map(TurnPlanStep::from)
|
||||
@@ -1988,11 +1989,11 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_turn_plan_update_emits_notification_for_v2() -> Result<()> {
|
||||
async fn test_handle_turn_todo_update_emits_notification_for_v2() -> Result<()> {
|
||||
let (tx, mut rx) = mpsc::channel(CHANNEL_CAPACITY);
|
||||
let outgoing = OutgoingMessageSender::new(tx);
|
||||
let update = UpdatePlanArgs {
|
||||
explanation: Some("need plan".to_string()),
|
||||
explanation: Some("need todo list".to_string()),
|
||||
plan: vec![
|
||||
PlanItemArg {
|
||||
step: "first".to_string(),
|
||||
@@ -2007,7 +2008,7 @@ mod tests {
|
||||
|
||||
let conversation_id = ThreadId::new();
|
||||
|
||||
handle_turn_plan_update(
|
||||
handle_turn_todo_update(
|
||||
conversation_id,
|
||||
"turn-123",
|
||||
update,
|
||||
@@ -2024,7 +2025,7 @@ mod tests {
|
||||
OutgoingMessage::AppServerNotification(ServerNotification::TurnPlanUpdated(n)) => {
|
||||
assert_eq!(n.thread_id, conversation_id.to_string());
|
||||
assert_eq!(n.turn_id, "turn-123");
|
||||
assert_eq!(n.explanation.as_deref(), Some("need plan"));
|
||||
assert_eq!(n.explanation.as_deref(), Some("need todo list"));
|
||||
assert_eq!(n.plan.len(), 2);
|
||||
assert_eq!(n.plan[0].step, "first");
|
||||
assert_eq!(n.plan[0].status, TurnPlanStepStatus::Pending);
|
||||
|
||||
@@ -60,31 +60,31 @@ Content:
|
||||
- “Alright, build pipeline order is interesting. Checking how it reports failures.”
|
||||
- “Spotted a clever caching util; now hunting where it gets used.”
|
||||
|
||||
## Planning
|
||||
## Todo Lists
|
||||
|
||||
You have access to an `update_plan` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
You have access to a `todo_write` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. A todo list can help make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good todo list should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
|
||||
Note that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.
|
||||
Note that todo lists are not for padding out simple work with filler steps or stating the obvious. The content of your list should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use todo lists for simple or single-step queries that you can just do or answer immediately.
|
||||
|
||||
Do not repeat the full contents of the plan after an `update_plan` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
Do not repeat the full contents of the todo list after a `todo_write` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. Sometimes, you may need to change plans in the middle of a task: call `update_plan` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your todo list after a single pass of implementation. If this is the case, you can simply mark all the steps as completed. Sometimes, you may need to change your list in the middle of a task: call `todo_write` with the updated list and make sure to provide an `explanation` of the rationale when doing so.
|
||||
|
||||
Maintain statuses in the tool: exactly one item in_progress at a time; mark items complete when done; post timely status transitions. Do not jump an item from pending to completed: always set it to in_progress first. Do not batch-complete multiple items after the fact. Finish with all items completed or explicitly canceled/deferred before ending the turn. Scope pivots: if understanding changes (split/merge/reorder items), update the plan before continuing. Do not let the plan go stale while coding.
|
||||
Maintain statuses in the tool: exactly one item in_progress at a time; mark items complete when done; post timely status transitions. Do not jump an item from pending to completed: always set it to in_progress first. Do not batch-complete multiple items after the fact. Finish with all items completed or explicitly canceled/deferred before ending the turn. Scope pivots: if understanding changes (split/merge/reorder items), update the list before continuing. Do not let the list go stale while coding.
|
||||
|
||||
Use a plan when:
|
||||
Use a todo list when:
|
||||
|
||||
- The task is non-trivial and will require multiple actions over a long time horizon.
|
||||
- There are logical phases or dependencies where sequencing matters.
|
||||
- The work has ambiguity that benefits from outlining high-level goals.
|
||||
- You want intermediate checkpoints for feedback and validation.
|
||||
- When the user asked you to do more than one thing in a single prompt
|
||||
- The user has asked you to use the plan tool (aka "TODOs")
|
||||
- You generate additional steps while working, and plan to do them before yielding to the user
|
||||
- The user has asked you to use the todo tool
|
||||
- You generate additional steps while working, and intend to do them before yielding to the user
|
||||
|
||||
### Examples
|
||||
|
||||
**High-quality plans**
|
||||
**High-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -111,7 +111,7 @@ Example 3:
|
||||
5. Persist messages in lightweight DB
|
||||
6. Add typing indicators + unread count
|
||||
|
||||
**Low-quality plans**
|
||||
**Low-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -320,12 +320,12 @@ It is important to remember:
|
||||
- You must include a header with your intended action (Add/Delete/Update)
|
||||
- You must prefix new lines with `+` even when creating a new file
|
||||
|
||||
## `update_plan`
|
||||
## `todo_write`
|
||||
|
||||
A tool named `update_plan` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.
|
||||
A tool named `todo_write` is available to you. You can use it to keep an up‑to‑date, step‑by‑step todo list for the task.
|
||||
|
||||
To create a new plan, call `update_plan` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
To create a new todo list, call `todo_write` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
|
||||
When steps have been completed, use `update_plan` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `update_plan` call.
|
||||
When steps have been completed, use `todo_write` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `todo_write` call.
|
||||
|
||||
If all steps are complete, ensure you call `update_plan` to mark all steps as `completed`.
|
||||
If all steps are complete, ensure you call `todo_write` to mark all steps as `completed`.
|
||||
|
||||
@@ -33,31 +33,31 @@ Unless the user explicitly asks for a plan, asks a question about the code, is b
|
||||
|
||||
## Responsiveness
|
||||
|
||||
## Planning
|
||||
## Todo Lists
|
||||
|
||||
You have access to an `update_plan` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
You have access to a `todo_write` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. A todo list can help make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good todo list should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
|
||||
Note that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.
|
||||
Note that todo lists are not for padding out simple work with filler steps or stating the obvious. The content of your list should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use todo lists for simple or single-step queries that you can just do or answer immediately.
|
||||
|
||||
Do not repeat the full contents of the plan after an `update_plan` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
Do not repeat the full contents of the todo list after a `todo_write` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. Sometimes, you may need to change plans in the middle of a task: call `update_plan` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your todo list after a single pass of implementation. If this is the case, you can simply mark all the steps as completed. Sometimes, you may need to change your list in the middle of a task: call `todo_write` with the updated list and make sure to provide an `explanation` of the rationale when doing so.
|
||||
|
||||
Maintain statuses in the tool: exactly one item in_progress at a time; mark items complete when done; post timely status transitions. Do not jump an item from pending to completed: always set it to in_progress first. Do not batch-complete multiple items after the fact. Finish with all items completed or explicitly canceled/deferred before ending the turn. Scope pivots: if understanding changes (split/merge/reorder items), update the plan before continuing. Do not let the plan go stale while coding.
|
||||
Maintain statuses in the tool: exactly one item in_progress at a time; mark items complete when done; post timely status transitions. Do not jump an item from pending to completed: always set it to in_progress first. Do not batch-complete multiple items after the fact. Finish with all items completed or explicitly canceled/deferred before ending the turn. Scope pivots: if understanding changes (split/merge/reorder items), update the list before continuing. Do not let the list go stale while coding.
|
||||
|
||||
Use a plan when:
|
||||
Use a todo list when:
|
||||
|
||||
- The task is non-trivial and will require multiple actions over a long time horizon.
|
||||
- There are logical phases or dependencies where sequencing matters.
|
||||
- The work has ambiguity that benefits from outlining high-level goals.
|
||||
- You want intermediate checkpoints for feedback and validation.
|
||||
- When the user asked you to do more than one thing in a single prompt
|
||||
- The user has asked you to use the plan tool (aka "TODOs")
|
||||
- You generate additional steps while working, and plan to do them before yielding to the user
|
||||
- The user has asked you to use the todo tool
|
||||
- You generate additional steps while working, and intend to do them before yielding to the user
|
||||
|
||||
### Examples
|
||||
|
||||
**High-quality plans**
|
||||
**High-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -84,7 +84,7 @@ Example 3:
|
||||
5. Persist messages in lightweight DB
|
||||
6. Add typing indicators + unread count
|
||||
|
||||
**Low-quality plans**
|
||||
**Low-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -104,7 +104,7 @@ Example 3:
|
||||
2. Run quick sanity check
|
||||
3. Summarize usage instructions
|
||||
|
||||
If you need to write a plan, only write high quality plans, not low quality ones.
|
||||
If you need to write a todo list, only write high quality todo lists, not low quality ones.
|
||||
|
||||
## Task execution
|
||||
|
||||
@@ -287,12 +287,12 @@ It is important to remember:
|
||||
- You must include a header with your intended action (Add/Delete/Update)
|
||||
- You must prefix new lines with `+` even when creating a new file
|
||||
|
||||
## `update_plan`
|
||||
## `todo_write`
|
||||
|
||||
A tool named `update_plan` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.
|
||||
A tool named `todo_write` is available to you. You can use it to keep an up‑to‑date, step‑by‑step todo list for the task.
|
||||
|
||||
To create a new plan, call `update_plan` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
To create a new todo list, call `todo_write` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
|
||||
When steps have been completed, use `update_plan` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `update_plan` call.
|
||||
When steps have been completed, use `todo_write` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `todo_write` call.
|
||||
|
||||
If all steps are complete, ensure you call `update_plan` to mark all steps as `completed`.
|
||||
If all steps are complete, ensure you call `todo_write` to mark all steps as `completed`.
|
||||
|
||||
File diff suppressed because one or more lines are too long
+15
-15
@@ -49,29 +49,29 @@ Before making tool calls, send a brief preamble to the user explaining what you
|
||||
- “Alright, build pipeline order is interesting. Checking how it reports failures.”
|
||||
- “Spotted a clever caching util; now hunting where it gets used.”
|
||||
|
||||
## Planning
|
||||
## Todo Lists
|
||||
|
||||
You have access to an `update_plan` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
You have access to a `todo_write` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. A todo list can help make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good todo list should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
|
||||
Note that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.
|
||||
Note that todo lists are not for padding out simple work with filler steps or stating the obvious. The content of your list should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use todo lists for simple or single-step queries that you can just do or answer immediately.
|
||||
|
||||
Do not repeat the full contents of the plan after an `update_plan` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
Do not repeat the full contents of the todo list after a `todo_write` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. Sometimes, you may need to change plans in the middle of a task: call `update_plan` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your todo list after a single pass of implementation. If this is the case, you can simply mark all the steps as completed. Sometimes, you may need to change your list in the middle of a task: call `todo_write` with the updated list and make sure to provide an `explanation` of the rationale when doing so.
|
||||
|
||||
Use a plan when:
|
||||
Use a todo list when:
|
||||
|
||||
- The task is non-trivial and will require multiple actions over a long time horizon.
|
||||
- There are logical phases or dependencies where sequencing matters.
|
||||
- The work has ambiguity that benefits from outlining high-level goals.
|
||||
- You want intermediate checkpoints for feedback and validation.
|
||||
- When the user asked you to do more than one thing in a single prompt
|
||||
- The user has asked you to use the plan tool (aka "TODOs")
|
||||
- You generate additional steps while working, and plan to do them before yielding to the user
|
||||
- The user has asked you to use the todo tool
|
||||
- You generate additional steps while working, and intend to do them before yielding to the user
|
||||
|
||||
### Examples
|
||||
|
||||
**High-quality plans**
|
||||
**High-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -98,7 +98,7 @@ Example 3:
|
||||
5. Persist messages in lightweight DB
|
||||
6. Add typing indicators + unread count
|
||||
|
||||
**Low-quality plans**
|
||||
**Low-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -264,12 +264,12 @@ When using the shell, you must adhere to the following guidelines:
|
||||
- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)
|
||||
- Do not use python scripts to attempt to output larger chunks of a file.
|
||||
|
||||
## `update_plan`
|
||||
## `todo_write`
|
||||
|
||||
A tool named `update_plan` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.
|
||||
A tool named `todo_write` is available to you. You can use it to keep an up‑to‑date, step‑by‑step todo list for the task.
|
||||
|
||||
To create a new plan, call `update_plan` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
To create a new todo list, call `todo_write` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
|
||||
When steps have been completed, use `update_plan` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `update_plan` call.
|
||||
When steps have been completed, use `todo_write` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `todo_write` call.
|
||||
|
||||
If all steps are complete, ensure you call `update_plan` to mark all steps as `completed`.
|
||||
If all steps are complete, ensure you call `todo_write` to mark all steps as `completed`.
|
||||
|
||||
@@ -49,29 +49,29 @@ Before making tool calls, send a brief preamble to the user explaining what you
|
||||
- “Alright, build pipeline order is interesting. Checking how it reports failures.”
|
||||
- “Spotted a clever caching util; now hunting where it gets used.”
|
||||
|
||||
## Planning
|
||||
## Todo Lists
|
||||
|
||||
You have access to an `update_plan` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
You have access to a `todo_write` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. A todo list can help make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good todo list should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
|
||||
Note that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.
|
||||
Note that todo lists are not for padding out simple work with filler steps or stating the obvious. The content of your list should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use todo lists for simple or single-step queries that you can just do or answer immediately.
|
||||
|
||||
Do not repeat the full contents of the plan after an `update_plan` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
Do not repeat the full contents of the todo list after a `todo_write` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. Sometimes, you may need to change plans in the middle of a task: call `update_plan` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your todo list after a single pass of implementation. If this is the case, you can simply mark all the steps as completed. Sometimes, you may need to change your list in the middle of a task: call `todo_write` with the updated list and make sure to provide an `explanation` of the rationale when doing so.
|
||||
|
||||
Use a plan when:
|
||||
Use a todo list when:
|
||||
|
||||
- The task is non-trivial and will require multiple actions over a long time horizon.
|
||||
- There are logical phases or dependencies where sequencing matters.
|
||||
- The work has ambiguity that benefits from outlining high-level goals.
|
||||
- You want intermediate checkpoints for feedback and validation.
|
||||
- When the user asked you to do more than one thing in a single prompt
|
||||
- The user has asked you to use the plan tool (aka "TODOs")
|
||||
- You generate additional steps while working, and plan to do them before yielding to the user
|
||||
- The user has asked you to use the todo tool
|
||||
- You generate additional steps while working, and intend to do them before yielding to the user
|
||||
|
||||
### Examples
|
||||
|
||||
**High-quality plans**
|
||||
**High-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -98,7 +98,7 @@ Example 3:
|
||||
5. Persist messages in lightweight DB
|
||||
6. Add typing indicators + unread count
|
||||
|
||||
**Low-quality plans**
|
||||
**Low-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -264,15 +264,15 @@ When using the shell, you must adhere to the following guidelines:
|
||||
- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)
|
||||
- Do not use python scripts to attempt to output larger chunks of a file.
|
||||
|
||||
## `update_plan`
|
||||
## `todo_write`
|
||||
|
||||
A tool named `update_plan` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.
|
||||
A tool named `todo_write` is available to you. You can use it to keep an up‑to‑date, step‑by‑step todo list for the task.
|
||||
|
||||
To create a new plan, call `update_plan` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
To create a new todo list, call `todo_write` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
|
||||
When steps have been completed, use `update_plan` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `update_plan` call.
|
||||
When steps have been completed, use `todo_write` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `todo_write` call.
|
||||
|
||||
If all steps are complete, ensure you call `update_plan` to mark all steps as `completed`.
|
||||
If all steps are complete, ensure you call `todo_write` to mark all steps as `completed`.
|
||||
|
||||
## `apply_patch`
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ mod test_sync;
|
||||
mod unified_exec;
|
||||
mod view_image;
|
||||
|
||||
pub use plan::PLAN_TOOL;
|
||||
pub use plan::TODO_WRITE_TOOL;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::function_tool::FunctionCallError;
|
||||
@@ -24,7 +24,7 @@ pub use grep_files::GrepFilesHandler;
|
||||
pub use list_dir::ListDirHandler;
|
||||
pub use mcp::McpHandler;
|
||||
pub use mcp_resource::McpResourceHandler;
|
||||
pub use plan::PlanHandler;
|
||||
pub use plan::TodoWriteHandler;
|
||||
pub use read_file::ReadFileHandler;
|
||||
pub use request_user_input::RequestUserInputHandler;
|
||||
pub use shell::ShellCommandHandler;
|
||||
|
||||
@@ -15,22 +15,22 @@ use codex_protocol::protocol::EventMsg;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
pub struct PlanHandler;
|
||||
pub struct TodoWriteHandler;
|
||||
|
||||
pub static PLAN_TOOL: LazyLock<ToolSpec> = LazyLock::new(|| {
|
||||
let mut plan_item_props = BTreeMap::new();
|
||||
plan_item_props.insert("step".to_string(), JsonSchema::String { description: None });
|
||||
plan_item_props.insert(
|
||||
pub static TODO_WRITE_TOOL: LazyLock<ToolSpec> = LazyLock::new(|| {
|
||||
let mut todo_item_props = BTreeMap::new();
|
||||
todo_item_props.insert("step".to_string(), JsonSchema::String { description: None });
|
||||
todo_item_props.insert(
|
||||
"status".to_string(),
|
||||
JsonSchema::String {
|
||||
description: Some("One of: pending, in_progress, completed".to_string()),
|
||||
},
|
||||
);
|
||||
|
||||
let plan_items_schema = JsonSchema::Array {
|
||||
let todo_items_schema = JsonSchema::Array {
|
||||
description: Some("The list of steps".to_string()),
|
||||
items: Box::new(JsonSchema::Object {
|
||||
properties: plan_item_props,
|
||||
properties: todo_item_props,
|
||||
required: Some(vec!["step".to_string(), "status".to_string()]),
|
||||
additional_properties: Some(false.into()),
|
||||
}),
|
||||
@@ -41,12 +41,13 @@ pub static PLAN_TOOL: LazyLock<ToolSpec> = LazyLock::new(|| {
|
||||
"explanation".to_string(),
|
||||
JsonSchema::String { description: None },
|
||||
);
|
||||
properties.insert("plan".to_string(), plan_items_schema);
|
||||
// NOTE: The "plan" field name is legacy tool payload shape, unrelated to Plan mode.
|
||||
properties.insert("plan".to_string(), todo_items_schema);
|
||||
|
||||
ToolSpec::Function(ResponsesApiTool {
|
||||
name: "update_plan".to_string(),
|
||||
description: r#"Updates the task plan.
|
||||
Provide an optional explanation and a list of plan items, each with a step and status.
|
||||
name: "todo_write".to_string(),
|
||||
description: r#"Updates the task list.
|
||||
Provide an optional explanation and a list of todo items, each with a step and status.
|
||||
At most one step can be in_progress at a time.
|
||||
"#
|
||||
.to_string(),
|
||||
@@ -60,7 +61,7 @@ At most one step can be in_progress at a time.
|
||||
});
|
||||
|
||||
#[async_trait]
|
||||
impl ToolHandler for PlanHandler {
|
||||
impl ToolHandler for TodoWriteHandler {
|
||||
fn kind(&self) -> ToolKind {
|
||||
ToolKind::Function
|
||||
}
|
||||
@@ -78,13 +79,13 @@ impl ToolHandler for PlanHandler {
|
||||
ToolPayload::Function { arguments } => arguments,
|
||||
_ => {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"update_plan handler received unsupported payload".to_string(),
|
||||
"todo_write handler received unsupported payload".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let content =
|
||||
handle_update_plan(session.as_ref(), turn.as_ref(), arguments, call_id).await?;
|
||||
handle_todo_write(session.as_ref(), turn.as_ref(), arguments, call_id).await?;
|
||||
|
||||
Ok(ToolOutput::Function {
|
||||
content,
|
||||
@@ -94,23 +95,24 @@ impl ToolHandler for PlanHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// This function doesn't do anything useful. However, it gives the model a structured way to record its plan that clients can read and render.
|
||||
/// So it's the _inputs_ to this function that are useful to clients, not the outputs and neither are actually useful for the model other
|
||||
/// than forcing it to come up and document a plan (TBD how that affects performance).
|
||||
pub(crate) async fn handle_update_plan(
|
||||
/// This function doesn't do anything useful. However, it gives the model a structured way to record
|
||||
/// its task list that clients can read and render. So it's the _inputs_ to this function that are
|
||||
/// useful to clients, not the outputs and neither are actually useful for the model other than
|
||||
/// forcing it to come up and document a task list (TBD how that affects performance).
|
||||
pub(crate) async fn handle_todo_write(
|
||||
session: &Session,
|
||||
turn_context: &TurnContext,
|
||||
arguments: String,
|
||||
_call_id: String,
|
||||
) -> Result<String, FunctionCallError> {
|
||||
let args = parse_update_plan_arguments(&arguments)?;
|
||||
let args = parse_todo_write_arguments(&arguments)?;
|
||||
session
|
||||
.send_event(turn_context, EventMsg::PlanUpdate(args))
|
||||
.await;
|
||||
Ok("Plan updated".to_string())
|
||||
Ok("Todo list updated".to_string())
|
||||
}
|
||||
|
||||
fn parse_update_plan_arguments(arguments: &str) -> Result<UpdatePlanArgs, FunctionCallError> {
|
||||
fn parse_todo_write_arguments(arguments: &str) -> Result<UpdatePlanArgs, FunctionCallError> {
|
||||
serde_json::from_str::<UpdatePlanArgs>(arguments).map_err(|e| {
|
||||
FunctionCallError::RespondToModel(format!("failed to parse function arguments: {e}"))
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::client_common::tools::ResponsesApiTool;
|
||||
use crate::client_common::tools::ToolSpec;
|
||||
use crate::features::Feature;
|
||||
use crate::features::Features;
|
||||
use crate::tools::handlers::PLAN_TOOL;
|
||||
use crate::tools::handlers::TODO_WRITE_TOOL;
|
||||
use crate::tools::handlers::apply_patch::create_apply_patch_freeform_tool;
|
||||
use crate::tools::handlers::apply_patch::create_apply_patch_json_tool;
|
||||
use crate::tools::handlers::collab::DEFAULT_WAIT_TIMEOUT_MS;
|
||||
@@ -1273,12 +1273,12 @@ pub(crate) fn build_specs(
|
||||
use crate::tools::handlers::ListDirHandler;
|
||||
use crate::tools::handlers::McpHandler;
|
||||
use crate::tools::handlers::McpResourceHandler;
|
||||
use crate::tools::handlers::PlanHandler;
|
||||
use crate::tools::handlers::ReadFileHandler;
|
||||
use crate::tools::handlers::RequestUserInputHandler;
|
||||
use crate::tools::handlers::ShellCommandHandler;
|
||||
use crate::tools::handlers::ShellHandler;
|
||||
use crate::tools::handlers::TestSyncHandler;
|
||||
use crate::tools::handlers::TodoWriteHandler;
|
||||
use crate::tools::handlers::UnifiedExecHandler;
|
||||
use crate::tools::handlers::ViewImageHandler;
|
||||
use std::sync::Arc;
|
||||
@@ -1287,7 +1287,7 @@ pub(crate) fn build_specs(
|
||||
|
||||
let shell_handler = Arc::new(ShellHandler);
|
||||
let unified_exec_handler = Arc::new(UnifiedExecHandler);
|
||||
let plan_handler = Arc::new(PlanHandler);
|
||||
let todo_handler = Arc::new(TodoWriteHandler);
|
||||
let apply_patch_handler = Arc::new(ApplyPatchHandler);
|
||||
let dynamic_tool_handler = Arc::new(DynamicToolHandler);
|
||||
let view_image_handler = Arc::new(ViewImageHandler);
|
||||
@@ -1332,8 +1332,10 @@ pub(crate) fn build_specs(
|
||||
builder.register_handler("list_mcp_resource_templates", mcp_resource_handler.clone());
|
||||
builder.register_handler("read_mcp_resource", mcp_resource_handler);
|
||||
|
||||
builder.push_spec(PLAN_TOOL.clone());
|
||||
builder.register_handler("update_plan", plan_handler);
|
||||
builder.push_spec(TODO_WRITE_TOOL.clone());
|
||||
builder.register_handler("todo_write", todo_handler.clone());
|
||||
// TODO: Remove this backward-compat alias once clients migrate; it is the old tool name, not Plan mode.
|
||||
builder.register_handler("update_plan", todo_handler);
|
||||
|
||||
if config.collaboration_modes_tools {
|
||||
builder.push_spec(create_request_user_input_tool());
|
||||
@@ -1590,7 +1592,7 @@ mod tests {
|
||||
create_list_mcp_resources_tool(),
|
||||
create_list_mcp_resource_templates_tool(),
|
||||
create_read_mcp_resource_tool(),
|
||||
PLAN_TOOL.clone(),
|
||||
TODO_WRITE_TOOL.clone(),
|
||||
create_request_user_input_tool(),
|
||||
create_apply_patch_freeform_tool(),
|
||||
ToolSpec::WebSearch {
|
||||
@@ -1737,7 +1739,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
@@ -1759,7 +1761,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
@@ -1783,7 +1785,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
@@ -1807,7 +1809,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
@@ -1829,7 +1831,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"web_search",
|
||||
"view_image",
|
||||
@@ -1850,7 +1852,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
@@ -1872,7 +1874,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"web_search",
|
||||
"view_image",
|
||||
@@ -1893,7 +1895,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
@@ -1916,7 +1918,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
@@ -1940,7 +1942,7 @@ mod tests {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"web_search",
|
||||
"view_image",
|
||||
@@ -1962,7 +1964,7 @@ mod tests {
|
||||
let (tools, _) = build_specs(&tools_config, Some(HashMap::new()), &[]).build();
|
||||
|
||||
// Only check the shell variant and a couple of core tools.
|
||||
let mut subset = vec!["exec_command", "write_stdin", "update_plan"];
|
||||
let mut subset = vec!["exec_command", "write_stdin", "todo_write"];
|
||||
if let Some(shell_tool) = shell_tool_name(&tools_config) {
|
||||
subset.push(shell_tool);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ async fn model_selects_expected_tools() {
|
||||
"list_mcp_resources".to_string(),
|
||||
"list_mcp_resource_templates".to_string(),
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"todo_write".to_string(),
|
||||
"request_user_input".to_string(),
|
||||
"web_search".to_string(),
|
||||
"view_image".to_string()
|
||||
@@ -81,7 +81,7 @@ async fn model_selects_expected_tools() {
|
||||
"list_mcp_resources".to_string(),
|
||||
"list_mcp_resource_templates".to_string(),
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"todo_write".to_string(),
|
||||
"request_user_input".to_string(),
|
||||
"apply_patch".to_string(),
|
||||
"web_search".to_string(),
|
||||
@@ -98,7 +98,7 @@ async fn model_selects_expected_tools() {
|
||||
"list_mcp_resources".to_string(),
|
||||
"list_mcp_resource_templates".to_string(),
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"todo_write".to_string(),
|
||||
"request_user_input".to_string(),
|
||||
"apply_patch".to_string(),
|
||||
"web_search".to_string(),
|
||||
@@ -115,7 +115,7 @@ async fn model_selects_expected_tools() {
|
||||
"list_mcp_resources".to_string(),
|
||||
"list_mcp_resource_templates".to_string(),
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"todo_write".to_string(),
|
||||
"request_user_input".to_string(),
|
||||
"web_search".to_string(),
|
||||
"view_image".to_string()
|
||||
@@ -131,7 +131,7 @@ async fn model_selects_expected_tools() {
|
||||
"list_mcp_resources".to_string(),
|
||||
"list_mcp_resource_templates".to_string(),
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"todo_write".to_string(),
|
||||
"request_user_input".to_string(),
|
||||
"apply_patch".to_string(),
|
||||
"web_search".to_string(),
|
||||
@@ -148,7 +148,7 @@ async fn model_selects_expected_tools() {
|
||||
"list_mcp_resources".to_string(),
|
||||
"list_mcp_resource_templates".to_string(),
|
||||
"read_mcp_resource".to_string(),
|
||||
"update_plan".to_string(),
|
||||
"todo_write".to_string(),
|
||||
"request_user_input".to_string(),
|
||||
"apply_patch".to_string(),
|
||||
"web_search".to_string(),
|
||||
|
||||
@@ -136,7 +136,7 @@ async fn prompt_tools_are_consistent_across_requests() -> anyhow::Result<()> {
|
||||
"list_mcp_resources",
|
||||
"list_mcp_resource_templates",
|
||||
"read_mcp_resource",
|
||||
"update_plan",
|
||||
"todo_write",
|
||||
"request_user_input",
|
||||
"apply_patch",
|
||||
"web_search",
|
||||
|
||||
@@ -108,7 +108,7 @@ async fn shell_tool_executes_command_and_streams_output() -> anyhow::Result<()>
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
|
||||
async fn todo_write_tool_emits_todo_update_event() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
@@ -121,8 +121,8 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
|
||||
..
|
||||
} = builder.build(&server).await?;
|
||||
|
||||
let call_id = "plan-tool-call";
|
||||
let plan_args = json!({
|
||||
let call_id = "todo-tool-call";
|
||||
let todo_args = json!({
|
||||
"explanation": "Tool harness check",
|
||||
"plan": [
|
||||
{"step": "Inspect workspace", "status": "in_progress"},
|
||||
@@ -133,13 +133,13 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
|
||||
|
||||
let first_response = sse(vec![
|
||||
ev_response_created("resp-1"),
|
||||
ev_function_call(call_id, "update_plan", &plan_args),
|
||||
ev_function_call(call_id, "todo_write", &todo_args),
|
||||
ev_completed("resp-1"),
|
||||
]);
|
||||
responses::mount_sse_once(&server, first_response).await;
|
||||
|
||||
let second_response = sse(vec![
|
||||
ev_assistant_message("msg-1", "plan acknowledged"),
|
||||
ev_assistant_message("msg-1", "todo list acknowledged"),
|
||||
ev_completed("resp-2"),
|
||||
]);
|
||||
let second_mock = responses::mount_sse_once(&server, second_response).await;
|
||||
@@ -149,7 +149,7 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
|
||||
codex
|
||||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please update the plan".into(),
|
||||
text: "please update the todo list".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
@@ -164,10 +164,10 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut saw_plan_update = false;
|
||||
let mut saw_todo_update = false;
|
||||
wait_for_event(&codex, |event| match event {
|
||||
EventMsg::PlanUpdate(update) => {
|
||||
saw_plan_update = true;
|
||||
saw_todo_update = true;
|
||||
assert_eq!(update.explanation.as_deref(), Some("Tool harness check"));
|
||||
assert_eq!(update.plan.len(), 2);
|
||||
assert_eq!(update.plan[0].step, "Inspect workspace");
|
||||
@@ -181,17 +181,17 @@ async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()> {
|
||||
})
|
||||
.await;
|
||||
|
||||
assert!(saw_plan_update, "expected PlanUpdate event");
|
||||
assert!(saw_todo_update, "expected PlanUpdate (todo list) event");
|
||||
|
||||
let req = second_mock.single_request();
|
||||
let (output_text, _success_flag) = call_output(&req, call_id);
|
||||
assert_eq!(output_text, "Plan updated");
|
||||
assert_eq!(output_text, "Todo list updated");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> {
|
||||
async fn todo_write_tool_rejects_malformed_payload() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
@@ -204,21 +204,21 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> {
|
||||
..
|
||||
} = builder.build(&server).await?;
|
||||
|
||||
let call_id = "plan-tool-invalid";
|
||||
let call_id = "todo-tool-invalid";
|
||||
let invalid_args = json!({
|
||||
"explanation": "Missing plan data"
|
||||
"explanation": "Missing todo data"
|
||||
})
|
||||
.to_string();
|
||||
|
||||
let first_response = sse(vec![
|
||||
ev_response_created("resp-1"),
|
||||
ev_function_call(call_id, "update_plan", &invalid_args),
|
||||
ev_function_call(call_id, "todo_write", &invalid_args),
|
||||
ev_completed("resp-1"),
|
||||
]);
|
||||
responses::mount_sse_once(&server, first_response).await;
|
||||
|
||||
let second_response = sse(vec![
|
||||
ev_assistant_message("msg-1", "malformed plan payload"),
|
||||
ev_assistant_message("msg-1", "malformed todo payload"),
|
||||
ev_completed("resp-2"),
|
||||
]);
|
||||
let second_mock = responses::mount_sse_once(&server, second_response).await;
|
||||
@@ -228,7 +228,7 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> {
|
||||
codex
|
||||
.submit(Op::UserTurn {
|
||||
items: vec![UserInput::Text {
|
||||
text: "please update the plan".into(),
|
||||
text: "please update the todo list".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
@@ -243,10 +243,10 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> {
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut saw_plan_update = false;
|
||||
let mut saw_todo_update = false;
|
||||
wait_for_event(&codex, |event| match event {
|
||||
EventMsg::PlanUpdate(_) => {
|
||||
saw_plan_update = true;
|
||||
saw_todo_update = true;
|
||||
false
|
||||
}
|
||||
EventMsg::TurnComplete(_) => true,
|
||||
@@ -255,8 +255,8 @@ async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()> {
|
||||
.await;
|
||||
|
||||
assert!(
|
||||
!saw_plan_update,
|
||||
"did not expect PlanUpdate event for malformed payload"
|
||||
!saw_todo_update,
|
||||
"did not expect PlanUpdate (todo list) event for malformed payload"
|
||||
);
|
||||
|
||||
let req = second_mock.single_request();
|
||||
|
||||
@@ -538,11 +538,14 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
ts_msg!(self, "model: {}", model);
|
||||
eprintln!();
|
||||
}
|
||||
EventMsg::PlanUpdate(plan_update_event) => {
|
||||
let UpdatePlanArgs { explanation, plan } = plan_update_event;
|
||||
EventMsg::PlanUpdate(todo_update_event) => {
|
||||
let UpdatePlanArgs {
|
||||
explanation,
|
||||
plan: todo_items,
|
||||
} = todo_update_event;
|
||||
|
||||
// Header
|
||||
ts_msg!(self, "{}", "Plan update".style(self.magenta));
|
||||
ts_msg!(self, "{}", "Todo list update".style(self.magenta));
|
||||
|
||||
// Optional explanation
|
||||
if let Some(explanation) = explanation
|
||||
@@ -551,21 +554,21 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
ts_msg!(self, "{}", explanation.style(self.italic));
|
||||
}
|
||||
|
||||
// Pretty-print the plan items with simple status markers.
|
||||
for item in plan {
|
||||
match item.status {
|
||||
// Pretty-print the todo items with simple status markers.
|
||||
for todo_item in todo_items {
|
||||
match todo_item.status {
|
||||
StepStatus::Completed => {
|
||||
ts_msg!(self, " {} {}", "✓".style(self.green), item.step);
|
||||
ts_msg!(self, " {} {}", "✓".style(self.green), todo_item.step);
|
||||
}
|
||||
StepStatus::InProgress => {
|
||||
ts_msg!(self, " {} {}", "→".style(self.cyan), item.step);
|
||||
ts_msg!(self, " {} {}", "→".style(self.cyan), todo_item.step);
|
||||
}
|
||||
StepStatus::Pending => {
|
||||
ts_msg!(
|
||||
self,
|
||||
" {} {}",
|
||||
"•".style(self.dimmed),
|
||||
item.step.style(self.dimmed)
|
||||
todo_item.step.style(self.dimmed)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ impl EventProcessorWithJsonOutput {
|
||||
};
|
||||
vec![ThreadEvent::Error(ThreadErrorEvent { message })]
|
||||
}
|
||||
protocol::EventMsg::PlanUpdate(ev) => self.handle_plan_update(ev),
|
||||
protocol::EventMsg::PlanUpdate(ev) => self.handle_todo_update(ev),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -700,7 +700,7 @@ impl EventProcessorWithJsonOutput {
|
||||
vec![ThreadEvent::ItemCompleted(ItemCompletedEvent { item })]
|
||||
}
|
||||
|
||||
fn todo_items_from_plan(&self, args: &UpdatePlanArgs) -> Vec<TodoItem> {
|
||||
fn todo_items_from_update(&self, args: &UpdatePlanArgs) -> Vec<TodoItem> {
|
||||
args.plan
|
||||
.iter()
|
||||
.map(|p| TodoItem {
|
||||
@@ -710,8 +710,8 @@ impl EventProcessorWithJsonOutput {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn handle_plan_update(&mut self, args: &UpdatePlanArgs) -> Vec<ThreadEvent> {
|
||||
let items = self.todo_items_from_plan(args);
|
||||
fn handle_todo_update(&mut self, args: &UpdatePlanArgs) -> Vec<ThreadEvent> {
|
||||
let items = self.todo_items_from_update(args);
|
||||
|
||||
if let Some(running) = &mut self.running_todo_list {
|
||||
running.items = items.clone();
|
||||
|
||||
@@ -220,10 +220,10 @@ fn web_search_begin_then_end_reuses_item_id() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plan_update_emits_todo_list_started_updated_and_completed() {
|
||||
fn todo_update_emits_todo_list_started_updated_and_completed() {
|
||||
let mut ep = EventProcessorWithJsonOutput::new(None);
|
||||
|
||||
// First plan update => item.started (todo_list)
|
||||
// First todo_write update (PlanUpdate event) => item.started (todo_list)
|
||||
let first = event(
|
||||
"p1",
|
||||
EventMsg::PlanUpdate(UpdatePlanArgs {
|
||||
@@ -262,7 +262,7 @@ fn plan_update_emits_todo_list_started_updated_and_completed() {
|
||||
})]
|
||||
);
|
||||
|
||||
// Second plan update in same turn => item.updated (same id)
|
||||
// Second todo_write update (PlanUpdate event) => item.updated (same id)
|
||||
let second = event(
|
||||
"p2",
|
||||
EventMsg::PlanUpdate(UpdatePlanArgs {
|
||||
@@ -660,7 +660,7 @@ fn collab_wait_end_without_begin_synthesizes_failed_item() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plan_update_after_complete_starts_new_todo_list_with_new_id() {
|
||||
fn todo_update_after_complete_starts_new_todo_list_with_new_id() {
|
||||
let mut ep = EventProcessorWithJsonOutput::new(None);
|
||||
|
||||
// First turn: start + complete
|
||||
|
||||
@@ -3,7 +3,8 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use ts_rs::TS;
|
||||
|
||||
// Types for the TODO tool arguments matching codex-vscode/todo-mcp/src/main.rs
|
||||
// Types for the todo_write tool arguments matching codex-vscode/todo-mcp/src/main.rs.
|
||||
// The plan-named types and fields are legacy payload shape and are not related to Plan mode.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StepStatus {
|
||||
@@ -24,5 +25,6 @@ pub struct PlanItemArg {
|
||||
pub struct UpdatePlanArgs {
|
||||
#[serde(default)]
|
||||
pub explanation: Option<String>,
|
||||
/// Legacy field name for todo items; kept for compatibility.
|
||||
pub plan: Vec<PlanItemArg>,
|
||||
}
|
||||
|
||||
@@ -49,29 +49,29 @@ Before making tool calls, send a brief preamble to the user explaining what you
|
||||
- “Alright, build pipeline order is interesting. Checking how it reports failures.”
|
||||
- “Spotted a clever caching util; now hunting where it gets used.”
|
||||
|
||||
## Planning
|
||||
## Todo Lists
|
||||
|
||||
You have access to an `update_plan` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. Plans can help to make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good plan should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
You have access to a `todo_write` tool which tracks steps and progress and renders them to the user. Using the tool helps demonstrate that you've understood the task and convey how you're approaching it. A todo list can help make complex, ambiguous, or multi-phase work clearer and more collaborative for the user. A good todo list should break the task into meaningful, logically ordered steps that are easy to verify as you go.
|
||||
|
||||
Note that plans are not for padding out simple work with filler steps or stating the obvious. The content of your plan should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use plans for simple or single-step queries that you can just do or answer immediately.
|
||||
Note that todo lists are not for padding out simple work with filler steps or stating the obvious. The content of your list should not involve doing anything that you aren't capable of doing (i.e. don't try to test things that you can't test). Do not use todo lists for simple or single-step queries that you can just do or answer immediately.
|
||||
|
||||
Do not repeat the full contents of the plan after an `update_plan` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
Do not repeat the full contents of the todo list after a `todo_write` call — the harness already displays it. Instead, summarize the change made and highlight any important context or next step.
|
||||
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your plan after a single pass of implementation. If this is the case, you can simply mark all the planned steps as completed. Sometimes, you may need to change plans in the middle of a task: call `update_plan` with the updated plan and make sure to provide an `explanation` of the rationale when doing so.
|
||||
Before running a command, consider whether or not you have completed the previous step, and make sure to mark it as completed before moving on to the next step. It may be the case that you complete all steps in your todo list after a single pass of implementation. If this is the case, you can simply mark all the steps as completed. Sometimes, you may need to change your list in the middle of a task: call `todo_write` with the updated list and make sure to provide an `explanation` of the rationale when doing so.
|
||||
|
||||
Use a plan when:
|
||||
Use a todo list when:
|
||||
|
||||
- The task is non-trivial and will require multiple actions over a long time horizon.
|
||||
- There are logical phases or dependencies where sequencing matters.
|
||||
- The work has ambiguity that benefits from outlining high-level goals.
|
||||
- You want intermediate checkpoints for feedback and validation.
|
||||
- When the user asked you to do more than one thing in a single prompt
|
||||
- The user has asked you to use the plan tool (aka "TODOs")
|
||||
- You generate additional steps while working, and plan to do them before yielding to the user
|
||||
- The user has asked you to use the todo tool
|
||||
- You generate additional steps while working, and intend to do them before yielding to the user
|
||||
|
||||
### Examples
|
||||
|
||||
**High-quality plans**
|
||||
**High-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -98,7 +98,7 @@ Example 3:
|
||||
5. Persist messages in lightweight DB
|
||||
6. Add typing indicators + unread count
|
||||
|
||||
**Low-quality plans**
|
||||
**Low-quality todo lists**
|
||||
|
||||
Example 1:
|
||||
|
||||
@@ -264,12 +264,12 @@ When using the shell, you must adhere to the following guidelines:
|
||||
- When searching for text or files, prefer using `rg` or `rg --files` respectively because `rg` is much faster than alternatives like `grep`. (If the `rg` command is not found, then use alternatives.)
|
||||
- Do not use python scripts to attempt to output larger chunks of a file.
|
||||
|
||||
## `update_plan`
|
||||
## `todo_write`
|
||||
|
||||
A tool named `update_plan` is available to you. You can use it to keep an up‑to‑date, step‑by‑step plan for the task.
|
||||
A tool named `todo_write` is available to you. You can use it to keep an up‑to‑date, step‑by‑step todo list for the task.
|
||||
|
||||
To create a new plan, call `update_plan` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
To create a new todo list, call `todo_write` with a short list of 1‑sentence steps (no more than 5-7 words each) with a `status` for each step (`pending`, `in_progress`, or `completed`).
|
||||
|
||||
When steps have been completed, use `update_plan` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `update_plan` call.
|
||||
When steps have been completed, use `todo_write` to mark each finished step as `completed` and the next step you are working on as `in_progress`. There should always be exactly one `in_progress` step until everything is done. You can mark multiple items as complete in a single `todo_write` call.
|
||||
|
||||
If all steps are complete, ensure you call `update_plan` to mark all steps as `completed`.
|
||||
If all steps are complete, ensure you call `todo_write` to mark all steps as `completed`.
|
||||
|
||||
@@ -810,6 +810,7 @@ pub enum EventMsg {
|
||||
/// Notification that skill data may have been updated and clients may want to reload.
|
||||
SkillsUpdateAvailable,
|
||||
|
||||
/// Todo list update from the todo_write tool. The plan name is legacy.
|
||||
PlanUpdate(UpdatePlanArgs),
|
||||
|
||||
TurnAborted(TurnAbortedEvent),
|
||||
|
||||
@@ -519,8 +519,8 @@ pub(crate) struct ChatWidget {
|
||||
// This gates rendering of the "Worked for …" separator so purely conversational turns don't
|
||||
// show an empty divider. It is reset when the separator is emitted.
|
||||
had_work_activity: bool,
|
||||
// Whether the current turn emitted a plan update.
|
||||
saw_plan_update_this_turn: bool,
|
||||
// Whether the current turn emitted a todo list update.
|
||||
saw_todo_update_this_turn: bool,
|
||||
// Status-indicator elapsed seconds captured at the last emitted final-message separator.
|
||||
//
|
||||
// This lets the separator show per-chunk work time (since the previous separator) rather than
|
||||
@@ -911,7 +911,7 @@ impl ChatWidget {
|
||||
|
||||
fn on_task_started(&mut self) {
|
||||
self.agent_turn_running = true;
|
||||
self.saw_plan_update_this_turn = false;
|
||||
self.saw_todo_update_this_turn = false;
|
||||
self.bottom_pane.clear_quit_shortcut_hint();
|
||||
self.quit_shortcut_expires_at = None;
|
||||
self.quit_shortcut_key = None;
|
||||
@@ -962,7 +962,7 @@ impl ChatWidget {
|
||||
return;
|
||||
}
|
||||
let has_message = last_agent_message.is_some_and(|message| !message.trim().is_empty());
|
||||
if !has_message && !self.saw_plan_update_this_turn {
|
||||
if !has_message && !self.saw_todo_update_this_turn {
|
||||
return;
|
||||
}
|
||||
if !self.bottom_pane.no_modal_or_popup_active() {
|
||||
@@ -1324,9 +1324,9 @@ impl ChatWidget {
|
||||
Some(combined)
|
||||
}
|
||||
|
||||
fn on_plan_update(&mut self, update: UpdatePlanArgs) {
|
||||
self.saw_plan_update_this_turn = true;
|
||||
self.add_to_history(history_cell::new_plan_update(update));
|
||||
fn on_todo_update(&mut self, update: UpdatePlanArgs) {
|
||||
self.saw_todo_update_this_turn = true;
|
||||
self.add_to_history(history_cell::new_todo_update(update));
|
||||
}
|
||||
|
||||
fn on_exec_approval_request(&mut self, id: String, ev: ExecApprovalRequestEvent) {
|
||||
@@ -2093,7 +2093,7 @@ impl ChatWidget {
|
||||
pre_review_token_info: None,
|
||||
needs_final_message_separator: false,
|
||||
had_work_activity: false,
|
||||
saw_plan_update_this_turn: false,
|
||||
saw_todo_update_this_turn: false,
|
||||
last_separator_elapsed_secs: None,
|
||||
last_rendered_width: std::cell::Cell::new(None),
|
||||
feedback,
|
||||
@@ -2221,7 +2221,7 @@ impl ChatWidget {
|
||||
retry_status_header: None,
|
||||
thread_id: None,
|
||||
forked_from: None,
|
||||
saw_plan_update_this_turn: false,
|
||||
saw_todo_update_this_turn: false,
|
||||
queued_user_messages: VecDeque::new(),
|
||||
show_welcome_banner: is_first_run,
|
||||
suppress_session_configured_redraw: false,
|
||||
@@ -2358,7 +2358,7 @@ impl ChatWidget {
|
||||
pre_review_token_info: None,
|
||||
needs_final_message_separator: false,
|
||||
had_work_activity: false,
|
||||
saw_plan_update_this_turn: false,
|
||||
saw_todo_update_this_turn: false,
|
||||
last_separator_elapsed_secs: None,
|
||||
last_rendered_width: std::cell::Cell::new(None),
|
||||
feedback,
|
||||
@@ -3099,7 +3099,7 @@ impl ChatWidget {
|
||||
self.on_interrupted_turn(ev.reason);
|
||||
}
|
||||
},
|
||||
EventMsg::PlanUpdate(update) => self.on_plan_update(update),
|
||||
EventMsg::PlanUpdate(update) => self.on_todo_update(update),
|
||||
EventMsg::ExecApprovalRequest(ev) => {
|
||||
// For replayed events, synthesize an empty id (these should not occur).
|
||||
self.on_exec_approval_request(id.unwrap_or_default(), ev)
|
||||
|
||||
@@ -833,7 +833,7 @@ async fn make_chatwidget_manual(
|
||||
pre_review_token_info: None,
|
||||
needs_final_message_separator: false,
|
||||
had_work_activity: false,
|
||||
saw_plan_update_this_turn: false,
|
||||
saw_todo_update_this_turn: false,
|
||||
last_separator_elapsed_secs: None,
|
||||
last_rendered_width: std::cell::Cell::new(None),
|
||||
feedback: codex_feedback::CodexFeedback::new(),
|
||||
@@ -1270,7 +1270,7 @@ async fn plan_implementation_popup_skips_when_messages_queued() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plan_implementation_popup_shows_on_plan_update_without_message() {
|
||||
async fn plan_implementation_popup_shows_on_todo_update_without_message() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
@@ -1279,7 +1279,7 @@ async fn plan_implementation_popup_shows_on_plan_update_without_message() {
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
chat.on_task_started();
|
||||
chat.on_plan_update(UpdatePlanArgs {
|
||||
chat.on_todo_update(UpdatePlanArgs {
|
||||
explanation: None,
|
||||
plan: vec![PlanItemArg {
|
||||
step: "First".to_string(),
|
||||
@@ -1291,7 +1291,7 @@ async fn plan_implementation_popup_shows_on_plan_update_without_message() {
|
||||
let popup = render_bottom_popup(&chat, 80);
|
||||
assert!(
|
||||
popup.contains(PLAN_IMPLEMENTATION_TITLE),
|
||||
"expected plan popup after plan update, got {popup:?}"
|
||||
"expected implementation popup after todo list update, got {popup:?}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1307,7 +1307,7 @@ async fn plan_implementation_popup_skips_when_rate_limit_prompt_pending() {
|
||||
chat.set_collaboration_mask(plan_mask);
|
||||
|
||||
chat.on_task_started();
|
||||
chat.on_plan_update(UpdatePlanArgs {
|
||||
chat.on_todo_update(UpdatePlanArgs {
|
||||
explanation: None,
|
||||
plan: vec![PlanItemArg {
|
||||
step: "First".to_string(),
|
||||
@@ -4404,7 +4404,7 @@ async fn apply_patch_request_shows_diff_summary() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn plan_update_renders_history_cell() {
|
||||
async fn todo_update_renders_history_cell() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
let update = UpdatePlanArgs {
|
||||
explanation: Some("Adapting plan".to_string()),
|
||||
@@ -4428,11 +4428,14 @@ async fn plan_update_renders_history_cell() {
|
||||
msg: EventMsg::PlanUpdate(update),
|
||||
});
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
assert!(!cells.is_empty(), "expected plan update cell to be sent");
|
||||
assert!(
|
||||
!cells.is_empty(),
|
||||
"expected todo list update cell to be sent"
|
||||
);
|
||||
let blob = lines_to_single_string(cells.last().unwrap());
|
||||
assert!(
|
||||
blob.contains("Updated Plan"),
|
||||
"missing plan header: {blob:?}"
|
||||
blob.contains("Updated Todo List"),
|
||||
"missing todo list header: {blob:?}"
|
||||
);
|
||||
assert!(blob.contains("Explore codebase"));
|
||||
assert!(blob.contains("Implement feature"));
|
||||
|
||||
@@ -1727,19 +1727,25 @@ pub(crate) fn new_error_event(message: String) -> PlainHistoryCell {
|
||||
PlainHistoryCell { lines }
|
||||
}
|
||||
|
||||
/// Render a user‑friendly plan update styled like a checkbox todo list.
|
||||
pub(crate) fn new_plan_update(update: UpdatePlanArgs) -> PlanUpdateCell {
|
||||
let UpdatePlanArgs { explanation, plan } = update;
|
||||
PlanUpdateCell { explanation, plan }
|
||||
/// Render a user-friendly todo list update styled like a checkbox list.
|
||||
pub(crate) fn new_todo_update(update: UpdatePlanArgs) -> TodoUpdateCell {
|
||||
let UpdatePlanArgs {
|
||||
explanation,
|
||||
plan: todo_items,
|
||||
} = update;
|
||||
TodoUpdateCell {
|
||||
explanation,
|
||||
todo_items,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PlanUpdateCell {
|
||||
pub(crate) struct TodoUpdateCell {
|
||||
explanation: Option<String>,
|
||||
plan: Vec<PlanItemArg>,
|
||||
todo_items: Vec<PlanItemArg>,
|
||||
}
|
||||
|
||||
impl HistoryCell for PlanUpdateCell {
|
||||
impl HistoryCell for TodoUpdateCell {
|
||||
fn display_lines(&self, width: u16) -> Vec<Line<'static>> {
|
||||
let render_note = |text: &str| -> Vec<Line<'static>> {
|
||||
let wrap_width = width.saturating_sub(4).max(1) as usize;
|
||||
@@ -1768,7 +1774,7 @@ impl HistoryCell for PlanUpdateCell {
|
||||
};
|
||||
|
||||
let mut lines: Vec<Line<'static>> = vec![];
|
||||
lines.push(vec!["• ".dim(), "Updated Plan".bold()].into());
|
||||
lines.push(vec!["• ".dim(), "Updated Todo List".bold()].into());
|
||||
|
||||
let mut indented_lines = vec![];
|
||||
let note = self
|
||||
@@ -1780,10 +1786,10 @@ impl HistoryCell for PlanUpdateCell {
|
||||
indented_lines.extend(render_note(expl));
|
||||
};
|
||||
|
||||
if self.plan.is_empty() {
|
||||
if self.todo_items.is_empty() {
|
||||
indented_lines.push(Line::from("(no steps provided)".dim().italic()));
|
||||
} else {
|
||||
for PlanItemArg { step, status } in self.plan.iter() {
|
||||
for PlanItemArg { step, status } in self.todo_items.iter() {
|
||||
indented_lines.extend(render_step(status, step));
|
||||
}
|
||||
}
|
||||
@@ -2910,7 +2916,7 @@ mod tests {
|
||||
],
|
||||
};
|
||||
|
||||
let cell = new_plan_update(update);
|
||||
let cell = new_todo_update(update);
|
||||
// Narrow width to force wrapping for both the note and steps
|
||||
let lines = cell.display_lines(32);
|
||||
let rendered = render_lines(&lines).join("\n");
|
||||
@@ -2933,7 +2939,7 @@ mod tests {
|
||||
],
|
||||
};
|
||||
|
||||
let cell = new_plan_update(update);
|
||||
let cell = new_todo_update(update);
|
||||
let lines = cell.display_lines(40);
|
||||
let rendered = render_lines(&lines).join("\n");
|
||||
insta::assert_snapshot!(rendered);
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
source: tui/src/history_cell.rs
|
||||
expression: rendered
|
||||
---
|
||||
• Updated Plan
|
||||
• Updated Todo List
|
||||
└ I’ll update Grafana call
|
||||
error handling by adding
|
||||
retries and clearer
|
||||
|
||||
+1
-1
@@ -2,6 +2,6 @@
|
||||
source: tui/src/history_cell.rs
|
||||
expression: rendered
|
||||
---
|
||||
• Updated Plan
|
||||
• Updated Todo List
|
||||
└ □ Define error taxonomy
|
||||
□ Implement mapping to user messages
|
||||
|
||||
Reference in New Issue
Block a user