resolve
Finalizes a pending action by applying or discarding it.
Source
- Entry:
packages/coding-agent/src/tools/resolve.ts - Model-facing prompt:
packages/coding-agent/src/prompts/tools/resolve.md - Key collaborators:
docs/resolve-tool-runtime.md— preview/apply runtime referencepackages/coding-agent/src/extensibility/custom-tools/loader.ts— forwards custom pending actions into the queuepackages/coding-agent/src/tools/ast-edit.ts— built-in preview producer examplepackages/coding-agent/src/session/agent-session.ts— tool-choice queue, standing resolve handler, and invoker access
Inputs
| Field | Type | Required | Description |
|---|---|---|---|
action |
`"apply" | "discard"` | Yes |
reason |
string |
Yes | Required explanation passed through to the handler. |
extra |
Record<string, unknown> |
No | Free-form metadata passed through to the handler. Plan approval uses this for data such as a title slug; preview-style actions usually ignore it. |
Outputs
- Single-shot result.
execute()returns whatever the queued or standing invoker returns, withdetailswrapped/augmented to include:actionreasonextra?sourceToolName?label?sourceResultDetails?— originalresult.detailsfrom the apply/reject callback when present
- If
discardhas no custom reject callback, or the reject callback returnsundefined, the default success payload isDiscarded: <label>. Reason: <reason>. - The TUI renderer is inline and merges call+result into one block.
Flow
- Preview-producing code can call
queueResolveHandler(...)with a label, source tool name,apply(reason, extra?)callback, and optionalreject(reason, extra?)callback. - Modes can also register a standing resolve handler through
session.setStandingResolveHandler(...);resolve.execute()consults it only when no queued invoker is active. queueResolveHandler(...)asks the session for a forcedresolvetool choice and pushes it into the tool-choice queue withpushOnce(...).- The queued entry is marked
now: true; if the model rejects that forced tool choice,onRejectedreturnsrequeue, so the reminder comes back. queueResolveHandler(...)also injects aresolve-remindersteering message:
<system-reminder>
This is a preview. Call the `resolve` tool to apply or discard these changes.
</system-reminder>
- When
resolve.execute()runs, it wraps the call inuntilAborted(...)and fetchessession.peekQueueInvoker?.() ?? session.peekStandingResolveHandler?.(). - If no invoker exists, it throws
ToolError("No pending action to resolve. Nothing to apply or discard."). - Otherwise it invokes the current handler with the full params object.
runResolveInvocation(...)builds base details fromaction,reason,extra,sourceToolName, andlabel.- For
apply, it calls the producer'sapply(reason, extra)callback. - If
applythrows,runResolveInvocation(...)callsonApplyErrorwhen present. The queued preview integration uses this to re-push the resolve directive and steering reminder so the action remains pending. Non-ToolErrorexceptions are wrapped asToolError("Apply failed: <message>"). - For
discard, it callsreject(reason, extra)when provided. If no reject callback exists or it returnsundefined,resolvefabricates the default discard message. - Before returning callback results, it merges resolve metadata into
result.detailsso renderer/UI code can show the action, label, and originating tool.
Modes / Variants
apply: runs the pending action'sapply(reason, extra?)callback and returns its content.discardwith reject callback: runsreject(reason, extra?)and returns that callback's content when non-undefined.discardwithout reject callback, or with a reject callback returningundefined: returns the built-inDiscarded: ...text payload.- Queued handler: one in-flight tool-choice queue invoker, used by preview producers such as
ast_edit. - Standing handler: long-lived mode-owned handler, used as a fallback when no queue invoker is active.
Side Effects
- Session state
- Consumes or invokes the current pending action through the session tool-choice queue or standing handler;
resolvedoes not maintain its own stack. - Adds a
resolve-remindersteering message when a queued preview is registered. - On queued apply failure, requeues the same pending action before rethrowing so the model can discard or retry instead of losing the pending preview.
- Consumes or invokes the current pending action through the session tool-choice queue or standing handler;
- User-visible prompts / interactive UI
- The visible effect depends on the preview-producing tool and the resolve renderer.
- Renderer result blocks show
Accept,Discard, orFailed, include the pending action label, and display the reason.
- Background work / cancellation
untilAborted(...)lets abort signals interrupt resolution before or while the callback awaits.
Limits & Caps
- Hidden tool:
ResolveTool.hidden = true, and normal requested-tool filtering removesresolve;createTools(...)adds it separately as a hidden tool. - Exactly one active queue invoker is consulted per call via
session.peekQueueInvoker(); if none exists, one standing handler may be consulted viasession.peekStandingResolveHandler(). - There is no independent queue depth cap in this tool; ordering follows the shared tool-choice queue and mode-owned standing handler lifecycle.
Errors
- No pending action or standing handler: throws
ToolError("No pending action to resolve. Nothing to apply or discard."). applycallback throwsToolError: the originalToolErrorpropagates.applycallback throws any other value:resolvewraps it asToolError("Apply failed: <message>")after runningonApplyErrorwhen present.rejectcallback exceptions propagate without the apply-specific wrapper.- Aborts during
untilAborted(...)surface as the underlying abort error from the utility.
Notes
reasonandextraare passed through;resolveitself does not interpret them.queueResolveHandler(...)is the canonical built-in preview integration point; custom tools usepushPendingAction(...), which the loader forwards into the same mechanism.- Standing handlers let modes accept
resolveinvocations without forcing the tool choice every turn. sourceResultDetailsis added only when the apply/reject callback returned a non-nulldetailsfield; custom pending-actiondetailsare not forwarded automatically by the loader.