openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
326 lines (325 loc) • 14.3 kB
JavaScript
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs";
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs";
import { a as executeSqliteQueryTakeFirstSync, d as sqliteStringSet, i as executeSqliteQuerySync, o as getNodeSqliteKysely } from "./kysely-sync-DUWzQvCU.mjs";
import { n as registerListener, t as notifyListeners } from "./listeners-BogSNJ-R.mjs";
import { i as tableExists } from "./openclaw-state-db-schema-helpers-DKz5xGgh.mjs";
import { a as stageSqliteCommittedPublication, i as publishSqliteCommittedState } from "./sqlite-post-commit-DENvzR6w.mjs";
import "./sqlite-worker-contract-C51cJHzg.mjs";
import { a as observeSqliteWorkerCommittedFacts, i as deferSqliteWorkerCommitReceipt } from "./sqlite-worker-operation-admission-C5lBzzTk.mjs";
import { o as readTrackedStateDatabaseIdentity } from "./openclaw-state-db-integrity-admission-D2ZPZec-.mjs";
import { l as runOpenClawStateWriteTransaction } from "./openclaw-state-db-DJZ7EKoI.mjs";
import { r as captureOpenClawStateWorkerContext } from "./openclaw-state-worker-context-BvvgY6-t.mjs";
import { n as createSqliteWorkerWriteAdmission } from "./sqlite-worker-store-C0QRqven.mjs";
import { n as hasSqliteCommitReceiptCoverage, t as createSqliteCommitReceipt } from "./sqlite-commit-receipt-DafgyYOc.mjs";
import { n as assertSessionEntryCurrentAdmission } from "./session-entry-current-admission-D6fbI5HG.mjs";
import { i as requestSessionEntryCurrentAdmission } from "./session-entry-current-admission.worker-CCmS10ef.mjs";
import { serialize } from "node:v8";
import { randomUUID } from "node:crypto";
//#region src/state/github-publication-receipts.ts
/** Request digests bind content; authority publication does not materialize presentation text. */
const sharedGitHubPublicationAuthorityColumns = [
"request_id",
"idempotency_key",
"request_digest",
"session_id",
"session_key",
"agent_id",
"worktree_id",
"repository_fingerprint",
"claim_id",
"run_id",
"environment_id",
"owner_epoch",
"placement_generation",
"identity_source",
"identity_profile_id",
"identity_account_id",
"identity_login",
"status",
"gateway_instance_id",
"repository",
"branch",
"base_branch",
"source_head_commit",
"source_index_tree",
"workspace_tree",
"head_commit",
"pull_request_url",
"error_code",
"created_at_ms",
"updated_at_ms",
"reported_at_ms"
];
const state = resolveGlobalSingleton(Symbol.for("openclaw.githubPublicationReceipts"), () => ({
sources: /* @__PURE__ */ new WeakMap(),
installingFacts: 0,
facts: /* @__PURE__ */ new Set()
}));
function install(change) {
const failures = [];
state.installingFacts++;
try {
notifyListeners(state.facts, change, (error) => failures.push(error));
} finally {
state.installingFacts--;
}
if (failures.length) throw new AggregateError(failures, "GitHub publication fact installation failed");
}
function publication(receipt) {
let change = {
kind: "committed",
receipt
};
return {
installFacts: () => install(change),
invalidate() {
change = {
kind: "unknown",
identity: receipt.source.identity
};
install(change);
},
notify() {}
};
}
/** Exact owner writes only: arbitrary raw SQL remains outside this coverage. */
const githubPublicationReceipts = {
stageRow: stageGitHubPublicationRow,
subscribeFacts: (listener) => registerListener(state.facts, listener)
};
function createGitHubPublicationReceipt(db, facts) {
let source = state.sources.get(db);
if (!source) {
source = {
identity: readTrackedStateDatabaseIdentity(db)?.key ?? Symbol("untracked-github-publication"),
incarnation: randomUUID()
};
state.sources.set(db, source);
}
return createSqliteCommitReceipt({
source,
domain: "github-publication",
keys: [...facts.keys()],
readFact: (key) => facts.get(key)
});
}
function deferGitHubPublicationDeletionReceipt(db, tombstones) {
const receipt = createGitHubPublicationReceipt(db, tombstones);
deferSqliteWorkerCommitReceipt(db, serialize(receipt).byteLength <= 33554432 ? receipt : {
kind: "unknown",
identity: receipt.source.identity
}, tombstones.size ? "commit" : "settlement");
}
function stageGitHubPublicationRow(db, kind, row) {
if (state.installingFacts) throw new Error("GitHub publication cannot mutate during fact installation");
const value = { ...row };
delete value.title;
delete value.body;
delete value.next_action;
const next = publication(createGitHubPublicationReceipt(db, /* @__PURE__ */ new Map([[JSON.stringify([kind, row.request_id]), {
kind: "postimage",
value
}]])));
if (!stageSqliteCommittedPublication(db, next) && !db.isTransaction) publishSqliteCommittedState(next);
}
/** The deletion worker sends tombstones, including lifecycle rows, before its ordinary reply. */
function withGitHubPublicationDeletionReceipt(createAdmission, context) {
return (operation) => {
const owner = createAdmission(operation);
const identity = () => context.admission.identity.key;
const operationId = randomUUID();
const superseded = /* @__PURE__ */ new Set();
let received = false;
let installing = false;
let unknown = false;
let finished = false;
const unsubscribe = githubPublicationReceipts.subscribeFacts((change) => {
if (installing) return;
if (change.kind === "committed" && change.receipt.source.identity === identity()) for (const key of change.receipt.facts.keys()) superseded.add(key);
else if (change.kind === "unknown" && change.identity === identity()) unknown = true;
});
const finish = owner.admission.finish.bind(owner.admission);
owner.admission.finish = () => {
try {
finish();
} finally {
if (!finished) {
finished = true;
unsubscribe();
if (!received || owner.admission.settlement?.kind !== "completed") install({
kind: "unknown",
identity: identity()
});
install({
kind: "settled",
identity: identity(),
operationId
});
}
}
};
try {
install({
kind: "pending",
identity: identity(),
operationId
});
} catch (error) {
unsubscribe();
owner.admission.finish();
throw error;
}
observeSqliteWorkerCommittedFacts(owner.admission, ({ facts }) => {
try {
(context.assertPublicationCurrent ?? context.admission.assertCurrent)();
if (isRecord(facts) && facts.kind === "unknown" && facts.identity === identity()) {
received = true;
install({
kind: "unknown",
identity: identity()
});
return;
}
if (!isRecord(facts) || !isRecord(facts.source) || facts.source.identity !== identity() || typeof facts.source.incarnation !== "string" || !(facts.facts instanceof Map) || !hasSqliteCommitReceiptCoverage(facts, {
source: {
identity: identity(),
incarnation: facts.source.incarnation
},
domain: "github-publication",
keys: [...facts.facts.keys()]
}) || ![...facts.facts].every(([key, fact]) => {
if (typeof key !== "string" || !isRecord(fact) || fact.kind !== "absent") return false;
const decoded = JSON.parse(key);
return Array.isArray(decoded) && decoded.length === 2 && [
"personal",
"repository",
"personal-lifecycle"
].includes(decoded[0]) && typeof decoded[1] === "string";
})) throw new Error("GitHub publication deletion receipt is invalid");
const receipt = facts;
installing = true;
publishSqliteCommittedState(publication({
...receipt,
facts: new Map([...receipt.facts].map(([key, fact]) => [key, unknown || superseded.has(key) ? { kind: "unknown" } : fact]))
}));
received = true;
} catch (error) {
install({
kind: "unknown",
identity: identity()
});
throw error;
} finally {
installing = false;
}
});
return owner;
};
}
//#endregion
//#region src/state/github-personal-publication-lifecycle.ts
/** Capture historical receipts while the session still owns its logical keys. */
async function preparePersonalGitHubSessionReceiptDeletion(params) {
params.assertCurrent?.();
const context = captureOpenClawStateWorkerContext({ env: params.env });
const generations = params.generations.map((generation) => ({ ...generation }));
const input = {
agentId: params.agentId,
sessionKeys: [...new Set(generations.map((generation) => generation.sessionKey))]
};
const { runOpenClawStateWorkerOperation } = await import("./openclaw-state-worker-store-x5r3Lkjl.mjs");
const receipts = await runOpenClawStateWorkerOperation(context, (scope) => scope.execute({
type: "githubPublication.prepareSessionReceiptDeletion",
input
}), {
assertCurrent: params.assertCurrent,
existingOnly: true
}) ?? {
personal: [],
repository: []
};
params.assertCurrent?.();
return async ({ assertCurrent, sessionEntryCurrent, retainedSessionKeys } = {}) => {
const selectedKeys = new Set(input.sessionKeys.filter((key) => !retainedSessionKeys?.has(key)));
if (selectedKeys.size === 0) return;
const assertAdmission = () => {
context.admission.assertCurrent();
assertCurrent?.();
};
await runOpenClawStateWorkerOperation(context, (scope) => scope.execute({
type: "githubPublication.deleteSessionReceipts",
input: {
agentId: input.agentId,
sessionKeys: [...selectedKeys],
generations: generations.filter((generation) => selectedKeys.has(generation.sessionKey)),
receipts: {
personal: receipts.personal.filter((receipt) => selectedKeys.has(receipt.session_key)),
repository: receipts.repository.filter((receipt) => selectedKeys.has(receipt.session_key))
},
sessionEntryCurrentSource: sessionEntryCurrent?.source
}
}), {
assertCurrent: assertAdmission,
createAdmission: withGitHubPublicationDeletionReceipt(createSqliteWorkerWriteAdmission((request) => {
assertAdmission();
assertSessionEntryCurrentAdmission(request, sessionEntryCurrent);
}, [context.admission.databasePath]), context)
});
};
}
function readSessionReceiptDeletionIdentitiesInDatabase(database, params) {
const read = (table) => params.sessionKeys.length && tableExists(database.db, table) ? executeSqliteQuerySync(database.db, getNodeSqliteKysely(database.db).selectFrom(table).select([
"request_id",
"session_id",
"session_key",
"created_at_ms"
]).where("agent_id", "=", params.agentId).where("session_key", "in", sqliteStringSet(params.sessionKeys))).rows : [];
return {
personal: read("github_personal_publication_requests"),
repository: read("github_repository_publication_requests")
};
}
/** Exact historical receipts and late inserts for the removed generation share one SQL edge. */
function deletePersonalGitHubSessionReceiptsInDatabase(database, params) {
const existing = ["github_personal_publication_requests", "github_repository_publication_requests"].filter((table) => tableExists(database.db, table));
if (existing.length === 0 || params.sessionKeys.length === 0) return;
const generations = new Map(params.generations.toReversed().map((generation) => [JSON.stringify([generation.sessionKey, generation.sessionId]), generation]));
runOpenClawStateWriteTransaction(({ db }) => {
requestSessionEntryCurrentAdmission(params.sessionEntryCurrentSource, {
stage: "transaction",
facts: void 0
}, { lookup: "logical" });
const query = getNodeSqliteKysely(db);
const tombstones = /* @__PURE__ */ new Map();
const hasLifecycles = tableExists(db, "github_publication_session_lifecycles");
const current = readSessionReceiptDeletionIdentitiesInDatabase(database, params);
for (const table of existing) {
const historical = table === "github_personal_publication_requests" ? params.receipts.personal : params.receipts.repository;
const captured = new Map(historical.map((receipt) => [receipt.request_id, receipt]));
const selected = (table === "github_personal_publication_requests" ? current.personal : current.repository).filter((receipt) => {
const previous = captured.get(receipt.request_id);
if (previous && previous.session_id === receipt.session_id && previous.session_key === receipt.session_key && previous.created_at_ms === receipt.created_at_ms) return true;
const generation = generations.get(JSON.stringify([receipt.session_key, receipt.session_id]));
if (!generation) return false;
const binding = table === "github_personal_publication_requests" ? hasLifecycles ? executeSqliteQueryTakeFirstSync(db, query.selectFrom("github_publication_session_lifecycles").select("lifecycle_revision").where("publication_kind", "=", "personal").where("request_id", "=", receipt.request_id)) : void 0 : executeSqliteQueryTakeFirstSync(db, query.selectFrom("github_repository_publication_requests").select("session_lifecycle_revision as lifecycle_revision").where("request_id", "=", receipt.request_id));
return binding !== void 0 && binding.lifecycle_revision === generation.lifecycleRevision;
}).map((receipt) => receipt.request_id);
if (selected.length === 0) continue;
if (table === "github_personal_publication_requests" && hasLifecycles) for (const requestId of selected) {
const deleted = executeSqliteQuerySync(db, query.deleteFrom("github_publication_session_lifecycles").where("publication_kind", "=", "personal").where("request_id", "=", requestId).returning("request_id")).rows;
for (const row of deleted) tombstones.set(JSON.stringify(["personal-lifecycle", row.request_id]), { kind: "absent" });
}
for (const requestId of selected) {
const deleted = executeSqliteQuerySync(db, query.deleteFrom(table).where("request_id", "=", requestId).returning("request_id")).rows;
for (const row of deleted) tombstones.set(JSON.stringify([table === "github_personal_publication_requests" ? "personal" : "repository", row.request_id]), { kind: "absent" });
}
}
requestSessionEntryCurrentAdmission(params.sessionEntryCurrentSource, {
stage: "commit",
facts: void 0
}, { lookup: "logical" });
deferGitHubPublicationDeletionReceipt(db, tombstones);
}, { database }, { operationLabel: "github-personal-publication.session-delete" });
}
//#endregion
export { sharedGitHubPublicationAuthorityColumns as a, githubPublicationReceipts as i, preparePersonalGitHubSessionReceiptDeletion as n, readSessionReceiptDeletionIdentitiesInDatabase as r, deletePersonalGitHubSessionReceiptsInDatabase as t };