test: use sqlite transcript locators in fixtures

This commit is contained in:
Peter Steinberger
2026-05-08 15:22:39 +01:00
parent 85e1458f92
commit 25fb84f66f
4 changed files with 61 additions and 61 deletions

View File

@@ -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",

View File

@@ -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 });

View File

@@ -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<TranscriptEntry[]> {
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 {

View File

@@ -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,