























1+import fs from "node:fs/promises";
2+import os from "node:os";
3+import path from "node:path";
4+import { describe, expect, it } from "vitest";
5+import { loadSessionStore, saveSessionStore } from "../../config/sessions.js";
6+import type { SessionEntry } from "../../config/sessions/types.js";
7+import { persistAbortTargetEntry, persistSessionEntry } from "./commands-session-store.js";
8+9+async function withTempStore<T>(run: (storePath: string) => Promise<T>): Promise<T> {
10+const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-command-session-store-"));
11+try {
12+return await run(path.join(dir, "sessions.json"));
13+} finally {
14+await fs.rm(dir, { recursive: true, force: true });
15+}
16+}
17+18+describe("commands session store persistence", () => {
19+it("persists a single command session entry through the accessor", async () => {
20+await withTempStore(async (storePath) => {
21+const sessionKey = "agent:main:command";
22+const otherKey = "agent:main:other";
23+const entry: SessionEntry = {
24+sessionId: "command-session",
25+updatedAt: 1,
26+model: "gpt-5.5",
27+};
28+const otherEntry: SessionEntry = {
29+sessionId: "other-session",
30+updatedAt: 2,
31+};
32+await saveSessionStore(
33+storePath,
34+{
35+[sessionKey]: { ...entry },
36+[otherKey]: { ...otherEntry },
37+},
38+{ skipMaintenance: true },
39+);
40+const sessionStore: Record<string, SessionEntry> = { [sessionKey]: entry };
41+42+await expect(
43+persistSessionEntry({
44+sessionEntry: entry,
45+ sessionStore,
46+ sessionKey,
47+ storePath,
48+}),
49+).resolves.toBe(true);
50+51+const persisted = loadSessionStore(storePath, { skipCache: true });
52+expect(sessionStore[sessionKey]).toBe(entry);
53+expect(entry.updatedAt).not.toBe(1);
54+expect(persisted[sessionKey]).toMatchObject({
55+sessionId: "command-session",
56+model: "gpt-5.5",
57+updatedAt: entry.updatedAt,
58+});
59+expect(persisted[otherKey]).toStrictEqual(otherEntry);
60+});
61+});
62+63+it("falls back to the supplied abort target when the persisted row is missing", async () => {
64+await withTempStore(async (storePath) => {
65+const sessionKey = "agent:main:abort-target";
66+const entry: SessionEntry = {
67+sessionId: "abort-session",
68+updatedAt: 1,
69+model: "gpt-5.5",
70+};
71+const sessionStore: Record<string, SessionEntry> = { [sessionKey]: entry };
72+await fs.writeFile(storePath, JSON.stringify({}, null, 2), "utf8");
73+74+await expect(
75+persistAbortTargetEntry({
76+ entry,
77+key: sessionKey,
78+ sessionStore,
79+ storePath,
80+abortCutoff: { messageSid: "42", timestamp: 123 },
81+}),
82+).resolves.toBe(true);
83+84+const persisted = loadSessionStore(storePath, { skipCache: true })[sessionKey];
85+expect(sessionStore[sessionKey]).toBe(entry);
86+expect(entry.abortedLastRun).toBe(true);
87+expect(entry.abortCutoffMessageSid).toBe("42");
88+expect(entry.abortCutoffTimestamp).toBe(123);
89+expect(persisted).toMatchObject({
90+sessionId: "abort-session",
91+model: "gpt-5.5",
92+abortedLastRun: true,
93+abortCutoffMessageSid: "42",
94+abortCutoffTimestamp: 123,
95+});
96+});
97+});
98+99+it("patches the persisted abort target when it already exists", async () => {
100+await withTempStore(async (storePath) => {
101+const sessionKey = "agent:main:abort-target";
102+const otherKey = "agent:main:other";
103+const entry: SessionEntry = {
104+sessionId: "memory-session",
105+updatedAt: 1,
106+};
107+const persistedEntry: SessionEntry = {
108+sessionId: "persisted-session",
109+updatedAt: 2,
110+model: "sonnet-4.6",
111+};
112+const otherEntry: SessionEntry = {
113+sessionId: "other-session",
114+updatedAt: 3,
115+};
116+await saveSessionStore(
117+storePath,
118+{
119+[sessionKey]: persistedEntry,
120+[otherKey]: otherEntry,
121+},
122+{ skipMaintenance: true },
123+);
124+125+await expect(
126+persistAbortTargetEntry({
127+ entry,
128+key: sessionKey,
129+sessionStore: { [sessionKey]: entry },
130+ storePath,
131+}),
132+).resolves.toBe(true);
133+134+const persisted = loadSessionStore(storePath, { skipCache: true });
135+expect(entry.abortedLastRun).toBe(true);
136+expect(persisted[sessionKey]).toMatchObject({
137+sessionId: "persisted-session",
138+model: "sonnet-4.6",
139+abortedLastRun: true,
140+});
141+expect(persisted[otherKey]).toStrictEqual(otherEntry);
142+});
143+});
144+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。