openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
566 lines (565 loc) • 25 kB
JavaScript
import "../src-SskUVPJG.mjs";
import { t as expectDefined } from "../expect-lbe3Hgrh.mjs";
import { c as isRecord } from "../record-coerce-TIsquoVR.mjs";
import { t as formatErrorMessage } from "../errors-BSdPMFao.mjs";
import { o as sqliteReaderDatabasePathKey } from "../sqlite-reader-lifecycle-CYQdd96B.mjs";
import { a as normalizeDatabasePath, c as readDatabasePathIdentitySync, t as assertExistingDatabaseIdentity } from "../sqlite-worker-identity-B_aK_kcL.mjs";
import { a as createSqliteLifecycleAggregateError, s as throwSqliteLifecycleErrors } from "../sqlite-lifecycle-errors-DuOygjcn.mjs";
import { t as assertTransactionUsable } from "../sqlite-transaction-DlnLAQiX.mjs";
import { d as onSqliteWalCheckpoint } from "../sqlite-wal-BeoOh_h5.mjs";
import { b as retainOpenClawStateDatabase, y as requireOpenClawStateDatabaseIdentity } from "../openclaw-state-db-cache-BEArcjbO.mjs";
import { _ as readOpenClawAgentDatabaseIdentity, i as getOpenClawAgentDatabaseValidation } from "../openclaw-agent-db-validation-cache-D5sa3PLF.mjs";
import { f as SQLITE_WORKER_OPERATION_CLEANUP, i as requestSqliteWorkerOperationAdmission, l as SQLITE_WORKER_CLOSE_RECEIPT, m as SQLITE_WORKER_PREPARE_COMMAND, p as SQLITE_WORKER_PREPARE_ADMITTED, r as deferSqliteWorkerCommitReceipt, s as takeSqliteWorkerOperationAdmissionAttachment, t as SqliteWorkerOpenRefusedError } from "../sqlite-worker-operation-admission-DqFz0WIG.mjs";
import { r as openOpenClawStateDatabase } from "../openclaw-state-db-VZFEEleX.mjs";
import { i as readAgentDeletionJournalStatusInDatabase } from "../agent-deletion-journal.read-BRtz-U_1.mjs";
import { g as ensureOpenClawAgentDatabasePermissions } from "../openclaw-agent-db-maintenance-DP9oTR3S.mjs";
import { d as prepareOpenClawAgentDatabaseWorkerLease } from "../openclaw-agent-db-lease-BALzR11f.mjs";
import { a as closeOpenClawAgentDatabaseByPath, y as retainAgentDatabase } from "../openclaw-agent-db-lifecycle-BmHpWusP.mjs";
import { c as openOpenClawAgentDatabase, d as runOpenClawAgentWriteTransaction, o as getOpenClawAgentDatabaseIfOpen } from "../openclaw-agent-db-CFSa_Qik.mjs";
import { MessageChannel, receiveMessageOnPort } from "node:worker_threads";
import { isPromise } from "node:util/types";
//#region src/state/openclaw-agent-execution-close.ts
function closeAgentDatabaseExecution({ database, identity, closeDomain, releaseBorrow, releaseSharedBorrow }) {
let checkpoint;
const errors = [];
for (const cleanup of [
closeDomain,
() => {
if (!database) return;
const closingPath = sqliteReaderDatabasePathKey(database.path);
const stopObserving = onSqliteWalCheckpoint((observation) => {
if (observation.databasePath === closingPath) checkpoint = {
health: observation.health,
observedAtNs: observation.observedAtNs
};
});
try {
closeOpenClawAgentDatabaseByPath(database.path, database.agentId);
} finally {
stopObserving();
}
},
() => releaseBorrow?.(),
releaseSharedBorrow
]) try {
cleanup();
} catch (error) {
errors.push(error);
}
throwSqliteLifecycleErrors(errors, "Agent database cleanup failed");
if (identity && checkpoint) return {
identity: {
key: `file:${identity.physicalIdentity}`,
canonicalPath: identity.nativeLocation
},
incarnation: identity.incarnation,
checkpoint
};
}
//#endregion
//#region src/state/openclaw-agent-execution-domain.ts
/** One admitted publication scope borrows the canonical connection; it never owns its close. */
function createAgentDatabaseDomainOwner(context) {
let binding;
let prepared;
let failedBinding = false;
const requireBinding = (id) => {
if (!binding || binding.id !== id || !binding.authority.active) throw new Error("Agent database operation lost its bound publication scope");
return binding;
};
const bind = (input) => {
const database = context.assertCurrent();
if (!prepared || prepared.id !== input.id || binding) throw new Error("Agent publication module was not prepared for this scope");
const factory = prepared.factory;
prepared = void 0;
failedBinding = true;
const authority = { active: true };
try {
const backend = factory(input.input, {
databasePath: context.databasePath,
database,
admit: (stage) => {
if (!authority.active) throw new Error("Agent publication cleanup cannot admit a transaction");
context.admit(stage);
}
});
if (isPromise(backend)) {
backend.catch(() => {});
throw new Error("Connection-bound publication factories must remain synchronous");
}
if (!isRecord(backend) || typeof backend.execute !== "function" || typeof backend.close !== "function" || typeof backend.assertSettled !== "function" || SQLITE_WORKER_PREPARE_COMMAND in backend && backend[SQLITE_WORKER_PREPARE_COMMAND] !== void 0 && typeof backend[SQLITE_WORKER_PREPARE_COMMAND] !== "function" || backend.prepare !== void 0 && typeof backend.prepare !== "function") throw new Error("Agent publication module returned an invalid connection-bound backend");
binding = {
id: input.id,
backend,
authority
};
failedBinding = false;
} catch (error) {
authority.active = false;
throw error;
}
};
const prepareCommand = async (input) => {
const current = requireBinding(input.id);
const loading = current.backend[SQLITE_WORKER_PREPARE_COMMAND]?.(input.command.type);
if (loading) await loading;
await current.backend.prepare?.(input.command);
if (requireBinding(input.id) !== current) throw new Error("Agent publication changed during command preparation");
};
const closeBinding = () => {
if (!binding) return;
binding.authority.active = false;
const closed = binding.backend.close();
if (closed !== void 0) {
closed.catch(() => {});
throw new Error("Connection-bound publication cleanup must remain synchronous");
}
binding = void 0;
};
return {
async prepare(command) {
if (command.type === "database.domain.bind" || command.type === "database.domain.publish") {
if (binding || prepared) throw new Error("Agent database already has an admitted publication scope");
const url = new URL(command.input.moduleUrl);
if (url.protocol !== "file:" || url.search || url.hash) throw new Error("Agent publication requires a static local module URL");
const module = await import(url.href);
if (!isRecord(module) || typeof module.bindSqliteWorkerBackend !== "function") throw new Error("Agent publication module must export bindSqliteWorkerBackend");
const factory = module.bindSqliteWorkerBackend;
prepared = {
id: command.input.id,
factory: (input, bindingContext) => factory(input, bindingContext)
};
} else if (command.type === "database.domain.execute") await prepareCommand(command.input);
},
async preparePublication(input) {
bind(input);
requireBinding(input.id).backend.assertSettled?.();
await Promise.resolve();
await prepareCommand(input);
},
cleanupPublication(id) {
if (binding?.id === id) {
context.assertCleanupCurrent();
closeBinding();
}
},
execute(command) {
if (command.type === "database.domain.bind") {
bind(command.input);
return;
}
context.assertCurrent();
const current = requireBinding(command.input.id);
if (command.type === "database.domain.close") {
closeBinding();
return;
}
return current.backend.execute(command.input.command);
},
assertSettled() {
prepared = void 0;
if (failedBinding) throw new Error("Agent publication binding did not settle");
if (binding && !binding.authority.active) throw new Error("Agent publication cleanup did not settle");
binding?.backend.assertSettled?.();
},
close() {
closeBinding();
prepared = void 0;
}
};
}
//#endregion
//#region src/state/openclaw-agent-execution.worker.ts
function createSqliteWorkerBackend(input, opening) {
const backend = openAgentDatabaseBackend(input, opening);
try {
backend.execute({
type: "database.prepareWrite",
input: void 0
});
backend.assertSettled?.();
return backend;
} catch (error) {
try {
backend.close();
} catch (cleanupError) {
throw createSqliteLifecycleAggregateError([error, cleanupError], "Agent creation and cleanup failed", error);
}
throw error;
}
}
/** The broker supplies a private admission channel before invoking this native factory. */
function openExistingSqliteWorkerBackend(input, opening) {
return openAgentDatabaseBackend(input, opening);
}
function openAgentDatabaseBackend(input, opening) {
if (opening.databasePath !== input.databasePath) throw new Error("Agent database open does not match its captured execution owner");
const admitOpen = () => {
try {
requestSqliteWorkerOperationAdmission({
stage: "open",
facts: input
});
} catch (error) {
throw new SqliteWorkerOpenRefusedError(error);
}
};
admitOpen();
const options = {
agentId: input.agentId,
path: input.databasePath,
env: input.environment
};
let admittedFileIdentity = input.creatingIdentity?.key ?? opening.existingIdentity ?? readDatabasePathIdentitySync(input.databasePath).key;
let admittedFileBirthtime = input.creatingIdentity?.birthtime;
const assertFileIdentity = () => {
if (input.expectedIdentity) assertExistingDatabaseIdentity(input.databasePath, `file:${input.expectedIdentity.physicalIdentity}`, input.expectedIdentity.birthtime);
if (admittedFileIdentity.startsWith("path:")) {
const current = readDatabasePathIdentitySync(input.databasePath);
if (current.key !== admittedFileIdentity || input.creatingIdentity && current.canonicalPath !== input.creatingIdentity.canonicalPath) throw new Error("Agent database target changed before creating open");
} else {
if (input.creatingIdentity && readDatabasePathIdentitySync(input.databasePath).canonicalPath !== input.creatingIdentity.canonicalPath) throw new Error("Agent database target changed before creating open");
assertExistingDatabaseIdentity(input.databasePath, admittedFileIdentity, admittedFileBirthtime);
}
};
let database;
let shared;
let sharedBorrow;
let releaseBorrow;
let identity;
let openingFailure;
let startupJournalRequested = false;
let publicationStartupJournal;
const readRequestPreparation = () => {
const attachment = takeSqliteWorkerOperationAdmissionAttachment();
if (!isRecord(attachment) || attachment.kind !== "agent-execution" || typeof attachment.startupJournal !== "boolean") throw new Error("Agent execution requires its request-local preparation facts");
return attachment.startupJournal;
};
const readDeletionJournal = () => readAgentDeletionJournalStatusInDatabase(expectDefined(shared, "Agent execution shared-state owner").db, input.agentId) !== "absent";
const openWriter = () => {
let validation;
if (!database) {
admitOpen();
assertFileIdentity();
if (!shared) {
shared = openOpenClawStateDatabase({
path: input.stateDatabasePath,
env: input.environment,
initializationAgentPaths: [input.databasePath]
});
sharedBorrow = retainOpenClawStateDatabase(shared);
}
const lease = prepareOpenClawAgentDatabaseWorkerLease(options, shared, input.leaseId);
const { port1, port2 } = new MessageChannel();
try {
requestSqliteWorkerOperationAdmission({
stage: "prepare",
facts: {
kind: "shared-owner",
identity: requireOpenClawStateDatabaseIdentity(shared),
lease: lease.receipt,
validationPort: port2
}
}, [port2]);
lease.validation = receiveMessageOnPort(port1)?.message;
} catch (error) {
throw new SqliteWorkerOpenRefusedError(error);
} finally {
port1.close();
port2.close();
}
assertFileIdentity();
let registration;
let openingResult;
try {
const opened = openOpenClawAgentDatabase(options, lease, {
starting: () => requestSqliteWorkerOperationAdmission({
stage: "prepare",
facts: {
kind: "agent-registration-start",
lease: lease.receipt
}
}),
committed(receipt) {
registration = receipt;
}
});
database = opened;
releaseBorrow = retainAgentDatabase(opened.db);
openingResult = {
ok: true,
value: opened
};
} catch (error) {
openingFailure = { error };
openingResult = {
ok: false,
error
};
}
if (registration) try {
requestSqliteWorkerOperationAdmission({
stage: "prepare",
facts: {
kind: "agent-registration-committed",
registration
}
});
} catch (error) {
if (!openingResult.ok) throw createSqliteLifecycleAggregateError([openingResult.error, error], `${String(openingResult.error)}; committed registration reporting failed: ${String(error)}`, openingResult.error);
throw error;
}
if (!openingResult.ok) throw openingResult.error;
const opened = openingResult.value;
const nativeIdentity = readOpenClawAgentDatabaseIdentity(opened);
if (typeof nativeIdentity.identity !== "string") throw new Error("Disk agent execution requires its canonical file identity");
const openedFileIdentity = `file:${nativeIdentity.identity}`;
if (admittedFileIdentity.startsWith("file:") && (openedFileIdentity !== admittedFileIdentity || admittedFileBirthtime !== void 0 && nativeIdentity.birthtime !== admittedFileBirthtime)) throw new Error("Agent writer differs from its admitted physical file");
if (input.expectedIdentity && nativeIdentity.identity !== input.expectedIdentity.physicalIdentity) throw new Error("Agent writer differs from its expected physical file");
admittedFileIdentity = openedFileIdentity;
admittedFileBirthtime = nativeIdentity.birthtime;
identity = {
kind: "file",
physicalIdentity: nativeIdentity.identity,
birthtime: nativeIdentity.birthtime,
incarnation: nativeIdentity.incarnation,
nativeLocation: nativeIdentity.filename
};
validation = getOpenClawAgentDatabaseValidation(opened);
}
if (!database || !database.db.isOpen || getOpenClawAgentDatabaseIfOpen(options) !== database) throw new Error("Agent execution lost its retained native database");
requestSqliteWorkerOperationAdmission({
stage: "prepare",
facts: {
identity,
validation,
...startupJournalRequested ? { agentDeletionJournalPresent: readDeletionJournal() } : {}
}
});
return database;
};
const admit = (stage, publication) => {
assertFileIdentity();
requestSqliteWorkerOperationAdmission({
stage,
facts: {
identity,
...startupJournalRequested ? { agentDeletionJournalPresent: readDeletionJournal() } : {},
...publication ? { publication } : {}
}
});
if (stage === "commit") ensureOpenClawAgentDatabasePermissions(input.databasePath, options);
};
const writeTransaction = (operationLabel, owner, write) => {
const opened = openWriter();
return runOpenClawAgentWriteTransaction((current) => {
if (current.db !== opened.db) throw new Error(`${owner} lost its canonical database owner`);
admit("transaction");
return write(current);
}, options, { operationLabel });
};
let providerReview;
let entryReader;
let archives;
let archivePruning;
let transcript;
let replacements;
let trajectory;
let acpEntry;
let pendingInputWithdrawal;
const domain = createAgentDatabaseDomainOwner({
databasePath: input.databasePath,
assertCurrent() {
assertOpen();
const current = openWriter();
assertFileIdentity();
return current.db;
},
assertCleanupCurrent() {
if (!database || !identity || !database.db.isOpen || normalizeDatabasePath(database.db.location() ?? "") !== identity.nativeLocation || getOpenClawAgentDatabaseIfOpen(options) !== database) throw new Error("Agent cleanup lost its retained native database");
assertFileIdentity();
},
admit
});
let closed = false;
let closeReceipt;
const assertOpen = () => {
if (closed) throw new Error("Agent database execution owner is closed");
};
const executeCommand = (command) => {
if (command.type === "database.domain.bind" || command.type === "database.domain.publish" || command.type === "database.domain.execute" || command.type === "database.domain.close") return domain.execute(command);
if (command.type === "database.prepareWrite") {
openWriter();
return;
}
if (command.type === "database.walMaintenance") return openWriter().walMaintenance.maintainPeriodic?.(command.input, admit) ?? { reclaimedPages: 0 };
if (command.type === "session.entry.read" && entryReader) return entryReader.readSessionEntryRow(openWriter(), command.input.sessionKey)?.entry;
if (command.type === "trajectory.events.append" && trajectory) {
const append = trajectory.appendSqliteTrajectoryRuntimeEventsInTransaction;
return writeTransaction("trajectory.runtime.append", "Trajectory append", (current) => {
append(current, command.input);
deferSqliteWorkerCommitReceipt(current.db, { kind: "trajectory-runtime-append" });
admit("commit");
});
}
if ((command.type === "session.archives.preparePublication" || command.type === "session.archives.recordPublication") && archives) {
const kernel = archives;
return writeTransaction("session.archive.publish", "Session archive publication", (current) => {
const result = command.type === "session.archives.preparePublication" ? kernel.prepareSessionTranscriptArchivePublishPlans(current, command.input) : kernel.recordSessionTranscriptArchivePublishResults(current, command.input.results, command.input.nowMs);
admit("commit");
return result;
});
}
if (command.type === "session.transcript.initialize" && transcript) {
const assertIdentity = transcript.assertIdentity;
assertIdentity(command.input);
const initialize = transcript.initialize;
return writeTransaction("session.entry.create-with-transcript", "Session transcript", (current) => {
const publication = {
kind: "session-transcript-initialized",
sessionKey: command.input.sessionKey
};
initialize(current, {
agentId: input.agentId,
path: input.databasePath,
...command.input
}, command.input.cwd, { onPlaceholderInserted: ({ sessionId }) => {
publication.placeholder = { sessionId };
} });
deferSqliteWorkerCommitReceipt(current.db, publication);
admit("commit", publication);
return publication;
});
}
if (command.type === "session.entry.acp" && acpEntry) return acpEntry.mutateAcpSessionEntryInWorker(openWriter(), options, command.input, admit);
if (command.type === "session.entries.replace" && replacements) {
const replace = replacements.commitSessionEntryReplacementsInDatabase;
const preparePublication = replacements.prepareSessionEntryReplacementPublication;
return writeTransaction("session.entry-replacements", "Session replacement", (current) => {
const result = replace(current, command.input, () => {
const initialization = command.input.initializeTranscript;
if (!initialization) return;
try {
if (!transcript) throw new Error("Session transcript initialization was not prepared");
const assertIdentity = transcript.assertIdentity;
assertIdentity(initialization);
transcript.initialize(current, {
agentId: input.agentId,
path: input.databasePath,
...initialization
}, initialization.cwd);
} catch (error) {
throw Object.assign(new Error(formatErrorMessage(error), { cause: error }), { name: "SessionTranscriptInitializationError" });
}
});
const publication = preparePublication(result);
deferSqliteWorkerCommitReceipt(current.db, publication);
admit("commit", publication);
return result;
});
}
if (command.type === "session.providerReview.compare" && providerReview) return providerReview.compareSessionProviderReviewInWorker(openWriter(), options, command.input, admit);
if (command.type === "session.pendingInputs.withdraw" && pendingInputWithdrawal) return pendingInputWithdrawal.discardSessionPendingInputInWorker(openWriter(), options, command.input, admit);
if (command.type === "session.archivePruning.deletePublished" && archivePruning) return archivePruning.deletePublishedSessionArchiveInDatabase(openWriter(), options, command.input, admit);
if (command.type === "session.archivePruning.removeLegacy" && archivePruning) return archivePruning.removeLegacySessionArchiveInDatabase(openWriter(), options, command.input.filePath, admit);
if (command.type === "session.archivePruning.reclaimPages" && archivePruning) return archivePruning.reclaimSessionArchivePagesInWorker(openWriter(), command.input.maxPages, admit);
throw new Error("Unknown agent database operation");
};
return {
prepare(command) {
if (command.type === "session.entry.acp") return import("../session-meta-entry.worker-DswcWfLS.mjs").then((module) => {
acpEntry = module;
});
if (command.type === "session.entry.read") return import("../session-accessor.sqlite-entry-read-_yBBNpZy.mjs").then((module) => {
entryReader = module;
});
if (command.type === "trajectory.events.append") return import("../runtime-store.sqlite-BiWpRGdU.mjs").then((module) => {
trajectory = module;
});
if (command.type === "session.archives.preparePublication" || command.type === "session.archives.recordPublication") return import("../session-accessor.sqlite-archive-store-kernel-BxxlZN8R.mjs").then((module) => {
archives = module;
});
if (command.type === "session.archivePruning.deletePublished" || command.type === "session.archivePruning.removeLegacy" || command.type === "session.archivePruning.reclaimPages") return import("../session-history-archive-pruning.worker-CbxB4TNa.mjs").then((module) => {
archivePruning = module;
});
if (command.type === "session.transcript.initialize" || command.type === "session.entries.replace" && command.input.initializeTranscript) return Promise.all([
import("../session-accessor.sqlite-transcript-header-PZz_RnW-.mjs"),
import("../session-accessor.sqlite-scope-BJL6cunB.mjs"),
command.type === "session.entries.replace" ? import("../session-accessor.sqlite-replacement-state-BY4reCe-.mjs") : void 0
]).then(([header, scope, replacement]) => {
replacements = replacement ?? replacements;
transcript = {
initialize: header.ensureTranscriptHeader,
assertIdentity: scope.assertSqliteTranscriptWriteIdentity
};
});
if (command.type === "session.entries.replace") return import("../session-accessor.sqlite-replacement-state-BY4reCe-.mjs").then((module) => {
replacements = module;
});
if (command.type === "session.providerReview.compare") return import("../provider-review-store.worker-B76lyJbH.mjs").then((module) => {
providerReview = module;
});
if (command.type === "session.pendingInputs.withdraw") return import("../session-pending-input-withdrawal.worker-ckfiMGOr.mjs").then((module) => {
pendingInputWithdrawal = module;
});
if (command.type === "database.domain.bind" || command.type === "database.domain.publish" || command.type === "database.domain.execute" || command.type === "database.domain.close") return domain.prepare(command);
},
[SQLITE_WORKER_PREPARE_ADMITTED](command) {
if (command.type !== "database.domain.publish") return;
publicationStartupJournal = readRequestPreparation();
startupJournalRequested = publicationStartupJournal;
try {
return domain.preparePublication(command.input);
} finally {
startupJournalRequested = false;
}
},
[SQLITE_WORKER_OPERATION_CLEANUP](command) {
if (command.type === "database.domain.publish") {
startupJournalRequested = publicationStartupJournal ?? false;
try {
domain.cleanupPublication(command.input.id);
} finally {
startupJournalRequested = false;
publicationStartupJournal = void 0;
}
}
},
assertSettled() {
if (openingFailure) throw openingFailure.error;
domain.assertSettled();
if (database) {
assertTransactionUsable(database.db);
if (!identity || !database.db.isOpen || database.db.isTransaction) throw new Error("Agent database command left an unsettled native connection");
}
},
execute(command) {
assertOpen();
if (command.type === "database.domain.publish") {
if (publicationStartupJournal === void 0) throw new Error("Agent publication lost its request-local preparation facts");
startupJournalRequested = publicationStartupJournal;
} else startupJournalRequested = readRequestPreparation();
try {
return executeCommand(command);
} finally {
startupJournalRequested = false;
}
},
[SQLITE_WORKER_CLOSE_RECEIPT]() {
return closeReceipt;
},
close() {
closed = true;
closeReceipt = void 0;
closeReceipt = closeAgentDatabaseExecution({
database,
identity,
closeDomain: () => domain.close(),
releaseBorrow,
releaseSharedBorrow: () => sharedBorrow?.release()
});
}
};
}
//#endregion
export { createSqliteWorkerBackend, openExistingSqliteWorkerBackend };