diff --git a/src/config/sessions/transcript.test.ts b/src/config/sessions/transcript.test.ts index 50abe30bfac9..fae9db58757f 100644 --- a/src/config/sessions/transcript.test.ts +++ b/src/config/sessions/transcript.test.ts @@ -736,6 +736,46 @@ describe("appendAssistantMessageToSessionTranscript", () => { } }); + it("dedupes concurrent exact assistant appends by idempotency key", async () => { + writeTranscriptStore(); + const idempotencyKey = "mirror:concurrent-assistant"; + + const results = await Promise.all( + Array.from({ length: 8 }, () => + appendExactAssistantMessageToSessionTranscript({ + sessionKey, + storePath: fixture.storePath(), + idempotencyKey, + updateMode: "none", + message: createExactAssistantMessage({ + text: "Mirrored reply", + provider: "openclaw", + model: "delivery-mirror", + }), + }), + ), + ); + + expect(results.every((result) => result.ok)).toBe(true); + const messageIds = results.map((result) => (result.ok ? result.messageId : "")); + expect(new Set(messageIds).size).toBe(1); + + const firstOk = results.find((result) => result.ok); + if (!firstOk?.ok) { + throw new Error("expected exact assistant append to succeed"); + } + const records = fs + .readFileSync(firstOk.sessionFile, "utf-8") + .trim() + .split("\n") + .map((line) => JSON.parse(line) as { message?: { role?: string; idempotencyKey?: string } }) + .filter( + (record) => + record.message?.role === "assistant" && record.message.idempotencyKey === idempotencyKey, + ); + expect(records).toHaveLength(1); + }); + it("can emit file-only transcript refresh events for exact assistant appends", async () => { writeTranscriptStore(); const emitSpy = vi.spyOn(transcriptEvents, "emitSessionTranscriptUpdate"); diff --git a/src/config/sessions/transcript.ts b/src/config/sessions/transcript.ts index ab96abc52f73..8aece357ee5a 100644 --- a/src/config/sessions/transcript.ts +++ b/src/config/sessions/transcript.ts @@ -353,7 +353,6 @@ export async function appendExactAssistantMessageToSessionTranscript(params: { return await appendSessionTranscriptMessage({ transcriptPath: sessionFile, message, - idempotencyLookup: explicitIdempotencyKey ? "caller-checked" : "scan", config: params.config, }); },