fix: abort embedded prompts on cancel

This commit is contained in:
Peter Steinberger
2026-01-18 05:17:28 +00:00
parent 89c5185f1c
commit 016693a1f5
16 changed files with 128 additions and 44 deletions

View File

@@ -146,7 +146,7 @@ const readSessionMessages = async (sessionFile: string) => {
};
describe("runEmbeddedPiAgent", () => {
it("appends new user + assistant after existing transcript entries", async () => {
it("appends new user + assistant after existing transcript entries", { timeout: 90_000 }, async () => {
const { SessionManager } = await import("@mariozechner/pi-coding-agent");
const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-agent-"));
@@ -271,7 +271,7 @@ describe("runEmbeddedPiAgent", () => {
expect(firstAssistantIndex).toBeGreaterThan(firstUserIndex);
expect(secondUserIndex).toBeGreaterThan(firstAssistantIndex);
expect(secondAssistantIndex).toBeGreaterThan(secondUserIndex);
}, 20_000);
}, 90_000);
it("repairs orphaned user messages and continues", async () => {
const { SessionManager } = await import("@mariozechner/pi-coding-agent");

View File

@@ -192,7 +192,7 @@ describe("runEmbeddedPiAgent", () => {
await expect(fs.stat(path.join(agentDir, "models.json"))).resolves.toBeTruthy();
});
it("persists the first user message before assistant output", { timeout: 45_000 }, async () => {
it("persists the first user message before assistant output", { timeout: 60_000 }, async () => {
const agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-agent-"));
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-workspace-"));
const sessionFile = path.join(workspaceDir, "session.jsonl");

View File

@@ -359,6 +359,33 @@ export async function runEmbeddedAttempt(
runAbortController.abort();
void activeSession.abort();
};
const abortable = <T>(promise: Promise<T>): Promise<T> => {
const signal = runAbortController.signal;
if (signal.aborted) {
const err = new Error("aborted");
(err as { name?: string }).name = "AbortError";
return Promise.reject(err);
}
return new Promise<T>((resolve, reject) => {
const onAbort = () => {
const err = new Error("aborted");
(err as { name?: string }).name = "AbortError";
signal.removeEventListener("abort", onAbort);
reject(err);
};
signal.addEventListener("abort", onAbort, { once: true });
promise.then(
(value) => {
signal.removeEventListener("abort", onAbort);
resolve(value);
},
(err) => {
signal.removeEventListener("abort", onAbort);
reject(err);
},
);
});
};
const subscription = subscribeEmbeddedPiSession({
session: activeSession,
@@ -454,7 +481,7 @@ export async function runEmbeddedAttempt(
}
try {
await activeSession.prompt(params.prompt, { images: params.images });
await abortable(activeSession.prompt(params.prompt, { images: params.images }));
} catch (err) {
promptError = err;
} finally {