feat(engine): reviewer uses prompt layers for cross-session caching

This commit is contained in:
Matthew Greenberg
2026-05-08 19:57:13 -04:00
parent e7ea1257ff
commit 00bc7cd442
2 changed files with 64 additions and 14 deletions

View File

@@ -0,0 +1,45 @@
import { describe, it, expect } from "vitest";
import { buildPromptLayers, collapsePromptLayers } from "../prompt-layers.js";
describe("reviewer prompt layering", () => {
const REVIEWER_BASE = "You are an independent code and plan reviewer.";
const MEMORY_INSTRUCTIONS = "\n## Memory\n\nUse fn_memory_search to look up context.";
it("puts base prompt + memory instructions in stable layer", () => {
const layers = buildPromptLayers({
basePrompt: REVIEWER_BASE + MEMORY_INSTRUCTIONS,
agentInstructions: "Custom reviewer guidance.",
pluginContributions: "## Plugin: lint\n\nCheck lint.",
});
expect(layers.stable).toBe(REVIEWER_BASE + MEMORY_INSTRUCTIONS);
expect(layers.stable).not.toContain("Custom reviewer guidance");
expect(layers.stable).not.toContain("lint");
});
it("produces identical stable layer across simulated sessions", () => {
const layers1 = buildPromptLayers({
basePrompt: REVIEWER_BASE + MEMORY_INSTRUCTIONS,
agentInstructions: "Session 1 instructions.",
});
const layers2 = buildPromptLayers({
basePrompt: REVIEWER_BASE + MEMORY_INSTRUCTIONS,
agentInstructions: "Session 2 instructions.",
});
expect(layers1.stable).toBe(layers2.stable);
});
it("collapsed layers match legacy concatenation", () => {
const layers = buildPromptLayers({
basePrompt: REVIEWER_BASE,
agentInstructions: "Check for bugs.",
pluginContributions: "## Plugin: sec\n\nScan.",
});
const collapsed = collapsePromptLayers(layers);
expect(collapsed).toBe(
`${REVIEWER_BASE}\n\n## Custom Instructions\n\nCheck for bugs.\n\n## Plugin: sec\n\nScan.`
);
});
});

View File

@@ -18,9 +18,9 @@ import { reviewerLog } from "./logger.js";
import { checkSessionError } from "./usage-limit-detector.js";
import {
resolveAgentInstructions,
buildSystemPromptWithInstructions,
buildPluginPromptSection,
} from "./agent-instructions.js";
import { buildPromptLayers, collapsePromptLayers } from "./prompt-layers.js";
import { createFallbackModelObserver } from "./fallback-model-observer.js";
import { createMemoryGetTool, createMemorySearchTool, createWebFetchTool } from "./agent-tools.js";
@@ -408,23 +408,27 @@ export async function reviewStep(
const memorySection = options.rootDir && options.settings?.memoryEnabled !== false
? "\n" + buildReviewerMemoryInstructions(options.rootDir, options.settings)
: "";
const reviewerSystemPrompt = buildSystemPromptWithInstructions(
reviewerBasePrompt + memorySection,
reviewerInstructions,
);
const reviewerContributions = options.pluginRunner
?.getPromptContributionsForSurface("reviewer")
?? [];
if (reviewerContributions.length > 0) {
reviewerLog.log(`applied ${reviewerContributions.length} plugin prompt contributions for reviewer surface`);
}
// Build structured layers for cross-session prompt caching.
// The stable layer (base prompt + memory instructions) is byte-identical
// across all reviewer sessions in this task, enabling cache hits.
const reviewerPluginContributions = buildPluginPromptSection(
"reviewer",
options.pluginRunner,
);
const reviewerSystemPromptFinal = reviewerPluginContributions
? `${reviewerSystemPrompt}\n\n${reviewerPluginContributions}`
: reviewerSystemPrompt;
if (reviewerPluginContributions) {
reviewerLog.log(`applied plugin prompt contributions for reviewer surface`);
}
const layers = buildPromptLayers({
basePrompt: reviewerBasePrompt + memorySection,
agentInstructions: reviewerInstructions,
pluginContributions: reviewerPluginContributions,
});
// Collapsed string for backward compatibility with runtimes that don't
// support layers (plugin runtimes, older pi versions).
const reviewerSystemPromptFinal = collapsePromptLayers(layers);
// Build skill selection context (assigned agent skills take precedence over role fallback)
let skillContext = undefined;
@@ -495,6 +499,7 @@ export async function reviewStep(
pluginRunner: options.pluginRunner,
cwd,
systemPrompt: reviewerSystemPromptFinal,
systemPromptLayers: layers,
tools: "readonly",
customTools: [createWebFetchTool(), ...(memoryTools ?? [])],
onText: agentLogger ? agentLogger.onText : (delta) => options.onText?.(delta),