openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
193 lines (192 loc) • 10.6 kB
JavaScript
import { a as executeSqliteQueryTakeFirstSync, i as executeSqliteQuerySync, o as getNodeSqliteKysely } from "./kysely-sync-DUWzQvCU.mjs";
import { i as tableExists } from "./openclaw-state-db-schema-helpers-DKz5xGgh.mjs";
import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs";
import "./session-key-DY3MOX4N.mjs";
import { i as deferSqliteWorkerCommitReceipt, s as requestSqliteWorkerOperationAdmission } from "./sqlite-worker-operation-admission-C5lBzzTk.mjs";
import { a as deleteAgentDeletionJournalInDatabase, d as readAgentDeletionJournalInDatabase, f as updateAgentDeletionJournalPathsInDatabase, i as completeAgentDeletionJournalInDatabase, o as handoffAgentDeletionJournalInDatabase, r as beginAgentDeletionJournalInDatabase } from "./agent-deletion-journal-DkfGlzu8.mjs";
import { a as assertNoOpenClawAgentDatabaseLeases } from "./openclaw-agent-db-lease-zP0JO4hP.mjs";
import { r as unregisterOpenClawAgentDatabases } from "./openclaw-agent-db-registry-iOyfY9cK.mjs";
import { t as createOpenClawStateSchemaEnsurer } from "./openclaw-state-feature-schema-BHGUhcMT.mjs";
import { n as readClawInstallRecordFromDatabase } from "./provenance-read.kernel-BQe6L4vF.mjs";
import { n as assertOpenClawStateLeaseWorkerOwnedInTransaction } from "./openclaw-state-lease-worker-Ddwq3QcA.mjs";
import { isDeepStrictEqual } from "node:util";
//#region src/state/agent-deletion-journal-authority.worker.ts
/** Deletion authority requires its admitted journal; unavailable rows must never grant cleanup. */
function readAgentDeletionJournalAuthorityInDatabase(database, agentId) {
const row = executeSqliteQueryTakeFirstSync(database, getNodeSqliteKysely(database).selectFrom("agent_deletion_journal").select([
"agent_id",
"operation_id",
"cleanup_completed"
]).where("agent_id", "=", normalizeAgentId(agentId)));
if (!row) return;
if (typeof row.operation_id !== "string" || !row.operation_id || row.cleanup_completed !== 0 && row.cleanup_completed !== 1) throw new Error("Agent deletion journal authority is unreadable.");
return {
agentId: row.agent_id,
operationId: row.operation_id,
cleanupCompleted: row.cleanup_completed === 1
};
}
//#endregion
//#region src/state/agent-provenance.schema.ts
const ensureAgentProvenanceSchema = createOpenClawStateSchemaEnsurer({
table: "agent_provenance",
operationLabel: "agent-provenance.schema.ensure"
});
//#endregion
//#region src/state/agent-deletion.worker.ts
/** The caller holds the real lease grant and this transaction's shared-state snapshot. */
function assertAgentDeletionWorkerPredicate(database, predicate, currentJournal) {
const journal = currentJournal === void 0 ? readAgentDeletionJournalAuthorityInDatabase(database.db, predicate.agentId) : currentJournal;
if (!journal || journal.agentId !== predicate.agentId || journal.operationId !== predicate.operationId || journal.cleanupCompleted) throw new Error(`Agent ${predicate.agentId} deletion no longer owns database cleanup.`);
assertClawInstall(database, predicate);
}
function assertClawInstall(database, predicate) {
if (predicate.expectedClawInstall !== void 0 && !isDeepStrictEqual(readClawInstallRecordFromDatabase(database.db, predicate.agentId) ?? null, predicate.expectedClawInstall)) throw Object.assign(/* @__PURE__ */ new Error(`Claw removal no longer owns agent ${predicate.agentId}.`), { code: "CLAW_INSTALL_CHANGED" });
}
function assertLease(database, lease, agentId, stage = "transaction") {
if (lease.scope !== "core:agent-deletion" || lease.key !== agentId) throw new Error("Agent deletion requires its original target lease");
assertOpenClawStateLeaseWorkerOwnedInTransaction(database.db, lease, "write", stage);
}
function guarded(guard, context, mutate, publication) {
return context.write((database) => {
assertLease(database, guard.lease, guard.predicate.agentId);
assertAgentDeletionWorkerPredicate(database, guard.predicate);
const result = mutate(database);
assertLease(database, guard.lease, guard.predicate.agentId, "commit");
if (publication) deferSqliteWorkerCommitReceipt(database.db, {
...publication.journalChanged ? {
kind: "agent-deletion-mutated",
agentId: guard.predicate.agentId,
operationId: guard.predicate.operationId,
unregisterDatabases: publication.unregisterDatabases === true
} : {},
...publication.nonce ? { receiptAuthority: {
nonce: publication.nonce,
sequence: 1
} } : {}
});
return result;
}, { operationLabel: "agent.deletion" });
}
const agentDeletionOperations = {
"agentDeletion.claimCompleted": (input, context) => context.write((database) => {
requestSqliteWorkerOperationAdmission({
stage: "transaction",
facts: void 0
});
const claimed = deleteAgentDeletionJournalInDatabase(database, input.agentId, input.operationId, true);
requestSqliteWorkerOperationAdmission({
stage: "commit",
facts: void 0
});
deferSqliteWorkerCommitReceipt(database.db, {
kind: "agent-deletion-claimed",
agentId: input.agentId,
operationId: input.operationId,
claimed,
receiptAuthority: {
nonce: input.nonce,
sequence: 1
}
});
return claimed;
}, { operationLabel: "agent.deletion.claim" }),
"agentDeletion.read": (input, { open }) => readAgentDeletionJournalInDatabase(open(), input.agentId),
"agentDeletion.begin": (input, context) => context.write((database) => {
assertLease(database, input.lease, input.entry.agentId);
assertClawInstall(database, {
agentId: input.entry.agentId,
expectedClawInstall: input.expectedClawInstall
});
ensureAgentProvenanceSchema({
database,
...context.stateOptions()
});
const result = beginAgentDeletionJournalInDatabase(database, input.entry, input.preserveDeleteFiles);
assertLease(database, input.lease, input.entry.agentId, "commit");
deferSqliteWorkerCommitReceipt(database.db, {
kind: "agent-deletion-began",
agentId: input.entry.agentId,
operationId: input.entry.operationId,
receiptAuthority: {
nonce: input.nonce,
sequence: 1
}
});
return result;
}, { operationLabel: "agent.deletion.begin" }),
"agentDeletion.assertCurrent": (input, context) => guarded(input.guard, context, () => void 0),
"agentDeletion.assertNoDatabaseLeasesUnowned": (input, context) => assertNoOpenClawAgentDatabaseLeases(input.agentId, {
database: context.open(),
...context.stateOptions()
}),
"agentDeletion.assertNoDatabaseLeases": (input, context) => guarded(input.guard, context, (database) => assertNoOpenClawAgentDatabaseLeases(input.guard.predicate.agentId, {
database,
...context.stateOptions()
})),
"agentDeletion.fencePaths": (input, context) => guarded(input.guard, context, (database) => {
const { agentId, operationId } = input.guard.predicate;
if (!updateAgentDeletionJournalPathsInDatabase(database, agentId, operationId, input.paths.kind === "database" ? "database_paths_json" : "cleanup_paths_json", input.paths.paths)) throw new Error(`Failed to fence cleanup paths for agent ${agentId}.`);
}, { journalChanged: true }),
"agentDeletion.finish": (input, context) => guarded(input.guard, context, (database) => {
const { agentId, operationId } = input.guard.predicate;
if (input.unregisterDatabases) unregisterOpenClawAgentDatabases({
agentId,
database,
env: context.stateOptions().env
});
if (!completeAgentDeletionJournalInDatabase(database, agentId, operationId)) throw new Error(`Failed to complete deletion journal for agent ${agentId}.`);
}, {
journalChanged: true,
unregisterDatabases: input.unregisterDatabases
}),
"agentDeletion.rollback": (input, context) => guarded(input.guard, context, (database) => {
const { agentId, operationId } = input.guard.predicate;
if (!deleteAgentDeletionJournalInDatabase(database, agentId, operationId, false)) throw new Error(`Failed to roll back deletion journal for agent ${agentId}.`);
}, {
nonce: input.nonce,
journalChanged: true
}),
"agentDeletion.releaseClawRows": (input, context) => guarded(input.guard, context, (database) => {
const { agentId, operationId } = input.guard.predicate;
if (input.complete) unregisterOpenClawAgentDatabases({
agentId,
database,
env: context.stateOptions().env
});
const query = getNodeSqliteKysely(database.db);
const paths = [...new Set(input.files.filter((file) => file.action !== "error").map((file) => file.path))];
if (paths.length && tableExists(database.db, "claw_workspace_files")) executeSqliteQuerySync(database.db, query.deleteFrom("claw_workspace_files").where("agent_id", "=", agentId).where("target_path", "in", paths));
if (!input.complete) return false;
if (tableExists(database.db, "claw_package_refs")) executeSqliteQuerySync(database.db, query.deleteFrom("claw_package_refs").where("agent_id", "=", agentId));
if (tableExists(database.db, "claw_installs")) executeSqliteQuerySync(database.db, query.deleteFrom("claw_installs").where("agent_id", "=", agentId));
if (!completeAgentDeletionJournalInDatabase(database, agentId, operationId)) throw new Error(`Failed to complete deletion journal for agent ${agentId}.`);
return true;
}, {
journalChanged: true,
unregisterDatabases: input.complete
}),
"agentDeletion.handoffClawRetry": (input, context) => context.write((database) => {
const { agentId, operationId } = input.guard.predicate;
assertLease(database, input.guard.lease, agentId);
const journal = readAgentDeletionJournalInDatabase(database, agentId);
if (!journal || journal.operationId !== operationId || journal.cleanupCompleted || input.guard.predicate.expectedClawInstall !== void 0 && !isDeepStrictEqual(readClawInstallRecordFromDatabase(database.db, agentId) ?? null, input.guard.predicate.expectedClawInstall)) {
assertLease(database, input.guard.lease, agentId, "commit");
return false;
}
if (executeSqliteQuerySync(database.db, getNodeSqliteKysely(database.db).updateTable("claw_installs").set({
status: "partial",
updated_at_ms: input.nowMs
}).where("agent_id", "=", agentId)).numAffectedRows !== 1n || !handoffAgentDeletionJournalInDatabase(database, agentId, operationId, input.retryOperationId)) throw new Error(`Failed to hand off deletion journal for agent ${agentId}.`);
assertLease(database, input.guard.lease, agentId, "commit");
deferSqliteWorkerCommitReceipt(database.db, {
kind: "agent-deletion-mutated",
agentId,
operationId,
unregisterDatabases: false
});
return true;
}, { operationLabel: "agent.deletion.handoff" })
};
//#endregion
export { assertAgentDeletionWorkerPredicate as n, ensureAgentProvenanceSchema as r, agentDeletionOperations as t };