mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-21 21:56:46 +00:00
refactor: resolve session transcripts through sqlite locators
This commit is contained in:
@@ -89,7 +89,7 @@ describe("session-updates lifecycle hooks", () => {
|
||||
});
|
||||
|
||||
it("emits compaction lifecycle hooks when newSessionId replaces the session", async () => {
|
||||
const { sessionKey, sessionStore, entry, transcriptPath } = await createFixture();
|
||||
const { sessionKey, sessionStore, entry } = await createFixture();
|
||||
const cfg = { session: {} } as OpenClawConfig;
|
||||
|
||||
await incrementCompactionCount({
|
||||
@@ -111,7 +111,9 @@ describe("session-updates lifecycle hooks", () => {
|
||||
sessionKey,
|
||||
reason: "compaction",
|
||||
});
|
||||
expect(endEvent?.sessionFile).toBe(path.resolve(transcriptPath));
|
||||
expect(endEvent?.sessionFile).toBe(
|
||||
createSqliteSessionTranscriptLocator({ agentId: "main", sessionId: "s1" }),
|
||||
);
|
||||
expect(endContext).toMatchObject({
|
||||
sessionId: "s1",
|
||||
sessionKey,
|
||||
|
||||
46
src/gateway/session-transcript-paths.test.ts
Normal file
46
src/gateway/session-transcript-paths.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createSqliteSessionTranscriptLocator } from "../config/sessions/paths.js";
|
||||
import {
|
||||
resolveSessionTranscriptCandidates,
|
||||
resolveStableSessionEndTranscript,
|
||||
} from "./session-transcript-paths.js";
|
||||
|
||||
describe("resolveSessionTranscriptCandidates", () => {
|
||||
it("returns sqlite locators and does not synthesize legacy jsonl paths", () => {
|
||||
expect(resolveSessionTranscriptCandidates("s2", path.join("/tmp", "s1.jsonl"), "main")).toEqual(
|
||||
[createSqliteSessionTranscriptLocator({ agentId: "main", sessionId: "s2" })],
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves explicit sqlite locators before generated agent locator candidates", () => {
|
||||
const topicLocator = createSqliteSessionTranscriptLocator({
|
||||
agentId: "main",
|
||||
sessionId: "s1",
|
||||
topicId: "alerts",
|
||||
});
|
||||
|
||||
expect(resolveSessionTranscriptCandidates("s2", topicLocator, "main")).toEqual([
|
||||
topicLocator,
|
||||
createSqliteSessionTranscriptLocator({ agentId: "main", sessionId: "s2" }),
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not return legacy paths when no agent can resolve a database locator", () => {
|
||||
expect(resolveSessionTranscriptCandidates("s1", path.join("/tmp", "s1.jsonl"))).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveStableSessionEndTranscript", () => {
|
||||
it("uses a generated sqlite locator instead of a legacy sessionFile path", () => {
|
||||
expect(
|
||||
resolveStableSessionEndTranscript({
|
||||
sessionId: "s1",
|
||||
sessionFile: path.join("/tmp", "s1.jsonl"),
|
||||
agentId: "main",
|
||||
}),
|
||||
).toEqual({
|
||||
sessionFile: createSqliteSessionTranscriptLocator({ agentId: "main", sessionId: "s1" }),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,51 +1,11 @@
|
||||
import path from "node:path";
|
||||
import {
|
||||
createSqliteSessionTranscriptLocator,
|
||||
isSqliteSessionTranscriptLocator,
|
||||
resolveSessionFilePath,
|
||||
} from "../config/sessions/paths.js";
|
||||
|
||||
function normalizeTranscriptLocator(value: string): string {
|
||||
return isSqliteSessionTranscriptLocator(value) ? value.trim() : path.resolve(value);
|
||||
}
|
||||
|
||||
function classifySessionTranscriptCandidate(
|
||||
sessionId: string,
|
||||
sessionFile?: string,
|
||||
): "current" | "stale" | "custom" {
|
||||
const transcriptSessionId = extractGeneratedTranscriptSessionId(sessionFile);
|
||||
if (!transcriptSessionId) {
|
||||
return "custom";
|
||||
}
|
||||
return transcriptSessionId === sessionId ? "current" : "stale";
|
||||
}
|
||||
|
||||
function extractGeneratedTranscriptSessionId(sessionFile?: string): string | undefined {
|
||||
const trimmed = sessionFile?.trim();
|
||||
if (!trimmed) {
|
||||
return undefined;
|
||||
}
|
||||
const base = path.basename(trimmed);
|
||||
if (!base.endsWith(".jsonl")) {
|
||||
return undefined;
|
||||
}
|
||||
const withoutExt = base.slice(0, -".jsonl".length);
|
||||
const topicIndex = withoutExt.indexOf("-topic-");
|
||||
if (topicIndex > 0) {
|
||||
const topicSessionId = withoutExt.slice(0, topicIndex);
|
||||
return looksLikeGeneratedSessionId(topicSessionId) ? topicSessionId : undefined;
|
||||
}
|
||||
const forkMatch = withoutExt.match(
|
||||
/^(\d{4}-\d{2}-\d{2}T[\w-]+(?:Z|[+-]\d{2}(?:-\d{2})?)?)_(.+)$/,
|
||||
);
|
||||
if (forkMatch?.[2]) {
|
||||
return looksLikeGeneratedSessionId(forkMatch[2]) ? forkMatch[2] : undefined;
|
||||
}
|
||||
return looksLikeGeneratedSessionId(withoutExt) ? withoutExt : undefined;
|
||||
}
|
||||
|
||||
function looksLikeGeneratedSessionId(value: string): boolean {
|
||||
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
||||
function normalizeTranscriptLocator(value: string | undefined): string | undefined {
|
||||
const trimmed = value?.trim();
|
||||
return trimmed && isSqliteSessionTranscriptLocator(trimmed) ? trimmed : undefined;
|
||||
}
|
||||
|
||||
export function resolveSessionTranscriptCandidates(
|
||||
@@ -54,7 +14,6 @@ export function resolveSessionTranscriptCandidates(
|
||||
agentId?: string,
|
||||
): string[] {
|
||||
const candidates: string[] = [];
|
||||
const sessionFileState = classifySessionTranscriptCandidate(sessionId, sessionFile);
|
||||
const pushCandidate = (resolve: () => string): void => {
|
||||
try {
|
||||
candidates.push(resolve());
|
||||
@@ -63,29 +22,13 @@ export function resolveSessionTranscriptCandidates(
|
||||
}
|
||||
};
|
||||
|
||||
if (sessionFile) {
|
||||
if (agentId) {
|
||||
if (sessionFileState === "custom") {
|
||||
const trimmed = sessionFile.trim();
|
||||
if (trimmed) {
|
||||
candidates.push(normalizeTranscriptLocator(trimmed));
|
||||
}
|
||||
} else if (sessionFileState !== "stale") {
|
||||
pushCandidate(() => resolveSessionFilePath(sessionId, { sessionFile }, { agentId }));
|
||||
}
|
||||
} else {
|
||||
const trimmed = sessionFile.trim();
|
||||
if (trimmed) {
|
||||
candidates.push(normalizeTranscriptLocator(trimmed));
|
||||
}
|
||||
}
|
||||
const normalizedSessionFile = normalizeTranscriptLocator(sessionFile);
|
||||
if (normalizedSessionFile) {
|
||||
candidates.push(normalizedSessionFile);
|
||||
}
|
||||
|
||||
if (agentId) {
|
||||
pushCandidate(() => createSqliteSessionTranscriptLocator({ sessionId, agentId }));
|
||||
if (sessionFile && sessionFileState === "stale") {
|
||||
pushCandidate(() => resolveSessionFilePath(sessionId, { sessionFile }, { agentId }));
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(new Set(candidates));
|
||||
@@ -96,9 +39,9 @@ export function resolveStableSessionEndTranscript(params: {
|
||||
sessionFile?: string;
|
||||
agentId?: string;
|
||||
}): { sessionFile?: string } {
|
||||
const stablePath = params.sessionFile?.trim();
|
||||
const stablePath = normalizeTranscriptLocator(params.sessionFile);
|
||||
if (stablePath) {
|
||||
return { sessionFile: normalizeTranscriptLocator(stablePath) };
|
||||
return { sessionFile: stablePath };
|
||||
}
|
||||
|
||||
const [candidate] = resolveSessionTranscriptCandidates(
|
||||
@@ -106,5 +49,5 @@ export function resolveStableSessionEndTranscript(params: {
|
||||
params.sessionFile,
|
||||
params.agentId,
|
||||
);
|
||||
return candidate ? { sessionFile: normalizeTranscriptLocator(candidate) } : {};
|
||||
return candidate ? { sessionFile: candidate } : {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user