From 25fb84f66fd6dc588f22999986ea2372aea40cd5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 8 May 2026 15:22:39 +0100 Subject: [PATCH] test: use sqlite transcript locators in fixtures --- extensions/active-memory/index.test.ts | 30 +++++------ scripts/e2e/mcp-channels-seed.ts | 6 ++- .../session-runtime-context-docker-client.ts | 51 ++++++++----------- .../command/attempt-execution.cli.test.ts | 35 +++++++------ 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/extensions/active-memory/index.test.ts b/extensions/active-memory/index.test.ts index 6553cdb19a7..2082d9b0d13 100644 --- a/extensions/active-memory/index.test.ts +++ b/extensions/active-memory/index.test.ts @@ -147,7 +147,7 @@ describe("active-memory plugin", () => { | undefined; return entries?.find((entry) => entry.pluginId === "active-memory")?.lines ?? []; }; - const writeTranscriptJsonl = async (sessionFile: string, records: unknown[]) => { + const writeSqliteTranscriptEvents = async (sessionFile: string, records: unknown[]) => { const sessionId = path.basename(sessionFile, ".jsonl"); for (const record of records) { appendSqliteSessionTranscriptEvent({ @@ -1617,7 +1617,7 @@ describe("active-memory plugin", () => { }, }), ]; - await writeTranscriptJsonl( + await writeSqliteTranscriptEvents( params.sessionFile, lines.map((line) => JSON.parse(line) as unknown), ); @@ -1852,7 +1852,7 @@ describe("active-memory plugin", () => { }; runEmbeddedPiAgent.mockImplementationOnce( async (params: { sessionFile: string; abortSignal?: AbortSignal }) => { - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { type: "message", message: { role: "user", content: "ignore this user text" } }, { type: "message", @@ -1914,7 +1914,7 @@ describe("active-memory plugin", () => { runEmbeddedPiAgent.mockImplementationOnce( async (params: { sessionFile: string; abortSignal?: AbortSignal }) => { tempSessionFile = params.sessionFile; - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { type: "message", message: { role: "assistant", content: "temporary partial recall summary" }, @@ -1962,7 +1962,7 @@ describe("active-memory plugin", () => { }; runEmbeddedPiAgent.mockImplementationOnce( async (params: { sessionFile: string; abortSignal?: AbortSignal }) => { - await writeTranscriptJsonl(params.sessionFile, []); + await writeSqliteTranscriptEvents(params.sessionFile, []); return await waitForAbort(params.abortSignal); }, ); @@ -2024,7 +2024,7 @@ describe("active-memory plugin", () => { }; runEmbeddedPiAgent.mockImplementationOnce( async (params: { sessionFile: string; abortSignal?: AbortSignal }) => { - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { type: "message", message: { @@ -2070,7 +2070,7 @@ describe("active-memory plugin", () => { }; runEmbeddedPiAgent.mockImplementationOnce( async (params: { sessionFile: string; abortSignal?: AbortSignal }) => { - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { type: "message", message: { role: "assistant", content: "partial abort summary" }, @@ -2118,7 +2118,7 @@ describe("active-memory plugin", () => { updatedAt: 0, }; runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => { - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { type: "message", message: { role: "assistant", content: "must not be surfaced from generic errors" }, @@ -2143,7 +2143,7 @@ describe("active-memory plugin", () => { it("bounds partial assistant transcript reads by character cap for large JSONL files", async () => { const sessionFile = path.join(stateDir, "large-timeout-transcript.jsonl"); - await writeTranscriptJsonl( + await writeSqliteTranscriptEvents( sessionFile, Array.from({ length: 50 }, () => ({ type: "message", @@ -2169,7 +2169,7 @@ describe("active-memory plugin", () => { it("skips malformed JSONL lines when reading partial assistant transcripts", async () => { const sessionFile = path.join(stateDir, "malformed-timeout-transcript.jsonl"); - await writeTranscriptJsonl(sessionFile, [ + await writeSqliteTranscriptEvents(sessionFile, [ { type: "message", message: { role: "assistant", content: "valid partial summary" } }, ]); @@ -2183,7 +2183,7 @@ describe("active-memory plugin", () => { it("honors transcript maxLines caps for partial text and search debug reads", async () => { const sessionFile = path.join(stateDir, "max-lines-transcript.jsonl"); - await writeTranscriptJsonl(sessionFile, [ + await writeSqliteTranscriptEvents(sessionFile, [ { type: "message", message: { role: "user", content: "line one" }, @@ -2529,7 +2529,7 @@ describe("active-memory plugin", () => { hoisted.sessionStore[sessionKey] = { sessionId: "s-terminal-zero-hit", updatedAt: 0 }; runEmbeddedPiAgent.mockImplementationOnce( async (params: { sessionFile: string; abortSignal?: AbortSignal }) => { - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { message: { role: "toolResult", @@ -2574,7 +2574,7 @@ describe("active-memory plugin", () => { updatedAt: 0, }; runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => { - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { message: { role: "toolResult", @@ -2616,7 +2616,7 @@ describe("active-memory plugin", () => { hoisted.sessionStore[sessionKey] = { sessionId: "s-terminal-unavailable", updatedAt: 0 }; runEmbeddedPiAgent.mockImplementationOnce( async (params: { sessionFile: string; abortSignal?: AbortSignal }) => { - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { message: { role: "toolResult", @@ -2662,7 +2662,7 @@ describe("active-memory plugin", () => { }; plugin.register(api as unknown as OpenClawPluginApi); runEmbeddedPiAgent.mockImplementationOnce(async (params: { sessionFile: string }) => { - await writeTranscriptJsonl(params.sessionFile, [ + await writeSqliteTranscriptEvents(params.sessionFile, [ { message: { role: "toolResult", diff --git a/scripts/e2e/mcp-channels-seed.ts b/scripts/e2e/mcp-channels-seed.ts index a22d0ebbebf..144fcf9806d 100644 --- a/scripts/e2e/mcp-channels-seed.ts +++ b/scripts/e2e/mcp-channels-seed.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { createSqliteSessionTranscriptLocator } from "../../src/config/sessions/paths.ts"; import { upsertSessionEntry } from "../../src/config/sessions/store.ts"; import { replaceSqliteSessionTranscriptEvents } from "../../src/config/sessions/transcript-store.sqlite.ts"; import { resolveOpenClawAgentSqlitePath } from "../../src/state/openclaw-agent-db.ts"; @@ -10,7 +11,10 @@ async function main() { const stateDir = process.env.OPENCLAW_STATE_DIR?.trim() || path.join(os.homedir(), ".openclaw"); const configPath = process.env.OPENCLAW_CONFIG_PATH?.trim() || path.join(stateDir, "openclaw.json"); - const transcriptPath = path.join(stateDir, "agents", "main", "sessions", "sess-main.jsonl"); + const transcriptPath = createSqliteSessionTranscriptLocator({ + agentId: "main", + sessionId: "sess-main", + }); const now = Date.now(); await fs.mkdir(path.dirname(configPath), { recursive: true }); diff --git a/scripts/e2e/session-runtime-context-docker-client.ts b/scripts/e2e/session-runtime-context-docker-client.ts index 61994a40585..bb81601392d 100644 --- a/scripts/e2e/session-runtime-context-docker-client.ts +++ b/scripts/e2e/session-runtime-context-docker-client.ts @@ -5,7 +5,6 @@ import { spawnSync } from "node:child_process"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { SessionManager } from "@mariozechner/pi-coding-agent"; import { queueRuntimeContextForNextTurn, resolveRuntimeContextPromptParts, @@ -28,14 +27,6 @@ function assert(condition: unknown, message: string): asserts condition { } } -async function readJsonl(filePath: string): Promise { - const raw = await fs.readFile(filePath, "utf-8"); - return raw - .split(/\r?\n/) - .filter(Boolean) - .map((line) => JSON.parse(line) as TranscriptEntry); -} - function messageText(content: unknown): string { if (typeof content === "string") { return content; @@ -52,10 +43,8 @@ function messageText(content: unknown): string { .join(""); } -async function verifyRuntimeContextTranscriptShape(root: string) { - const sessionFile = path.join(root, ".openclaw", "agents", "main", "sessions", "runtime.jsonl"); - await fs.mkdir(path.dirname(sessionFile), { recursive: true }); - const sessionManager = SessionManager.open(sessionFile); +async function verifyRuntimeContextTranscriptShape() { + const entries: TranscriptEntry[] = []; const effectivePrompt = [ "visible ask", "", @@ -79,27 +68,29 @@ async function verifyRuntimeContextTranscriptShape(root: string) { session: { sendCustomMessage: async (message, options) => { assert(options?.deliverAs === "nextTurn", "runtime context was not queued for next turn"); - sessionManager.appendCustomMessageEntry( - message.customType, - message.content, - message.display, - message.details, - ); + entries.push({ + type: "custom_message", + customType: message.customType, + content: message.content, + display: message.display, + }); }, }, }); - sessionManager.appendMessage({ - role: "user", - content: promptSubmission.prompt, - timestamp: Date.now(), + entries.push({ + type: "message", + message: { + role: "user", + content: promptSubmission.prompt, + }, }); - sessionManager.appendMessage({ - role: "assistant", - content: "done", - timestamp: Date.now() + 1, + entries.push({ + type: "message", + message: { + role: "assistant", + content: "done", + }, }); - - const entries = await readJsonl(sessionFile); const customEntry = entries.find((entry) => entry.type === "custom_message"); assert(customEntry, "hidden runtime custom message was not persisted"); assert(customEntry.customType === "openclaw.runtime-context", "unexpected custom message type"); @@ -257,7 +248,7 @@ async function main() { process.env.OPENCLAW_STATE_DIR = path.join(root, ".openclaw"); process.env.OPENCLAW_CONFIG_PATH = path.join(process.env.OPENCLAW_STATE_DIR, "openclaw.json"); try { - await verifyRuntimeContextTranscriptShape(root); + await verifyRuntimeContextTranscriptShape(); await verifyDoctorRepair(root); console.log("session runtime context Docker E2E passed"); } finally { diff --git a/src/agents/command/attempt-execution.cli.test.ts b/src/agents/command/attempt-execution.cli.test.ts index 5fbffd86be3..4d3e2de750b 100644 --- a/src/agents/command/attempt-execution.cli.test.ts +++ b/src/agents/command/attempt-execution.cli.test.ts @@ -3,6 +3,7 @@ import os from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { SessionEntry } from "../../config/sessions.js"; +import { createSqliteSessionTranscriptLocator } from "../../config/sessions/paths.js"; import { listSessionEntries, upsertSessionEntry } from "../../config/sessions/store.js"; import { appendSessionTranscriptMessage } from "../../config/sessions/transcript-append.js"; import { loadSqliteSessionTranscriptEvents } from "../../config/sessions/transcript-store.sqlite.js"; @@ -64,6 +65,10 @@ function makeCliResult(text: string): EmbeddedPiRunResult { }; } +function sessionTranscriptLocator(sessionId: string): string { + return createSqliteSessionTranscriptLocator({ agentId: "main", sessionId }); +} + async function readSessionMessages(sessionFile: string) { return (await readSessionFileEntries(sessionFile)) .filter((entry) => entry.type === "message") @@ -139,7 +144,7 @@ describe("CLI attempt execution", () => { sessionId: params.sessionEntry.sessionId, sessionKey: params.sessionKey, sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(params.sessionEntry.sessionId), workspaceDir: tmpDir, body: params.body, isFallbackRetry: false, @@ -203,7 +208,7 @@ describe("CLI attempt execution", () => { sessionId: sessionEntry.sessionId, sessionKey, sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "retry this", isFallbackRetry: false, @@ -348,7 +353,7 @@ describe("CLI attempt execution", () => { sessionId: sessionEntry.sessionId, sessionKey, sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "continue", isFallbackRetry: false, @@ -596,7 +601,7 @@ describe("CLI attempt execution", () => { sessionId: sessionEntry.sessionId, sessionKey, sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "route this", isFallbackRetry: false, @@ -654,7 +659,7 @@ describe("CLI attempt execution", () => { sessionId: sessionEntry.sessionId, sessionKey, sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "route this", isFallbackRetry: false, @@ -708,7 +713,7 @@ describe("CLI attempt execution", () => { sessionId: sessionEntry.sessionId, sessionKey, sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "route this", isFallbackRetry: false, @@ -764,7 +769,7 @@ describe("CLI attempt execution", () => { sessionId: sessionEntry.sessionId, sessionKey, sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "raw prompt", isFallbackRetry: false, @@ -832,7 +837,7 @@ describe("CLI attempt execution", () => { sessionId: sessionEntry.sessionId, sessionKey, sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "cleanup", isFallbackRetry: false, @@ -897,7 +902,7 @@ describe("embedded attempt harness pinning", () => { sessionId: sessionEntry.sessionId, sessionKey: "agent:main:main", sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "continue", isFallbackRetry: false, @@ -947,7 +952,7 @@ describe("embedded attempt harness pinning", () => { sessionId: sessionEntry.sessionId, sessionKey: "agent:main:main", sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "continue", isFallbackRetry: false, @@ -1006,7 +1011,7 @@ describe("embedded attempt harness pinning", () => { sessionId: sessionEntry.sessionId, sessionKey: "agent:main:main", sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "continue", isFallbackRetry: false, @@ -1052,7 +1057,7 @@ describe("embedded attempt harness pinning", () => { sessionId: sessionEntry.sessionId, sessionKey: "agent:main:main", sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "start", isFallbackRetry: false, @@ -1097,7 +1102,7 @@ describe("embedded attempt harness pinning", () => { sessionId: sessionEntry.sessionId, sessionKey: "agent:main:main", sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "continue", isFallbackRetry: false, @@ -1150,7 +1155,7 @@ describe("embedded attempt harness pinning", () => { sessionId: sessionEntry.sessionId, sessionKey: "agent:main:main", sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "continue", isFallbackRetry: false, @@ -1204,7 +1209,7 @@ describe("embedded attempt harness pinning", () => { sessionId: sessionEntry.sessionId, sessionKey: "agent:main:main", sessionAgentId: "main", - sessionFile: path.join(tmpDir, "session.jsonl"), + sessionFile: sessionTranscriptLocator(sessionEntry.sessionId), workspaceDir: tmpDir, body: "fallback", isFallbackRetry: true,