From db7286187f16ccc832ea945f9a88676d9eb6675d Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 4 Jul 2026 04:02:55 -0700 Subject: [PATCH] fix(transcripts): mark delivery mirrors as artifacts (#99855) * fix(transcripts): mark delivery mirrors as artifacts * fix(transcripts): mark delivery mirrors as artifacts * fix(transcripts): mark delivery mirrors as artifacts --- ...ent-runner.sanitize-session-history.test.ts | 5 +++-- .../replay-history.test.ts | 3 ++- .../run/attempt.session-lock.test.ts | 3 ++- src/agents/session-file-repair.test.ts | 5 +++-- src/config/sessions/transcript.test.ts | 5 +++++ src/config/sessions/transcript.ts | 18 +++++++++++++----- src/gateway/sessions-history-http.test.ts | 14 +++++++++----- .../transcript-only-openclaw-assistant.ts | 15 +++++++++++---- 8 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/agents/embedded-agent-runner.sanitize-session-history.test.ts b/src/agents/embedded-agent-runner.sanitize-session-history.test.ts index da2df8a582ee..824ccd799d8c 100644 --- a/src/agents/embedded-agent-runner.sanitize-session-history.test.ts +++ b/src/agents/embedded-agent-runner.sanitize-session-history.test.ts @@ -7,6 +7,7 @@ import type { Usage, } from "openclaw/plugin-sdk/llm"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { OPENCLAW_TRANSCRIPT_ARTIFACT_API } from "../shared/transcript-only-openclaw-assistant.js"; import { expectOpenAIResponsesStrictSanitizeCall, loadSanitizeSessionHistoryWithCleanMocks, @@ -887,7 +888,7 @@ describe("sanitizeSessionHistory", () => { }); }); - it("repairs a message-tool delivery-mirror poisoned OpenAI Responses replay", async () => { + it("repairs a message-tool delivery-mirror poisoned replay", async () => { const messages: AgentMessage[] = [ makeUserMessage("start"), makeAssistantMessage( @@ -905,7 +906,7 @@ describe("sanitizeSessionHistory", () => { role: "assistant", provider: "openclaw", model: "delivery-mirror", - api: "openai-responses", + api: OPENCLAW_TRANSCRIPT_ARTIFACT_API, content: [{ type: "text", text: "visible reply" }], stopReason: "stop", }), diff --git a/src/agents/embedded-agent-runner/replay-history.test.ts b/src/agents/embedded-agent-runner/replay-history.test.ts index 6ea32fed8cd4..5de179e0737e 100644 --- a/src/agents/embedded-agent-runner/replay-history.test.ts +++ b/src/agents/embedded-agent-runner/replay-history.test.ts @@ -1,6 +1,7 @@ // Coverage for normalizing assistant replay content before provider requests. import type { AgentMessage } from "openclaw/plugin-sdk/agent-core"; import { describe, expect, it } from "vitest"; +import { OPENCLAW_TRANSCRIPT_ARTIFACT_API } from "../../shared/transcript-only-openclaw-assistant.js"; import { INTERNAL_RUNTIME_CONTEXT_BEGIN, INTERNAL_RUNTIME_CONTEXT_END, @@ -50,7 +51,7 @@ function openclawTranscriptAssistant(model: "delivery-mirror" | "gateway-injecte return { role: "assistant", content: [{ type: "text", text: "channel mirror" }], - api: "openai-responses", + api: OPENCLAW_TRANSCRIPT_ARTIFACT_API, provider: "openclaw", model, usage: { diff --git a/src/agents/embedded-agent-runner/run/attempt.session-lock.test.ts b/src/agents/embedded-agent-runner/run/attempt.session-lock.test.ts index 572e2ad3d035..b5fee600a735 100644 --- a/src/agents/embedded-agent-runner/run/attempt.session-lock.test.ts +++ b/src/agents/embedded-agent-runner/run/attempt.session-lock.test.ts @@ -16,6 +16,7 @@ import { withOwnedSessionTranscriptWrites, } from "../../../config/sessions/transcript-write-context.js"; import { appendExactAssistantMessageToSessionTranscript } from "../../../config/sessions/transcript.js"; +import { OPENCLAW_TRANSCRIPT_ARTIFACT_API } from "../../../shared/transcript-only-openclaw-assistant.js"; import { guardSessionManager } from "../../session-tool-result-guard-wrapper.js"; import { SessionWriteLockStaleError, @@ -2602,7 +2603,7 @@ describe("embedded attempt session lock lifecycle", () => { message: { role: "assistant", content: [{ type: "text", text: "first-turn delivery" }], - api: "openai-responses", + api: OPENCLAW_TRANSCRIPT_ARTIFACT_API, provider: "openclaw", model: "delivery-mirror", usage: { diff --git a/src/agents/session-file-repair.test.ts b/src/agents/session-file-repair.test.ts index 0ef333f93dec..f84b449c34df 100644 --- a/src/agents/session-file-repair.test.ts +++ b/src/agents/session-file-repair.test.ts @@ -6,6 +6,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { OPENCLAW_TRANSCRIPT_ARTIFACT_API } from "../shared/transcript-only-openclaw-assistant.js"; import { repairSessionFileIfNeeded } from "./session-file-repair.js"; const BLANK_USER_FALLBACK_TEXT = "(continue)"; @@ -832,7 +833,7 @@ describe("repairSessionFileIfNeeded", () => { role: "assistant", provider: "openclaw", model: "delivery-mirror", - api: "openai-responses", + api: OPENCLAW_TRANSCRIPT_ARTIFACT_API, content: [{ type: "text", text: "Process: `wild-wharf`" }], stopReason: "stop", }, @@ -892,7 +893,7 @@ describe("repairSessionFileIfNeeded", () => { role: "assistant", provider: "openclaw", model: "delivery-mirror", - api: "openai-responses", + api: OPENCLAW_TRANSCRIPT_ARTIFACT_API, content: [{ type: "text", text: "visible reply" }], stopReason: "stop", }, diff --git a/src/config/sessions/transcript.test.ts b/src/config/sessions/transcript.test.ts index 6861632bd7ae..d5c0c74954ff 100644 --- a/src/config/sessions/transcript.test.ts +++ b/src/config/sessions/transcript.test.ts @@ -6,6 +6,7 @@ import { beforeAll, describe, expect, it, vi } from "vitest"; import { repairToolUseResultPairing } from "../../agents/session-transcript-repair.js"; import * as transcriptEvents from "../../sessions/transcript-events.js"; import type { SessionTranscriptUpdate } from "../../sessions/transcript-events.js"; +import { OPENCLAW_TRANSCRIPT_ARTIFACT_API } from "../../shared/transcript-only-openclaw-assistant.js"; import { deleteTestEnvValue, setTestEnvValue } from "../../test-utils/env.js"; import { resolveSessionTranscriptPathInDir } from "./paths.js"; import { updateSessionStoreEntry } from "./store.js"; @@ -513,6 +514,7 @@ describe("appendAssistantMessageToSessionTranscript", () => { const message = event.message as | { role?: string; + api?: string; provider?: string; model?: string; content?: unknown; @@ -522,6 +524,7 @@ describe("appendAssistantMessageToSessionTranscript", () => { expect(event?.sessionKey).toBe(sessionKey); expect(event?.messageId).toBeTypeOf("string"); expect(message?.role).toBe("assistant"); + expect(message?.api).toBe(OPENCLAW_TRANSCRIPT_ARTIFACT_API); expect(message?.provider).toBe("openclaw"); expect(message?.model).toBe("delivery-mirror"); expect(message?.content).toEqual([{ type: "text", text: "Hello from delivery mirror!" }]); @@ -982,6 +985,7 @@ describe("appendAssistantMessageToSessionTranscript", () => { expect(lines.length).toBe(4); const messageLine = JSON.parse(lines[3]); + expect(messageLine.message.api).toBe(OPENCLAW_TRANSCRIPT_ARTIFACT_API); expect(messageLine.message.provider).toBe("openclaw"); expect(messageLine.message.model).toBe("delivery-mirror"); expect(messageLine.message.content[0].text).toBe("Repeated answer"); @@ -1023,6 +1027,7 @@ describe("appendAssistantMessageToSessionTranscript", () => { const linesAfterMirror = fs.readFileSync(sessionFile, "utf-8").trim().split("\n"); expect(linesAfterMirror).toHaveLength(3); const mirrorLine = JSON.parse(linesAfterMirror[2]); + expect(mirrorLine.message.api).toBe(OPENCLAW_TRANSCRIPT_ARTIFACT_API); expect(mirrorLine.message.model).toBe("delivery-mirror"); await appendSessionTranscriptMessage({ diff --git a/src/config/sessions/transcript.ts b/src/config/sessions/transcript.ts index a5e87420d647..d4af9d49e211 100644 --- a/src/config/sessions/transcript.ts +++ b/src/config/sessions/transcript.ts @@ -9,7 +9,12 @@ import { extractAssistantVisibleText, extractFirstTextBlock, } from "../../shared/chat-message-content.js"; -import { isTranscriptOnlyOpenClawAssistantModel } from "../../shared/transcript-only-openclaw-assistant.js"; +import { + OPENCLAW_DELIVERY_MIRROR_MODEL, + OPENCLAW_TRANSCRIPT_ARTIFACT_API, + OPENCLAW_TRANSCRIPT_ARTIFACT_PROVIDER, + isTranscriptOnlyOpenClawAssistantModel, +} from "../../shared/transcript-only-openclaw-assistant.js"; import type { OpenClawConfig } from "../types.openclaw.js"; import { resolveDefaultSessionStorePath, @@ -358,9 +363,9 @@ export async function appendAssistantMessageToSessionTranscript(params: { message: { role: "assistant" as const, content: [{ type: "text", text: mirrorText }], - api: "openai-responses", - provider: "openclaw", - model: "delivery-mirror", + api: OPENCLAW_TRANSCRIPT_ARTIFACT_API, + provider: OPENCLAW_TRANSCRIPT_ARTIFACT_PROVIDER, + model: OPENCLAW_DELIVERY_MIRROR_MODEL, usage: { input: 0, output: 0, @@ -548,7 +553,10 @@ export async function appendExactAssistantMessageToSessionTranscript(params: { } function isRedundantDeliveryMirror(message: SessionTranscriptAssistantMessage): boolean { - return message.provider === "openclaw" && message.model === "delivery-mirror"; + return ( + message.provider === OPENCLAW_TRANSCRIPT_ARTIFACT_PROVIDER && + message.model === OPENCLAW_DELIVERY_MIRROR_MODEL + ); } function isChannelFinalDeliveryMirror(message: SessionTranscriptAssistantMessage): boolean { diff --git a/src/gateway/sessions-history-http.test.ts b/src/gateway/sessions-history-http.test.ts index 7d2058434440..bea8f2b33bac 100644 --- a/src/gateway/sessions-history-http.test.ts +++ b/src/gateway/sessions-history-http.test.ts @@ -14,6 +14,7 @@ import { emitInternalSessionTranscriptUpdate, emitSessionTranscriptUpdate, } from "../sessions/transcript-events.js"; +import { OPENCLAW_TRANSCRIPT_ARTIFACT_API } from "../shared/transcript-only-openclaw-assistant.js"; import { testState } from "./test-helpers.runtime-state.js"; import { connectReq, @@ -126,11 +127,14 @@ function makeTranscriptAssistantMessage(params: { function makeDeliveryMirrorAssistantMessage( params: Parameters[0], ): AssistantMessage { - return makeTranscriptAssistantMessage({ - ...params, - provider: "openclaw", - model: "delivery-mirror", - }); + return { + ...makeTranscriptAssistantMessage({ + ...params, + provider: "openclaw", + model: "delivery-mirror", + }), + api: OPENCLAW_TRANSCRIPT_ARTIFACT_API, + }; } async function appendTranscriptMessage(params: { diff --git a/src/shared/transcript-only-openclaw-assistant.ts b/src/shared/transcript-only-openclaw-assistant.ts index 6e0f2b0445c1..c5c4d9700c7c 100644 --- a/src/shared/transcript-only-openclaw-assistant.ts +++ b/src/shared/transcript-only-openclaw-assistant.ts @@ -1,14 +1,19 @@ // Identifies OpenClaw-authored assistant rows that are transcript bookkeeping, // not provider model output. Some history surfaces keep gateway-injected rows // visible, so use the narrower delivery-mirror predicate when visibility matters. +export const OPENCLAW_TRANSCRIPT_ARTIFACT_API = "openclaw-transcript" as const; +export const OPENCLAW_TRANSCRIPT_ARTIFACT_PROVIDER = "openclaw" as const; +export const OPENCLAW_DELIVERY_MIRROR_MODEL = "delivery-mirror" as const; +export const OPENCLAW_GATEWAY_INJECTED_MODEL = "gateway-injected" as const; + const TRANSCRIPT_ONLY_OPENCLAW_ASSISTANT_MODELS = new Set([ - "delivery-mirror", - "gateway-injected", + OPENCLAW_DELIVERY_MIRROR_MODEL, + OPENCLAW_GATEWAY_INJECTED_MODEL, ]); export function isTranscriptOnlyOpenClawAssistantModel(provider: unknown, model: unknown): boolean { return ( - provider === "openclaw" && + provider === OPENCLAW_TRANSCRIPT_ARTIFACT_PROVIDER && typeof model === "string" && TRANSCRIPT_ONLY_OPENCLAW_ASSISTANT_MODELS.has(model) ); @@ -31,6 +36,8 @@ export function isOpenClawDeliveryMirrorAssistantMessage(message: unknown): bool } const entry = message as { role?: unknown; provider?: unknown; model?: unknown }; return ( - entry.role === "assistant" && entry.provider === "openclaw" && entry.model === "delivery-mirror" + entry.role === "assistant" && + entry.provider === OPENCLAW_TRANSCRIPT_ARTIFACT_PROVIDER && + entry.model === OPENCLAW_DELIVERY_MIRROR_MODEL ); }