1dba122c创建于 1 天前历史提交

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 reference
    • packages/coding-agent/src/extensibility/custom-tools/loader.ts — forwards custom pending actions into the queue
    • packages/coding-agent/src/tools/ast-edit.ts — built-in preview producer example
    • packages/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, with details wrapped/augmented to include:
    • action
    • reason
    • extra?
    • sourceToolName?
    • label?
    • sourceResultDetails? — original result.details from the apply/reject callback when present
  • If discard has no custom reject callback, or the reject callback returns undefined, the default success payload is Discarded: <label>. Reason: <reason>.
  • The TUI renderer is inline and merges call+result into one block.

Flow

  1. Preview-producing code can call queueResolveHandler(...) with a label, source tool name, apply(reason, extra?) callback, and optional reject(reason, extra?) callback.
  2. Modes can also register a standing resolve handler through session.setStandingResolveHandler(...); resolve.execute() consults it only when no queued invoker is active.
  3. queueResolveHandler(...) asks the session for a forced resolve tool choice and pushes it into the tool-choice queue with pushOnce(...).
  4. The queued entry is marked now: true; if the model rejects that forced tool choice, onRejected returns requeue, so the reminder comes back.
  5. queueResolveHandler(...) also injects a resolve-reminder steering message:
<system-reminder>
This is a preview. Call the `resolve` tool to apply or discard these changes.
</system-reminder>
  1. When resolve.execute() runs, it wraps the call in untilAborted(...) and fetches session.peekQueueInvoker?.() ?? session.peekStandingResolveHandler?.().
  2. If no invoker exists, it throws ToolError("No pending action to resolve. Nothing to apply or discard.").
  3. Otherwise it invokes the current handler with the full params object.
  4. runResolveInvocation(...) builds base details from action, reason, extra, sourceToolName, and label.
  5. For apply, it calls the producer's apply(reason, extra) callback.
  6. If apply throws, runResolveInvocation(...) calls onApplyError when present. The queued preview integration uses this to re-push the resolve directive and steering reminder so the action remains pending. Non-ToolError exceptions are wrapped as ToolError("Apply failed: <message>").
  7. For discard, it calls reject(reason, extra) when provided. If no reject callback exists or it returns undefined, resolve fabricates the default discard message.
  8. Before returning callback results, it merges resolve metadata into result.details so renderer/UI code can show the action, label, and originating tool.

Modes / Variants

  • apply: runs the pending action's apply(reason, extra?) callback and returns its content.
  • discard with reject callback: runs reject(reason, extra?) and returns that callback's content when non-undefined.
  • discard without reject callback, or with a reject callback returning undefined: returns the built-in Discarded: ... 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; resolve does not maintain its own stack.
    • Adds a resolve-reminder steering 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.
  • User-visible prompts / interactive UI
    • The visible effect depends on the preview-producing tool and the resolve renderer.
    • Renderer result blocks show Accept, Discard, or Failed, 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 removes resolve; 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 via session.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.").
  • apply callback throws ToolError: the original ToolError propagates.
  • apply callback throws any other value: resolve wraps it as ToolError("Apply failed: <message>") after running onApplyError when present.
  • reject callback exceptions propagate without the apply-specific wrapper.
  • Aborts during untilAborted(...) surface as the underlying abort error from the utility.

Notes

  • reason and extra are passed through; resolve itself does not interpret them.
  • queueResolveHandler(...) is the canonical built-in preview integration point; custom tools use pushPendingAction(...), which the loader forwards into the same mechanism.
  • Standing handlers let modes accept resolve invocations without forcing the tool choice every turn.
  • sourceResultDetails is added only when the apply/reject callback returned a non-null details field; custom pending-action details are not forwarded automatically by the loader.