import { fileURLToPath, pathToFileURL } from "node:url"; import { join } from "node:path"; import { resolvePrimeRoot } from "./prime-root.mjs"; const primeRoot = resolvePrimeRoot(); const { loadExtensions } = await import( pathToFileURL(join(primeRoot, "dist/core/extensions/loader.js")) ); const projectionPath = fileURLToPath(new URL("../projection.ts", import.meta.url)); const loaded = await loadExtensions([projectionPath], process.cwd()); if (loaded.errors.length) throw new Error(JSON.stringify(loaded.errors)); const handler = loaded.extensions[0].handlers.get("session_before_compact")[0]; const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, }; const assistant = { role: "assistant", content: [ { type: "thinking", thinking: "private reasoning" }, { type: "text", text: "running tools" }, ...["t1", "t2", "t3", "t4", "empty", "useless"].map((id) => ({ type: "toolCall", id, name: "ipython", arguments: { id, nested: { value: "original" } }, })), ], api: "openai-responses", provider: "openai", model: "test", usage, stopReason: "toolUse", timestamp: 1, }; const result = (id, text) => ({ role: "toolResult", toolCallId: id, toolName: "ipython", content: [{ type: "text", text }], isError: false, details: { nested: { value: "original" } }, timestamp: 1, }); const preparation = { firstKeptEntryId: "kept", messagesToSummarize: [ { role: "user", content: [ { type: "text", text: "inspect" }, { type: "image", data: "raw-image-bytes", mimeType: "image/png" }, ], timestamp: 1, }, assistant, result("t1", "old output"), ], turnPrefixMessages: [ result("t2", "second output"), result("t3", "third output"), result("t4", "fourth output"), result("empty", ""), { ...result("useless", "no matches"), useless: true }, { role: "user", content: "next round", timestamp: 2 }, result("t4", "orphan output"), { ...assistant, content: [{ type: "thinking", thinking: "thinking only" }] }, { ...assistant, content: [{ type: "toolCall", id: "useless-only", name: "ipython", arguments: {} }], }, { ...result("useless-only", "no matches"), useless: true }, ], isSplitTurn: true, tokensBefore: 10, fileOps: { read: new Set(), written: new Set(), edited: new Set() }, settings: { enabled: true, reserveTokens: 1000, keepRecentTokens: 1000 }, }; const original = JSON.stringify(preparation); const openAiContext = { model: { api: "openai-responses", id: "gpt-5.6-sol", provider: "openai" }, }; const projected = await handler( { type: "session_before_compact", preparation, branchEntries: [], signal: new AbortController().signal, }, openAiContext, ); if (JSON.stringify(preparation) !== original) throw new Error("projection mutated raw preparation"); const groups = [ projected.preparation.messagesToSummarize, projected.preparation.turnPrefixMessages, ]; if (groups.length !== 2) throw new Error("projection lost split-turn groups"); const projectedCall = groups .flat() .find((message) => message.role === "assistant") .content.find((block) => block.type === "toolCall"); projectedCall.arguments.nested.value = "changed"; const projectedResult = groups.flat().find((message) => message.role === "toolResult"); projectedResult.details.nested.value = "changed"; if (JSON.stringify(preparation) !== original) { throw new Error("mutating nested projection data changed raw preparation"); } const text = JSON.stringify(groups); if (!text.includes("private reasoning")) throw new Error("projection lost compatible reasoning"); if (text.includes("raw-image-bytes") || !text.includes("[image]")) { throw new Error("projection did not replace image bytes"); } if (text.includes("old output") || !text.includes("[tool result]")) { throw new Error("projection did not mask the oldest tool result"); } for (const exact of ["second output", "third output", "fourth output"]) { if (!text.includes(exact)) throw new Error(`projection lost ${exact}`); } if (text.includes("no matches") || text.includes('"id":"empty"') || text.includes('"id":"useless"')) { throw new Error("projection retained an empty or useless tool pair"); } if (text.includes("orphan output")) throw new Error("projection retained an orphan result"); const anthropicProjection = await handler( { type: "session_before_compact", preparation, branchEntries: [], signal: new AbortController().signal, }, { model: { api: "anthropic-messages", id: "claude-sonnet-4-5", provider: "anthropic", }, }, ); if (JSON.stringify(anthropicProjection.preparation).includes("private reasoning")) { throw new Error("projection replayed reasoning to Claude"); } if ( [ ...anthropicProjection.preparation.messagesToSummarize, ...anthropicProjection.preparation.turnPrefixMessages, ].some((message) => message.role === "assistant" && message.content.length === 0) ) { throw new Error("projection retained an empty assistant message"); } const emptyErrorProjection = await handler( { type: "session_before_compact", preparation: { ...preparation, messagesToSummarize: [ { ...assistant, content: [{ type: "toolCall", id: "empty-error", name: "ipython", arguments: {} }], }, { ...result("empty-error", ""), content: [], isError: true }, ], turnPrefixMessages: [], }, branchEntries: [], signal: new AbortController().signal, }, openAiContext, ); const emptyErrorText = JSON.stringify(emptyErrorProjection.preparation); if (!emptyErrorText.includes("empty-error") || !emptyErrorText.includes('"isError":true')) { throw new Error("projection dropped an empty tool error"); } console.log(JSON.stringify({ groups: groups.map((group) => group.length), exactToolResults: 3 }));