





















@@ -15,8 +15,8 @@ import {
1515resolveAllAgentSessionStoreTargetsSync,
1616resolveSessionFilePath,
1717resolveSessionTranscriptPathInDir,
18-updateSessionStore,
1918} from "../config/sessions.js";
19+import { applyRestartRecoveryLifecycle } from "../config/sessions/session-accessor.js";
2020import type { OpenClawConfig } from "../config/types.openclaw.js";
2121import { callGateway } from "../gateway/call.js";
2222import { readSessionMessagesAsync } from "../gateway/session-transcript-readers.js";
@@ -213,13 +213,13 @@ export async function markRestartAbortedMainSessions(params: {
213213}
214214215215for (const storePath of storePaths) {
216-await updateSessionStore(
216+const storeResult = await applyRestartRecoveryLifecycle({
217217 storePath,
218-(store) => {
219- for (const [sessionKey, entry] of Object.entries(store)) {
220- if (!entry) {
221- continue;
222- }
218+requireWriteSuccess: true,
219+update: (entries) => {
220+const replacements: Array<{ sessionKey: string; entry: SessionEntry }> = [];
221+const counts = { marked: 0, skipped: 0 };
222+for (const { sessionKey, entry } of entries) {
223223const registeredActiveRuns = listAgentRunsForSession({
224224 sessionKey,
225225sessionId: entry.sessionId,
@@ -248,7 +248,7 @@ export async function markRestartAbortedMainSessions(params: {
248248continue;
249249}
250250if (shouldSkipMainRecovery(entry, sessionKey)) {
251-result.skipped++;
251+counts.skipped++;
252252continue;
253253}
254254const wasRunning = entry.status === "running";
@@ -288,12 +288,14 @@ export async function markRestartAbortedMainSessions(params: {
288288 : a.runId.localeCompare(b.runId),
289289);
290290entry.updatedAt = Date.now();
291-store[sessionKey] = entry;
292-result.marked++;
291+replacements.push({ sessionKey, entry });
292+counts.marked++;
293293}
294+return { result: counts, replacements };
294295},
295-{ skipMaintenance: true, requireWriteSuccess: true },
296-);
296+});
297+result.marked += storeResult.marked;
298+result.skipped += storeResult.skipped;
297299}
298300299301if (result.marked > 0) {
@@ -327,18 +329,17 @@ export async function markStartupOrphanedMainSessionsForRecovery(params: {
327329providedActiveSessionKeys ?? normalizeStringSet(listActiveEmbeddedRunSessionKeys());
328330329331for (const storePath of await resolveRestartRecoveryStorePaths(params)) {
330-await updateSessionStore(
332+const storeResult = await applyRestartRecoveryLifecycle({
331333 storePath,
332-(store) => {
333-for (const [sessionKey, entry] of Object.entries(store)) {
334-if (!entry) {
335-continue;
336-}
334+update: (entries) => {
335+const replacements: Array<{ sessionKey: string; entry: SessionEntry }> = [];
336+const counts = { marked: 0, skipped: 0 };
337+for (const { sessionKey, entry } of entries) {
337338if (entry.status !== "running" || entry.abortedLastRun === true) {
338339continue;
339340}
340341if (shouldSkipMainRecovery(entry, sessionKey)) {
341-result.skipped++;
342+counts.skipped++;
342343continue;
343344}
344345const updatedAt = normalizeFiniteTimestamp(entry.updatedAt);
@@ -361,12 +362,14 @@ export async function markStartupOrphanedMainSessionsForRecovery(params: {
361362}
362363entry.abortedLastRun = true;
363364entry.updatedAt = Date.now();
364-store[sessionKey] = entry;
365-result.marked++;
365+replacements.push({ sessionKey, entry });
366+counts.marked++;
366367}
368+return { result: counts, replacements };
367369},
368-{ skipMaintenance: true },
369-);
370+});
371+result.marked += storeResult.marked;
372+result.skipped += storeResult.skipped;
370373}
371374372375if (result.marked > 0) {
@@ -438,12 +441,13 @@ async function markSessionFailed(params: {
438441sessionKey: string;
439442reason: string;
440443}): Promise<void> {
441-await updateSessionStore(
442-params.storePath,
443-(store) => {
444-const entry = store[params.sessionKey];
444+await applyRestartRecoveryLifecycle({
445+storePath: params.storePath,
446+update: (entries) => {
447+const current = entries.find((entry) => entry.sessionKey === params.sessionKey);
448+const entry = current?.entry;
445449if (!entry || entry.status !== "running") {
446-return;
450+return { result: undefined };
447451}
448452entry.status = "failed";
449453entry.abortedLastRun = true;
@@ -458,10 +462,12 @@ async function markSessionFailed(params: {
458462entry.pendingFinalDeliveryContext = undefined;
459463entry.restartRecoveryDeliveryContext = undefined;
460464entry.restartRecoveryDeliveryRunId = undefined;
461-store[params.sessionKey] = entry;
465+return {
466+result: undefined,
467+replacements: [{ sessionKey: params.sessionKey, entry }],
468+};
462469},
463-{ skipMaintenance: true },
464-);
470+});
465471log.warn(`marked interrupted main session failed: ${params.sessionKey} (${params.reason})`);
466472}
467473@@ -594,12 +600,13 @@ async function resumeMainSession(params: {
594600params: agentParams,
595601timeoutMs: 10_000,
596602});
597-await updateSessionStore(
598-params.storePath,
599-(store) => {
600-const entry = store[params.sessionKey];
603+await applyRestartRecoveryLifecycle({
604+storePath: params.storePath,
605+update: (entries) => {
606+const current = entries.find((entry) => entry.sessionKey === params.sessionKey);
607+const entry = current?.entry;
601608if (!entry) {
602-return;
609+return { result: undefined };
603610}
604611const now = Date.now();
605612entry.abortedLastRun = false;
@@ -621,10 +628,12 @@ async function resumeMainSession(params: {
621628entry.pendingFinalDeliveryContext = undefined;
622629}
623630}
624-store[params.sessionKey] = entry;
631+return {
632+result: undefined,
633+replacements: [{ sessionKey: params.sessionKey, entry }],
634+};
625635},
626-{ skipMaintenance: true },
627-);
636+});
628637log.info(
629638`resumed interrupted main session: ${params.sessionKey}${
630639 sanitizedPendingText ? " (with pending payload)" : ""
@@ -653,28 +662,32 @@ export async function markRestartAbortedMainSessionsFromLocks(params: {
653662}
654663655664const storePath = path.join(sessionsDir, "sessions.json");
656-await updateSessionStore(
665+const storeResult = await applyRestartRecoveryLifecycle({
657666 storePath,
658-(store) => {
659-for (const [sessionKey, entry] of Object.entries(store)) {
660-if (!entry || entry.status !== "running") {
667+update: (entries) => {
668+const replacements: Array<{ sessionKey: string; entry: SessionEntry }> = [];
669+const counts = { marked: 0, skipped: 0 };
670+for (const { sessionKey, entry } of entries) {
671+if (entry.status !== "running") {
661672continue;
662673}
663674if (shouldSkipMainRecovery(entry, sessionKey)) {
664-result.skipped++;
675+counts.skipped++;
665676continue;
666677}
667678const entryLockPaths = resolveEntryTranscriptLockPaths({ entry, sessionsDir });
668679if (!entryLockPaths.some((lockPath) => interruptedLockPaths.has(lockPath))) {
669680continue;
670681}
671682entry.abortedLastRun = true;
672-store[sessionKey] = entry;
673-result.marked++;
683+replacements.push({ sessionKey, entry });
684+counts.marked++;
674685}
686+return { result: counts, replacements };
675687},
676-{ skipMaintenance: true },
677-);
688+});
689+result.marked += storeResult.marked;
690+result.skipped += storeResult.skipped;
678691679692if (result.marked > 0) {
680693log.warn(`marked ${result.marked} interrupted main session(s) from stale transcript locks`);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。