chore(vendor): import pi 1.3.0

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
2026-10-15 17:58:48 -04:00
parent 719370c8f7
commit 6081e3e325
1743 changed files with 205460 additions and 131705 deletions

View File

@@ -1,6 +1,6 @@
import type { AgentTool } from "@earendil-works/pi-agent-core";
import { fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import type { ExtensionAPI, InputEvent } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
import { afterEach, describe, expect, it } from "vitest";
import { createHarness, getAssistantTexts, getMessageText, getUserTexts, type Harness } from "./harness.ts";
@@ -155,6 +155,54 @@ describe("AgentSession queue characterization", () => {
expect(getAssistantTexts(harness)).toContain("follow-up response");
});
// Regression test for #8718.
it("runs direct steering and follow-up messages through input handlers", async () => {
const inputEvents: Array<Pick<InputEvent, "text" | "source" | "streamingBehavior">> = [];
const waiting = await createWaitingHarness({
extensionFactories: [
(pi) => {
pi.on("input", (event) => {
inputEvents.push({
text: event.text,
source: event.source,
streamingBehavior: event.streamingBehavior,
});
if (event.text.startsWith("handle")) return { action: "handled" };
return { action: "transform", text: `transformed: ${event.text}` };
});
},
],
});
const { harness, waitForToolStart, promptPromise, releaseToolExecution } = waiting;
harnesses.push(harness);
harness.setResponses([
fauxAssistantMessage(fauxToolCall("wait", {}), { stopReason: "toolUse" }),
fauxAssistantMessage("steered"),
fauxAssistantMessage("followed up"),
]);
await waitForToolStart;
inputEvents.length = 0;
try {
await harness.session.steer("steer me", undefined, { source: "rpc" });
await harness.session.steer("handle steer", undefined, { source: "rpc" });
await harness.session.followUp("follow me", undefined, { source: "rpc" });
await harness.session.followUp("handle follow", undefined, { source: "rpc" });
expect(inputEvents).toEqual([
{ text: "steer me", source: "rpc", streamingBehavior: "steer" },
{ text: "handle steer", source: "rpc", streamingBehavior: "steer" },
{ text: "follow me", source: "rpc", streamingBehavior: "followUp" },
{ text: "handle follow", source: "rpc", streamingBehavior: "followUp" },
]);
expect(harness.session.getSteeringMessages()).toEqual(["transformed: steer me"]);
expect(harness.session.getFollowUpMessages()).toEqual(["transformed: follow me"]);
} finally {
releaseToolExecution();
}
await promptPromise;
});
it("delivers multiple steering messages in order in one-at-a-time mode", async () => {
const waiting = await createWaitingHarness();
const { harness, waitForToolStart, promptPromise, releaseToolExecution } = waiting;
@@ -350,7 +398,12 @@ describe("AgentSession queue characterization", () => {
await harness.session.prompt("normal prompt");
expect(sawCustomMessage).toBe(true);
expect(harness.session.messages.map((message) => message.role)).toEqual(["user", "custom", "assistant"]);
expect(harness.session.messages.map((message) => message.role)).toEqual([
"system",
"user",
"custom",
"assistant",
]);
});
it("updates pendingMessageCount and removes queued text before message_start is emitted", async () => {