From 135aa4b38f4e50c81285b2e8d71fb629b952a3cd Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 8 May 2026 12:37:35 +0100 Subject: [PATCH] fix: preserve sqlite transcript locators --- src/config/sessions/transcript-append.ts | 9 +++- src/config/sessions/transcript.test.ts | 58 ++++++++++++++++++++---- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/config/sessions/transcript-append.ts b/src/config/sessions/transcript-append.ts index 05d4a31d1c4..c87fd6cd9c4 100644 --- a/src/config/sessions/transcript-append.ts +++ b/src/config/sessions/transcript-append.ts @@ -1,4 +1,5 @@ import path from "node:path"; +import { isSqliteSessionTranscriptLocator } from "./paths.js"; import { appendSqliteSessionTranscriptMessage as appendSqliteSessionTranscriptMessageAtomically } from "./transcript-store.sqlite.js"; async function loadCurrentSessionVersion(): Promise { @@ -38,7 +39,13 @@ export async function appendSessionTranscriptMessage(params: { agentId: scope.agentId, sessionId: scope.sessionId, sessionVersion, - ...(params.transcriptPath ? { transcriptPath: path.resolve(params.transcriptPath) } : {}), + ...(params.transcriptPath + ? { + transcriptPath: isSqliteSessionTranscriptLocator(params.transcriptPath) + ? params.transcriptPath + : path.resolve(params.transcriptPath), + } + : {}), cwd: params.cwd, message: params.message, now: () => params.now ?? Date.now(), diff --git a/src/config/sessions/transcript.test.ts b/src/config/sessions/transcript.test.ts index 1e40d50c6dc..2048788d9a6 100644 --- a/src/config/sessions/transcript.test.ts +++ b/src/config/sessions/transcript.test.ts @@ -3,8 +3,15 @@ import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import * as transcriptEvents from "../../sessions/transcript-events.js"; -import { closeOpenClawStateDatabaseForTest } from "../../state/openclaw-state-db.js"; -import { resolveSessionTranscriptPathInDir } from "./paths.js"; +import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js"; +import { + closeOpenClawStateDatabaseForTest, + openOpenClawStateDatabase, +} from "../../state/openclaw-state-db.js"; +import { + createSqliteSessionTranscriptLocator, + resolveSessionTranscriptPathInDir, +} from "./paths.js"; import { upsertSessionEntry } from "./store.js"; import { useTempSessionsFixture } from "./test-helpers.js"; import { appendSessionTranscriptMessage } from "./transcript-append.js"; @@ -21,6 +28,7 @@ import { import type { SessionEntry } from "./types.js"; afterEach(() => { + closeOpenClawAgentDatabasesForTest(); closeOpenClawStateDatabaseForTest(); vi.unstubAllEnvs(); }); @@ -128,15 +136,14 @@ describe("appendAssistantMessageToSessionTranscript", () => { await writeTranscriptStore(); const emitSpy = vi.spyOn(transcriptEvents, "emitSessionTranscriptUpdate"); - await appendAssistantMessageToSessionTranscript({ + const result = await appendAssistantMessageToSessionTranscript({ sessionKey, text: "Hello from delivery mirror!", }); - const sessionFile = resolveSessionTranscriptPathInDir(sessionId, fixture.sessionsDir()); expect(emitSpy).toHaveBeenCalledWith( expect.objectContaining({ - sessionFile, + sessionFile: result.ok ? result.sessionFile : expect.any(String), sessionKey, messageId: expect.any(String), message: expect.objectContaining({ @@ -412,10 +419,12 @@ describe("appendAssistantMessageToSessionTranscript", () => { expect(result.ok).toBe(true); if (result.ok) { - expect(emitSpy).toHaveBeenCalledWith({ - sessionFile: result.sessionFile, - sessionKey, - }); + expect(emitSpy).toHaveBeenCalledWith( + expect.objectContaining({ + sessionFile: result.sessionFile, + sessionKey, + }), + ); } emitSpy.mockRestore(); }); @@ -597,6 +606,37 @@ describe("appendAssistantMessageToSessionTranscript", () => { fs.rmSync(stateDir, { recursive: true, force: true }); }); + it("preserves virtual SQLite transcript locators instead of registering fake file paths", async () => { + const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-transcript-state-")); + const env = { OPENCLAW_STATE_DIR: stateDir }; + vi.stubEnv("OPENCLAW_STATE_DIR", stateDir); + const sessionId = "sqlite-locator-session"; + const sessionFile = createSqliteSessionTranscriptLocator({ agentId: "main", sessionId }); + + await appendSessionTranscriptMessage({ + transcriptPath: sessionFile, + agentId: "main", + sessionId, + cwd: "/workspace", + message: { role: "assistant", content: "locator reply" }, + now: 789, + }); + + const events = loadSqliteSessionTranscriptEvents({ + env, + agentId: "main", + sessionId, + }).map((entry) => entry.event as { type?: string; message?: unknown }); + expect(events.map((event) => event.type)).toEqual(["session", "message"]); + + const stateDatabase = openOpenClawStateDatabase({ env }); + expect( + stateDatabase.db.prepare("SELECT COUNT(*) AS count FROM transcript_files").get(), + ).toEqual({ count: 0 }); + + fs.rmSync(stateDir, { recursive: true, force: true }); + }); + it("reads latest and tail assistant text from scoped SQLite transcripts", async () => { const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-transcript-state-")); vi.stubEnv("OPENCLAW_STATE_DIR", stateDir);