From 8da24dfd6c73075377bab7aa72272c8e1cd93cc9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 8 May 2026 12:40:42 +0100 Subject: [PATCH] fix: keep sqlite transcript locators virtual --- src/agents/pi-embedded-helpers/bootstrap.ts | 6 +++++- .../compaction-successor-transcript.ts | 13 +++++++++++++ src/agents/transcript/transcript-state.ts | 8 ++++++-- src/gateway/server-methods/sessions.ts | 11 +++++++++-- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/agents/pi-embedded-helpers/bootstrap.ts b/src/agents/pi-embedded-helpers/bootstrap.ts index 1ebe963d497..dbc23b67054 100644 --- a/src/agents/pi-embedded-helpers/bootstrap.ts +++ b/src/agents/pi-embedded-helpers/bootstrap.ts @@ -1,4 +1,5 @@ import path from "node:path"; +import { isSqliteSessionTranscriptLocator } from "../../config/sessions.js"; import { appendSqliteSessionTranscriptEvent, hasSqliteSessionTranscriptEvents, @@ -256,7 +257,10 @@ export async function ensureSessionHeader(params: { agentId?: string; env?: OpenClawStateDatabaseOptions["env"]; }) { - const transcriptPath = path.resolve(params.sessionFile); + const trimmedSessionFile = params.sessionFile.trim(); + const transcriptPath = isSqliteSessionTranscriptLocator(trimmedSessionFile) + ? trimmedSessionFile + : path.resolve(trimmedSessionFile); const existingScope = resolveSqliteSessionTranscriptScopeForPath({ transcriptPath, env: params.env, diff --git a/src/agents/pi-embedded-runner/compaction-successor-transcript.ts b/src/agents/pi-embedded-runner/compaction-successor-transcript.ts index 89fe0400172..93eb3c66939 100644 --- a/src/agents/pi-embedded-runner/compaction-successor-transcript.ts +++ b/src/agents/pi-embedded-runner/compaction-successor-transcript.ts @@ -1,5 +1,9 @@ import { randomUUID } from "node:crypto"; import path from "node:path"; +import { + createSqliteSessionTranscriptLocator, + isSqliteSessionTranscriptLocator, +} from "../../config/sessions.js"; import { loadSqliteSessionTranscriptEvents, replaceSqliteSessionTranscriptEvents, @@ -358,6 +362,15 @@ function resolveSuccessorTranscriptPath(params: { sessionId: string; timestamp: string; }): string { + if (isSqliteSessionTranscriptLocator(params.transcriptPath)) { + const existing = resolveSqliteSessionTranscriptScopeForPath({ + transcriptPath: params.transcriptPath, + }); + return createSqliteSessionTranscriptLocator({ + agentId: existing?.agentId, + sessionId: params.sessionId, + }); + } const fileTimestamp = params.timestamp.replace(/[:.]/g, "-"); return path.join( path.dirname(params.transcriptPath), diff --git a/src/agents/transcript/transcript-state.ts b/src/agents/transcript/transcript-state.ts index 780cd6a8146..b8f4d51b987 100644 --- a/src/agents/transcript/transcript-state.ts +++ b/src/agents/transcript/transcript-state.ts @@ -1,5 +1,6 @@ import { randomUUID } from "node:crypto"; import path from "node:path"; +import { isSqliteSessionTranscriptLocator } from "../../config/sessions.js"; import { appendSqliteSessionTranscriptEvent, loadSqliteSessionTranscriptEvents, @@ -82,8 +83,11 @@ function resolveTranscriptWriteScope( sessionFile: string, entries: Array, ): { agentId: string; sessionId: string; transcriptPath: string } | undefined { + const transcriptPath = isSqliteSessionTranscriptLocator(sessionFile) + ? sessionFile + : path.resolve(sessionFile); const header = entries.find((entry): entry is SessionHeader => entry.type === "session"); - const existing = resolveSqliteSessionTranscriptScopeForPath({ transcriptPath: sessionFile }); + const existing = resolveSqliteSessionTranscriptScopeForPath({ transcriptPath }); const sessionId = header?.id ?? existing?.sessionId; if (!sessionId) { return undefined; @@ -91,7 +95,7 @@ function resolveTranscriptWriteScope( return { agentId: existing?.agentId ?? resolveAgentIdFromTranscriptPath(sessionFile), sessionId, - transcriptPath: path.resolve(sessionFile), + transcriptPath, }; } diff --git a/src/gateway/server-methods/sessions.ts b/src/gateway/server-methods/sessions.ts index 3acc2e4fd3b..fa1209706a1 100644 --- a/src/gateway/server-methods/sessions.ts +++ b/src/gateway/server-methods/sessions.ts @@ -19,6 +19,7 @@ import { resolveMainSessionKey, resolveSessionFilePath, resolveSessionFilePathOptions, + isSqliteSessionTranscriptLocator, type SessionEntry, upsertSessionEntry, } from "../../config/sessions.js"; @@ -1281,7 +1282,10 @@ export const sessionsHandlers: GatewayRequestHandlers = { agentId: target.agentId, sourceFile: checkpoint.preCompaction.sessionFile, sourceSessionId: checkpoint.preCompaction.sessionId, - sessionDir: entry.sessionFile ? path.dirname(entry.sessionFile) : undefined, + sessionDir: + entry.sessionFile && !isSqliteSessionTranscriptLocator(entry.sessionFile) + ? path.dirname(entry.sessionFile) + : undefined, }); if (!branchedSession?.sessionFile) { respond( @@ -1399,7 +1403,10 @@ export const sessionsHandlers: GatewayRequestHandlers = { agentId: target.agentId, sourceFile: checkpoint.preCompaction.sessionFile, sourceSessionId: checkpoint.preCompaction.sessionId, - sessionDir: entry.sessionFile ? path.dirname(entry.sessionFile) : undefined, + sessionDir: + entry.sessionFile && !isSqliteSessionTranscriptLocator(entry.sessionFile) + ? path.dirname(entry.sessionFile) + : undefined, }); if (!restoredSession?.sessionFile) { respond(