





























@@ -1,5 +1,6 @@
11import fs from "node:fs";
22import { describe, expect, it, vi } from "vitest";
3+import { repairToolUseResultPairing } from "../../agents/session-transcript-repair.js";
34import * as transcriptEvents from "../../sessions/transcript-events.js";
45import type { SessionTranscriptUpdate } from "../../sessions/transcript-events.js";
56import { resolveSessionTranscriptPathInDir } from "./paths.js";
@@ -18,6 +19,7 @@ describe("appendAssistantMessageToSessionTranscript", () => {
1819type ExactAssistantMessage = Parameters<
1920typeof appendExactAssistantMessageToSessionTranscript
2021>[0]["message"];
22+type TranscriptRepairMessage = Parameters<typeof repairToolUseResultPairing>[0][number];
2123type TranscriptUpdateEmitterSpy = {
2224mock: {
2325calls: [string | SessionTranscriptUpdate][];
@@ -283,6 +285,78 @@ describe("appendAssistantMessageToSessionTranscript", () => {
283285}
284286});
285287288+it("keeps delivery mirrors in transcripts while repair preserves real tool results", async () => {
289+writeTranscriptStore();
290+const sessionFile = resolveSessionTranscriptPathInDir(sessionId, fixture.sessionsDir());
291+const toolCallId = "call_maniple_list";
292+293+const toolCallResult = await appendSessionTranscriptMessage({
294+transcriptPath: sessionFile,
295+message: {
296+role: "assistant",
297+content: [
298+{
299+type: "toolCall",
300+id: toolCallId,
301+name: "maniple__list_workers",
302+arguments: {},
303+},
304+],
305+stopReason: "toolUse",
306+},
307+});
308+309+const mirrorResult = await appendAssistantMessageToSessionTranscript({
310+ sessionKey,
311+text: "Maniple List Workers",
312+storePath: fixture.storePath(),
313+});
314+315+expect(mirrorResult.ok).toBe(true);
316+if (!mirrorResult.ok) {
317+return;
318+}
319+expect(mirrorResult.messageId).not.toBe(toolCallResult.messageId);
320+const linesAfterMirror = fs.readFileSync(sessionFile, "utf-8").trim().split("\n");
321+expect(linesAfterMirror).toHaveLength(3);
322+const mirrorLine = JSON.parse(linesAfterMirror[2]);
323+expect(mirrorLine.message.model).toBe("delivery-mirror");
324+325+await appendSessionTranscriptMessage({
326+transcriptPath: sessionFile,
327+message: {
328+role: "toolResult",
329+ toolCallId,
330+toolName: "maniple__list_workers",
331+content: [{ type: "text", text: "workers listed" }],
332+isError: false,
333+},
334+});
335+336+const messages = fs
337+.readFileSync(sessionFile, "utf-8")
338+.trim()
339+.split("\n")
340+.map((line) => JSON.parse(line) as { message?: TranscriptRepairMessage })
341+.flatMap((entry) => (entry.message ? [entry.message] : []));
342+expect(messages.map((message) => message.role)).toEqual([
343+"assistant",
344+"assistant",
345+"toolResult",
346+]);
347+const repair = repairToolUseResultPairing(messages, {
348+missingToolResultText: "aborted",
349+});
350+351+expect(repair.added).toHaveLength(0);
352+expect(repair.messages.map((message) => message.role)).toEqual([
353+"assistant",
354+"toolResult",
355+"assistant",
356+]);
357+expect((repair.messages[2] as { model?: string }).model).toBe("delivery-mirror");
358+});
359+286360it("finds session entry using normalized (lowercased) key", async () => {
287361const storeKey = "agent:main:imessage:direct:+15551234567";
288362const store = {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。