diff --git a/docs/concepts/context-engine.md b/docs/concepts/context-engine.md index ef6ed7e7577..17a07cd9710 100644 --- a/docs/concepts/context-engine.md +++ b/docs/concepts/context-engine.md @@ -210,8 +210,9 @@ Required members: `compact` returns a `CompactResult`. When compaction rotates the active -transcript, `result.sessionId` and `result.sessionFile` identify the successor -session that the next retry or turn must use. +transcript, `result.sessionId` identifies the successor session that the next +retry or turn must use. Transcript rows stay in SQLite; compaction does not +handoff a transcript file or locator. Optional members: diff --git a/docs/concepts/session-tool.md b/docs/concepts/session-tool.md index 21d5fe1980c..1efe339e49e 100644 --- a/docs/concepts/session-tool.md +++ b/docs/concepts/session-tool.md @@ -81,8 +81,8 @@ The returned view is intentionally bounded and safety-filtered: Both tools accept either a **session key** (like `"main"`) or a **session ID** from a previous list call. -If you need the exact byte-for-byte transcript, inspect the transcript file on -disk instead of treating `sessions_history` as a raw dump. +If you need the exact byte-for-byte transcript for debugging, export it from +SQLite instead of treating `sessions_history` as a raw dump. ## Sending cross-session messages diff --git a/docs/tools/subagents.md b/docs/tools/subagents.md index 38d94a1c011..8cba64f2ff3 100644 --- a/docs/tools/subagents.md +++ b/docs/tools/subagents.md @@ -501,7 +501,7 @@ Announce payloads include a stats line at the end (even when wrapped): - Runtime (e.g. `runtime 5m12s`). - Token usage (input/output/total). - Estimated cost when model pricing is configured (`models.providers.*.models[].cost`). -- `sessionKey`, `sessionId`, and transcript path so the main agent can fetch history via `sessions_history` or inspect the file on disk. +- `sessionKey` and `sessionId` so the main agent can fetch history via `sessions_history` or inspect the SQLite transcript rows. Internal metadata is meant for orchestration only; user-facing replies should be rewritten in normal assistant voice. diff --git a/src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts b/src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts index db2c5562225..b4ca8376143 100644 --- a/src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts +++ b/src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts @@ -42,7 +42,7 @@ describe("ensureSessionHeader", () => { it("creates the transcript header in SQLite without writing a JSONL file", async () => { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-session-header-")); try { - const transcriptLocator = path.join(tempDir, "nested", "session.jsonl"); + const legacyTranscriptPath = path.join(tempDir, "nested", "session.jsonl"); const env = { ...process.env, OPENCLAW_STATE_DIR: path.join(tempDir, "state"), @@ -54,7 +54,7 @@ describe("ensureSessionHeader", () => { env, }); - await expect(fs.access(transcriptLocator)).rejects.toThrow(); + await expect(fs.access(legacyTranscriptPath)).rejects.toThrow(); const events = loadSqliteSessionTranscriptEvents({ agentId: "main", sessionId: "session-1", diff --git a/src/auto-reply/reply/reply-state.test.ts b/src/auto-reply/reply/reply-state.test.ts index de8c086328b..84d5968aa6f 100644 --- a/src/auto-reply/reply/reply-state.test.ts +++ b/src/auto-reply/reply/reply-state.test.ts @@ -53,10 +53,7 @@ async function createCompactionSessionFixture(entry: SessionEntry) { return { sessionKey, sessionStore }; } -async function rotateCompactionTranscriptLocator(params: { - tempPrefix: string; - newSessionId: string; -}) { +async function rotateCompactionSessionId(params: { tempPrefix: string; newSessionId: string }) { const tmp = await fs.mkdtemp(path.join(os.tmpdir(), params.tempPrefix)); tempDirs.push(tmp); vi.stubEnv("OPENCLAW_STATE_DIR", tmp); @@ -547,8 +544,8 @@ describe("incrementCompactionCount", () => { expect(stored[sessionKey].totalTokensFresh).toBe(true); }); - it("updates sessionId without persisting transcript locators when compaction rotated transcripts", async () => { - const { stored, sessionKey } = await rotateCompactionTranscriptLocator({ + it("updates sessionId without persisting transcript locators when compaction rotates sessions", async () => { + const { stored, sessionKey } = await rotateCompactionSessionId({ tempPrefix: "openclaw-compact-rotate-", newSessionId: "s2", }); @@ -556,8 +553,8 @@ describe("incrementCompactionCount", () => { expect(stored[sessionKey]).not.toHaveProperty("transcriptLocator"); }); - it("drops legacy fork transcript filenames when compaction rotates transcripts", async () => { - const { stored, sessionKey } = await rotateCompactionTranscriptLocator({ + it("drops legacy fork transcript filenames when compaction rotates sessions", async () => { + const { stored, sessionKey } = await rotateCompactionSessionId({ tempPrefix: "openclaw-compact-fork-", newSessionId: "s2", }); @@ -565,8 +562,8 @@ describe("incrementCompactionCount", () => { expect(stored[sessionKey]).not.toHaveProperty("transcriptLocator"); }); - it("replaces absolute transcriptLocator paths with sqlite locators during compaction rotation", async () => { - const { stored, sessionKey } = await rotateCompactionTranscriptLocator({ + it("drops legacy transcript locator paths during compaction rotation", async () => { + const { stored, sessionKey } = await rotateCompactionSessionId({ tempPrefix: "openclaw-compact-unsafe-", newSessionId: "s2", }); diff --git a/src/commands/agent.test.ts b/src/commands/agent.test.ts index 8d67a6f9494..7fef4ec544e 100644 --- a/src/commands/agent.test.ts +++ b/src/commands/agent.test.ts @@ -217,41 +217,16 @@ vi.mock("../agents/command/delivery.runtime.js", () => { }); vi.mock("../config/sessions/transcript-resolve.runtime.js", () => { - const joinPath = (...parts: string[]): string => { - const separator = parts.some((part) => part.includes("\\")) ? "\\" : "/"; - const normalizedParts: string[] = []; - for (const [index, part] of parts.entries()) { - const normalized = - index === 0 ? part.replace(/[\\/]+$/u, "") : part.replace(/^[\\/]+|[\\/]+$/gu, ""); - if (normalized.length > 0) { - normalizedParts.push(normalized); - } - } - return normalizedParts.join(separator); - }; - const resolveTranscriptLocator = ( - sessionId: string, - agentId: string, - sessionsDir?: string, - ): string => - joinPath(sessionsDir ?? ".openclaw", "agents", agentId, "sessions", `${sessionId}.jsonl`); - return { resolveSessionTranscriptTarget: vi.fn( async (params: { sessionId: string; sessionKey: string; - sessionEntry?: { transcriptLocator?: string; sessionId?: string }; - sessionStore?: Record; + sessionEntry?: { sessionId?: string }; + sessionStore?: Record; agentId: string; threadId?: string | number; }) => { - const transcriptLocatorFromStorePath = - params.sessionEntry?.transcriptLocator ?? - resolveTranscriptLocator(params.sessionId, params.agentId); - const transcriptLocator = params.sessionEntry?.transcriptLocator - ? transcriptLocatorFromStorePath - : resolveTranscriptLocator(params.sessionId, params.agentId); let sessionEntry = params.sessionEntry; if (params.sessionStore && params.sessionKey) { const existingEntry = @@ -261,12 +236,11 @@ vi.mock("../config/sessions/transcript-resolve.runtime.js", () => { sessionEntry = { ...existingEntry, sessionId: params.sessionId, - transcriptLocator, }; params.sessionStore[params.sessionKey] = sessionEntry; await replaceTestSessionRows(params.agentId, params.sessionStore as never); } - return { transcriptLocator, sessionEntry }; + return { agentId: params.agentId, sessionId: params.sessionId, sessionEntry }; }, ), };