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

@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Agent, type AgentEvent, type AgentTool } from "@earendil-works/pi-agent-core";
import { type AssistantMessage, type AssistantMessageEvent, EventStream, getModel } from "@earendil-works/pi-ai/compat";
import { type AssistantMessage, createAssistantMessageEventStream, getModel } from "@earendil-works/pi-ai/compat";
import { Type } from "typebox";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { AgentSession } from "../src/core/agent-session.ts";
@@ -12,19 +12,6 @@ import { SettingsManager } from "../src/core/settings-manager.ts";
import { createModelRegistry, getModelRuntime } from "./model-runtime-test-utils.ts";
import { createTestResourceLoader } from "./utilities.ts";
class MockAssistantStream extends EventStream<AssistantMessageEvent, AssistantMessage> {
constructor() {
super(
(event) => event.type === "done" || event.type === "error",
(event) => {
if (event.type === "done") return event.message;
if (event.type === "error") return event.error;
throw new Error("Unexpected event type");
},
);
}
}
function createAssistantMessage(text: string, overrides?: Partial<AssistantMessage>): AssistantMessage {
return {
role: "assistant",
@@ -71,10 +58,12 @@ describe("AgentSession retry", () => {
async function createSession(options?: {
failCount?: number;
maxRetries?: number;
maxAgentDelayMs?: number;
delayAssistantMessageEndMs?: number;
}) {
const failCount = options?.failCount ?? 1;
const maxRetries = options?.maxRetries ?? 3;
const maxAgentDelayMs = options?.maxAgentDelayMs ?? 60000;
const delayAssistantMessageEndMs = options?.delayAssistantMessageEndMs ?? 0;
let callCount = 0;
@@ -84,7 +73,7 @@ describe("AgentSession retry", () => {
initialState: { model, systemPrompt: "Test", tools: [] },
streamFn: () => {
callCount++;
const stream = new MockAssistantStream();
const stream = createAssistantMessageEventStream();
queueMicrotask(() => {
if (callCount <= failCount) {
const msg = createAssistantMessage("", {
@@ -108,7 +97,7 @@ describe("AgentSession retry", () => {
const authStorage = AuthStorage.create(join(tempDir, "auth.json"));
const modelRegistry = await createModelRegistry(authStorage, tempDir);
await authStorage.modify("anthropic", async () => ({ type: "api_key", key: "test-key" }));
settingsManager.applyOverrides({ retry: { enabled: true, maxRetries, baseDelayMs: 1 } });
settingsManager.applyOverrides({ retry: { enabled: true, maxRetries, baseDelayMs: 1, maxAgentDelayMs } });
session = new AgentSession({
agent,
@@ -165,6 +154,19 @@ describe("AgentSession retry", () => {
expect(created.session.isRetrying).toBe(false);
});
it("caps agent retry delay", async () => {
// Regression for #8826.
const created = await createSession({ failCount: 4, maxRetries: 5, maxAgentDelayMs: 5 });
const delays: number[] = [];
created.session.subscribe((event) => {
if (event.type === "auto_retry_start") delays.push(event.delayMs);
});
await created.session.prompt("Test");
expect(delays).toEqual([1, 2, 4, 5]);
});
it("prompt waits for retry completion even when assistant message_end handling is delayed", async () => {
const created = await createSession({ failCount: 1, delayAssistantMessageEndMs: 40 });
@@ -179,7 +181,7 @@ describe("AgentSession retry", () => {
let callCount = 0;
const streamFn = () => {
callCount++;
const stream = new MockAssistantStream();
const stream = createAssistantMessageEventStream();
queueMicrotask(() => {
if (callCount === 1) {
const msg = createAssistantMessage("", {
@@ -257,7 +259,7 @@ describe("AgentSession retry", () => {
initialState: { model, systemPrompt: "Test", tools: [] },
streamFn: () => {
callCount++;
const stream = new MockAssistantStream();
const stream = createAssistantMessageEventStream();
queueMicrotask(() => {
if (callCount === 1) {
// First call: overloaded error