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

@@ -223,15 +223,18 @@ describe("AgentSession bash and persistence characterization", () => {
await harness.session.prompt("start");
const entries = harness.sessionManager.getEntries();
// The prompt is declared by the first request, after the queued custom message.
expect(entries.map((entry) => entry.type)).toEqual([
"custom_message",
"message",
"message",
"message",
"message",
"message",
]);
expect(harness.session.messages.map((message) => message.role)).toEqual([
"custom",
"system",
"user",
"assistant",
"toolResult",
@@ -334,4 +337,63 @@ describe("AgentSession bash and persistence characterization", () => {
{ id: "bash-1", delta: "world" },
]);
});
// Regression tests for https://github.com/earendil-works/pi/issues/10504
describe("escape sequences split across output chunks", () => {
/** Runs a user bash command whose fake shell emits the given chunks; `beforeExit` sees what was streamed by then. */
async function runChunks(chunks: Array<string | Buffer>, beforeExit?: (streamed: string) => void) {
const harness = await createHarness();
harnesses.push(harness);
const deltas: string[] = [];
const operations: BashOperations = {
exec: async (_command, _cwd, options) => {
for (const chunk of chunks) {
options.onData(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
}
beforeExit?.(deltas.join(""));
return { exitCode: 0 };
},
};
const result = await harness.session.executeBash("custom", (delta) => deltas.push(delta), { operations });
const recorded = harness.session.messages[harness.session.messages.length - 1];
return {
output: result.output,
streamed: deltas.join(""),
recorded: recorded?.role === "bashExecution" ? recorded.output : undefined,
};
}
it("strips a color reset split inside its parameters", async () => {
const { output, streamed, recorded } = await runChunks(["\x1b[31mERROR: file.py:1\x1b[0", "m\n"]);
expect(output).toBe("ERROR: file.py:1\n");
expect(streamed).toBe("ERROR: file.py:1\n");
expect(recorded).toBe("ERROR: file.py:1\n");
});
it("strips a color code split right after ESC", async () => {
const { output, streamed } = await runChunks(["before\x1b", "[32mafter\n"]);
expect(output).toBe("beforeafter\n");
expect(streamed).toBe("beforeafter\n");
});
it("strips an OSC sequence split before its terminator", async () => {
const { output } = await runChunks(["a\x1b]0;window ", "title\x1b", "\\b\n"]);
expect(output).toBe("ab\n");
});
it("flushes an incomplete multi-byte character at the end of output", async () => {
const { output, streamed } = await runChunks(["ok", Buffer.from("\u00e9").subarray(0, 1)]);
expect(output).toBe("ok\uFFFD");
expect(streamed).toBe("ok\uFFFD");
});
it("does not hold back output behind a long unterminated sequence", async () => {
const long = "x".repeat(300);
let streamedBeforeExit = "";
await runChunks([`\x1b]${long}`], (streamed) => {
streamedBeforeExit = streamed;
});
expect(streamedBeforeExit).toBe(`]${long}`);
});
});
});