refactor: drop legacy transcript path builders

This commit is contained in:
Peter Steinberger
2026-05-08 17:39:33 +01:00
parent ae58f2f621
commit 4db36bf370
8 changed files with 32 additions and 94 deletions

View File

@@ -1,4 +1,3 @@
import path from "node:path";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
import type { ResolvedAgentRoute } from "openclaw/plugin-sdk/routing";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@@ -55,7 +54,6 @@ const sessionMocks = vi.hoisted(() => {
),
recordSessionMetaFromInbound: vi.fn(),
resolveAndPersistSessionFile: vi.fn(),
resolveSessionTranscriptPath: vi.fn(),
sessionStore,
};
});
@@ -163,7 +161,6 @@ vi.mock("openclaw/plugin-sdk/session-store-runtime", async () => {
getSessionEntry: sessionMocks.getSessionEntry,
listSessionEntries: sessionMocks.listSessionEntries,
resolveAndPersistSessionFile: sessionMocks.resolveAndPersistSessionFile,
resolveSessionTranscriptPath: sessionMocks.resolveSessionTranscriptPath,
};
});
vi.mock("openclaw/plugin-sdk/command-auth-native", async () => {
@@ -499,13 +496,6 @@ describe("registerTelegramNativeCommands — session metadata", () => {
},
};
});
sessionMocks.resolveSessionTranscriptPath
.mockClear()
.mockImplementation((sessionId: string, _agentId?: string, threadId?: string | number) =>
threadId === undefined
? `/tmp/openclaw-sessions/${sessionId}.jsonl`
: `/tmp/openclaw-sessions/${sessionId}-topic-${threadId}.jsonl`,
);
pluginRuntimeMocks.executePluginCommand.mockClear().mockResolvedValue({ text: "ok" });
pluginRuntimeMocks.matchPluginCommand.mockClear().mockReturnValue(null);
replyMocks.dispatchReplyWithBufferedBlockDispatcher
@@ -1211,14 +1201,14 @@ describe("registerTelegramNativeCommands — session metadata", () => {
expect.objectContaining({
sessionId: "sess-topic",
sessionKey: "agent:main:telegram:group:-1001234567890:topic:42",
fallbackSessionFile: path.resolve("/tmp/openclaw-sessions", "sess-topic-topic-42.jsonl"),
fallbackSessionFile: "sqlite-transcript://main/sess-topic.jsonl",
}),
);
expect(pluginRuntimeMocks.executePluginCommand).toHaveBeenCalledWith(
expect.objectContaining({
sessionKey: "agent:main:telegram:group:-1001234567890:topic:42",
sessionId: "sess-topic",
sessionFile: path.resolve("/tmp/openclaw-sessions", "sess-topic-topic-42.jsonl"),
sessionFile: "sqlite-transcript://main/sess-topic.jsonl",
messageThreadId: 42,
}),
);

View File

@@ -24,7 +24,7 @@ export function resolveBtwSessionTranscriptPath(params: {
return createSqliteSessionTranscriptLocator({ agentId, sessionId: params.sessionId });
} catch (error) {
diag.debug(
`resolveSessionTranscriptPath failed: sessionId=${params.sessionId} err=${String(error)}`,
`createSqliteSessionTranscriptLocator failed: sessionId=${params.sessionId} err=${String(error)}`,
);
return undefined;
}

View File

@@ -12,11 +12,7 @@ import {
resolveSessionKey,
updateLastRoute,
} from "./sessions.js";
import {
resolveSessionFilePath,
resolveSessionTranscriptPath,
resolveSessionTranscriptsDir,
} from "./sessions/paths.js";
import { resolveSessionFilePath, resolveSessionTranscriptsDir } from "./sessions/paths.js";
import {
deleteSessionEntry,
listSessionEntries,
@@ -611,21 +607,6 @@ describe("sessions", () => {
expect(dir).toBe(path.join(path.resolve("/custom/state"), "agents", "main", "sessions"));
});
it("includes topic ids in session transcript filenames", () => {
withStateDir("/custom/state", () => {
const sessionFile = resolveSessionTranscriptPath("sess-1", "main", 123);
expect(sessionFile).toBe(
path.join(
path.resolve("/custom/state"),
"agents",
"main",
"sessions",
"sess-1-topic-123.jsonl",
),
);
});
});
it("uses agent id when resolving session file fallback paths", () => {
withStateDir("/custom/state", () => {
const sessionFile = resolveSessionFilePath("sess-2", undefined, {

View File

@@ -99,33 +99,6 @@ export function isSqliteSessionTranscriptLocator(locator: string | undefined): b
return typeof locator === "string" && parseSqliteSessionTranscriptLocator(locator) !== undefined;
}
export function resolveSessionTranscriptPathInDir(
sessionId: string,
sessionsDir: string,
topicId?: string | number,
): string {
const safeSessionId = validateSessionId(sessionId);
const safeTopicId =
typeof topicId === "string"
? encodeURIComponent(topicId)
: typeof topicId === "number"
? String(topicId)
: undefined;
const fileName =
safeTopicId !== undefined
? `${safeSessionId}-topic-${safeTopicId}.jsonl`
: `${safeSessionId}.jsonl`;
return path.resolve(sessionsDir, fileName);
}
export function resolveSessionTranscriptPath(
sessionId: string,
agentId?: string,
topicId?: string | number,
): string {
return resolveSessionTranscriptPathInDir(sessionId, resolveAgentSessionsDir(agentId), topicId);
}
export function resolveSessionFilePath(
sessionId: string,
entry?: { sessionFile?: string },

View File

@@ -9,7 +9,6 @@ import { resolveSessionLifecycleTimestamps } from "./lifecycle.js";
import {
createSqliteSessionTranscriptLocator,
resolveSessionFilePath,
resolveSessionTranscriptPathInDir,
validateSessionId,
} from "./paths.js";
import { evaluateSessionFreshness, resolveSessionResetPolicy } from "./reset.js";
@@ -38,13 +37,6 @@ describe("session path safety", () => {
}
});
it("resolves transcript path inside an explicit sessions dir", () => {
const sessionsDir = "/tmp/openclaw/agents/main/sessions";
const resolved = resolveSessionTranscriptPathInDir("sess-1", sessionsDir, "topic/a+b");
expect(resolved).toBe(path.resolve(sessionsDir, "sess-1-topic-topic%2Fa%2Bb.jsonl"));
});
it("ignores legacy sessionFile paths", () => {
const resolved = resolveSessionFilePath("sess-1", {
sessionFile: "/tmp/openclaw/agents/work/not-sessions/abc-123.jsonl",
@@ -574,7 +566,7 @@ describe("resolveAndPersistSessionFile", () => {
it("creates and persists a SQLite locator when session is not yet present", async () => {
const sessionId = "new-session-id";
const sessionKey = "agent:main:telegram:group:123";
const fallbackSessionFile = resolveSessionTranscriptPathInDir(sessionId, fixture.sessionsDir());
const fallbackSessionFile = path.join(fixture.sessionsDir(), `${sessionId}.jsonl`);
const expectedSessionFile = createSqliteSessionTranscriptLocator({
agentId: "main",
sessionId,
@@ -596,7 +588,7 @@ describe("resolveAndPersistSessionFile", () => {
it("normalizes legacy stored transcript paths to SQLite locators", async () => {
const sessionId = "legacy-path-session-id";
const sessionKey = "agent:main:telegram:group:456";
const legacySessionFile = resolveSessionTranscriptPathInDir(sessionId, fixture.sessionsDir());
const legacySessionFile = path.join(fixture.sessionsDir(), `${sessionId}.jsonl`);
const expectedSessionFile = createSqliteSessionTranscriptLocator({
agentId: "main",
sessionId,
@@ -626,10 +618,7 @@ describe("resolveAndPersistSessionFile", () => {
const previousSessionId = "old-session-id";
const nextSessionId = "new-session-id";
const sessionKey = "agent:main:telegram:group:123";
const previousSessionFile = resolveSessionTranscriptPathInDir(
previousSessionId,
fixture.sessionsDir(),
);
const previousSessionFile = path.join(fixture.sessionsDir(), `${previousSessionId}.jsonl`);
const expectedNextSessionFile = createSqliteSessionTranscriptLocator({
agentId: "main",
sessionId: nextSessionId,

View File

@@ -8,10 +8,7 @@ import {
closeOpenClawStateDatabaseForTest,
openOpenClawStateDatabase,
} from "../../state/openclaw-state-db.js";
import {
createSqliteSessionTranscriptLocator,
resolveSessionTranscriptPathInDir,
} from "./paths.js";
import { createSqliteSessionTranscriptLocator } from "./paths.js";
import { upsertSessionEntry } from "./store.js";
import { useTempSessionsFixture } from "./test-helpers.js";
import { appendSessionTranscriptMessage } from "./transcript-append.js";
@@ -431,7 +428,10 @@ describe("appendAssistantMessageToSessionTranscript", () => {
it("serializes concurrent parent-linked transcript appends", async () => {
const targetSessionId = "concurrent-tree-session";
const sessionFile = resolveSessionTranscriptPathInDir(targetSessionId, fixture.sessionsDir());
const sessionFile = createSqliteSessionTranscriptLocator({
agentId: "main",
sessionId: targetSessionId,
});
appendSqliteSessionTranscriptEvent({
agentId: "main",
sessionId: targetSessionId,
@@ -470,7 +470,10 @@ describe("appendAssistantMessageToSessionTranscript", () => {
it("appends to existing SQLite transcript chains", async () => {
const targetSessionId = "small-linear-session";
const sessionFile = resolveSessionTranscriptPathInDir(targetSessionId, fixture.sessionsDir());
const sessionFile = createSqliteSessionTranscriptLocator({
agentId: "main",
sessionId: targetSessionId,
});
appendSqliteSessionTranscriptEvent({
agentId: "main",
sessionId: targetSessionId,
@@ -525,10 +528,10 @@ describe("appendAssistantMessageToSessionTranscript", () => {
it("appends scoped SQLite transcript entries without importing JSONL at runtime", async () => {
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-transcript-state-"));
vi.stubEnv("OPENCLAW_STATE_DIR", stateDir);
const sessionFile = resolveSessionTranscriptPathInDir(
"sqlite-import-session",
fixture.sessionsDir(),
);
const sessionFile = createSqliteSessionTranscriptLocator({
agentId: "main",
sessionId: "sqlite-import-session",
});
appendSqliteSessionTranscriptEvent({
agentId: "main",
sessionId: "sqlite-import-session",
@@ -572,10 +575,10 @@ describe("appendAssistantMessageToSessionTranscript", () => {
it("mirrors a newly created scoped transcript header into SQLite", async () => {
const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-transcript-state-"));
vi.stubEnv("OPENCLAW_STATE_DIR", stateDir);
const sessionFile = resolveSessionTranscriptPathInDir(
"sqlite-new-session",
fixture.sessionsDir(),
);
const sessionFile = createSqliteSessionTranscriptLocator({
agentId: "main",
sessionId: "sqlite-new-session",
});
const appended = await appendSessionTranscriptMessage({
transcriptPath: sessionFile,

View File

@@ -84,7 +84,7 @@ const normalizeVerboseLevelMock = createMock();
export const isThinkingLevelSupportedMock = createMock();
export const resolveSupportedThinkingLevelMock = createMock();
const supportsXHighThinkingMock = createMock();
const resolveSessionTranscriptPathMock = createMock();
const createSqliteSessionTranscriptLocatorMock = createMock();
const setSessionRuntimeModelMock = createMock();
const registerAgentRunContextMock = createMock();
const buildSafeExternalPromptMock = createMock();
@@ -117,7 +117,7 @@ vi.mock("./run.runtime.js", () => ({
isThinkingLevelSupported: isThinkingLevelSupportedMock,
resolveSupportedThinkingLevel: resolveSupportedThinkingLevelMock,
supportsXHighThinking: supportsXHighThinkingMock,
resolveSessionTranscriptPath: resolveSessionTranscriptPathMock,
createSqliteSessionTranscriptLocator: createSqliteSessionTranscriptLocatorMock,
setSessionRuntimeModel: setSessionRuntimeModelMock,
setCliSessionId: vi.fn(),
logWarn: (...args: unknown[]) => logWarnMock(...args),
@@ -174,7 +174,6 @@ vi.mock("./run-execution.runtime.js", () => ({
countActiveDescendantRuns: countActiveDescendantRunsMock,
listDescendantRunsForRequester: listDescendantRunsForRequesterMock,
normalizeVerboseLevel: normalizeVerboseLevelMock,
resolveSessionTranscriptPath: resolveSessionTranscriptPathMock,
registerAgentRunContext: registerAgentRunContextMock,
logWarn: (...args: unknown[]) => logWarnMock(...args),
}));
@@ -357,7 +356,10 @@ function resetRunExecutionMocks(): void {
resolveFastModeStateMock.mockImplementation((params) => resolveFastModeStateImpl(params));
resolveCronAgentLaneMock.mockReturnValue(undefined);
normalizeVerboseLevelMock.mockImplementation((value: unknown) => value ?? "off");
resolveSessionTranscriptPathMock.mockReturnValue("/tmp/transcript.jsonl");
createSqliteSessionTranscriptLocatorMock.mockImplementation(
({ agentId = "main", sessionId }: { agentId?: string; sessionId: string }) =>
`sqlite-transcript://${agentId}/${sessionId}.jsonl`,
);
registerAgentRunContextMock.mockReturnValue(undefined);
runWithModelFallbackMock.mockReset();
runWithModelFallbackMock.mockResolvedValue(makeDefaultModelFallbackResult());

View File

@@ -1,7 +1,7 @@
import fs from "node:fs/promises";
import path from "node:path";
import { afterAll, beforeAll, beforeEach, describe, expect, it, type Mock } from "vitest";
import { resolveSessionTranscriptPath } from "../config/sessions/paths.js";
import { createSqliteSessionTranscriptLocator } from "../config/sessions/paths.js";
import { replaceSqliteSessionTranscriptEvents } from "../config/sessions/transcript-store.sqlite.js";
import { emitAgentEvent } from "../infra/agent-events.js";
import { captureEnv } from "../test-utils/env.js";
@@ -51,7 +51,7 @@ async function emitLifecycleAssistantReply(params: {
};
const sessionId = commandParams.sessionId ?? params.defaultSessionId;
const runId = commandParams.runId ?? sessionId;
const sessionFile = resolveSessionTranscriptPath(sessionId);
const sessionFile = createSqliteSessionTranscriptLocator({ agentId: "main", sessionId });
const startedAt = Date.now();
emitAgentEvent({