Initial planning tool (#1753)
We need to optimize the prompt, but this causes the model to use the new planning_tool. <img width="765" height="110" alt="image" src="https://github.com/user-attachments/assets/45633f7f-3c85-4e60-8b80-902f1b3b508d" />
This commit is contained in:
@@ -96,3 +96,12 @@ You can invoke apply_patch like:
|
|||||||
```
|
```
|
||||||
shell {"command":["apply_patch","*** Begin Patch\n*** Add File: hello.txt\n+Hello, world!\n*** End Patch\n"]}
|
shell {"command":["apply_patch","*** Begin Patch\n*** Add File: hello.txt\n+Hello, world!\n*** End Patch\n"]}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Plan updates
|
||||||
|
|
||||||
|
A tool named `update_plan` is available. Use it to keep an up‑to‑date, step‑by‑step plan for the task so you can follow your progress. When making your plans, keep in mind that you are a deployed coding agent - `update_plan` calls should not involve doing anything that you aren't capable of doing. For example, `update_plan` calls should NEVER contain tasks to merge your own pull requests. Only stop to ask the user if you genuinely need their feedback on a change.
|
||||||
|
|
||||||
|
- At the start of the task, call `update_plan` with an initial plan: a short list of 1‑sentence steps with a `status` for each step (`pending`, `in_progress`, or `completed`). There should always be exactly one `in_progress` step until everything is done.
|
||||||
|
- Whenever you finish a step, call `update_plan` again, marking the finished step as `completed` and the next step as `in_progress`.
|
||||||
|
- If your plan needs to change, call `update_plan` with the revised steps and include an `explanation` describing the change.
|
||||||
|
- When all steps are complete, make a final `update_plan` call with all steps marked `completed`.
|
||||||
|
|||||||
@@ -293,6 +293,10 @@ impl ChatWidget<'_> {
|
|||||||
self.add_to_history(HistoryCell::new_error_event(message.clone()));
|
self.add_to_history(HistoryCell::new_error_event(message.clone()));
|
||||||
self.bottom_pane.set_task_running(false);
|
self.bottom_pane.set_task_running(false);
|
||||||
}
|
}
|
||||||
|
EventMsg::PlanUpdate(update) => {
|
||||||
|
self.add_to_history(HistoryCell::new_plan_update(update));
|
||||||
|
self.request_redraw();
|
||||||
|
}
|
||||||
EventMsg::ExecApprovalRequest(ExecApprovalRequestEvent {
|
EventMsg::ExecApprovalRequest(ExecApprovalRequestEvent {
|
||||||
call_id: _,
|
call_id: _,
|
||||||
command,
|
command,
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ use codex_common::summarize_sandbox_policy;
|
|||||||
use codex_core::WireApi;
|
use codex_core::WireApi;
|
||||||
use codex_core::config::Config;
|
use codex_core::config::Config;
|
||||||
use codex_core::model_supports_reasoning_summaries;
|
use codex_core::model_supports_reasoning_summaries;
|
||||||
|
use codex_core::plan_tool::PlanItemArg;
|
||||||
|
use codex_core::plan_tool::StepStatus;
|
||||||
|
use codex_core::plan_tool::UpdatePlanArgs;
|
||||||
use codex_core::protocol::FileChange;
|
use codex_core::protocol::FileChange;
|
||||||
use codex_core::protocol::McpInvocation;
|
use codex_core::protocol::McpInvocation;
|
||||||
use codex_core::protocol::SessionConfiguredEvent;
|
use codex_core::protocol::SessionConfiguredEvent;
|
||||||
@@ -109,6 +112,10 @@ pub(crate) enum HistoryCell {
|
|||||||
/// behaviour of `ActiveExecCommand` so the user sees *what* patch the
|
/// behaviour of `ActiveExecCommand` so the user sees *what* patch the
|
||||||
/// model wants to apply before being prompted to approve or deny it.
|
/// model wants to apply before being prompted to approve or deny it.
|
||||||
PendingPatch { view: TextBlock },
|
PendingPatch { view: TextBlock },
|
||||||
|
|
||||||
|
/// A human‑friendly rendering of the model's current plan and step
|
||||||
|
/// statuses provided via the `update_plan` tool.
|
||||||
|
PlanUpdate { view: TextBlock },
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOOL_CALL_MAX_LINES: usize = 5;
|
const TOOL_CALL_MAX_LINES: usize = 5;
|
||||||
@@ -130,6 +137,7 @@ impl HistoryCell {
|
|||||||
| HistoryCell::CompletedExecCommand { view }
|
| HistoryCell::CompletedExecCommand { view }
|
||||||
| HistoryCell::CompletedMcpToolCall { view }
|
| HistoryCell::CompletedMcpToolCall { view }
|
||||||
| HistoryCell::PendingPatch { view }
|
| HistoryCell::PendingPatch { view }
|
||||||
|
| HistoryCell::PlanUpdate { view }
|
||||||
| HistoryCell::ActiveExecCommand { view, .. }
|
| HistoryCell::ActiveExecCommand { view, .. }
|
||||||
| HistoryCell::ActiveMcpToolCall { view, .. } => {
|
| HistoryCell::ActiveMcpToolCall { view, .. } => {
|
||||||
view.lines.iter().map(line_to_static).collect()
|
view.lines.iter().map(line_to_static).collect()
|
||||||
@@ -477,6 +485,94 @@ impl HistoryCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Render a user‑friendly plan update with colourful status icons and a
|
||||||
|
/// simple progress indicator so users can follow along.
|
||||||
|
pub(crate) fn new_plan_update(update: UpdatePlanArgs) -> Self {
|
||||||
|
let UpdatePlanArgs { explanation, plan } = update;
|
||||||
|
|
||||||
|
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||||
|
|
||||||
|
// Title
|
||||||
|
lines.push(Line::from("plan".magenta().bold()));
|
||||||
|
|
||||||
|
if !plan.is_empty() {
|
||||||
|
// Progress bar – show completed/total with a visual bar
|
||||||
|
let total = plan.len();
|
||||||
|
let completed = plan
|
||||||
|
.iter()
|
||||||
|
.filter(|p| matches!(p.status, StepStatus::Completed))
|
||||||
|
.count();
|
||||||
|
let width: usize = 20;
|
||||||
|
let filled = (completed * width + total / 2) / total;
|
||||||
|
let empty = width.saturating_sub(filled);
|
||||||
|
let mut bar_spans: Vec<Span> = Vec::new();
|
||||||
|
if filled > 0 {
|
||||||
|
bar_spans.push(Span::styled(
|
||||||
|
"█".repeat(filled),
|
||||||
|
Style::default().fg(Color::Green),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if empty > 0 {
|
||||||
|
bar_spans.push(Span::styled(
|
||||||
|
"░".repeat(empty),
|
||||||
|
Style::default().fg(Color::Gray),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let progress_prefix = Span::raw("progress [");
|
||||||
|
let progress_suffix = Span::raw("] ");
|
||||||
|
let fraction = Span::raw(format!("{completed}/{total}"));
|
||||||
|
let mut progress_line_spans = vec![progress_prefix];
|
||||||
|
progress_line_spans.extend(bar_spans);
|
||||||
|
progress_line_spans.push(progress_suffix);
|
||||||
|
progress_line_spans.push(fraction);
|
||||||
|
lines.push(Line::from(progress_line_spans));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional explanation/note from the model
|
||||||
|
if let Some(expl) = explanation.and_then(|s| {
|
||||||
|
let t = s.trim().to_string();
|
||||||
|
if t.is_empty() { None } else { Some(t) }
|
||||||
|
}) {
|
||||||
|
lines.push(Line::from("note".gray().italic()));
|
||||||
|
for l in expl.lines() {
|
||||||
|
lines.push(Line::from(l.to_string()).gray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steps (1‑based numbering) with fun, readable status icons
|
||||||
|
if plan.is_empty() {
|
||||||
|
lines.push(Line::from("(no steps provided)".gray().italic()));
|
||||||
|
} else {
|
||||||
|
for (idx, PlanItemArg { step, status }) in plan.into_iter().enumerate() {
|
||||||
|
let num = idx + 1;
|
||||||
|
let (icon, style): (&str, Style) = match status {
|
||||||
|
StepStatus::Completed => ("✓", Style::default().fg(Color::Green)),
|
||||||
|
StepStatus::InProgress => (
|
||||||
|
"▶",
|
||||||
|
Style::default()
|
||||||
|
.fg(Color::Yellow)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
),
|
||||||
|
StepStatus::Pending => ("○", Style::default().fg(Color::Gray)),
|
||||||
|
};
|
||||||
|
let prefix = vec![
|
||||||
|
Span::raw(format!("{num:>2}. [")),
|
||||||
|
Span::styled(icon.to_string(), style),
|
||||||
|
Span::raw("] "),
|
||||||
|
];
|
||||||
|
let mut spans = prefix;
|
||||||
|
spans.push(Span::raw(step));
|
||||||
|
lines.push(Line::from(spans));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(Line::from(""));
|
||||||
|
|
||||||
|
HistoryCell::PlanUpdate {
|
||||||
|
view: TextBlock::new(lines),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new `PendingPatch` cell that lists the file‑level summary of
|
/// Create a new `PendingPatch` cell that lists the file‑level summary of
|
||||||
/// a proposed patch. The summary lines should already be formatted (e.g.
|
/// a proposed patch. The summary lines should already be formatted (e.g.
|
||||||
/// "A path/to/file.rs").
|
/// "A path/to/file.rs").
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ pub async fn run_main(
|
|||||||
config_profile: cli.config_profile.clone(),
|
config_profile: cli.config_profile.clone(),
|
||||||
codex_linux_sandbox_exe,
|
codex_linux_sandbox_exe,
|
||||||
base_instructions: None,
|
base_instructions: None,
|
||||||
include_plan_tool: None,
|
include_plan_tool: Some(true),
|
||||||
};
|
};
|
||||||
// Parse `-c` overrides from the CLI.
|
// Parse `-c` overrides from the CLI.
|
||||||
let cli_kv_overrides = match cli.config_overrides.parse_overrides() {
|
let cli_kv_overrides = match cli.config_overrides.parse_overrides() {
|
||||||
|
|||||||
Reference in New Issue
Block a user