56 lines
3 KiB
TypeScript
56 lines
3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import { extendsTranscript, restoredContent, transcriptKeys, type TranscriptMessage } from "../transcript.ts";
|
|
|
|
const user = { role: "user", content: "Remember MEMORY_73921" };
|
|
const assistant = { role: "assistant", content: [{ type: "text", text: "ACK", index: 0 }], timestamp: 1 };
|
|
|
|
test("fingerprints ignore runtime fields but detect branch and content changes", () => {
|
|
const previous = transcriptKeys([user, assistant]);
|
|
const current = transcriptKeys([
|
|
{ role: "system", content: "Changed prompt" }, user,
|
|
{ ...assistant, timestamp: 2, usage: { input: 10 }, content: [{ type: "text", text: "ACK" }] },
|
|
{ role: "user", content: "Next" },
|
|
]);
|
|
assert.ok(extendsTranscript(previous, current));
|
|
assert.ok(!extendsTranscript(previous, transcriptKeys([user])));
|
|
assert.ok(!extendsTranscript(previous, transcriptKeys([user, { ...assistant, content: "Different" }])));
|
|
});
|
|
|
|
test("tool argument key order does not force a restart", () => {
|
|
const message = (args: unknown): TranscriptMessage => ({ role: "assistant", content: [{ type: "toolCall", id: "id", name: "read", arguments: args }] });
|
|
assert.deepEqual(transcriptKeys([message({ a: 1, b: 2 })]), transcriptKeys([message({ b: 2, a: 1 })]));
|
|
});
|
|
|
|
test("restoration includes prior messages, tool results, and current input without native calls", () => {
|
|
const blocks = restoredContent([
|
|
{ role: "system", content: "SYSTEM_PROMPT_NOT_REPLAYED" }, user, assistant,
|
|
{ role: "toolResult", toolCallId: "old-id", toolName: "read", content: "ERROR_73921", isError: true },
|
|
{ role: "user", content: "Current request" },
|
|
]);
|
|
const text = blocks.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
assert.match(text, /MEMORY_73921/);
|
|
assert.match(text, /ERROR_73921/);
|
|
assert.match(text, /"isError":true/);
|
|
assert.ok(!text.includes("SYSTEM_PROMPT_NOT_REPLAYED"));
|
|
assert.equal(blocks.at(-1)?.type, "text");
|
|
assert.equal((blocks.at(-1) as { text: string }).text, "Current request");
|
|
assert.ok(blocks.every((block) => block.type === "text" || block.type === "image"));
|
|
});
|
|
|
|
test("historical images stay image blocks; thinking signatures are not replayed", () => {
|
|
const blocks = restoredContent([
|
|
{ role: "user", content: [{ type: "image", data: "aW1hZ2U=", mimeType: "image/png" }] },
|
|
{ role: "assistant", content: [{ type: "thinking", thinking: "private", thinkingSignature: "secret-signature" }] },
|
|
{ role: "user", content: "Next" },
|
|
]);
|
|
assert.equal(blocks.filter((block) => block.type === "image").length, 1);
|
|
const text = JSON.stringify(blocks);
|
|
assert.ok(!text.includes("secret-signature"));
|
|
assert.ok(!text.includes("private"));
|
|
});
|
|
|
|
test("tool-result tail gets a continuation request instead of replaying tool calls", () => {
|
|
const blocks = restoredContent([{ role: "toolResult", toolCallId: "id", content: "DONE" }]);
|
|
assert.match((blocks.at(-1) as { text: string }).text, /Continue the latest user request/);
|
|
});
|