UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

190 lines (189 loc) 8.48 kB
import { w as resolveStateDir } from "./paths-D2sRr1a_.js"; import { c as getAgentEventLifecycleGeneration, l as isAgentEventLifecycleGenerationCurrent, t as assertAgentRunLifecycleGenerationCurrent } from "./agent-events-CoxiItUi.js"; import { f as listAgentRunsForSession } from "./agent-run-registry-CKYKdfNd.js"; import { Gt as applySessionEntryReplacements } from "./session-accessor-YsytfDtG.js"; import { a as listSessionEntriesReadOnly } from "./session-accessor.sqlite-entry-CWk3jL7s.js"; import { r as captureGatewaySessionWorkAdmissions } from "./session-lifecycle-admission-CS8v45tk.js"; import { n as listActiveEmbeddedRunSessionIds, r as listActiveEmbeddedRunSessionKeys } from "./active-run-projections-C4mdt8WG.js"; import { d as hasCurrentProcessOwner, f as mainSessionRecoveryLog, h as resolveRestartRecoveryStorePaths, i as isMainRestartRecoveryCandidate, m as normalizeStringSet, o as normalizeMainSessionRecoveryRunFences, p as normalizeFiniteTimestamp, r as isMainRestartRecoveryAggregateTerminalOnly, s as transitionMainSessionRecovery, u as discoverRestartRecoveryStorePaths } from "./main-session-recovery-state-DD1DvBOq.js"; import { randomUUID } from "node:crypto"; //#region src/agents/main-session-recovery/main-session-restart-recovery-marking.ts async function markRecoveryStore(params) { return await applySessionEntryReplacements({ storePath: params.storePath, sessionKeys: params.sessionKey ? [params.sessionKey] : void 0, statuses: params.statuses, requireWriteSuccess: true, assertCommitAllowed: params.assertCommitAllowed, update: (entries) => { const replacements = []; const counts = { marked: 0, skipped: 0 }; for (const { sessionKey, entry } of entries) { const plan = params.plan(entry, sessionKey); if (!plan) continue; if (!isMainRestartRecoveryCandidate(entry, sessionKey)) { counts.skipped++; continue; } if (plan.action === "retire_terminal") { transitionMainSessionRecovery(entry, { kind: "observe", cycleId: randomUUID(), lifecycleGeneration: getAgentEventLifecycleGeneration(), sessionKey }); replacements.push({ sessionKey, entry }); counts.skipped++; continue; } if (plan.replaceRuns) entry.restartRecoveryRuns = plan.runs; if (plan.forceRestartSafeTools) entry.restartRecoveryForceSafeTools = true; transitionMainSessionRecovery(entry, { kind: "mark_interrupted", cycleId: randomUUID(), now: Date.now(), ...plan }); replacements.push({ sessionKey, entry }); counts.marked++; } return { result: counts, replacements }; } }); } async function markRestartAbortedMainSessions(params) { const activeRuns = [...params.activeRuns]; const currentLifecycleGeneration = getAgentEventLifecycleGeneration(); const result = { marked: 0, skipped: 0 }; const activeAdmissions = captureGatewaySessionWorkAdmissions(params.resolveGatewayContext); if (activeRuns.length === 0 && activeAdmissions.targets.size === 0) return result; const storePaths = /* @__PURE__ */ new Set(); const stateDir = params.stateDir ?? resolveStateDir(process.env); const configs = [params.cfg, ...params.additionalCfgs ?? []].filter(Boolean); for (const cfg of configs.length > 0 ? configs : [void 0]) try { for (const storePath of await discoverRestartRecoveryStorePaths({ cfg, stateDir })) storePaths.add(storePath); } catch (err) { if (!cfg) throw err; mainSessionRecoveryLog.warn(`failed to resolve configured session stores for restart marker: ${String(err)}`); } for (const storePath of activeAdmissions.targets.keys()) storePaths.add(storePath); for (const storePath of storePaths) { const sessionKeys = listSessionEntriesReadOnly({ storePath, projection: "list", clone: false }).filter(({ sessionKey, entry }) => activeRuns.some((run) => run.sessionKey === sessionKey && run.sessionId === entry.sessionId) || activeAdmissions.isActive({ scope: storePath, sessionKey, sessionId: entry.sessionId })).map(({ sessionKey }) => sessionKey); for (const selectedSessionKey of sessionKeys) { let isCurrent; try { const storeResult = await markRecoveryStore({ storePath, sessionKey: selectedSessionKey, assertCommitAllowed: () => { if (isCurrent && !isCurrent()) throw new Error("Restart recovery owner changed before commit"); }, plan: (entry, sessionKey) => { const matchingActiveRuns = activeRuns.filter((run) => run.sessionKey === sessionKey && run.sessionId === entry.sessionId && (entry.status === "running" || run.observedAt === void 0 || normalizeFiniteTimestamp(entry.updatedAt) === void 0 || entry.updatedAt < run.observedAt && run.lifecycleGeneration !== currentLifecycleGeneration) && params.isActiveRun?.(run) !== false); const matchedActiveAdmission = activeAdmissions.isActive({ scope: storePath, sessionKey, sessionId: entry.sessionId }); if (matchingActiveRuns.length === 0 && !matchedActiveAdmission) return; const wasRunning = entry.status === "running"; const runs = normalizeMainSessionRecoveryRunFences([ ...(entry.restartRecoveryRuns ?? []).filter((run) => run.lifecycleGeneration === currentLifecycleGeneration), ...listAgentRunsForSession({ sessionKey, sessionId: entry.sessionId }), ...matchingActiveRuns.map(({ runId, lifecycleGeneration }) => ({ runId, lifecycleGeneration })) ]); isCurrent = () => isAgentEventLifecycleGenerationCurrent(currentLifecycleGeneration) && (matchedActiveAdmission && activeAdmissions.isActive({ scope: storePath, sessionKey, sessionId: entry.sessionId }) || matchingActiveRuns.some((run) => params.isActiveRun?.(run) !== false)); return { action: "mark", forceRestartSafeTools: matchedActiveAdmission, replaceRuns: true, resetRuntime: !wasRunning, runs }; } }); result.marked += storeResult.marked; result.skipped += storeResult.skipped; } catch (error) { assertAgentRunLifecycleGenerationCurrent(currentLifecycleGeneration); if (!isCurrent || isCurrent()) throw error; result.skipped++; } } } if (result.marked > 0) mainSessionRecoveryLog.warn(`marked ${result.marked} interrupted main session(s) for restart recovery${params.reason ? ` (${params.reason})` : ""}`); return result; } async function markStartupOrphanedMainSessionsForRecovery(params) { const result = { marked: 0, skipped: 0 }; const providedActiveSessionIds = params.activeSessionIds === void 0 ? void 0 : normalizeStringSet(params.activeSessionIds); const providedActiveSessionKeys = params.activeSessionKeys === void 0 ? void 0 : normalizeStringSet(params.activeSessionKeys); const updatedBeforeMs = normalizeFiniteTimestamp(params.updatedBeforeMs); const resolveActiveSessionIds = () => providedActiveSessionIds ?? normalizeStringSet(listActiveEmbeddedRunSessionIds()); const resolveActiveSessionKeys = () => providedActiveSessionKeys ?? normalizeStringSet(listActiveEmbeddedRunSessionKeys()); const storePaths = (await resolveRestartRecoveryStorePaths(params)).filter((storePath) => !params.startupCheckedStorePaths?.has(storePath)); for (const storePath of storePaths) { const storeResult = await markRecoveryStore({ storePath, statuses: ["running"], plan: (entry, sessionKey) => { if (entry.status !== "running" || entry.abortedLastRun === true) return; const updatedAt = normalizeFiniteTimestamp(entry.updatedAt); if (updatedBeforeMs !== void 0 && updatedAt !== void 0 && updatedAt > updatedBeforeMs) return; if (hasCurrentProcessOwner({ activeSessionIds: resolveActiveSessionIds(), activeSessionKeys: resolveActiveSessionKeys(), entry, sessionKey })) return; return isMainRestartRecoveryAggregateTerminalOnly(entry) ? { action: "retire_terminal" } : { action: "mark" }; } }); result.marked += storeResult.marked; result.skipped += storeResult.skipped; } storePaths.forEach((storePath) => params.startupCheckedStorePaths?.add(storePath)); if (result.marked > 0) mainSessionRecoveryLog.warn(`marked ${result.marked} startup-orphaned main session(s) for restart recovery`); return result; } //#endregion export { markStartupOrphanedMainSessionsForRecovery as n, markRestartAbortedMainSessions as t };