UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

164 lines (163 loc) • 8.73 kB
import { i as executeSqliteQuerySync, o as getNodeSqliteKysely } from "./kysely-sync-DUWzQvCU.mjs"; import { i as deferSqliteWorkerCommitReceipt, s as requestSqliteWorkerOperationAdmission } from "./sqlite-worker-operation-admission-C5lBzzTk.mjs"; import { l as runOpenClawStateWriteTransaction } from "./openclaw-state-db-DJZ7EKoI.mjs"; import { i as matchesWorkerPlacementTarget, r as isFailedWorkerPlacementEnvironmentGone } from "./placement-target-BZL1jHxv.mjs"; import { c as normalizeIdentity, o as nextGeneration, u as normalizeWorkerPlacementExecutionMode } from "./placement-record-BZ5ExJ2S.mjs"; import { a as query, f as updateTransition, i as getRequired, n as find, o as readWorkerPlacementsForReconcileInDatabase, s as readWorkerPlacementsInDatabase, t as ensureLocal } from "./placement-row-codec-Cpn-JKmv.mjs"; import { o as hasWorkerWorkspacePendingResult } from "./placement-workspace-result-DabzjaLS.mjs"; import { i as readWorkerPlacementMovesReadOnly, n as readWorkerSessionPlacementProjectionInDatabase, r as createPlacementMoveOps } from "./placement-read-projection-GVzVGrUt.mjs"; import { r as assertSessionWorkspaceUnreserved } from "./placement-workspace-reservation.kernel-BwT507ly.mjs"; import { n as consumePreparedEnvironment } from "./prepared-environment-store-DDlKn4GK.mjs"; import { r as findWorkerEnvironment } from "./store-row-codec-l38X6-mv.mjs"; import { t as retireWorkerSessionPlacement } from "./placement-retirement-Dk5eSCzb.mjs"; //#region src/gateway/worker-environments/placement-lifecycle.worker.ts function operation(type, execute, deferredAdmission = false) { return (input, { write }) => { return write(({ db, path }) => { const admit = (facts) => requestSqliteWorkerOperationAdmission({ stage: "transaction", facts }); if (!deferredAdmission) admit(); const receipt = execute({ path, instanceId: "", now: () => input.nowMs ?? Date.now(), read: () => db, write: (mutate) => mutate(db) }, input, admit); requestSqliteWorkerOperationAdmission({ stage: "commit", facts: receipt }); deferSqliteWorkerCommitReceipt(db, receipt); return receipt; }, { operationLabel: type }); }; } const placementReadOperations = { "workerPlacements.read": (input, { open }) => readWorkerPlacementsInDatabase(open().db, input.sessionIds), "workerPlacements.readWithMove": (input, { open }) => { const { projection } = readWorkerSessionPlacementProjectionInDatabase(open().db, [input.sessionId], []); return { placement: projection.placements.get(input.sessionId), move: projection.moves.get(input.sessionId) }; }, "workerPlacements.readMove": (input, { open }) => readWorkerPlacementMovesReadOnly(open().db, [input.sessionId]).get(input.sessionId), "workerPlacements.readReconcile": (input, { open }) => readWorkerPlacementsForReconcileInDatabase(open().db, input.sessionKey) }; const placementLifecycleOperations = { "workerPlacements.beginMove": operation("workerPlacements.beginMove", (runtime, input, admit) => ({ sessionId: input.sessionId, ...createPlacementMoveOps(runtime).beginPlacementMove(input, (placement, joined) => admit({ sessionId: input.sessionId, placement, joined })) }), true), "workerPlacements.moveError": operation("workerPlacements.moveError", (runtime, input) => ({ sessionId: input.sessionId, changed: createPlacementMoveOps(runtime).recordPlacementMoveError(input) })), "workerPlacements.cancelMove": operation("workerPlacements.cancelMove", (runtime, input) => ({ sessionId: input.sessionId, changed: createPlacementMoveOps(runtime).cancelPlacementMove(input) })), "workerPlacements.completeMoveSource": operation("workerPlacements.completeMoveSource", (runtime, input) => ({ sessionId: input.sessionId, ...createPlacementMoveOps(runtime).completePlacementMoveSourceToLocal(input) })), "workerPlacements.completeAbandonedMoveSource": operation("workerPlacements.completeAbandonedMoveSource", (runtime, input) => ({ sessionId: input.sessionId, ...createPlacementMoveOps(runtime).completeAbandonedPlacementMoveSourceToLocal(input) })), "workerPlacements.completeMove": operation("workerPlacements.completeMove", (runtime, input) => ({ sessionId: input.sessionId, placement: createPlacementMoveOps(runtime).completePlacementMoveToWorker(input) })), "workerPlacements.retire": operation("workerPlacements.retire", (runtime, input) => { const db = runtime.read(); const retired = retireWorkerSessionPlacement(db, input, { onlyIfCurrent: true }); if (!retired && find(db, input.sessionId)) throw new Error(`Worker session placement ${input.sessionId} changed before retirement`); return retired ? { sessionId: input.sessionId, retired: input.expectedState } : { sessionId: input.sessionId, changed: false }; }), "workerPlacements.bindPrepared": operation("workerPlacements.bindPrepared", (runtime, input) => { const nowMs = runtime.now(); const consumed = consumePreparedEnvironment(runtime.read(), input, nowMs); if (!consumed) return { sessionId: input.sessionId }; return { sessionId: input.sessionId, placement: updateTransition(runtime.read(), consumed.placement, "provisioning", { environmentId: input.environmentId }, nowMs), environment: { environmentId: input.environmentId, patch: consumed.environmentPatch } }; }) }; //#endregion //#region src/gateway/worker-environments/placement-dispatch-store.worker.ts function startWorkerPlacementDispatchInWorker(input, database) { const identity = normalizeIdentity(input.placement); const executionMode = normalizeWorkerPlacementExecutionMode(input.placement.executionMode); return runOpenClawStateWriteTransaction(({ db }) => { requestSqliteWorkerOperationAdmission({ stage: "transaction", facts: void 0 }); const current = ensureLocal(db, identity, input.nowMs); assertSessionWorkspaceUnreserved(db, identity.sessionId); if (current.state !== "local" && current.state !== "reclaimed" && current.state !== "failed") throw new Error(`Cannot dispatch session ${identity.sessionId} from placement ${current.state}`); const expected = input.placement.expectedPlacement; if (expected) { if (!matchesWorkerPlacementTarget(current, expected) || current.executionMode !== executionMode || current.state !== "reclaimed" && current.state !== "failed" || current.turnClaim) throw new Error(`Worker placement ${identity.sessionId} changed before redispatch`); const journal = executeSqliteQuerySync(db, getNodeSqliteKysely(db).selectFrom("worker_workspace_reconciliations").select("session_id").where("session_id", "=", identity.sessionId)).rows[0]; if (hasWorkerWorkspacePendingResult(db, identity.sessionId) || journal || readWorkerPlacementMovesReadOnly(db, [identity.sessionId]).has(identity.sessionId)) throw new Error(`Worker placement ${identity.sessionId} still has pending workspace recovery`); if (current.state === "failed") { const environment = current.environmentId ? findWorkerEnvironment(db, current.environmentId) : void 0; if (current.activeOwnerEpoch === null || !environment || !isFailedWorkerPlacementEnvironmentGone({ environmentService: { get: () => environment }, placement: current })) throw new Error(`Failed worker placement ${identity.sessionId} still requires recovery`); } } if (executeSqliteQuerySync(db, query(db).updateTable("worker_session_placements").set({ state: "requested", execution_mode: executionMode, environment_id: null, transition_generation: nextGeneration(current.generation), active_owner_epoch: null, workspace_base_manifest_ref: null, remote_workspace_dir: null, worker_bundle_hash: null, last_transcript_ack_cursor: null, last_live_event_ack_cursor: null, recovery_error: null, terminal_reason: null, terminal_at_ms: null, updated_at_ms: input.nowMs, state_changed_at_ms: input.nowMs }).where("session_id", "=", current.sessionId).where("state", "=", current.state).where("transition_generation", "=", current.generation)).numAffectedRows !== 1n) throw new Error(`Session ${identity.sessionId} placement changed during dispatch barrier`); const updated = getRequired(db, identity.sessionId); deferSqliteWorkerCommitReceipt(db, updated); requestSqliteWorkerOperationAdmission({ stage: "commit", facts: updated }); return updated; }, { database }, { operationLabel: "workerPlacements.startDispatch" }); } const workerPlacementOperations = { ...placementLifecycleOperations, ...placementReadOperations, "workerPlacements.startDispatch": (input, { open }) => startWorkerPlacementDispatchInWorker(input, open()) }; //#endregion export { workerPlacementOperations };