What happened?
When an extension calls ctx.abort() from an interactive tool_call confirmation, I saw two abort lifecycle problems:
- With one tool call,
Operation aborted was rendered twice. The session JSONL had one aborted toolResult, followed by an extra empty assistant message with stopReason: "aborted".
- With two tool calls in the same assistant response, choosing
Abort run on the first confirmation did not stop the batch. Pi still continued to the second pending edit confirmation.
This is related to #3830, but it is a different abort path. #3830 fixed duplicated rendering for { block: true, reason }; this repro uses ctx.abort().
Tested/reproduced on Pi 0.74.0.
Steps to reproduce
Use this extension:
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
export default function ctxAbortToolCallRepro(pi: ExtensionAPI) {
pi.on("tool_call", async (event, ctx) => {
if (!isToolCallEventType("edit", event) && !isToolCallEventType("write", event)) return undefined;
const path = event.input.path;
if (!ctx.hasUI) {
ctx.abort();
return undefined;
}
const choice = await ctx.ui.select(`Confirm ${event.toolName} ${path}`, ["Allow", "Abort run"]);
if (choice === "Allow") return undefined;
ctx.abort();
return undefined;
});
}
Problem 1, duplicate abort output:
- Ask for one edit, for example
edit test.txt and replace one word.
- Choose
Abort run in the extension UI.
- I saw
Operation aborted twice. The session JSONL had one aborted toolResult, followed by an extra empty assistant message with stopReason: "aborted".
Problem 2, abort continues pending tool calls:
- Create two test files:
printf 'a\n' > abort-edit-a.txt
printf 'b\n' > abort-edit-b.txt
- Ask the model:
Call edit twice in one response:
1. edit abort-edit-a.txt: replace "a\n" with "x\n"
2. edit abort-edit-b.txt: replace "b\n" with "y\n"
Use exactly two edit tool calls. No text.
- Choose
Abort run on the first confirmation.
- Pi still continued to the second pending edit confirmation.
Expected behavior
Calling ctx.abort() from a tool_call handler should stop the current agent run. The abort message should be rendered once, and remaining tool calls from the same assistant response should not be confirmed or executed.
Version
0.74.0
What happened?
When an extension calls
ctx.abort()from an interactivetool_callconfirmation, I saw two abort lifecycle problems:Operation abortedwas rendered twice. The session JSONL had one abortedtoolResult, followed by an extra empty assistant message withstopReason: "aborted".Abort runon the first confirmation did not stop the batch. Pi still continued to the second pending edit confirmation.This is related to #3830, but it is a different abort path. #3830 fixed duplicated rendering for
{ block: true, reason }; this repro usesctx.abort().Tested/reproduced on Pi
0.74.0.Steps to reproduce
Use this extension:
Problem 1, duplicate abort output:
edit test.txt and replace one word.Abort runin the extension UI.Operation abortedtwice. The session JSONL had one abortedtoolResult, followed by an extra empty assistant message withstopReason: "aborted".Problem 2, abort continues pending tool calls:
Abort runon the first confirmation.Expected behavior
Calling
ctx.abort()from atool_callhandler should stop the current agent run. The abort message should be rendered once, and remaining tool calls from the same assistant response should not be confirmed or executed.Version
0.74.0