git-subtree-dir: src/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
73 lines
2.6 KiB
TypeScript
73 lines
2.6 KiB
TypeScript
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
import { fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai";
|
|
import { Type } from "typebox";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { createHarness, getMessageText, type Harness } from "../harness.ts";
|
|
|
|
describe("#8935 parallel preflight abort", () => {
|
|
const harnesses: Harness[] = [];
|
|
|
|
afterEach(() => {
|
|
while (harnesses.length > 0) {
|
|
harnesses.pop()?.cleanup();
|
|
}
|
|
});
|
|
|
|
it("does not start prepared tools after a later preflight aborts", async () => {
|
|
const executions: string[] = [];
|
|
const preflights: string[] = [];
|
|
const resultHooks: string[] = [];
|
|
const externalWrite: AgentTool = {
|
|
name: "external_write",
|
|
label: "External write",
|
|
description: "Perform an external write",
|
|
parameters: Type.Object({ value: Type.String() }),
|
|
execute: async (_toolCallId, params) => {
|
|
const value =
|
|
typeof params === "object" && params !== null && "value" in params ? String(params.value) : "";
|
|
executions.push(value);
|
|
return { content: [{ type: "text", text: value }], details: { value } };
|
|
},
|
|
};
|
|
const harness = await createHarness({
|
|
tools: [externalWrite],
|
|
extensionFactories: [
|
|
(pi) => {
|
|
pi.on("tool_call", async (event, ctx) => {
|
|
const value = "value" in event.input ? String(event.input.value) : "";
|
|
preflights.push(value);
|
|
if (value === "second") ctx.abort();
|
|
});
|
|
pi.on("tool_result", async (event) => {
|
|
resultHooks.push(event.toolCallId);
|
|
});
|
|
},
|
|
],
|
|
});
|
|
harnesses.push(harness);
|
|
harness.setResponses([
|
|
fauxAssistantMessage(
|
|
[fauxToolCall("external_write", { value: "first" }), fauxToolCall("external_write", { value: "second" })],
|
|
{ stopReason: "toolUse" },
|
|
),
|
|
]);
|
|
|
|
await harness.session.prompt("run both writes");
|
|
|
|
expect(preflights).toEqual(["first", "second"]);
|
|
expect(executions).toEqual([]);
|
|
expect(resultHooks).toEqual([]);
|
|
|
|
const starts = harness.eventsOfType("tool_execution_start");
|
|
const ends = harness.eventsOfType("tool_execution_end");
|
|
expect(starts).toHaveLength(2);
|
|
expect(ends).toHaveLength(2);
|
|
expect(new Set(ends.map((event) => event.toolCallId))).toEqual(new Set(starts.map((event) => event.toolCallId)));
|
|
expect(ends.every((event) => event.isError)).toBe(true);
|
|
|
|
const toolResults = harness.session.messages.filter((message) => message.role === "toolResult");
|
|
expect(toolResults.map((message) => message.toolCallId)).toEqual(starts.map((event) => event.toolCallId));
|
|
expect(toolResults.map(getMessageText)).toEqual(["Operation aborted", "Operation aborted"]);
|
|
});
|
|
});
|