openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
222 lines (221 loc) • 11.2 kB
JavaScript
import { d as getAgentEventLifecycleGeneration } from "./agent-events-jWdQ_ESE.mjs";
import { N as hasRestartRecoveryTerminalRun, P as isMainRestartRecoveryCandidate, j as hasMainSessionRecoveryClaim } from "./session-accessor.sqlite-entry-worker-publication-BNeFwCVE.mjs";
import "./session-accessor-C3ljJ974.mjs";
import { g as applySessionEntryExactReplacements } from "./session-accessor.entry-mutation-telsi3Ig.mjs";
import { a as isMainSessionRecoveryPending, s as transitionMainSessionRecovery } from "./main-session-recovery-state-DuO6mGeL.mjs";
import { a as scheduleMainSessionRecoveryMutation, i as retryMainSessionRecoveryMutation } from "./main-session-recovery-lifecycle-A6Nifju3.mjs";
import { t as captureYieldedMainSessionContinuation } from "./main-session-restart-recovery-target-CHHaj5yO.mjs";
import { randomUUID } from "node:crypto";
//#region src/agents/main-session-recovery/main-session-recovery-store.ts
function matchesReservation(entry, reservation) {
const state = entry.mainRestartRecovery;
return entry.sessionId === reservation.sessionId && state?.cycleId === reservation.cycleId && state.reservation?.runId === reservation.runId && state.reservation.lifecycleGeneration === reservation.lifecycleGeneration;
}
function currentGenerationRequiredBy(command) {
if (command.kind === "validate_foreground" || command.kind === "bind_foreground_run") return command.claim.lifecycleGeneration;
return "lifecycleGeneration" in command ? command.lifecycleGeneration : void 0;
}
async function commitMainSessionRecovery(params) {
const reservationCleanup = params.command.kind === "cancel_reservation" || params.command.kind === "abandon_reservation" ? params.command.reservation : void 0;
const recoveryAdmission = params.command.kind === "admit_recovery" || params.command.kind === "validate_recovery" ? params.command : void 0;
const ownerClaim = params.command.kind === "claim_foreground" ? params.command : void 0;
const exactOwnerClaim = params.command.kind === "validate_foreground" || params.command.kind === "release_foreground" ? params.command.claim : void 0;
let yieldedContinuation;
return await applySessionEntryExactReplacements({
agentId: params.target.agentId,
requireWriteSuccess: params.requireWriteSuccess,
sessionKeys: [params.target.sessionKey],
includeSessionWindowOwner: reservationCleanup?.sessionId ?? recoveryAdmission?.sessionId ?? exactOwnerClaim?.sessionId ?? ownerClaim?.sessionId ?? params.expectedSessionId,
storePath: params.target.storePath,
assertCommitAllowed: () => {
if (yieldedContinuation && (!yieldedContinuation() || ownerClaim?.lifecycleGeneration !== getAgentEventLifecycleGeneration() || params.shouldContinue?.() === false)) throw new Error("Yielded requester continuation changed before recovery handoff");
},
update: (entries) => {
const expectedGeneration = currentGenerationRequiredBy(params.command);
if (params.shouldContinue?.() === false || expectedGeneration && expectedGeneration !== getAgentEventLifecycleGeneration()) return { result: { transition: {
kind: "rejected",
reason: "stale_generation"
} } };
const selected = entries.find(({ sessionKey }) => sessionKey === params.target.sessionKey);
let candidate = params.expectedSessionId && selected?.entry.sessionId !== params.expectedSessionId || ownerClaim && selected?.entry.sessionId !== ownerClaim.sessionId ? void 0 : selected;
if (reservationCleanup) candidate = entries.find(({ entry }) => matchesReservation(entry, reservationCleanup));
else if (recoveryAdmission) candidate = entries.find(({ entry }) => {
const reservation = entry.mainRestartRecovery?.reservation;
return entry.sessionId === recoveryAdmission.sessionId && reservation?.runId === recoveryAdmission.runId && reservation.lifecycleGeneration === recoveryAdmission.lifecycleGeneration;
});
else if (exactOwnerClaim) candidate = entries.find(({ entry }) => {
const state = entry.mainRestartRecovery;
return state?.cycleId === exactOwnerClaim.cycleId && state.foregroundClaims?.lifecycleGeneration === exactOwnerClaim.lifecycleGeneration && state.foregroundClaims.tokens.includes(exactOwnerClaim.claimId);
});
else if (ownerClaim && (!selected || selected.entry.sessionId !== ownerClaim.sessionId)) candidate = entries.find(({ entry }) => entry.sessionId === ownerClaim.sessionId);
else if (params.expectedSessionId) candidate = entries.find(({ entry }) => entry.sessionId === params.expectedSessionId);
if (reservationCleanup || recoveryAdmission || exactOwnerClaim) candidate ??= selected;
if (!candidate) return { result: {
entry: selected?.entry,
sessionKey: selected?.sessionKey,
transition: {
kind: "rejected",
reason: "session_replaced"
}
} };
const entry = candidate.entry;
const previousRecoveryState = entry.mainRestartRecovery;
const previousRecoveryRuns = entry.restartRecoveryRuns;
const previousDeliveryRunId = entry.restartRecoveryDeliveryRunId;
const command = (params.command.kind === "claim_foreground" || params.command.kind === "observe" || params.command.kind === "inspect") && params.command.sessionKey !== candidate.sessionKey ? {
...params.command,
sessionKey: candidate.sessionKey
} : params.command;
if (ownerClaim && entry.sessionId === ownerClaim.sessionId && isMainRestartRecoveryCandidate(entry, candidate.sessionKey) && entry.abortedLastRun !== true && !entry.mainRestartRecovery && !entry.pendingFinalDelivery && !entry.restartRecoveryDeliveryRunId && entry.restartRecoveryRuns?.length && entry.restartRecoveryRuns.every((run) => run.runId === entry.lifecycleRunId || hasRestartRecoveryTerminalRun(entry, run.runId))) {
yieldedContinuation = captureYieldedMainSessionContinuation({
storeAgentId: params.target.agentId,
entry,
sessionKey: candidate.sessionKey,
storePath: params.target.storePath
});
if (yieldedContinuation?.()) transitionMainSessionRecovery(entry, { kind: "clear" });
}
const transition = transitionMainSessionRecovery(entry, command);
const changed = previousRecoveryState !== entry.mainRestartRecovery || previousRecoveryRuns !== entry.restartRecoveryRuns || previousDeliveryRunId !== entry.restartRecoveryDeliveryRunId || transition.kind !== "foreground_validated" && transition.kind !== "no_change" && transition.kind !== "observed" && transition.kind !== "rejected";
return {
result: {
entry,
sessionKey: candidate.sessionKey,
transition
},
...changed ? { replacements: [{
sessionKey: candidate.sessionKey,
entry
}] } : {}
};
}
});
}
async function refreshMainSessionRecoveryOwner(lease, runId) {
const result = await commitMainSessionRecovery({
command: runId ? {
kind: "bind_foreground_run",
claim: lease,
runId
} : {
kind: "validate_foreground",
claim: lease
},
requireWriteSuccess: true,
target: lease
});
return (runId ? result.transition.kind === "applied" : result.transition.kind === "foreground_validated") && result.entry && result.sessionKey ? {
lease: runId ? {
...lease,
runId
} : lease,
entry: result.entry,
sessionKey: result.sessionKey
} : void 0;
}
async function claimMainSessionRecoveryOwner(params) {
const claim = await commitMainSessionRecovery({
command: {
kind: "claim_foreground",
cycleId: randomUUID(),
lifecycleGeneration: params.lifecycleGeneration,
sessionId: params.sessionId,
sessionKey: params.target.sessionKey,
claimId: randomUUID(),
...params.runId ? { runId: params.runId } : {}
},
requireWriteSuccess: true,
target: params.target
});
if (claim.transition.kind === "foreground_claimed") {
if (!claim.entry || !claim.sessionKey) return {
kind: "invalidated",
reason: "state_changed"
};
return {
kind: "claimed",
lease: {
...params.target,
...claim.transition.claim
},
entry: claim.entry,
sessionKey: claim.sessionKey
};
}
if (claim.transition.kind === "rejected" && claim.transition.reason === "stale_generation") return {
kind: "invalidated",
reason: claim.transition.reason
};
if (!claim.entry && (params.allowMissingSession || params.replacementSessionId)) return {
kind: "not_required",
entry: claim.entry,
sessionKey: claim.sessionKey
};
if (claim.entry && (claim.entry.abortedLastRun !== true || !hasMainSessionRecoveryClaim(claim.entry)) && claim.entry.restartRecoveryRuns === void 0 && claim.entry.mainRestartRecovery === void 0 && (claim.entry.sessionId === params.sessionId || claim.entry.sessionId === params.replacementSessionId) || claim.entry?.sessionId === params.sessionId && claim.sessionKey && !isMainRestartRecoveryCandidate(claim.entry, claim.sessionKey)) return {
kind: "not_required",
entry: claim.entry,
sessionKey: claim.sessionKey
};
return {
kind: "invalidated",
reason: claim.transition.kind === "rejected" ? claim.transition.reason : "state_changed"
};
}
async function inspectMainSessionRecoveryRequired(params) {
const result = await commitMainSessionRecovery({
command: {
kind: "inspect",
lifecycleGeneration: params.lifecycleGeneration,
sessionKey: params.target.sessionKey
},
expectedSessionId: params.expectedSessionId,
requireWriteSuccess: true,
target: params.target
});
if (result.transition.kind === "observed") return result.transition.view.status === "inactive" ? { kind: "not_required" } : { kind: "required" };
if (result.transition.kind === "rejected" && result.transition.reason === "session_replaced") return !result.entry && params.allowMissingSession ? { kind: "not_required" } : {
kind: "invalidated",
reason: result.transition.reason
};
return {
kind: "invalidated",
reason: result.transition.kind === "rejected" ? result.transition.reason : "state_changed"
};
}
async function releaseMainSessionRecoveryOwnerWithRetries(lease) {
const released = await retryMainSessionRecoveryMutation(async () => commitMainSessionRecovery({
command: {
kind: "release_foreground",
claim: lease
},
requireWriteSuccess: true,
target: lease
}));
const { entry, sessionKey } = released;
if (released.transition.kind !== "applied" && released.transition.kind !== "no_change" || !entry || !sessionKey || entry.sessionId !== lease.sessionId || !isMainSessionRecoveryPending(entry, sessionKey)) return;
return {
agentId: lease.agentId,
sessionId: entry.sessionId,
sessionKey,
storePath: lease.storePath
};
}
async function releaseMainSessionRecoveryOwner(lease) {
if (!lease) return;
try {
return await releaseMainSessionRecoveryOwnerWithRetries(lease);
} catch (error) {
scheduleMainSessionRecoveryMutation({
mutation: () => releaseMainSessionRecoveryOwnerWithRetries(lease),
onSuccess: async (pending) => {
if (pending) {
const { scheduleMainSessionRecoveryPendingTarget } = await import("./main-session-recovery-owner-release-Rrvejf22.mjs");
scheduleMainSessionRecoveryPendingTarget(pending);
}
}
});
throw error;
}
}
//#endregion
export { releaseMainSessionRecoveryOwner as a, refreshMainSessionRecoveryOwner as i, commitMainSessionRecovery as n, inspectMainSessionRecoveryRequired as r, claimMainSessionRecoveryOwner as t };