UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

251 lines (250 loc) • 9.31 kB
import { join } from "node:path"; import { assertAdoptionBootGate } from "./adoption/marker.js"; import { checkpointExecutionRecorded, executionRecordDisposition, ExecutionOutcomeConflictError, } from "../../core/checkpoint-execution-record.js"; import { appendPendingSteer, CheckpointError, checkpointOccMatches, checkpointRowMatches, encodeAtFidelity, summarizeCheckpoint, validatePendingSteer, winnerFromOutcome, } from "../../core/checkpoint-store.js"; import { SharedLedgerTable } from "./shared-ledger.js"; import { guardCorruptSink } from "./corrupt-read-sink.js"; const CHECKPOINT_LEDGER_EVENT_REGISTRY = { put: true, resolve: true, reopen: true, execution: true, expire: true, steer_append: true, }; const KNOWN_CHECKPOINT_LEDGER_EVENTS = new Set(Object.keys(CHECKPOINT_LEDGER_EVENT_REGISTRY)); function applyCheckpointEvent(cps, ev) { if (!KNOWN_CHECKPOINT_LEDGER_EVENTS.has(ev.t)) { throw new CheckpointError("checkpoint.unsupported_version", `file checkpoint ledger: unrecognised event kind "${String(ev.t)}" — ` + `it was written by a newer worker, or it is a kind this engine retired; refusing the replay rather than serving an incomplete authority`, { reason: "ledger_event_unknown" }); } if (ev.t === "put") { cps.set(ev.token, ev.cp); return; } const cp = cps.get(ev.token); if (!cp) return; switch (ev.t) { case "resolve": cp.status = "resolved"; cp.rev = ev.rev; if (ev.resolvedOutcome !== undefined) cp.resolvedOutcome = ev.resolvedOutcome; cp.reopenReason = undefined; break; case "reopen": cp.status = "pending"; cp.rev = ev.rev; cp.reopenReason = ev.reason; break; case "execution": cp.executionOutcome = ev.gate; cp.executionAtMs = ev.atMs; break; case "expire": cp.status = "expired"; break; case "steer_append": { const queue = cp.state.pendingSteerQueue ?? []; if (!queue.some((e) => e.inputId === ev.entry.inputId)) cp.state.pendingSteerQueue = [...queue, ev.entry]; break; } default: { const _exhaustive = ev; void _exhaustive; break; } } } const isCheckpointRow = (x) => x !== null && typeof x === "object" && typeof x.token === "string" && typeof x.scope === "string" && typeof x.status === "string"; const checkpointLedgers = new SharedLedgerTable({ label: "checkpoint ledger", keyOf: (cp) => cp.token, apply: applyCheckpointEvent, judgeRow: isCheckpointRow, judgeEvent: (x) => { const ev = x; if (typeof ev.t !== "string") return false; if (!KNOWN_CHECKPOINT_LEDGER_EVENTS.has(ev.t)) return true; if (typeof ev.token !== "string") return false; return ev.t === "put" ? isCheckpointRow(ev.cp) : true; }, }); export class FileCheckpointStore { durability = "durable"; fidelity = "json"; redecision = { reopen: true }; execution = { outcome: true }; fsyncEnabled; compactEvery; ledger; closed = false; get cps() { return this.ledger.rows; } fault = null; constructor(root, opts = {}) { assertAdoptionBootGate(root, "FileCheckpointStore"); this.fsyncEnabled = opts.fsync !== false; this.compactEvery = opts.compactEvery ?? 1000; const dir = join(root, "checkpoints"); this.ledger = checkpointLedgers.acquire({ dir, tmpDir: join(root, "tmp"), ...(opts.onCorruptRead !== undefined ? { onCorruptRead: guardCorruptSink(opts.onCorruptRead) } : {}), ledgerPath: join(dir, "checkpoints.jsonl"), snapshotPath: join(dir, "checkpoints.snapshot.jsonl"), }); } withLock(token, fn) { return this.ledger.withLock(token, fn); } commit(ev, fsync) { const held = encodeAtFidelity(this.fidelity, ev); if (!held.ok) throw held.cause; this.ledger.commit(held.value, fsync, this.compactEvery); } async put(token, cp) { return this.withLock(token, () => { if (this.cps.has(token)) { throw new CheckpointError("checkpoint.already_exists", `checkpoint token already exists`); } const stored = structuredClone(cp); this.commit({ t: "put", token, cp: stored }, this.fsyncEnabled); }); } async get(token) { const cp = this.cps.get(token); return cp ? structuredClone(cp) : null; } async resolve(token, scope, outcome, expect) { return this.withLock(token, () => { if (this.fault === "resolve-before-commit") { this.fault = null; throw new Error("injected fault: resolve before commit"); } const cp = this.cps.get(token); if (!checkpointRowMatches(cp, scope, "pending")) { return false; } if (!checkpointOccMatches(cp, expect)) { return false; } const winner = winnerFromOutcome(outcome); const rev = (cp.rev ?? 0) + 1; this.commit({ t: "resolve", token, rev, resolvedOutcome: winner }, true); if (this.fault === "resolve-after-commit") { this.fault = null; throw new Error("injected fault: resolve after commit"); } return true; }); } async recordExecutionOutcome(token, scope, gate) { return this.withLock(token, () => { const cp = this.cps.get(token); const disposition = executionRecordDisposition(cp, scope, gate); if (disposition === "conflict") throw new ExecutionOutcomeConflictError(cp.executionOutcome, gate); if (disposition !== "commit") return disposition; this.commit({ t: "execution", token, gate: structuredClone(gate), atMs: Date.now() }, true); return "recorded"; }); } async reopen(token, scope, reason) { return this.withLock(token, () => { const cp = this.cps.get(token); if (!checkpointRowMatches(cp, scope, "resolved") || checkpointExecutionRecorded(cp)) { return false; } const rev = (cp.rev ?? 0) + 1; this.commit({ t: "reopen", token, rev, reason }, true); return true; }); } async setPendingSteer(token, scope, steer) { const clean = validatePendingSteer(steer); return this.withLock(token, () => { const cp = this.cps.get(token); if (!checkpointRowMatches(cp, scope, "pending")) { return false; } const next = appendPendingSteer(cp.state, clean); const appended = next[next.length - 1]; if (appended === undefined || appended.inputId !== clean.inputId) return true; this.commit({ t: "steer_append", token, entry: appended }, this.fsyncEnabled); return true; }); } async expire(token, scope) { return this.withLock(token, () => { const cp = this.cps.get(token); if (!checkpointRowMatches(cp, scope, "pending")) { return false; } this.commit({ t: "expire", token }, true); return true; }); } async reap(scope, cutoff) { let n = 0; const deadlinePassed = (deadline) => deadline !== undefined && (Number.isFinite(deadline) ? deadline <= cutoff : true); const candidates = [...this.cps.values()].filter((cp) => cp.scope === scope && cp.status === "pending" && deadlinePassed(cp.deadline)); for (const cp of candidates) { const won = await this.withLock(cp.token, () => { const live = this.cps.get(cp.token); if (!live || live.scope !== scope || live.status !== "pending") return false; if (!deadlinePassed(live.deadline)) return false; this.commit({ t: "expire", token: cp.token }, true); return true; }); if (won) n++; } return n; } async listByScope(scope) { const out = []; for (const cp of this.cps.values()) { if (cp.scope === scope && cp.status === "pending") out.push(summarizeCheckpoint(cp)); } return out; } async listScopes() { const out = new Set(); for (const cp of this.cps.values()) if (cp.status === "pending") out.add(cp.scope); return [...out]; } compactNow() { this.ledger.compact(); } testInjectFault(mode) { this.fault = mode; } get size() { return this.cps.size; } close() { if (this.closed) return; this.closed = true; this.ledger.release(); } }