openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
213 lines (212 loc) • 10.3 kB
JavaScript
import { d as getAgentEventLifecycleGeneration } from "./agent-events-jWdQ_ESE.mjs";
import { z as onUserProfilesChanged } from "./user-profiles-internal-B_G1rF82.mjs";
import { o as createAdmittedRunOperatorAuthority } from "./admitted-run-context-D0yzk2io.mjs";
import { a as restoreOperatorModelPolicySnapshot } from "./operator-model-policy-dg8HlQ48.mjs";
import { s as prepareUserProfileIdentity } from "./user-profile-list-B5oc-3kB.mjs";
import { r as captureGatewayDeviceRevocation, s as onGatewayDeviceSourceRevoked } from "./device-revocation-Cyf_TEpJ.mjs";
import { a as onOperatorRolePolicyChanged, f as resolveOperatorRolePolicyForAssignment } from "./operator-role-policy-BPiQfn95.mjs";
import { r as sourceRolePolicy } from "./operator-role-source-policy-BO9lXFYe.mjs";
import { a as decodeGatewayOperatorRecoverySource, r as intersectOperatorRunModelPolicy, t as captureChannelOperatorRunAuthority } from "./operator-run-authority-XZsmK2PN.mjs";
import { c as resumeGatewayOperatorAccessGrant } from "./operator-access-policy-DQXLvTEQ.mjs";
import { r as onSessionIdentityMutation } from "./session-lifecycle-events-CfTrpgyR.mjs";
import { i as readSessionEntryReadOnlyInWorker } from "./session-entry-read-runtime-BdnQjoL6.mjs";
import { n as isGatewayAuthGrantCurrent } from "./auth-policy-tP8zZBpP.mjs";
import { r as loadPairedDevicePairingStoreRecordReadOnly } from "./device-pairing-store-readonly-BVo-mzyt.mjs";
import { r as isPairedDeviceTokenIdentityCurrent } from "./device-pairing-identity-D10qDl39.mjs";
import { t as GatewayOperatorRecoveryOwnershipError } from "./restart-recovery-errors-CxiWafBU.mjs";
import { isDeepStrictEqual } from "node:util";
//#region src/gateway/operator-run-recovery.ts
const recoverySources = /* @__PURE__ */ new WeakMap();
/** Only an exact live restore can carry the original operator admission classification. */
function readGatewayOperatorRecoverySource(authority, target) {
const restored = authority && recoverySources.get(authority);
if (!authority || !restored || restored.target.recoveryRunId !== target.runId || restored.target.sessionKey !== target.sessionKey || restored.target.sessionId !== target.sessionId) return;
authority.assertCurrent();
return restored.source;
}
/** Re-admit the exact durable source under current policy; never revive an old run capability. */
async function restoreGatewayOperatorRecovery(params) {
const target = { ...params.target };
const read = () => readSessionEntryReadOnlyInWorker(target, params.assertCurrent);
const initialRead = new AbortController();
const stopInitialRead = onSessionIdentityMutation((mutation) => {
if (mutation.agentId === target.agentId && (mutation.previous.sessionId === target.sessionId || mutation.previous.sessionKeys.includes(target.sessionKey))) initialRead.abort(/* @__PURE__ */ new Error("Restart recovery operator source no longer owns this input."));
});
let entry;
try {
entry = await read();
initialRead.signal.throwIfAborted();
} finally {
stopInitialRead();
}
if (!entry?.restartRecoveryOperatorSource) return;
const source = decodeGatewayOperatorRecoverySource(entry.restartRecoveryOperatorSource);
const assertClaim = (current) => {
const reservation = current?.mainRestartRecovery?.reservation;
if (!current || current.sessionId !== target.sessionId || current.lifecycleRevision !== source.lifecycleRevision || current.restartRecoveryDeliveryRunId !== target.recoveryRunId || current.restartRecoveryDeliverySourceRunId !== target.sourceRunId || reservation && (reservation.runId !== target.recoveryRunId || reservation.lifecycleGeneration !== getAgentEventLifecycleGeneration()) || source.agentId !== target.agentId || source.sessionKey !== target.sessionKey || source.sessionId !== target.sessionId || source.sourceRunId !== target.sourceRunId || !isDeepStrictEqual(current.restartRecoveryOperatorSource, source)) throw new GatewayOperatorRecoveryOwnershipError();
};
assertClaim(entry);
const restored = await restoreGatewayOperatorSource({
snapshot: source.snapshot,
target,
context: params.context,
assertCurrent: params.assertCurrent,
prepareCurrent: async () => assertClaim(await read())
});
try {
restored.authority.assertCurrent();
recoverySources.set(restored.authority, {
target,
source: Object.freeze({
controlUiAdmin: source.snapshot.controlUiAdmin,
localOperator: source.snapshot.localOperator
})
});
return restored;
} catch (error) {
restored.release();
throw error;
}
}
/** Restore current policy for owner-held source evidence; the caller owns the exact work claim. */
async function restoreGatewayOperatorSource(params) {
const target = { ...params.target };
const snapshot = params.snapshot;
const { context, assertCurrent } = params;
const getConfig = context.getCommittedRuntimeConfig ?? context.getRuntimeConfig;
const gatewayOwner = context.resolveGatewayContext?.() ?? context;
let references = 1;
const signal = new AbortController();
const revoke = (cause) => signal.abort(new Error("Restart recovery operator authority changed; start a new user turn.", { cause }));
const assertOwner = () => {
if (references === 0) throw new Error("Restart recovery operator authority is no longer active.");
signal.signal.throwIfAborted();
assertCurrent();
};
const subscriptions = [onSessionIdentityMutation((mutation) => {
if (mutation.agentId === target.agentId && (mutation.previous.sessionId === target.sessionId || mutation.previous.sessionKeys.includes(target.sessionKey))) revoke();
})];
let identity;
const releaseHold = () => {
let released = false;
return () => {
if (released) return;
released = true;
if (--references === 0) {
subscriptions.splice(0).forEach((release) => release());
identity?.release();
}
};
};
const release = releaseHold();
try {
const generationOwner = context.sharedGatewaySessionGenerationState;
if (snapshot.sharedGeneration !== void 0) {
if (!generationOwner || generationOwner.requiredGeneration !== snapshot.sharedGeneration) throw new Error("Restart recovery authentication owner changed; start a new user turn.");
subscriptions.push(generationOwner.onInvalidated(snapshot.sharedGeneration, revoke, snapshot.authPolicy));
}
const device = captureGatewayDeviceRevocation(gatewayOwner, {
deviceId: snapshot.device?.deviceId,
role: "operator"
}, () => !signal.signal.aborted);
subscriptions.push(device.release);
const releaseDeviceNotification = onGatewayDeviceSourceRevoked(device.isCurrent, revoke);
if (releaseDeviceNotification) subscriptions.push(releaseDeviceNotification);
const assertSourcePolicy = () => {
assertOwner();
const cfg = getConfig();
const role = resolveOperatorRolePolicyForAssignment(snapshot.profileId, snapshot.assignedRole, cfg, snapshot.githubLogin);
if (!device.isCurrent() || !isDeepStrictEqual(sourceRolePolicy(role), snapshot.rolePolicy) || !isGatewayAuthGrantCurrent(snapshot.authPolicy, cfg) || snapshot.sharedGeneration !== void 0 && context.sharedGatewaySessionGenerationState !== generationOwner) throw new Error("Restart recovery operator source policy changed.");
return cfg;
};
let assertPolicy = assertSourcePolicy;
const recheck = () => {
if (references > 0 && !signal.signal.aborted) try {
assertPolicy();
} catch (error) {
revoke(error);
}
};
subscriptions.push(onOperatorRolePolicyChanged((change) => {
if (change.kind === "assignment" && change.profileId === snapshot.profileId) revoke();
else if (change.kind === "config" && change.context === gatewayOwner) recheck();
}));
assertSourcePolicy();
identity = await prepareUserProfileIdentity(snapshot.profileId);
const profileIdentity = identity;
const assertFacts = () => {
const cfg = assertSourcePolicy();
const profile = profileIdentity.readCurrentFacts(snapshot.aliasBindingIds).profile;
if (profile.profileId !== snapshot.profileId || profile.assignedRole !== snapshot.assignedRole || (profile.githubLogin ?? null) !== snapshot.githubLogin) throw new Error("Restart recovery operator identity changed.");
return {
cfg,
profile
};
};
let accessAuthority;
assertPolicy = () => {
try {
const { profile, cfg } = assertFacts();
const access = resumeGatewayOperatorAccessGrant(profile, cfg, snapshot.grant);
if (access && !accessAuthority) {
accessAuthority = access;
const onAbort = () => revoke(access.signal.reason);
access.signal.addEventListener("abort", onAbort, { once: true });
subscriptions.push(() => access.signal.removeEventListener("abort", onAbort));
}
accessAuthority?.signal.throwIfAborted();
accessAuthority?.assertCurrent();
accessAuthority?.signal.throwIfAborted();
return assertFacts().cfg;
} catch (error) {
revoke(error);
throw error;
}
};
subscriptions.push(onUserProfilesChanged(recheck));
assertPolicy();
if (snapshot.device) {
const paired = await loadPairedDevicePairingStoreRecordReadOnly(snapshot.device.deviceId);
if (!isPairedDeviceTokenIdentityCurrent(paired, "operator", snapshot.device, snapshot.scopes)) revoke();
assertPolicy();
}
assertPolicy();
await params.prepareCurrent?.();
assertPolicy();
const live = captureChannelOperatorRunAuthority({
profileId: snapshot.profileId,
assignedRole: snapshot.assignedRole,
githubLogin: snapshot.githubLogin,
scopes: snapshot.scopes,
gatewayAccessGrant: snapshot.grant,
getRuntimeConfig: getConfig,
assertCurrent: assertPolicy,
signal: signal.signal
});
const originalModels = snapshot.modelPolicy ? restoreOperatorModelPolicySnapshot(snapshot.modelPolicy) : void 0;
const authority = createAdmittedRunOperatorAuthority({
...live,
signal: signal.signal,
recoverySnapshot: snapshot,
retain: () => {
assertPolicy();
references += 1;
return releaseHold();
},
get modelPolicy() {
return intersectOperatorRunModelPolicy(originalModels, live.modelPolicy);
}
});
authority.assertCurrent();
return {
authority,
controlUiAdmin: snapshot.controlUiAdmin,
release
};
} catch (error) {
release();
throw error;
}
}
//#endregion
export { restoreGatewayOperatorRecovery as n, restoreGatewayOperatorSource as r, readGatewayOperatorRecoverySource as t };