Files
2026-07-13 13:39:12 +08:00

164 lines
5.5 KiB
TypeScript

import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
readLifecycleEngine,
extractInvocations,
findSupersededReadCallIds,
} from "../../../open-sse/services/compression/engines/readLifecycle/index.ts";
// T08/H7 — read-lifecycle: collapse superseded file-Read tool results (opt-in, lossy).
const STUB_MARK = "read superseded";
function run(messages: unknown[], enabled = true) {
return readLifecycleEngine.apply({ messages }, { stepConfig: { enabled } });
}
// ── Anthropic fixtures ──
const anthropicRead = (id: string, path: string) => ({
role: "assistant",
content: [{ type: "tool_use", id, name: "Read", input: { file_path: path } }],
});
const anthropicWrite = (id: string, path: string) => ({
role: "assistant",
content: [{ type: "tool_use", id, name: "Edit", input: { file_path: path } }],
});
const anthropicResult = (id: string, text: string) => ({
role: "user",
content: [{ type: "tool_result", tool_use_id: id, content: text }],
});
function anthropicResultText(msg: unknown, id: string): string | undefined {
const blocks = (msg as { content?: unknown[] }).content;
const b = Array.isArray(blocks)
? (blocks.find((x) => (x as { tool_use_id?: string }).tool_use_id === id) as
{ content?: string } | undefined)
: undefined;
return b?.content;
}
// ── OpenAI fixtures ──
const openaiRead = (id: string, path: string) => ({
role: "assistant",
tool_calls: [
{
id,
type: "function",
function: { name: "read_file", arguments: JSON.stringify({ file_path: path }) },
},
],
});
const openaiResult = (id: string, text: string) => ({
role: "tool",
tool_call_id: id,
content: text,
});
describe("read-lifecycle — pure analysis", () => {
it("extracts invocations from both shapes", () => {
const { invocations, readPathByCallId } = extractInvocations([
anthropicRead("c1", "/a.ts"),
openaiRead("c2", "/b.ts"),
]);
assert.equal(invocations.length, 2);
assert.equal(invocations[0].kind, "read");
assert.equal(invocations[0].path, "/a.ts");
assert.equal(readPathByCallId.get("c1"), "/a.ts");
assert.equal(readPathByCallId.get("c2"), "/b.ts");
});
it("marks a read superseded by a later read of the same path", () => {
const { invocations } = extractInvocations([
anthropicRead("c1", "/a.ts"),
anthropicRead("c2", "/a.ts"),
]);
const s = findSupersededReadCallIds(invocations);
assert.equal(s.has("c1"), true);
assert.equal(s.has("c2"), false);
});
it("marks a read superseded by a later write of the same path", () => {
const { invocations } = extractInvocations([
anthropicRead("c1", "/a.ts"),
anthropicWrite("w1", "/a.ts"),
]);
const s = findSupersededReadCallIds(invocations);
assert.equal(s.has("c1"), true);
assert.equal(s.has("w1"), false); // writes are never collapsed
});
it("does not supersede reads of different paths", () => {
const { invocations } = extractInvocations([
anthropicRead("c1", "/a.ts"),
anthropicRead("c2", "/b.ts"),
]);
const s = findSupersededReadCallIds(invocations);
assert.equal(s.size, 0);
});
});
describe("read-lifecycle — engine integration", () => {
it("collapses an earlier Anthropic read, keeping the latest intact", () => {
const out = run([
anthropicRead("c1", "/a.ts"),
anthropicResult("c1", "OLD content of a.ts"),
anthropicRead("c2", "/a.ts"),
anthropicResult("c2", "NEW content of a.ts"),
]);
assert.equal(out.compressed, true);
const msgs = (out.body as { messages: unknown[] }).messages;
assert.match(anthropicResultText(msgs[1], "c1") ?? "", new RegExp(STUB_MARK));
assert.equal(anthropicResultText(msgs[3], "c2"), "NEW content of a.ts");
});
it("collapses a read superseded by a later write", () => {
const out = run([
anthropicRead("c1", "/a.ts"),
anthropicResult("c1", "OLD"),
anthropicWrite("w1", "/a.ts"),
anthropicResult("w1", "edit applied"),
]);
assert.equal(out.compressed, true);
const msgs = (out.body as { messages: unknown[] }).messages;
assert.match(anthropicResultText(msgs[1], "c1") ?? "", new RegExp(STUB_MARK));
assert.equal(anthropicResultText(msgs[3], "w1"), "edit applied"); // write result untouched
});
it("collapses an earlier OpenAI read (role:tool message)", () => {
const out = run([
openaiRead("c1", "/a.ts"),
openaiResult("c1", "OLD"),
openaiRead("c2", "/a.ts"),
openaiResult("c2", "NEW"),
]);
assert.equal(out.compressed, true);
const msgs = (out.body as { messages: Array<{ content?: string }> }).messages;
assert.match(msgs[1].content ?? "", new RegExp(STUB_MARK));
assert.equal(msgs[3].content, "NEW");
});
it("no-ops for a single read of a path", () => {
const out = run([anthropicRead("c1", "/a.ts"), anthropicResult("c1", "only read")]);
assert.equal(out.compressed, false);
});
it("is a pass-through when disabled (default)", () => {
const messages = [
anthropicRead("c1", "/a.ts"),
anthropicResult("c1", "OLD"),
anthropicRead("c2", "/a.ts"),
anthropicResult("c2", "NEW"),
];
const out = run(messages, false);
assert.equal(out.compressed, false);
assert.equal(
anthropicResultText((out.body as { messages: unknown[] }).messages[1], "c1"),
"OLD"
);
});
it("schema defaults enabled to false (opt-in)", () => {
const field = readLifecycleEngine.getConfigSchema!().find((f) => f.key === "enabled");
assert.equal(field?.defaultValue, false);
});
});