UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

788 lines (787 loc) • 32.2 kB
import { t as createDeferredCore } from "./deferred-D0La5CRk.mjs"; import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs"; import { a as runInDetachedAsyncContext } from "./async-work-scope-BYeX3gh6.mjs"; import { o as racePromiseWithAbortSignal } from "./src-D1BHGNTj.mjs"; import "./abort-signal-QqAmLWTl.mjs"; import { n as assertExistingDatabaseIdentity } from "./sqlite-worker-identity-KSR14F6C.mjs"; import { t as createSubsystemLogger } from "./subsystem-I-uqdx4f.mjs"; import { s as throwSqliteLifecycleErrors } from "./sqlite-lifecycle-errors-DuOygjcn.mjs"; import { i as isStateDatabaseReadAdmissionInvalidatedError, s as captureOpenClawDatabaseMaintenanceResource, t as StateDatabaseReadAdmissionInvalidatedError } from "./openclaw-state-db-async-lifecycle-DLKxSPCk.mjs"; import { t as captureRuntimeWorkerSource } from "./runtime-worker-generation-DCJaEqeS.mjs"; import { n as resolveRuntimeProcessEntrypointUrl } from "./runtime-process-url-D3H3_asn.mjs"; import { t as findOpenClawStateDatabaseFailure } from "./openclaw-state-db-failure-3stJt7Pa.mjs"; import { n as hydrateOpenClawStateWorkerError } from "./openclaw-state-worker-error-Cmtk9G4_.mjs"; import { _ as registerOpenClawStateDatabaseAsyncResource, f as openClawStateDatabaseCache, g as recordOpenClawStateDatabaseOpenFailure, m as publishOpenClawStateDatabaseWorkerAdmission, n as captureOpenClawStateDatabaseReadAdmission, u as getOpenClawStateDatabaseTerminalFailureAsync, v as registerOpenClawStateDatabaseLifecycleListener } from "./openclaw-state-db-cache-BKCnj4of.mjs"; import { t as SQLITE_IDLE_HANDLE_TTL_MS } from "./sqlite-handle-lifecycle-dWd9h3ii.mjs"; import { c as SqliteWorkerError } from "./sqlite-worker-contract-C51cJHzg.mjs"; import { a as isExistingOpenClawStateSchema, i as getExistingOpenClawStateSchemaPath } from "./openclaw-state-db-schema-policy-BxAJZRG6.mjs"; import { a as isSqliteWorkerStoreAvailable, c as openSharedStateSqliteWorkerStore, d as retireSqliteWorkerActor, f as runSqliteWorkerStoreOperation, i as hasUnclaimedSharedStateSqliteCleanup, r as getSqliteWorkerActorIdentity, t as closeUnclaimedSharedStateSqliteWorkers } from "./sqlite-worker-store-CsIRv3py.mjs"; import { AsyncLocalStorage } from "node:async_hooks"; import { channel } from "node:diagnostics_channel"; import { performance } from "node:perf_hooks"; //#region src/state/openclaw-state-worker-operation.ts /** A live alias cannot authorize a worker still bound to a vanished opening path. */ function assertOpenClawStateWorkerActorPath(actor) { if (openClawStateDatabaseCache.getKnownOpenClawStateDatabaseIdentity(actor.databasePath)?.key !== actor.key) throw new StateDatabaseReadAdmissionInvalidatedError("Shared-state worker opening path changed"); } function runWithCapturedWorkerContext(context, operation) { const maintenance = context.maintenanceScope; const run = () => maintenance ? maintenance.run(() => maintenance.track(operation())) : operation(); return context.runInCapturedSchemaScope ? context.runInCapturedSchemaScope(run) : run(); } function runWithOpenClawStateWorkerStore(store, context, operation, assertCurrent, createAdmission, signal) { const { admission } = context; const actor = getSqliteWorkerActorIdentity(store); return runSqliteWorkerStoreOperation(store, (scope) => operation(signal ? { execute: (command, options) => scope.execute(command, { signal: options?.signal ? AbortSignal.any([signal, options.signal]) : signal }) } : scope), context, (commandType) => { admission.assertCurrent(); assertOpenClawStateWorkerActorPath(actor); assertCurrent?.(commandType); }, createAdmission); } function assertOpenClawStateWorkerSchemaContext(context) { isExistingOpenClawStateSchema(context.admission.databasePath); if (getExistingOpenClawStateSchemaPath() !== context.existingSchemaPath) throw new Error("Shared-state worker schema context does not match its caller"); } /** Only native opening owns the caller scope; a cached actor must not retain it. */ function captureOpenClawStateWorkerOpeningGuard(context, assertCurrent, signal) { const admission = { assertCurrent, signal }; let captured = AsyncLocalStorage.bind(() => { context.admission.assertCurrent(); try { assertCurrent?.(); } catch (error) { admission.refusal = { error }; throw error; } }); return { admission, assertCurrent: () => { if (!captured) throw new Error("Shared-state worker opening admission is closed"); captured(); }, async run(open) { try { return await open(); } catch (error) { if (admission.signal?.aborted) admission.refusal = { error }; throw error; } finally { captured = void 0; admission.signal = void 0; } } }; } //#endregion //#region src/state/openclaw-state-worker-lease.ts /** Retain a logical actor resource while tracking only acquisition and individual commands. */ function retainOpenClawStateWorkerLease(context, owner, finalize) { const maintenance = context.maintenanceScope; context.admission.assertCurrent(); maintenance?.assertAdmission(); const ready = createDeferredCore(); const admitted = createDeferredCore(); const released = createDeferredCore(); const invocations = /* @__PURE__ */ new Set(); const operations = /* @__PURE__ */ new Set(); let retained; let releaseOperation; let retirement; let closing; let sealed = false; let invalidated = false; let store; let scope; let acquisitionReady = false; ready.promise.catch(() => void 0); const assertResourceCurrent = () => resource?.assertCurrent(); const assertInvocation = (invocation) => { if (invocation.phase === "closed" || invalidated) throw new SqliteWorkerError("Shared-state worker operation is closed", "closed"); context.admission.assertCurrent(); assertResourceCurrent(); }; const assertCommandAdmission = (invocation) => { assertInvocation(invocation); if (invocation.phase !== "accepting") throw new SqliteWorkerError("Shared-state worker operation is closed", "closed"); }; const assertNewOperation = () => { if (sealed) throw new SqliteWorkerError("Shared-state worker lease is closed", "closed"); context.admission.assertCurrent(); maintenance?.assertAdmission(); }; const retireNative = () => { retirement ??= (async () => { await admitted.promise.catch(() => void 0); released.resolve(); const errors = []; try { await retained; } catch (error) { errors.push(error); } try { await releaseOperation?.(); } catch (error) { errors.push(error); } throwSqliteLifecycleErrors(errors, "Shared-state worker lease retirement failed"); })(); retirement.catch(() => void 0); return retirement; }; const retire = () => { sealed = true; invalidated = true; for (const invocation of invocations) invocation.phase = "closed"; return retireNative(); }; const createOperationScope = (invocation) => ({ execute: (command, options) => { try { assertCommandAdmission(invocation); } catch (error) { const rejected = createDeferredCore(); rejected.reject(error); rejected.promise.catch(() => void 0); return rejected.promise; } const run = async () => { if (!acquisitionReady) await racePromiseWithAbortSignal(ready.promise, options?.signal); assertInvocation(invocation); if (!scope) throw new SqliteWorkerError("Shared-state worker lease is not ready", "closed"); return scope.execute(command, options); }; const result = (resource ? resource.run(run) : run()).catch(async (error) => { if (store && !sealed && !isSqliteWorkerStoreAvailable(store)) try { await retire(); } catch (cleanupError) { throw new AggregateError([error, cleanupError], "Shared-state operation and retirement failed", { cause: cleanupError }); } throw error; }); invocation.pending.add(result); const settled = () => invocation.pending.delete(result); result.then(settled, settled); return maintenance ? maintenance.track(result) : result; } }); const runOperation = (operation) => { try { assertNewOperation(); } catch (error) { const rejected = createDeferredCore(); rejected.reject(error); rejected.promise.catch(() => void 0); return rejected.promise; } const invocation = { phase: "accepting", pending: /* @__PURE__ */ new Set() }; invocations.add(invocation); const completion = createDeferredCore(); operations.add(completion.promise); completion.promise.catch(() => void 0); const run = async () => { try { return await operation(createOperationScope(invocation)); } finally { if (invocation.phase === "accepting") invocation.phase = "draining"; await Promise.allSettled(invocation.pending); invocation.phase = "closed"; invocations.delete(invocation); } }; run().then((value) => { operations.delete(completion.promise); completion.resolve(value); }, (error) => { operations.delete(completion.promise); completion.reject(error); }); return completion.promise; }; const release = () => { if (invalidated) return retireNative(); if (closing) return closing; sealed = true; closing = (async () => { const errors = []; const terminal = { phase: "accepting", pending: /* @__PURE__ */ new Set() }; invocations.add(terminal); try { await ready.promise; await finalize?.(createOperationScope(terminal)); } catch (error) { errors.push(error); } finally { if (terminal.phase === "accepting") terminal.phase = "draining"; await Promise.allSettled(terminal.pending); terminal.phase = "closed"; invocations.delete(terminal); } const results = await Promise.allSettled(operations); errors.push(...results.flatMap((result) => result.status === "rejected" ? [result.reason] : [])); try { await retireNative(); } catch (error) { errors.push(error); } throwSqliteLifecycleErrors(errors, "Shared-state worker lease cleanup failed"); })(); closing.catch(() => void 0); return closing; }; const lease = { ready: ready.promise, execute: (command, options) => runOperation((operationScope) => operationScope.execute(command, options)), runOperation, release, retire }; maintenance?.own(lease, "shared-resources", release); const resource = maintenance ? captureOpenClawDatabaseMaintenanceResource(lease, maintenance) : void 0; const acquire = async () => { try { const failure = await getOpenClawStateDatabaseTerminalFailureAsync(context); if (failure) throw failure; context.admission.assertCurrent(); store = await owner.open(context); context.admission.assertCurrent(); if (!store) throw new Error("Canonical shared-state worker did not open its database"); releaseOperation = owner.retainOperation(store); retained = runWithOpenClawStateWorkerStore(store, context, async (operationScope) => { scope = operationScope; admitted.resolve(); await released.promise; }, assertResourceCurrent); retained.catch(admitted.reject); await admitted.promise; context.admission.assertCurrent(); if (invalidated) throw new SqliteWorkerError("Shared-state worker lease is closed", "closed"); } catch (error) { const failure = error instanceof Error ? hydrateOpenClawStateWorkerError(error) : error; admitted.reject(failure); try { await retire(); } catch (cleanupError) { if (cleanupError !== failure) throw new AggregateError([failure, cleanupError], "Shared-state lease acquisition failed", { cause: cleanupError }); } throw failure; } }; runWithCapturedWorkerContext(context, acquire).then(() => { acquisitionReady = true; ready.resolve(); }, ready.reject); return lease; } //#endregion //#region src/state/openclaw-state-worker-owner.ts const log = createSubsystemLogger("state/worker"); const SHARED_STATE_WORKER_IDLE_INSPECT_MS = 6e4; function createSharedStateWorkerOwner() { const moduleUrl = resolveRuntimeProcessEntrypointUrl("sharedStateStore"); const stores = /* @__PURE__ */ new Set(); const activeEntries = /* @__PURE__ */ new Set(); const retiring = /* @__PURE__ */ new Map(); const retiringActors = /* @__PURE__ */ new Map(); const memoryPressure = channel("openclaw.memory.critical"); let pressureSubscribed = false; const refreshPressureSubscription = () => { const hasCustody = stores.size > 0 || retiring.size > 0 || retiringActors.size > 0 || activeEntries.size > 0; if (hasCustody === pressureSubscribed) return; pressureSubscribed = hasCustody; if (hasCustody) memoryPressure.subscribe(retireIdleWorkers); else memoryPressure.unsubscribe(retireIdleWorkers); }; const matches = (entry, identity) => identity === void 0 || entry.context.admission.identity.key === identity.key; const hasActiveActorOperations = (entry) => [...activeEntries].some((active) => entry.actor ? active.actor === entry.actor : active === entry); const clearIdleRetirement = (entry) => { if (entry.idleTimer) { clearTimeout(entry.idleTimer); entry.idleTimer = void 0; } }; const hasPendingCleanup = (entry) => entry.cleanup?.pending ?? (!entry.context.maintenanceScope && hasUnclaimedSharedStateSqliteCleanup(entry.context.admission.databasePath)); const retire = (entry) => { clearIdleRetirement(entry); stores.delete(entry); let attempt = retiring.get(entry); if (!attempt) { attempt = {}; retiring.set(entry, attempt); } refreshPressureSubscription(); if (attempt.pending) return attempt.pending; if (entry.actor && retiringActors.has(entry.actor)) return retireActor(entry.actor, entry.context.admission.identity); const pending = (entry.store ? entry.store.close() : entry.opening.then((store) => store?.close(), () => entry.cleanup ? entry.cleanup.close() : entry.context.maintenanceScope ? void 0 : closeUnclaimedSharedStateSqliteWorkers(entry.context.admission.databasePath))).catch(async (error) => { if (!attempt.actorSettlement) throw error; await attempt.actorSettlement; }); attempt.pending = pending; const settled = () => { attempt.pending = void 0; if (!hasPendingCleanup(entry)) retiring.delete(entry); refreshPressureSubscription(); }; pending.then(settled, settled); return pending; }; const scheduleIdleRetirement = (entry) => { if (entry.activeOperations !== 0 || entry.idleTimer || entry.context.maintenanceScope || !entry.store || !stores.has(entry)) return; const store = entry.store; const generation = entry.operationGeneration; const deadline = performance.now() + SQLITE_IDLE_HANDLE_TTL_MS; const arm = (delay, inspect) => { const isCurrentIdle = () => entry.idleTimer === timer && entry.operationGeneration === generation && entry.activeOperations === 0 && stores.has(entry); const settle = async () => { if (!isCurrentIdle()) return; let healthy = false; if (inspect) try { healthy = await runWithCapturedWorkerContext(entry.context, () => runSqliteWorkerStoreOperation(store, (scope) => scope.execute({ type: "database.inspectIdle", input: void 0 }), entry.context, () => { entry.context.admission.assertCurrent(); if (!isCurrentIdle()) throw new Error("Shared-state worker resumed before idle inspection"); })) === "healthy" && isSqliteWorkerStoreAvailable(store); entry.context.admission.assertCurrent(); } catch { healthy = false; } if (!isCurrentIdle()) return; entry.idleTimer = void 0; const remaining = deadline - performance.now(); if (healthy && remaining > 0) arm(remaining, false); else await retire(entry); }; const timer = runInDetachedAsyncContext(() => setTimeout(() => { settle().catch((error) => { log.warn("Idle shared-state worker retirement failed", { path: entry.context.admission.databasePath, error }); }); }, delay)); entry.idleTimer = timer; timer.unref?.(); }; arm(SHARED_STATE_WORKER_IDLE_INSPECT_MS, true); }; const retireAfterFailure = async (entry, error, stage) => { try { await retire(entry); } catch (cleanupError) { throw new AggregateError([error, cleanupError], `Shared-state worker ${stage} and cleanup failed`, { cause: cleanupError }); } throw error; }; const retainOperation = (store) => { const entry = [...stores].find((candidate) => candidate.store === store); if (!entry) throw new Error("Shared-state worker operation lost its actor owner"); clearIdleRetirement(entry); entry.operationGeneration += 1; entry.activeOperations += 1; activeEntries.add(entry); let released = false; return () => { if (released) return; released = true; entry.activeOperations -= 1; if (entry.activeOperations === 0) activeEntries.delete(entry); refreshPressureSubscription(); if (stores.has(entry) && !isSqliteWorkerStoreAvailable(store) && !hasActiveActorOperations(entry)) { const pending = entry.actor ? retireActor(entry.actor, entry.context.admission.identity) : retire(entry); pending.catch((error) => { log.warn("Shared-state worker retirement failed", { path: entry.context.admission.databasePath, error }); }); return pending; } scheduleIdleRetirement(entry); }; }; const retainActorSettlement = (attempt, pending) => { for (const entry of attempt.entries) { const closing = retiring.get(entry); if (closing) closing.actorSettlement = pending; } return pending; }; const retireActor = (actor, identity) => { let attempt = retiringActors.get(actor); if (!attempt) { attempt = { identity, entries: /* @__PURE__ */ new Set() }; retiringActors.set(actor, attempt); } for (const entry of [...stores, ...retiring.keys()]) if (entry.actor === actor) { attempt.entries.add(entry); clearIdleRetirement(entry); stores.delete(entry); } refreshPressureSubscription(); if (attempt.pending) return retainActorSettlement(attempt, attempt.pending); const current = attempt; const complete = () => { for (const entry of current.entries) { clearIdleRetirement(entry); stores.delete(entry); retiring.delete(entry); } retiringActors.delete(actor); refreshPressureSubscription(); }; const pending = retireSqliteWorkerActor(actor).then(complete, (error) => { current.pending = void 0; if (current.entries.size > 0 && [...current.entries].every((entry) => entry.cleanup?.pending === false)) { complete(); return; } throw error; }); current.pending = pending; return retainActorSettlement(current, pending); }; const joinActorRetirement = async (attempt) => { if (!attempt.pending) throw new Error("Shared-state actor cleanup is pending; close the database before reopening"); await attempt.pending; }; const inspectInvalidEntry = (entry) => { const actor = entry.actor; let kind = "actor"; try { if (entry.bound && actor) { assertOpenClawStateWorkerActorPath(actor); if (entry.store && isSqliteWorkerStoreAvailable(entry.store)) kind = "client"; } entry.databaseAdmission.assertCurrent(); } catch (error) { if (!isStateDatabaseReadAdmissionInvalidatedError(error)) throw error; if (kind === "client" && openClawStateDatabaseCache.getKnownOpenClawStateDatabaseIdentity(entry.context.admission.databasePath)?.key === actor?.key) kind = "actor"; return { kind, error }; } }; const retireInvalidEntry = (entry) => { const invalid = inspectInvalidEntry(entry); if (!invalid) return; if (invalid.kind === "actor") { if (hasActiveActorOperations(entry)) throw invalid.error; return { pending: entry.opening.then(() => { if (hasActiveActorOperations(entry)) throw invalid.error; return entry.actor ? retireActor(entry.actor, entry.context.admission.identity) : retire(entry); }, () => retire(entry)) }; } if (!stores.has(entry)) return; const closing = retire(entry); return { pending: entry.activeOperations === 0 ? closing : void 0 }; }; async function close(identity) { for (const entry of stores.values()) if (matches(entry, identity)) retire(entry); const errors = (await Promise.allSettled([...[...retiring.keys()].filter((entry) => matches(entry, identity)).map(retire), ...[...retiringActors].filter(([, attempt]) => !identity || attempt.identity.key === identity.key).map(([actor, attempt]) => retireActor(actor, attempt.identity))])).flatMap((result) => result.status === "rejected" ? [result.reason] : []); refreshPressureSubscription(); if (errors.length) throw new AggregateError(errors, "Failed to close shared-state SQLite workers"); } registerOpenClawStateDatabaseAsyncResource({ close }); registerOpenClawStateDatabaseLifecycleListener((event) => { if (event.kind !== "opened") { if (!event.identity) return; close(event.identity).catch((error) => { log.warn("Shared-state worker retirement failed", { path: event.path, error }); }); } }); function retireIdleWorkers() { for (const entry of stores) if (entry.idleTimer && entry.store && !entry.context.maintenanceScope && !hasActiveActorOperations(entry)) runInDetachedAsyncContext(() => retire(entry)).catch((error) => { log.warn("Idle shared-state worker retirement failed", { path: entry.context.admission.databasePath, error }); }); } return { close, retainOperation, async openCleanup(databasePath, context, assertOwned, identity) { const expectedIdentity = { ...identity }; assertOwned(); if (!expectedIdentity.key.startsWith("file:")) return; const assertCurrent = () => { assertOwned(); assertExistingDatabaseIdentity(databasePath, expectedIdentity.key, expectedIdentity.birthtime); for (const entry of activeEntries) if (matches(entry, expectedIdentity) && entry.store && !isSqliteWorkerStoreAvailable(entry.store)) throw new SqliteWorkerError("Shared-state cleanup is waiting for accepted operations to settle", "unavailable"); }; assertCurrent(); for (const attempt of retiringActors.values()) if (attempt.identity.key === expectedIdentity.key) { assertCurrent(); await joinActorRetirement(attempt); assertCurrent(); } for (const entry of /* @__PURE__ */ new Set([...stores, ...retiring.keys()])) if ((stores.has(entry) || retiring.has(entry)) && matches(entry, expectedIdentity) && entry.store && !isSqliteWorkerStoreAvailable(entry.store)) { assertCurrent(); await (entry.actor ? retireActor(entry.actor, expectedIdentity) : retire(entry)); assertCurrent(); } return openSharedStateSqliteWorkerStore({ ...captureRuntimeWorkerSource(moduleUrl), databasePath, existingOnly: true }, context, assertCurrent); }, async open(context, options = {}) { const { existingOnly = false, assertCurrent, preparation, signal } = options; const wait = (pending) => racePromiseWithAbortSignal(pending, signal); const { admission } = context; const source = captureRuntimeWorkerSource(moduleUrl); const assertAdmission = () => { signal?.throwIfAborted(); admission.assertCurrent(); assertCurrent?.(); }; assertAdmission(); assertOpenClawStateWorkerSchemaContext(context); let entry; let ownsOpening = false; for (;;) { for (const candidate of /* @__PURE__ */ new Set([...stores, ...retiring.keys()])) { if (!matches(candidate, admission.identity)) continue; const retirement = retireInvalidEntry(candidate); if (retirement) { if (retirement.pending) await wait(retirement.pending); return this.open(context, options); } if (stores.has(candidate) && (candidate.context.existingSchemaPath !== context.existingSchemaPath || candidate.source.moduleUrl.href !== source.moduleUrl.href)) { await wait(retire(candidate)); assertAdmission(); } } for (const attempt of retiringActors.values()) if (attempt.identity.key === admission.identity.key) { await wait(joinActorRetirement(attempt)); assertAdmission(); } for (const [retiringEntry, attempt] of retiring) if (matches(retiringEntry, admission.identity) && retiringEntry.context.maintenanceScope === context.maintenanceScope) { if (inspectInvalidEntry(retiringEntry)?.kind === "client") continue; if (!attempt.pending) { if (!hasPendingCleanup(retiringEntry)) { retiring.delete(retiringEntry); refreshPressureSubscription(); continue; } throw new Error("Shared-state SQLite cleanup is pending; close the database before reopening"); } try { await wait(attempt.pending); } catch (error) { if (retiring.get(retiringEntry) === attempt) throw error; } assertAdmission(); } assertAdmission(); entry = [...stores].find((candidate) => matches(candidate, admission.identity) && candidate.context.maintenanceScope === context.maintenanceScope && candidate.context.existingSchemaPath === context.existingSchemaPath); if (!entry || entry.store || entry.openingAdmission.assertCurrent === assertCurrent && entry.openingAdmission.signal === signal) break; let rejected; try { await wait(entry.opening); } catch (error) { rejected = { error }; } assertAdmission(); if (rejected && (!entry.openingAdmission.refusal || !Object.is(rejected.error, entry.openingAdmission.refusal.error))) throw rejected.error; } if (!entry) { ownsOpening = true; assertAdmission(); const databaseAdmission = captureOpenClawStateDatabaseReadAdmission(admission.databasePath); assertAdmission(); const openingGuard = captureOpenClawStateWorkerOpeningGuard(context, assertCurrent, signal); const open = () => openingGuard.run(() => openSharedStateSqliteWorkerStore({ ...source, databasePath: admission.databasePath, existingOnly }, context, openingGuard.assertCurrent, { maintenanceScope: context.maintenanceScope, preparation, signal, retainCleanup: (cleanup) => { admitted.cleanup = cleanup; } })); const admitted = { source, context, databaseAdmission, openingAdmission: openingGuard.admission, existingOnly, activeOperations: 0, operationGeneration: 0, opening: context.maintenanceScope ? open() : runInDetachedAsyncContext(open) }; entry = admitted; admitted.opening = admitted.opening.then((store) => { if (store) { admitted.store = store; admitted.actor = getSqliteWorkerActorIdentity(store); } else { stores.delete(admitted); refreshPressureSubscription(); } return store; }); stores.add(entry); refreshPressureSubscription(); context.maintenanceScope?.own(entry, "shared-resources", () => retire(admitted)); entry.opening.catch(() => { stores.delete(admitted); if (hasPendingCleanup(admitted) && !retiring.has(admitted)) retiring.set(admitted, {}); refreshPressureSubscription(); }); } const store = await (ownsOpening ? entry.opening : wait(entry.opening)); try { admission.assertCurrent(); } catch (error) { return retireAfterFailure(entry, error, "admission"); } assertCurrent?.(); if (!store) return !existingOnly && entry.existingOnly ? this.open(context, { ...options, existingOnly: false }) : void 0; if (!stores.has(entry)) return this.open(context, options); clearIdleRetirement(entry); const actorRetirement = entry.actor ? retiringActors.get(entry.actor) : void 0; if (entry.actor && actorRetirement) { actorRetirement.entries.add(entry); stores.delete(entry); refreshPressureSubscription(); await wait(joinActorRetirement(actorRetirement)); return this.open(context, options); } if (!isSqliteWorkerStoreAvailable(store) && !hasActiveActorOperations(entry)) { await wait(entry.actor ? retireActor(entry.actor, admission.identity) : retire(entry)); return this.open(context, options); } try { if (!entry.bound) { publishOpenClawStateDatabaseWorkerAdmission(entry.context.admission); entry.bound = true; } } catch (error) { return retireAfterFailure(entry, error, "binding"); } const retirement = retireInvalidEntry(entry); if (retirement) { if (retirement.pending) await wait(retirement.pending); return this.open(context, options); } stores.delete(entry); stores.add(entry); return store; } }; } function getOpenClawStateWorkerOwner() { return resolveGlobalSingleton(Symbol.for("openclaw.sharedStateWorkerOwner"), createSharedStateWorkerOwner, (sharedOwner) => sharedOwner.close()); } //#endregion //#region src/state/openclaw-state-worker-store.ts /** Retired cleanup uses the retained owner's backend without renewing read admission. */ function openOpenClawStateWorkerCleanupStore(databasePath, context, assertOwned, identity) { return getOpenClawStateWorkerOwner().openCleanup(databasePath, context, assertOwned, identity); } async function executeOpenClawStateWorker(context, command) { const result = await runOpenClawStateWorkerOperation(context, (scope) => scope.execute(command)); context.admission.assertCurrent(); return result; } /** Retain the canonical actor for capture callbacks and their terminal writes. */ function createOpenClawStateWorkerLease(context, finalize) { return retainOpenClawStateWorkerLease(context, { open: (captured) => getOpenClawStateWorkerOwner().open(captured), retainOperation: (store) => getOpenClawStateWorkerOwner().retainOperation(store) }, finalize); } async function runOpenClawStateWorkerOperation(context, operation, options) { return runWithCapturedWorkerContext(context, async () => { try { options?.signal?.throwIfAborted(); context.admission.assertCurrent(); options?.assertCurrent?.(); const failure = await getOpenClawStateDatabaseTerminalFailureAsync(context, options?.signal); if (failure) throw failure; context.admission.assertCurrent(); options?.assertCurrent?.(); const store = await getOpenClawStateWorkerOwner().open(context, options); if (!store) { options?.signal?.throwIfAborted(); context.admission.assertCurrent(); if (options?.existingOnly) return; throw new Error("Canonical shared-state worker did not open its database"); } const releaseOperation = getOpenClawStateWorkerOwner().retainOperation(store); try { options?.signal?.throwIfAborted(); context.admission.assertCurrent(); options?.assertCurrent?.(); return await runWithOpenClawStateWorkerStore(store, context, operation, options?.assertCurrent, options?.createAdmission, options?.signal); } finally { releaseOperation(); } } catch (error) { const hydrated = hydrateOpenClawStateWorkerError(error); const failure = findOpenClawStateDatabaseFailure(hydrated, context.admission.databasePath); if (failure && !openClawStateDatabaseCache.getOpenClawStateDatabaseRecordedFailure(context.admission.databasePath)) { try { context.admission.assertCurrent(); } catch { throw hydrated; } recordOpenClawStateDatabaseOpenFailure(context.admission.databasePath, failure); } throw hydrated; } }); } /** Inspect the existing file without recursively admitting a domain operation. */ async function inspectOpenClawStateDatabaseGeneration(context, generation, signal) { return runWithCapturedWorkerContext(context, async () => { try { const store = await getOpenClawStateWorkerOwner().open(context, { existingOnly: true, signal }); context.admission.assertCurrent(); if (!store) return; const releaseOperation = getOpenClawStateWorkerOwner().retainOperation(store); try { context.admission.assertCurrent(); return await runWithOpenClawStateWorkerStore(store, context, (scope) => scope.execute({ type: "database.generationMatches", input: { generation } }), void 0, void 0, signal); } finally { releaseOperation(); } } catch (error) { throw hydrateOpenClawStateWorkerError(error); } }); } //#endregion export { runOpenClawStateWorkerOperation as a, openOpenClawStateWorkerCleanupStore as i, executeOpenClawStateWorker as n, runWithCapturedWorkerContext as o, inspectOpenClawStateDatabaseGeneration as r, createOpenClawStateWorkerLease as t };