






















@@ -1,3 +1,7 @@
1+import crypto from "node:crypto";
2+import fs from "node:fs/promises";
3+import os from "node:os";
4+import path from "node:path";
15import { describe, expect, it, vi } from "vitest";
26import type { SessionEntry } from "../../config/sessions.js";
37import { createPersistCronSessionEntry, type MutableCronSession } from "./run-session-state.js";
@@ -26,6 +30,7 @@ describe("createPersistCronSessionEntry", () => {
2630it("persists isolated cron state only under the stable cron session key", async () => {
2731const cronSession = makeCronSession(
2832makeSessionEntry({
33+sessionFile: await createTranscriptFile(),
2934status: "running",
3035startedAt: 900,
3136skillsSnapshot: {
@@ -56,6 +61,81 @@ describe("createPersistCronSessionEntry", () => {
5661expect(cronSession.store["agent:main:cron:job:run:run-session-id"]).toBeUndefined();
5762});
586364+it("does not register cron sessions as resumable until the transcript exists", async () => {
65+const missingTranscriptPath = path.join(
66+os.tmpdir(),
67+`openclaw-missing-cron-${crypto.randomUUID()}.jsonl`,
68+);
69+const cronSession = makeCronSession(
70+makeSessionEntry({
71+sessionFile: missingTranscriptPath,
72+label: "Cron: shell-only",
73+status: "running",
74+}),
75+);
76+const updateSessionStore = vi.fn(
77+async (_storePath, update: (store: Record<string, SessionEntry>) => void) => {
78+const store: Record<string, SessionEntry> = {};
79+update(store);
80+expect(store["agent:main:cron:shell-only"]).toEqual(
81+expect.objectContaining({
82+label: "Cron: shell-only",
83+status: "running",
84+updatedAt: 1000,
85+}),
86+);
87+expect(store["agent:main:cron:shell-only"]?.sessionId).toBeUndefined();
88+expect(store["agent:main:cron:shell-only"]?.sessionFile).toBeUndefined();
89+},
90+);
91+92+const persist = createPersistCronSessionEntry({
93+isFastTestEnv: false,
94+ cronSession,
95+agentSessionKey: "agent:main:cron:shell-only",
96+ updateSessionStore,
97+});
98+99+await persist();
100+101+expect(cronSession.store["agent:main:cron:shell-only"]?.sessionId).toBeUndefined();
102+expect(cronSession.store["agent:main:cron:shell-only"]?.sessionFile).toBeUndefined();
103+});
104+105+it("restores resumable cron fields once the transcript exists", async () => {
106+const transcriptPath = await createTranscriptFile();
107+const cronSession = makeCronSession(
108+makeSessionEntry({
109+sessionFile: transcriptPath,
110+label: "Cron: completed",
111+}),
112+);
113+114+const persist = createPersistCronSessionEntry({
115+isFastTestEnv: false,
116+ cronSession,
117+agentSessionKey: "agent:main:cron:completed",
118+updateSessionStore: vi.fn(
119+async (_storePath, update: (store: Record<string, SessionEntry>) => void) => {
120+const store: Record<string, SessionEntry> = {};
121+update(store);
122+expect(store["agent:main:cron:completed"]).toMatchObject({
123+sessionId: "run-session-id",
124+sessionFile: transcriptPath,
125+label: "Cron: completed",
126+});
127+},
128+),
129+});
130+131+await persist();
132+133+expect(cronSession.store["agent:main:cron:completed"]).toMatchObject({
134+sessionId: "run-session-id",
135+sessionFile: transcriptPath,
136+});
137+});
138+59139it("persists explicit session-bound cron state under the requested session key", async () => {
60140const cronSession = makeCronSession();
61141const updateSessionStore = vi.fn(
@@ -78,3 +158,10 @@ describe("createPersistCronSessionEntry", () => {
78158expect(cronSession.store["agent:main:session"]).toBe(cronSession.sessionEntry);
79159});
80160});
161+162+async function createTranscriptFile(): Promise<string> {
163+const dir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-cron-session-"));
164+const file = path.join(dir, "session.jsonl");
165+await fs.writeFile(file, `${JSON.stringify({ type: "session", sessionId: "run-session-id" })}\n`);
166+return file;
167+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。