-
Notifications
You must be signed in to change notification settings - Fork 162
feat(agent): pause, amend and resume a run instead of tearing it down #3386
Description
Stopping the agent throws away everything it was doing. The next query starts over from a clean planning state — the plan, the step index and the execution state are all reset. So a user who wants to redirect a run mid-flight has only one move: kill it and re-explain from the beginning.
Being able to pause, amend the goal and resume with state intact turns a teardown into a course correction.
Nearest related work: #2212 (resumable confirmations and graduated autonomy profiles) touches the same lifecycle.
Scheduling note, stated honestly: this is the lowest-priority item in this milestone. Everything else here either unblocks work the agent cannot currently do, or stops it giving a wrong answer. This one improves how a user recovers from a run they have already decided to abandon. Worth doing; not worth doing first.
Acceptance criteria
- Execution state, plan and step index survive a cancel and can be resumed.
- A resumed run accepts an amended goal without discarding completed steps.
- Cancellation stays cooperative and never leaves a partial write, demonstrated on a named scenario — a cancel arriving mid-edit — rather than asserted in general.
- Resume is available from the Agent UI and the CLI, not one only.
- Resuming into a stale world (files changed underneath) is detected and surfaced rather than silently continuing. Reuse the content-hash staleness signal from fix(tools): ambiguous old_content silently edits the first match #3377 /perf(tools): content-hash read cache for unchanged re-reads #3385 rather than inventing a third.
- The console-cancellation path's deliberately-empty result is preserved; feat(agent): say when an answer is unverified, not just when it's done #3376 has a matching exception for it.
🔍 Technical details
There are two distinct cancellation mechanisms and the design must not conflate them, because they behave differently:
_cancel_eventis checked at step boundaries, sets a timeout-flavoured final answer and breaks. The result status is then computed normally as success/failed/incomplete — it is never"cancelled".cancelled_by_console(the Agent UI Stop button) returns{"status": "cancelled", "result": ""}— deliberately empty, not a partial result.
Whichever one resume is built against, it has to be named explicitly. What neither preserves is the execution state: execution_state, current_plan, current_step, total_plan_steps and plan_iterations are all reset at the top of each process_query, so there is nothing to resume into.
The stale-world criterion is the one worth designing carefully. A resumed plan carries assumptions gathered before the pause, and if the user amended the goal because they changed something, silently continuing is worse than restarting.