UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

326 lines (325 loc) • 14.3 kB
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 };