From 733bf86caa6d1e0693995d5ca40c04bccdceaa1d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 16 May 2026 04:15:58 +0100 Subject: [PATCH] fix: preserve session source sqlite paths --- .../combined-session-entries-gateway.ts | 9 ++- src/gateway/server-methods/sessions.ts | 7 ++- src/gateway/session-utils.subagent.test.ts | 58 ++++++++++++++++++- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/config/sessions/combined-session-entries-gateway.ts b/src/config/sessions/combined-session-entries-gateway.ts index d26b994f329..1fce4803a9d 100644 --- a/src/config/sessions/combined-session-entries-gateway.ts +++ b/src/config/sessions/combined-session-entries-gateway.ts @@ -15,6 +15,8 @@ import type { SessionEntry } from "./types.js"; function mergeSessionEntryIntoCombined(params: { cfg: OpenClawConfig; combined: Record; + sourceDatabasePathBySessionKey: Record; + sourceDatabasePath: string; entry: SessionEntry; agentId: string; canonicalKey: string; @@ -50,6 +52,7 @@ function mergeSessionEntryIntoCombined(params: { spawnedBy, }; } + params.sourceDatabasePathBySessionKey[canonicalKey] = params.sourceDatabasePath; } export function loadCombinedSessionEntriesForGateway( @@ -58,6 +61,7 @@ export function loadCombinedSessionEntriesForGateway( ): { databasePath: string; entries: Record; + sourceDatabasePathBySessionKey?: Record; } { const requestedAgentId = typeof opts.agentId === "string" && opts.agentId.trim() @@ -71,6 +75,7 @@ export function loadCombinedSessionEntriesForGateway( ) : resolveAllAgentSessionDatabaseTargetsSync(cfg); const combined: Record = {}; + const sourceDatabasePathBySessionKey: Record = {}; for (const target of targets) { const agentId = target.agentId; for (const { sessionKey: key, entry } of listSessionEntries({ @@ -85,6 +90,8 @@ export function loadCombinedSessionEntriesForGateway( mergeSessionEntryIntoCombined({ cfg, combined, + sourceDatabasePathBySessionKey, + sourceDatabasePath: target.databasePath, entry, agentId, canonicalKey, @@ -98,5 +105,5 @@ export function loadCombinedSessionEntriesForGateway( : targets.length === 1 ? targets[0].databasePath : "(multiple)"; - return { databasePath, entries: combined }; + return { databasePath, entries: combined, sourceDatabasePathBySessionKey }; } diff --git a/src/gateway/server-methods/sessions.ts b/src/gateway/server-methods/sessions.ts index 6ad14933c58..919258e2589 100644 --- a/src/gateway/server-methods/sessions.ts +++ b/src/gateway/server-methods/sessions.ts @@ -861,7 +861,11 @@ export const sessionsHandlers: GatewayRequestHandlers = { const payload = await measureDiagnosticsTimelineSpan( "gateway.sessions.list", async () => { - const { databasePath, entries: store } = measureDiagnosticsTimelineSpanSync( + const { + databasePath, + entries: store, + sourceDatabasePathBySessionKey, + } = measureDiagnosticsTimelineSpanSync( "gateway.sessions.list.store_load", () => { const loaded = loadCombinedSessionEntriesForGateway(cfg, { @@ -897,6 +901,7 @@ export const sessionsHandlers: GatewayRequestHandlers = { listSessionsFromStoreAsync({ cfg, databasePath, + sourceDatabasePathBySessionKey, store, modelCatalog, opts: p, diff --git a/src/gateway/session-utils.subagent.test.ts b/src/gateway/session-utils.subagent.test.ts index f151b95dec8..17bb01ffc3a 100644 --- a/src/gateway/session-utils.subagent.test.ts +++ b/src/gateway/session-utils.subagent.test.ts @@ -6,6 +6,7 @@ import { } from "../agents/subagent-registry.js"; import type { OpenClawConfig } from "../config/config.js"; import { getSessionEntry, upsertSessionEntry, type SessionEntry } from "../config/sessions.js"; +import { replaceSqliteSessionTranscriptEvents } from "../config/sessions/transcript-store.sqlite.js"; import { registerAgentRunContext, resetAgentRunContextForTest } from "../infra/agent-events.js"; import { openOpenClawAgentDatabase } from "../state/openclaw-agent-db.js"; import { withStateDirEnv } from "../test-helpers/state-dir-env.js"; @@ -1171,9 +1172,64 @@ describe("loadCombinedSessionEntriesForGateway includes SQLite-registered agents }, } as OpenClawConfig; - const { entries } = loadCombinedSessionEntriesForGateway(cfg); + const { entries, sourceDatabasePathBySessionKey } = loadCombinedSessionEntriesForGateway(cfg); expect(entries["agent:retired:archived-task"]?.sessionId).toBe("s-retired"); + expect(sourceDatabasePathBySessionKey?.["agent:retired:archived-task"]).toBe(databasePath); + }); + }); + + test("list rows read transcript fields from registered database paths", async () => { + await withStateDirEnv("openclaw-acp-registered-transcript-path-", async ({ tempRoot }) => { + const databasePath = path.join(tempRoot, "relocated", "work.sqlite"); + openOpenClawAgentDatabase({ agentId: "work", path: databasePath }); + upsertSessionEntry({ + agentId: "work", + path: databasePath, + sessionKey: "agent:work:task", + entry: { + sessionId: "s-work", + updatedAt: 400, + modelProvider: "openai", + model: "gpt-5.4", + }, + }); + replaceSqliteSessionTranscriptEvents({ + agentId: "work", + path: databasePath, + sessionId: "s-work", + events: [ + { type: "session", version: 1, id: "s-work" }, + { message: { role: "user", content: "relocated title" } }, + { message: { role: "assistant", content: "relocated last" } }, + ], + }); + const cfg = { + session: { + mainKey: "main", + }, + agents: { + list: [{ id: "main", default: true }, { id: "work" }], + }, + } as OpenClawConfig; + + const combined = loadCombinedSessionEntriesForGateway(cfg, { agentId: "work" }); + expect(Object.keys(combined.entries)).toContain("agent:work:task"); + const result = listSessionsFromStore({ + cfg, + databasePath: combined.databasePath, + sourceDatabasePathBySessionKey: combined.sourceDatabasePathBySessionKey, + store: combined.entries, + opts: { agentId: "work", includeDerivedTitles: true, includeLastMessage: true }, + }); + + expect(result.sessions[0]).toEqual( + expect.objectContaining({ + key: "agent:work:task", + derivedTitle: "relocated title", + lastMessagePreview: "relocated last", + }), + ); }); });