111 lines
3.3 KiB
TypeScript
111 lines
3.3 KiB
TypeScript
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
buildBootstrapContextFiles,
|
|
formatAssistantErrorText,
|
|
isContextOverflowError,
|
|
sanitizeGoogleTurnOrdering,
|
|
} from "./pi-embedded-helpers.js";
|
|
import {
|
|
DEFAULT_AGENTS_FILENAME,
|
|
type WorkspaceBootstrapFile,
|
|
} from "./workspace.js";
|
|
|
|
const makeFile = (
|
|
overrides: Partial<WorkspaceBootstrapFile>,
|
|
): WorkspaceBootstrapFile => ({
|
|
name: DEFAULT_AGENTS_FILENAME,
|
|
path: "/tmp/AGENTS.md",
|
|
content: "",
|
|
missing: false,
|
|
...overrides,
|
|
});
|
|
|
|
describe("buildBootstrapContextFiles", () => {
|
|
it("keeps missing markers", () => {
|
|
const files = [makeFile({ missing: true, content: undefined })];
|
|
expect(buildBootstrapContextFiles(files)).toEqual([
|
|
{
|
|
path: DEFAULT_AGENTS_FILENAME,
|
|
content: "[MISSING] Expected at: /tmp/AGENTS.md",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("skips empty or whitespace-only content", () => {
|
|
const files = [makeFile({ content: " \n " })];
|
|
expect(buildBootstrapContextFiles(files)).toEqual([]);
|
|
});
|
|
|
|
it("truncates large bootstrap content", () => {
|
|
const head = `HEAD-${"a".repeat(6000)}`;
|
|
const tail = `${"b".repeat(3000)}-TAIL`;
|
|
const long = `${head}${tail}`;
|
|
const files = [makeFile({ content: long })];
|
|
const [result] = buildBootstrapContextFiles(files);
|
|
expect(result?.content).toContain(
|
|
"[...truncated, read AGENTS.md for full content...]",
|
|
);
|
|
expect(result?.content.length).toBeLessThan(long.length);
|
|
expect(result?.content.startsWith(long.slice(0, 120))).toBe(true);
|
|
expect(result?.content.endsWith(long.slice(-120))).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("isContextOverflowError", () => {
|
|
it("matches known overflow hints", () => {
|
|
const samples = [
|
|
"request_too_large",
|
|
"Request exceeds the maximum size",
|
|
"context length exceeded",
|
|
"Maximum context length",
|
|
"413 Request Entity Too Large",
|
|
];
|
|
for (const sample of samples) {
|
|
expect(isContextOverflowError(sample)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("ignores unrelated errors", () => {
|
|
expect(isContextOverflowError("rate limit exceeded")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("formatAssistantErrorText", () => {
|
|
const makeAssistantError = (errorMessage: string): AssistantMessage =>
|
|
({
|
|
stopReason: "error",
|
|
errorMessage,
|
|
}) as AssistantMessage;
|
|
|
|
it("returns a friendly message for context overflow", () => {
|
|
const msg = makeAssistantError("request_too_large");
|
|
expect(formatAssistantErrorText(msg)).toContain("Context overflow");
|
|
});
|
|
});
|
|
|
|
describe("sanitizeGoogleTurnOrdering", () => {
|
|
it("prepends a synthetic user turn when history starts with assistant", () => {
|
|
const input = [
|
|
{
|
|
role: "assistant",
|
|
content: [
|
|
{ type: "toolCall", id: "call_1", name: "bash", arguments: {} },
|
|
],
|
|
},
|
|
] satisfies AgentMessage[];
|
|
|
|
const out = sanitizeGoogleTurnOrdering(input);
|
|
expect(out[0]?.role).toBe("user");
|
|
expect(out[1]?.role).toBe("assistant");
|
|
});
|
|
|
|
it("is a no-op when history starts with user", () => {
|
|
const input = [{ role: "user", content: "hi" }] satisfies AgentMessage[];
|
|
const out = sanitizeGoogleTurnOrdering(input);
|
|
expect(out).toBe(input);
|
|
});
|
|
});
|