import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; import { createRectificationActivityReceiptState, reduceRectificationActivityReceipt, receiptFromRectificationActivityState, } from "../src/lib/rectification-activity-receipt"; test("a successful retry clears the same tool's earlier failure", () => { let state = createRectificationActivityReceiptState(); for (const event of [ { tool: "rectification-read-case", status: "completed" }, { tool: "rectification-propose-evidence", status: "failed" }, { tool: "rectification-propose-evidence", status: "started" }, { tool: "rectification-propose-evidence", status: "completed" }, { tool: "rectification-confirm-evidence", status: "completed" }, ] as const) { state = reduceRectificationActivityReceipt(state, event); } assert.deepEqual(receiptFromRectificationActivityState(state), { steps: [ "rectification-read-case", "rectification-propose-evidence", "rectification-confirm-evidence", ], methods: [], }); }); test("an unrecovered latest tool failure remains visible", () => { let state = createRectificationActivityReceiptState(); state = reduceRectificationActivityReceipt(state, { tool: "rectification-read-case", status: "completed", }); state = reduceRectificationActivityReceipt(state, { tool: "rectification-propose-evidence", status: "failed", }); assert.deepEqual(receiptFromRectificationActivityState(state), { steps: ["rectification-read-case"], methods: [], failedTool: "rectification-propose-evidence", }); }); test("a later failure overrides an earlier completion for the same tool", () => { let state = createRectificationActivityReceiptState(); state = reduceRectificationActivityReceipt(state, { tool: "rectification-read-case", status: "completed", }); state = reduceRectificationActivityReceipt(state, { tool: "rectification-read-case", status: "failed", }); assert.deepEqual(receiptFromRectificationActivityState(state), { steps: [], methods: [], failedTool: "rectification-read-case", }); }); test("a later failure removes methods contributed by the failed tool", () => { let state = createRectificationActivityReceiptState(); state = reduceRectificationActivityReceipt(state, { tool: "rectification-compare-candidates", status: "completed", methods: ["d1-rashi", "vimshottari-dasha"], }); state = reduceRectificationActivityReceipt(state, { tool: "rectification-read-diagnostics", status: "completed", methods: ["d1-rashi", "shadbala"], }); state = reduceRectificationActivityReceipt(state, { tool: "rectification-compare-candidates", status: "failed", }); assert.deepEqual(receiptFromRectificationActivityState(state), { steps: ["rectification-read-diagnostics"], methods: ["d1-rashi", "shadbala"], failedTool: "rectification-compare-candidates", }); }); const chatSource = readFileSync( new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url), "utf8", ); test("answer deltas preserve a still-running server activity", () => { const deltaBranch = chatSource.slice( chatSource.indexOf('event.type === "answer.delta"'), chatSource.indexOf('event.type === "run.failed"'), ); assert.match(deltaBranch, /state: "streaming"/); assert.match(deltaBranch, /正在组织回答/); assert.doesNotMatch(deltaBranch, /activeActivity:\s*undefined/); }); test("failed server turns retain their receipt state without parsing activity from answer text", () => { assert.match(chatSource, /failed:\s*true/); assert.match(chatSource, /completedReceipt/); assert.match(chatSource, /message\.state === "settled" && message\.failed/); assert.doesNotMatch(chatSource, /parsed\.(?:activity|receipt|tools|methods)/); }); test("candidate acceptance copy never presents adoption as confirmation", () => { assert.match(chatSource, /当前可能的出生时间/); assert.match(chatSource, /采用不等于确认出生时间/); assert.match(chatSource, /当前排盘时间(代表性候选,还不能确认唯一分钟)/); assert.match(chatSource, /已确认校正时间/); assert.doesNotMatch(chatSource, /candidateResult\.confirmationAllowed \? "确认校正时间"/); });