@sema-agent/core
Version:
Stateless, task-oriented AI agent core
248 lines (247 loc) • 9.17 kB
JavaScript
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, 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) {
this.ledger.commit(ev, 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();
}
}