


















@@ -5,7 +5,6 @@ import { afterEach, describe, expect, it } from "vitest";
55import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js";
66import { withTempDir } from "../test-helpers/temp-dir.js";
77import {
8-createFileAcpEventLedger,
98createInMemoryAcpEventLedger,
109createSqliteAcpEventLedger,
1110migrateFileAcpEventLedgerToSqlite,
@@ -117,42 +116,6 @@ describe("ACP event ledger", () => {
117116});
118117});
119118120-it("persists file-backed replay state across ledger instances", async () => {
121-await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => {
122-const filePath = path.join(dir, "acp", "event-ledger.json");
123-const first = createFileAcpEventLedger({ filePath, now: () => 1000 });
124-await first.startSession({
125-sessionId: "session-1",
126-sessionKey: "agent:main:work",
127-cwd: "/work",
128-complete: true,
129-});
130-await first.recordUpdate({
131-sessionId: "session-1",
132-sessionKey: "agent:main:work",
133-runId: "run-1",
134-update: {
135-sessionUpdate: "agent_thought_chunk",
136-content: { type: "text", text: "Thinking" },
137-},
138-});
139-140-const second = createFileAcpEventLedger({ filePath });
141-const replay = await second.readReplay({
142-sessionId: "session-1",
143-sessionKey: "agent:main:work",
144-});
145-146-expect(replay.complete).toBe(true);
147-expect(replay.events).toHaveLength(1);
148-expect(replay.events[0]?.update).toEqual({
149-sessionUpdate: "agent_thought_chunk",
150-content: { type: "text", text: "Thinking" },
151-});
152-await expect(fs.readFile(filePath, "utf8")).resolves.toContain('"version":1');
153-});
154-});
155-156119it("persists SQLite-backed replay state across ledger instances", async () => {
157120await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => {
158121const databasePath = path.join(dir, "openclaw.sqlite");
@@ -193,22 +156,38 @@ describe("ACP event ledger", () => {
193156await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => {
194157const filePath = path.join(dir, "acp", "event-ledger.json");
195158const databasePath = path.join(dir, "openclaw.sqlite");
196-const legacy = createFileAcpEventLedger({ filePath, now: () => 1000 });
197-await legacy.startSession({
198-sessionId: "session-1",
199-sessionKey: "agent:main:work",
200-cwd: "/work",
201-complete: true,
202-});
203-await legacy.recordUpdate({
204-sessionId: "session-1",
205-sessionKey: "agent:main:work",
206-runId: "run-1",
207-update: {
208-sessionUpdate: "agent_message_chunk",
209-content: { type: "text", text: "Answer" },
210-},
211-});
159+await fs.mkdir(path.dirname(filePath), { recursive: true });
160+await fs.writeFile(
161+filePath,
162+JSON.stringify({
163+version: 1,
164+sessions: {
165+"session-1": {
166+sessionId: "session-1",
167+sessionKey: "agent:main:work",
168+cwd: "/work",
169+complete: true,
170+createdAt: 1000,
171+updatedAt: 1000,
172+nextSeq: 2,
173+events: [
174+{
175+seq: 1,
176+at: 1000,
177+sessionId: "session-1",
178+sessionKey: "agent:main:work",
179+runId: "run-1",
180+update: {
181+sessionUpdate: "agent_message_chunk",
182+content: { type: "text", text: "Answer" },
183+},
184+},
185+],
186+},
187+},
188+}),
189+"utf8",
190+);
212191213192const migrated = await migrateFileAcpEventLedgerToSqlite({
214193 filePath,
@@ -449,81 +428,4 @@ describe("ACP event ledger", () => {
449428).resolves.toEqual({ complete: false, events: [] });
450429});
451430452-it("keeps the persisted ledger file under the serialized byte budget", async () => {
453-await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => {
454-const filePath = path.join(dir, "acp", "event-ledger.json");
455-const ledger = createFileAcpEventLedger({ filePath, maxSerializedBytes: 1024 });
456-await ledger.startSession({
457-sessionId: "session-1",
458-sessionKey: "agent:main:work",
459-cwd: "/work",
460-complete: true,
461-});
462-await ledger.recordUpdate({
463-sessionId: "session-1",
464-sessionKey: "agent:main:work",
465-update: {
466-sessionUpdate: "tool_call_update",
467-toolCallId: "tool-1",
468-status: "completed",
469-rawOutput: { content: "x".repeat(5_000) },
470-},
471-});
472-473-const bytes = Buffer.byteLength(await fs.readFile(filePath, "utf8"), "utf8");
474-expect(bytes).toBeLessThanOrEqual(1024);
475-await expect(
476-ledger.readReplay({ sessionId: "session-1", sessionKey: "agent:main:work" }),
477-).resolves.toEqual({ complete: false, events: [] });
478-});
479-});
480-481-it("ignores corrupt ledger files instead of replaying unknown state", async () => {
482-await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => {
483-const filePath = path.join(dir, "event-ledger.json");
484-await fs.writeFile(filePath, "{bad json", "utf8");
485-const ledger = createFileAcpEventLedger({ filePath });
486-487-await expect(
488-ledger.readReplay({ sessionId: "session-1", sessionKey: "agent:main:work" }),
489-).resolves.toEqual({ complete: false, events: [] });
490-});
491-});
492-493-it("reloads file-backed state under lock before writing", async () => {
494-await withTempDir({ prefix: "openclaw-acp-ledger-" }, async (dir) => {
495-const filePath = path.join(dir, "acp", "event-ledger.json");
496-const first = createFileAcpEventLedger({ filePath });
497-const second = createFileAcpEventLedger({ filePath });
498-499-await first.startSession({
500-sessionId: "session-1",
501-sessionKey: "acp:gateway-session-1",
502-cwd: "/work",
503-complete: true,
504-});
505-await second.startSession({
506-sessionId: "session-2",
507-sessionKey: "acp:gateway-session-2",
508-cwd: "/work",
509-complete: true,
510-});
511-await first.recordUpdate({
512-sessionId: "session-1",
513-sessionKey: "acp:gateway-session-1",
514-update: {
515-sessionUpdate: "agent_message_chunk",
516-content: { type: "text", text: "Answer" },
517-},
518-});
519-520-const reader = createFileAcpEventLedger({ filePath });
521-const replay = await reader.readReplay({
522-sessionId: "session-2",
523-sessionKey: "acp:gateway-session-2",
524-});
525-expect(replay.complete).toBe(true);
526-expect(replay.sessionKey).toBe("acp:gateway-session-2");
527-});
528-});
529431});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。