mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-11 12:58:34 +00:00
refactor: preserve custom session transcript paths
This commit is contained in:
@@ -319,16 +319,14 @@ describe("forkSessionFromParentRuntime", () => {
|
||||
if (fork === null) {
|
||||
throw new Error("Expected forked session");
|
||||
}
|
||||
const agentSessionsDir = path.join(root, "agents", "main", "sessions");
|
||||
expect(fork.sessionFile).toBe(fork.sessionId);
|
||||
expect(fork.sessionId).not.toBe(parentSessionId);
|
||||
const forkedEntries = readTranscript("main", fork.sessionId) as Array<Record<string, unknown>>;
|
||||
const resolvedParentSessionFile = path.join(agentSessionsDir, `${parentSessionId}.jsonl`);
|
||||
expect(forkedEntries[0]).toMatchObject({
|
||||
type: "session",
|
||||
id: fork.sessionId,
|
||||
cwd,
|
||||
parentSession: resolvedParentSessionFile,
|
||||
parentSession: path.resolve(parentSessionFile),
|
||||
});
|
||||
expect(forkedEntries.map((entry) => entry.type)).toEqual([
|
||||
"session",
|
||||
@@ -377,17 +375,10 @@ describe("forkSessionFromParentRuntime", () => {
|
||||
}
|
||||
const entries = readTranscript("main", fork.sessionId) as Array<Record<string, unknown>>;
|
||||
expect(entries).toHaveLength(1);
|
||||
const resolvedParentSessionFile = path.join(
|
||||
root,
|
||||
"agents",
|
||||
"main",
|
||||
"sessions",
|
||||
`${parentSessionId}.jsonl`,
|
||||
);
|
||||
expect(entries[0]).toMatchObject({
|
||||
type: "session",
|
||||
id: fork.sessionId,
|
||||
parentSession: resolvedParentSessionFile,
|
||||
parentSession: path.resolve(parentSessionFile),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import crypto from "node:crypto";
|
||||
import path from "node:path";
|
||||
import {
|
||||
CURRENT_SESSION_VERSION,
|
||||
migrateSessionEntries,
|
||||
@@ -54,11 +55,7 @@ async function estimateParentTranscriptTokensFromSqlite(params: {
|
||||
agentId: string;
|
||||
}): Promise<number | undefined> {
|
||||
try {
|
||||
const filePath = resolveSessionFilePath(
|
||||
params.parentEntry.sessionId,
|
||||
params.parentEntry,
|
||||
resolveSessionFilePathOptions({ agentId: params.agentId }),
|
||||
);
|
||||
const filePath = resolveForkParentSessionFile(params.parentEntry, params.agentId);
|
||||
const scope = resolveSqliteSessionTranscriptScope({
|
||||
agentId: params.agentId,
|
||||
sessionId: params.parentEntry.sessionId,
|
||||
@@ -77,6 +74,18 @@ async function estimateParentTranscriptTokensFromSqlite(params: {
|
||||
}
|
||||
}
|
||||
|
||||
function resolveForkParentSessionFile(parentEntry: StoreSessionEntry, agentId: string): string {
|
||||
const sessionFile = parentEntry.sessionFile?.trim();
|
||||
if (sessionFile && path.isAbsolute(sessionFile)) {
|
||||
return path.resolve(sessionFile);
|
||||
}
|
||||
return resolveSessionFilePath(
|
||||
parentEntry.sessionId,
|
||||
parentEntry,
|
||||
resolveSessionFilePathOptions({ agentId }),
|
||||
);
|
||||
}
|
||||
|
||||
export async function resolveParentForkTokenCountRuntime(params: {
|
||||
parentEntry: StoreSessionEntry;
|
||||
agentId: string;
|
||||
@@ -298,11 +307,7 @@ export async function forkSessionFromParentRuntime(params: {
|
||||
parentEntry: StoreSessionEntry;
|
||||
agentId: string;
|
||||
}): Promise<{ sessionId: string; sessionFile: string } | null> {
|
||||
const parentSessionFile = resolveSessionFilePath(
|
||||
params.parentEntry.sessionId,
|
||||
params.parentEntry,
|
||||
{ agentId: params.agentId },
|
||||
);
|
||||
const parentSessionFile = resolveForkParentSessionFile(params.parentEntry, params.agentId);
|
||||
if (!parentSessionFile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -119,9 +119,7 @@ afterAll(async () => {
|
||||
async function makeCaseDir(prefix: string): Promise<string> {
|
||||
const stateDir = path.join(suiteRoot, `${prefix}${++suiteCase}`);
|
||||
vi.stubEnv("OPENCLAW_STATE_DIR", stateDir);
|
||||
const sessionsDir = path.join(stateDir, "agents", "main", "sessions");
|
||||
await fs.mkdir(sessionsDir, { recursive: true });
|
||||
return sessionsDir;
|
||||
return path.join(stateDir, "transcript-fixtures", "main");
|
||||
}
|
||||
|
||||
type TestSessionRowsTarget = {
|
||||
@@ -353,11 +351,10 @@ describe("initSessionState thread forking", () => {
|
||||
it("forks a new session from the parent session file", async () => {
|
||||
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
const root = await makeCaseDir("openclaw-thread-session-");
|
||||
const sessionsDir = path.join(root, "sessions");
|
||||
await fs.mkdir(sessionsDir);
|
||||
const transcriptDir = path.join(root, "thread-transcripts");
|
||||
|
||||
const parentSessionId = "parent-session";
|
||||
const parentSessionFile = path.join(sessionsDir, "parent.jsonl");
|
||||
const parentSessionFile = path.join(transcriptDir, "parent.jsonl");
|
||||
const header = {
|
||||
type: "session",
|
||||
version: 3,
|
||||
@@ -438,11 +435,10 @@ describe("initSessionState thread forking", () => {
|
||||
it("forks from parent when thread session key already exists but was not forked yet", async () => {
|
||||
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
const root = await makeCaseDir("openclaw-thread-session-existing-");
|
||||
const sessionsDir = path.join(root, "sessions");
|
||||
await fs.mkdir(sessionsDir);
|
||||
const transcriptDir = path.join(root, "thread-transcripts");
|
||||
|
||||
const parentSessionId = "parent-session";
|
||||
const parentSessionFile = path.join(sessionsDir, "parent.jsonl");
|
||||
const parentSessionFile = path.join(transcriptDir, "parent.jsonl");
|
||||
const header = {
|
||||
type: "session",
|
||||
version: 3,
|
||||
@@ -520,11 +516,10 @@ describe("initSessionState thread forking", () => {
|
||||
|
||||
it("skips fork and creates fresh session when parent tokens exceed threshold", async () => {
|
||||
const root = await makeCaseDir("openclaw-thread-session-overflow-");
|
||||
const sessionsDir = path.join(root, "sessions");
|
||||
await fs.mkdir(sessionsDir);
|
||||
const transcriptDir = path.join(root, "thread-transcripts");
|
||||
|
||||
const parentSessionId = "parent-overflow";
|
||||
const parentSessionFile = path.join(sessionsDir, "parent.jsonl");
|
||||
const parentSessionFile = path.join(transcriptDir, "parent.jsonl");
|
||||
const header = {
|
||||
type: "session",
|
||||
version: 3,
|
||||
@@ -590,11 +585,10 @@ describe("initSessionState thread forking", () => {
|
||||
|
||||
it("skips fork when resolved parent token estimate exceeds threshold", async () => {
|
||||
const root = await makeCaseDir("openclaw-thread-session-overflow-estimated-");
|
||||
const sessionsDir = path.join(root, "sessions");
|
||||
await fs.mkdir(sessionsDir);
|
||||
const transcriptDir = path.join(root, "thread-transcripts");
|
||||
|
||||
const parentSessionId = "parent-overflow-estimated";
|
||||
const parentSessionFile = path.join(sessionsDir, "parent.jsonl");
|
||||
const parentSessionFile = path.join(transcriptDir, "parent.jsonl");
|
||||
replaceSqliteSessionTranscriptEvents({
|
||||
agentId: "main",
|
||||
sessionId: parentSessionId,
|
||||
@@ -1244,15 +1238,13 @@ describe("initSessionState RawBody", () => {
|
||||
|
||||
it("uses the default per-agent sessions store when config store is unset", async () => {
|
||||
const root = await makeCaseDir("openclaw-session-store-default-");
|
||||
const stateDir = path.join(root, ".openclaw");
|
||||
const stateDir = path.dirname(path.dirname(root));
|
||||
const agentId = "worker1";
|
||||
const sessionKey = `agent:${agentId}:telegram:12345`;
|
||||
const sessionId = "sess-worker-1";
|
||||
const sessionFile = path.join(stateDir, "agents", agentId, "sessions", `${sessionId}.jsonl`);
|
||||
const sessionRowsTarget = createSessionRowsTargetFromSessionsDir(
|
||||
path.join(stateDir, "agents", agentId, "sessions"),
|
||||
agentId,
|
||||
);
|
||||
const transcriptDir = path.join(stateDir, "transcript-fixtures", agentId);
|
||||
const sessionFile = path.join(transcriptDir, `${sessionId}.jsonl`);
|
||||
const sessionRowsTarget = createSessionRowsTargetFromSessionsDir(transcriptDir, agentId);
|
||||
|
||||
vi.stubEnv("OPENCLAW_STATE_DIR", stateDir);
|
||||
try {
|
||||
|
||||
@@ -33,13 +33,16 @@ export async function resolveAndPersistSessionFile(params: {
|
||||
: !baseEntry.sessionFile && fallbackSessionFile
|
||||
? { ...baseEntry, sessionFile: fallbackSessionFile }
|
||||
: baseEntry;
|
||||
const entrySessionFile = entryForResolve.sessionFile?.trim();
|
||||
const sessionFile =
|
||||
fallbackSessionFile && !params.sessionsDir
|
||||
? path.resolve(fallbackSessionFile)
|
||||
: resolveSessionFilePath(sessionId, entryForResolve, {
|
||||
agentId: params.agentId,
|
||||
sessionsDir: params.sessionsDir,
|
||||
});
|
||||
!params.sessionsDir && entrySessionFile && path.isAbsolute(entrySessionFile)
|
||||
? path.resolve(entrySessionFile)
|
||||
: fallbackSessionFile && !params.sessionsDir
|
||||
? path.resolve(fallbackSessionFile)
|
||||
: resolveSessionFilePath(sessionId, entryForResolve, {
|
||||
agentId: params.agentId,
|
||||
sessionsDir: params.sessionsDir,
|
||||
});
|
||||
const persistedEntry: SessionEntry = {
|
||||
...baseEntry,
|
||||
sessionId,
|
||||
|
||||
Reference in New Issue
Block a user