UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

185 lines (184 loc) • 8.16 kB
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs"; import { c as registerNodeSqliteDisposeCallback } from "./kysely-sync-cache-state-DbAMR_DD.mjs"; import { h as runSqliteReadOperationSync } from "./sqlite-schema-facts-BVf0E_0z.mjs"; import { n as isArtifactPreservingStateRead } from "./artifact-preserving-state-reads-BQnQLAJR.mjs"; import { t as createSubsystemLogger } from "./subsystem-4NzR43uz.mjs"; import { o as runInSqliteMaintenanceContext } from "./sqlite-wal-DfKCrDTQ.mjs"; import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs"; import "./session-key-DY3MOX4N.mjs"; import { r as assertCanonicalSessionValidationSchema } from "./openclaw-agent-canonical-validation-schema-4dL3LLg2.mjs"; import { a as readOpenClawAgentDatabaseIdentity, i as isOpenClawAgentDatabasePathCurrent, n as createOpenClawAgentDatabaseClaim } from "./openclaw-agent-db-identity-BlmER9-X.mjs"; import { t as SQLITE_IDLE_HANDLE_TTL_MS } from "./sqlite-handle-lifecycle-dWd9h3ii.mjs"; import { i as assertSupportedAgentSchemaVersion, t as assertCanonicalAgentPersistenceVersion } from "./openclaw-agent-db-schema-read-y-s2h2dU.mjs"; import { a as readOpenClawAgentDatabaseSnapshot, i as readOpenClawAgentDatabase, n as hasOpenClawAgentReadOnlySchema, o as withFreshOpenClawAgentDatabaseReadOnly, r as openOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-open-CLVyHzjq.mjs"; import { i as isIncognitoOpenClawAgentSqlitePath, l as resolveOpenClawAgentSqlitePath } from "./openclaw-agent-db.paths-BbmZlEP5.mjs"; import { a as withScopedOpenClawAgentDatabaseReadOnly, i as retainCachedOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-scope-8pVQvbhg.mjs"; import { i as getOpenClawAgentDatabaseIfOpen, t as borrowOpenClawAgentDatabase } from "./openclaw-agent-db-DWI9Bgur.mjs"; //#region src/state/openclaw-agent-db-readonly-companion.ts const log = createSubsystemLogger("state/agent-db"); const companions = resolveGlobalSingleton(Symbol.for("openclaw.agentDatabaseReadOnlyCompanions"), () => /* @__PURE__ */ new WeakMap()); function matchesWriter(reader, writer) { return isOpenClawAgentDatabasePathCurrent(writer) && isOpenClawAgentDatabasePathCurrent(reader) && readOpenClawAgentDatabaseIdentity(reader).identity === readOpenClawAgentDatabaseIdentity(writer).identity; } /** Keep committed reads separate from the active writer without reopening per assertion. */ function withCommittedOpenClawAgentDatabaseReadOnly(writer, operation, options, behavior = {}) { let companion = companions.get(writer.db); if (companion?.active) return withFreshOpenClawAgentDatabaseReadOnly(operation, options, behavior); if (companion && (!matchesWriter(companion.reader, writer) || companion.reader.db.isTransaction)) { companion.close(); companion = void 0; } if (!companion && !isOpenClawAgentDatabasePathCurrent(writer)) return withFreshOpenClawAgentDatabaseReadOnly(operation, options, behavior); if (!companion) { const opened = openOpenClawAgentDatabaseReadOnly(options); if (!opened.found) return opened; const reader = opened.database; let unregisterDispose = () => {}; let idleTimer; const close = () => { if (reader.db.isOpen) reader.close(); clearTimeout(idleTimer); if (companions.get(writer.db)?.reader === reader) companions.delete(writer.db); unregisterDispose(); }; try { if (!matchesWriter(reader, writer)) return behavior.snapshot ? readOpenClawAgentDatabaseSnapshot(reader, operation) : readOpenClawAgentDatabase(reader, operation); unregisterDispose = registerNodeSqliteDisposeCallback(writer.db, close); idleTimer = runInSqliteMaintenanceContext(() => setTimeout(() => { if (companions.get(writer.db)?.reader !== reader) return; try { close(); } catch (error) { log.warn("Idle committed agent reader cleanup failed", { path: reader.path, error }); idleTimer?.refresh(); } }, SQLITE_IDLE_HANDLE_TTL_MS)); idleTimer.unref(); const next = { reader, active: false, close, idleTimer }; companions.set(writer.db, next); companion = next; } finally { if (!companion) close(); } } const owned = companion; try { if (!behavior.snapshot && !hasOpenClawAgentReadOnlySchema(owned.reader)) { owned.close(); return { found: false, reason: "schema-missing" }; } owned.idleTimer.refresh(); owned.active = true; const result = behavior.snapshot ? readOpenClawAgentDatabaseSnapshot(owned.reader, operation) : readOpenClawAgentDatabase(owned.reader, operation); if (!result.found) owned.close(); return result; } catch (error) { owned.close(); throw error; } finally { owned.active = false; if (!owned.reader.db.isOpen || owned.reader.db.isTransaction || !matchesWriter(owned.reader, writer)) owned.close(); } } //#endregion //#region src/state/openclaw-agent-db-readonly.ts /** * Look up a process-held handle without adopting writer-side failures. * * Read-only reads are meant to survive a latched open failure or an ownership * mismatch that only the writable lifecycle cares about; those callers fall * back to a fresh connection, which reports the precise reason. */ function findOpenAgentDatabase(options) { try { return getOpenClawAgentDatabaseIfOpen(options); } catch { return; } } /** Retain an existing store across awaits without materializing a writable database. */ function retainOpenClawAgentDatabaseReadOnly(options) { if (isArtifactPreservingStateRead("agent", resolveOpenClawAgentSqlitePath(options))) { const inspected = openOpenClawAgentDatabaseReadOnly(options); return inspected.found ? { found: true, database: inspected.database, claim: createOpenClawAgentDatabaseClaim(inspected.database, inspected.database.close) } : inspected; } const opened = findOpenAgentDatabase(options); if (opened && !opened.db.isTransaction) { const borrowed = borrowOpenClawAgentDatabase(options); return { found: true, database: opened, claim: createOpenClawAgentDatabaseClaim(opened, borrowed.release) }; } const agentId = normalizeAgentId(options.agentId); const pathname = resolveOpenClawAgentSqlitePath({ ...options, agentId }); return retainCachedOpenClawAgentDatabaseReadOnly({ ...options, agentId, path: pathname }); } /** Read agent state without creating, registering, migrating, or joining its writable lifecycle. */ function withOpenClawAgentDatabaseReadOnly(operation, options, behavior = {}) { const agentId = normalizeAgentId(options.agentId); const pathname = resolveOpenClawAgentSqlitePath({ ...options, agentId }); if (isArtifactPreservingStateRead("agent", pathname)) return withFreshOpenClawAgentDatabaseReadOnly(operation, options, behavior); if (isIncognitoOpenClawAgentSqlitePath(pathname, { agentId, env: options.env })) { const database = getOpenClawAgentDatabaseIfOpen({ ...options, agentId }); if (database && behavior.allowExtension) throw new Error("Extension-capable read-only access is unavailable for incognito databases."); return database ? readOpenClawAgentDatabase(database, operation) : { found: false, reason: "database-missing" }; } const processOpened = behavior.allowExtension ? void 0 : findOpenAgentDatabase({ ...options, agentId }); if (processOpened?.db.isTransaction) return withCommittedOpenClawAgentDatabaseReadOnly(processOpened, operation, { ...options, agentId }, behavior); if (!processOpened) return withScopedOpenClawAgentDatabaseReadOnly(operation, { ...options, agentId, path: pathname }, behavior); if (behavior.snapshot) return readOpenClawAgentDatabaseSnapshot(processOpened, operation); return runSqliteReadOperationSync(processOpened.db, () => { const userVersion = assertSupportedAgentSchemaVersion(processOpened.db, pathname); assertCanonicalAgentPersistenceVersion(processOpened.db, pathname, userVersion); assertCanonicalSessionValidationSchema(processOpened.db); return readOpenClawAgentDatabase(processOpened, operation); }, "fresh"); } //#endregion export { withOpenClawAgentDatabaseReadOnly as n, retainOpenClawAgentDatabaseReadOnly as t };