mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
Remove SystemMessage.replace. A prompt forced from before_agent_start is a rendering of the current prompt for the run, not conversation state: persisting it as a replacing system message wrote the full prompt and tool list on every change, lost the structured sections while forced, and disabled mid-conversation system messages for the rest of the session once a replacement existed. The transcript now always records section patches. AgentSession wraps transformContext and, when a forced prompt is active, collapses the system messages into one head carrying the forced text and the current tools. Providers receive the same request as before; nothing is persisted and the projection ends with the run. The evals harness validated the without_docs variant from the transcript, which relied on the persisted replacement. Its transform extension now records the prompt it forced and validates that instead.
116 lines
4.2 KiB
TypeScript
116 lines
4.2 KiB
TypeScript
import { homedir } from "node:os";
|
|
import { getDocsPath, getExamplesPath, getReadmePath } from "@earendil-works/pi-coding-agent";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { buildSystemPrompt } from "../../coding-agent/src/core/system-prompt.ts";
|
|
import {
|
|
applyIsolatedEnvironment,
|
|
createPiDocumentationEvalHarness,
|
|
DOCUMENTATION_EVAL_TOOLS,
|
|
excludePiDocumentation,
|
|
resolveDocumentationVariant,
|
|
resolveModelSelection,
|
|
verifySystemPrompt,
|
|
} from "../src/harness.ts";
|
|
|
|
describe("resolveModelSelection", () => {
|
|
it("prefers an explicit harness model", () => {
|
|
expect(
|
|
resolveModelSelection(
|
|
{ provider: "anthropic", id: "claude-opus-4-6" },
|
|
{ PI_PROVIDER: "openai-codex", PI_MODEL: "gpt-5.6-sol" },
|
|
),
|
|
).toEqual({ provider: "anthropic", id: "claude-opus-4-6" });
|
|
});
|
|
|
|
it("uses trimmed environment defaults", () => {
|
|
expect(resolveModelSelection(undefined, { PI_PROVIDER: " openai-codex ", PI_MODEL: " gpt-5.6-sol " })).toEqual({
|
|
provider: "openai-codex",
|
|
id: "gpt-5.6-sol",
|
|
});
|
|
});
|
|
|
|
it.each([{}, { PI_PROVIDER: "openai-codex" }, { PI_MODEL: "gpt-5.6-sol" }])(
|
|
"rejects incomplete model selection",
|
|
(environment) => {
|
|
expect(() => resolveModelSelection(undefined, environment)).toThrow("Select a harness model explicitly");
|
|
},
|
|
);
|
|
});
|
|
|
|
describe("isolateProcessEnvironment", () => {
|
|
it("removes runner metadata and restores the process environment", () => {
|
|
vi.stubEnv("PI_EVAL_VARIANT", "with_docs");
|
|
vi.stubEnv("PI_EVAL_ARTIFACT_DIR", "/tmp/artifacts");
|
|
const oldHome = process.env.HOME;
|
|
try {
|
|
const restore = applyIsolatedEnvironment("/tmp/eval-home", "/tmp/eval-agent");
|
|
try {
|
|
expect(homedir()).toBe("/tmp/eval-home");
|
|
expect(process.env.PI_CODING_AGENT_DIR).toBe("/tmp/eval-agent");
|
|
expect(process.env.PI_EVAL_VARIANT).toBeUndefined();
|
|
expect(process.env.PI_EVAL_ARTIFACT_DIR).toBeUndefined();
|
|
} finally {
|
|
restore();
|
|
}
|
|
expect(process.env.HOME).toBe(oldHome);
|
|
expect(process.env.PI_EVAL_VARIANT).toBe("with_docs");
|
|
} finally {
|
|
vi.unstubAllEnvs();
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("documentation variant", () => {
|
|
it.each(["without_docs", "with_docs"] as const)("accepts %s", (variant) => {
|
|
expect(resolveDocumentationVariant(variant)).toBe(variant);
|
|
});
|
|
|
|
it.each([undefined, "", "other"])("rejects invalid variant %s", (variant) => {
|
|
expect(() => resolveDocumentationVariant(variant)).toThrow("PI_EVAL_VARIANT");
|
|
});
|
|
|
|
it("strips only the documentation routing section from the default Pi prompt", () => {
|
|
const prompt = buildSystemPrompt({
|
|
cwd: "/workspace",
|
|
selectedTools: [...DOCUMENTATION_EVAL_TOOLS],
|
|
});
|
|
expect(prompt).toContain("\n<docs>\nPi documentation (read only");
|
|
expect(prompt).toContain("\n<rules>\n");
|
|
expect(prompt).toContain("\n<cwd>\n/workspace\n</cwd>");
|
|
expect(prompt).toContain("docs/models.md");
|
|
|
|
const stripped = excludePiDocumentation(prompt);
|
|
expect(stripped).toContain("\n<rules>\n");
|
|
expect(stripped).toContain("\n<cwd>\n/workspace\n</cwd>");
|
|
expect(stripped).not.toContain("<docs>");
|
|
expect(stripped).not.toContain("Pi documentation");
|
|
expect(stripped).not.toContain("docs/models.md");
|
|
expect(stripped).not.toContain(getReadmePath());
|
|
expect(stripped).not.toContain(getDocsPath());
|
|
expect(stripped).not.toContain(getExamplesPath());
|
|
});
|
|
|
|
it("verifies the prompt that was sent", () => {
|
|
const prompt = buildSystemPrompt({
|
|
cwd: "/workspace",
|
|
selectedTools: [...DOCUMENTATION_EVAL_TOOLS],
|
|
});
|
|
const stripped = excludePiDocumentation(prompt);
|
|
|
|
expect(verifySystemPrompt(stripped, { name: "without_docs", expectedPiDocumentation: false })).toBe(stripped);
|
|
expect(() => verifySystemPrompt(prompt, { name: "without_docs", expectedPiDocumentation: false })).toThrow(
|
|
"does not match",
|
|
);
|
|
});
|
|
|
|
it("fails closed when prompt markers are missing", () => {
|
|
expect(() => excludePiDocumentation("Instructions")).toThrow("no Pi documentation section");
|
|
expect(() => excludePiDocumentation("\n<docs>\nPi documentation\n</docs>")).toThrow(
|
|
"no working-directory section",
|
|
);
|
|
});
|
|
|
|
it("rejects documentation harnesses outside the container sandbox", () => {
|
|
expect(() => createPiDocumentationEvalHarness()).toThrow("isolated container sandbox");
|
|
});
|
|
});
|