UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

377 lines (376 loc) • 18.8 kB
import { existsSync, readdirSync, renameSync } from "node:fs"; import { join } from "node:path"; import { uuidv7 } from "../../engine/session/uuid.js"; import { atomicWriteFileSync, lockedStrictUpdate, readStrictSidecar, ControlPlaneCorruptError, ensureDirExists } from "./layout.js"; import { thrownText } from "../thrown-value.js"; export function deriveSupersededSet(headers, opts = {}) { const byId = new Map(); for (const h of headers) byId.set(h.id, h); const out = new Map(); for (const h of headers) { const d = h.distilled; if (d === undefined || d.supersedes.length === 0) continue; if (opts.excluded?.(h.id) === true) continue; if (h.rev !== d.carrierRev) continue; for (const edge of d.supersedes) { const target = byId.get(edge.id); if (target === undefined) continue; if (target.scope !== h.scope) continue; if (target.rev !== edge.rev) continue; if (!out.has(edge.id)) out.set(edge.id, h.id); } } return out; } export function memorySupersededNote(carrierId) { return `note: a newer consolidated entry supersedes this one — memory entry ${carrierId} carries the current form; this entry remains readable as evidence.`; } export const MEMORY_SEARCH_SUPERSEDED_TAG = "[superseded]"; export const CONSOLIDATION_DEFAULTS = { supersedeRatioCap: 0.25, supersedeAbsoluteFloor: 4, maxProductsPerRun: 64, maxInputsPerProduct: 32, maxDirectedPatchesPerPlan: 32, minRunIntervalMs: 24 * 60 * 60 * 1000, minSessionsBetweenRuns: 5, scanRefusalRatioCap: 0.25, scanRefusalMaxPerPlan: 8, }; export const CONSOLIDATION_FORCE_THROTTLE_FLOOR_MS = 60_000; export const CONSOLIDATION_SESSION_RING_MAX = 64; export const CONSOLIDATION_SEAT_FREEZE_GRACE_MS = 10 * 60_000; function coded(message, code) { const e = new Error(message); e.code = code; return e; } export function screenConsolidationOptions(raw, provenance) { if (provenance === "off") { throw coded(`MemoryEngineOptions.consolidation cannot be enabled under provenance: "off" — the consolidation fold law must be able to mint origin markers (a marked input's product would otherwise commit unmarked). Enable provenance "carry" or remove the consolidation options.`, "config.memory_consolidation_provenance_off"); } const num = (name, v, check, want) => { if (v === undefined) return CONSOLIDATION_DEFAULTS[name]; if (typeof v !== "number" || !Number.isFinite(v) || !check(v)) { const got = typeof v === "number" ? String(v) : v === null ? "null" : typeof v; throw coded(`MemoryEngineOptions.consolidation.${name} must be ${want} when present (got ${got}) — an unevaluable knob is refused loudly, never folded to the default.`, "config.memory_consolidation"); } return v; }; const screened = { supersedeRatioCap: num("supersedeRatioCap", raw.supersedeRatioCap, (n) => n > 0 && n <= 1, "a number in (0, 1]"), supersedeAbsoluteFloor: num("supersedeAbsoluteFloor", raw.supersedeAbsoluteFloor, (n) => Number.isInteger(n) && n >= 0, "a non-negative integer"), maxProductsPerRun: num("maxProductsPerRun", raw.maxProductsPerRun, (n) => Number.isInteger(n) && n >= 1, "a positive integer"), maxInputsPerProduct: num("maxInputsPerProduct", raw.maxInputsPerProduct, (n) => Number.isInteger(n) && n >= 1, "a positive integer"), maxDirectedPatchesPerPlan: num("maxDirectedPatchesPerPlan", raw.maxDirectedPatchesPerPlan, (n) => Number.isInteger(n) && n >= 1, "a positive integer"), minRunIntervalMs: num("minRunIntervalMs", raw.minRunIntervalMs, (n) => n > 0, "a positive number of milliseconds"), minSessionsBetweenRuns: num("minSessionsBetweenRuns", raw.minSessionsBetweenRuns, (n) => Number.isInteger(n) && n >= 0, "a non-negative integer"), scanRefusalRatioCap: num("scanRefusalRatioCap", raw.scanRefusalRatioCap, (n) => n > 0 && n <= 1, "a number in (0, 1]"), scanRefusalMaxPerPlan: num("scanRefusalMaxPerPlan", raw.scanRefusalMaxPerPlan, (n) => Number.isInteger(n) && n >= 1, "a positive integer"), allowInstructionProducts: raw.allowInstructionProducts === true, multiNode: raw.multiNode === true, ...(raw.lease !== undefined ? { lease: raw.lease } : {}), }; if (raw.multiNode !== undefined && raw.multiNode !== true) { throw coded(`MemoryEngineOptions.consolidation.multiNode must be exactly true when present (a posture declaration, not a boolean knob).`, "config.memory_consolidation"); } if (raw.allowInstructionProducts !== undefined && raw.allowInstructionProducts !== true) { throw coded(`MemoryEngineOptions.consolidation.allowInstructionProducts must be exactly true when present (a posture declaration, not a boolean knob — absent means the instruction-form gate stays closed).`, "config.memory_consolidation"); } if (screened.multiNode && screened.lease === undefined) { throw coded(`MemoryEngineOptions.consolidation.multiNode declared without a lease seat — a multi-node deployment MUST inject the host's global lease (the local plan-seat CAS does not reach across nodes). Inject consolidation.lease or remove the multiNode declaration.`, "config.memory_consolidation_lease_required"); } if (raw.lease !== undefined && typeof raw.lease.acquire !== "function") { throw coded(`MemoryEngineOptions.consolidation.lease.acquire must be a function.`, "config.memory_consolidation"); } return screened; } export function supersessionFuseCeiling(activeSetSize, opts) { if (activeSetSize <= 1) return 0; return Math.min(activeSetSize - 1, Math.max(opts.supersedeAbsoluteFloor, Math.floor(opts.supersedeRatioCap * activeSetSize))); } export function scanRefusalCeiling(submittedProducts, opts) { if (submittedProducts <= 0) return 0; return Math.min(Math.max(1, Math.floor(opts.scanRefusalRatioCap * submittedProducts)), opts.scanRefusalMaxPerPlan); } export function consolidationTypeEligible(type) { return type !== "episodic" && type !== "procedural"; } export const CONSOLIDATION_GATE_FILE = "consolidation-gate.json"; export const CONSOLIDATION_PLANS_DIR = "consolidation-plans"; export const CONSOLIDATION_INTENTS_FILE = "consolidation-intents.json"; function coerceGateFile(raw) { if (raw === undefined) return { v: 1, scopes: {} }; if (typeof raw !== "object" || raw === null || raw.v !== 1 || typeof raw.scopes !== "object" || raw.scopes === null) { throw new ControlPlaneCorruptError("memory consolidation gate store has an unrecognized shape (fail-closed sidecar)"); } const scopes = raw.scopes; const out = { v: 1, scopes: {} }; for (const [scope, rowRaw] of Object.entries(scopes)) { if (typeof rowRaw !== "object" || rowRaw === null) throw new ControlPlaneCorruptError(`memory consolidation gate store row for scope ${JSON.stringify(scope)} is malformed (fail-closed sidecar)`); const r = rowRaw; const sessions = r["sessions"]; if (typeof sessions !== "object" || sessions === null || typeof sessions.count !== "number" || !Array.isArray(sessions.ring ?? []) || (sessions.ring !== undefined && !sessions.ring.every((s) => typeof s === "string")) || typeof r["epoch"] !== "number" || !Number.isInteger(r["epoch"])) { throw new ControlPlaneCorruptError(`memory consolidation gate store row for scope ${JSON.stringify(scope)} is malformed (fail-closed sidecar)`); } out.scopes[scope] = rowRaw; } return out; } function freshRow() { return { sessions: { count: 0, ring: [] }, epoch: 0 }; } export function updateConsolidationGate(controlDir, fn) { return lockedStrictUpdate(controlDir, CONSOLIDATION_GATE_FILE, "memory consolidation gate store", coerceGateFile, fn); } export function readConsolidationGateFile(controlDir) { return coerceGateFile(readStrictSidecar(controlDir, CONSOLIDATION_GATE_FILE, "memory consolidation gate store")); } export function consolidationGateFileExists(controlDir) { return existsSync(join(controlDir, CONSOLIDATION_GATE_FILE)); } export function recommendationUnconsumed(row) { return row.lastRecommendedAt !== undefined && (row.lastRunAt === undefined || row.lastRecommendedAt > row.lastRunAt); } export function sessionsSinceExcludingCurrent(row) { return Math.max(0, (row?.sessions.count ?? 0) - 1); } export function recordConsolidationSession(controlDir, scope, sessionId, opts, now) { return updateConsolidationGate(controlDir, (file) => { const row = file.scopes[scope] ?? freshRow(); if (row.sessions.ring.includes(sessionId)) { return { result: { recommended: false, sessionsSince: sessionsSinceExcludingCurrent(row) } }; } row.sessions.count += 1; row.sessions.ring.push(sessionId); if (row.sessions.ring.length > CONSOLIDATION_SESSION_RING_MAX) row.sessions.ring.splice(0, row.sessions.ring.length - CONSOLIDATION_SESSION_RING_MAX); const at = now(); const banked = sessionsSinceExcludingCurrent(row); const timeOpen = row.lastRunAt === undefined || at - row.lastRunAt >= opts.minRunIntervalMs; const sessionsOpen = banked >= opts.minSessionsBetweenRuns; const edgeArmed = !recommendationUnconsumed(row); let recommended = false; if (timeOpen && sessionsOpen && edgeArmed) { recommended = true; row.lastRecommendedAt = at; } file.scopes[scope] = row; return { next: file, result: { recommended, sessionsSince: banked } }; }); } export function recordActiveSetBaseline(controlDir, scope, size, ratioCap, _now) { return updateConsolidationGate(controlDir, (file) => { const row = file.scopes[scope] ?? freshRow(); const prev = row.activeSetBaseline; row.activeSetBaseline = size; file.scopes[scope] = row; const collapsed = prev !== undefined && prev > 0 && size < prev && (prev - size) / prev > ratioCap ? { from: prev, to: size } : undefined; return { next: file, result: collapsed !== undefined ? { collapsed } : {} }; }); } export function isValidConsolidationPlanId(value) { return /^[A-Za-z0-9][A-Za-z0-9_-]{7,63}$/.test(value); } function planPath(controlDir, planId) { if (!isValidConsolidationPlanId(planId)) { throw new ControlPlaneCorruptError(`consolidation plan id ${JSON.stringify(planId.slice(0, 80))} fails the id grammar — a plan id doubles as a control-plane file name and must never be joined as a path`); } return join(controlDir, CONSOLIDATION_PLANS_DIR, `${planId}.json`); } export function writeConsolidationPlan(controlDir, plan) { ensureDirExists(join(controlDir, CONSOLIDATION_PLANS_DIR)); atomicWriteFileSync(planPath(controlDir, plan.planId), `${JSON.stringify(plan, null, 2)}\n`); } const CONSOLIDATION_PLAN_STATES = new Set(["open", "applying", "completed", "conflict", "discarded"]); const CONSOLIDATION_DIRECTED_STATES = new Set(["pending", "applied", "satisfied", "conflict"]); const CONSOLIDATION_INTENT_STATES = new Set(["pending", "settled", "abandoned"]); function planShapeInvalid(raw, planId) { if (typeof raw !== "object" || raw === null) return "not an object"; const p = raw; if (p["v"] !== 1 && p["v"] !== 2) return "unrecognized version"; if (p["planId"] !== planId) return "planId mismatch"; if (typeof p["scope"] !== "string" || p["scope"].length === 0) return "missing scope"; if (typeof p["requestId"] !== "string") return "missing requestId"; if (typeof p["createdAt"] !== "number") return "missing createdAt"; if (typeof p["visibleMarked"] !== "boolean") return "missing visibleMarked"; if (typeof p["state"] !== "string" || !CONSOLIDATION_PLAN_STATES.has(p["state"])) return "unrecognized state"; if (!Array.isArray(p["products"]) || !p["products"].every((e) => typeof e === "object" && e !== null && typeof e.id === "string")) return "malformed products"; const productIds = p["products"].map((e) => e.id); const states = p["productStates"]; if (typeof states !== "object" || states === null) return "malformed productStates"; const stateKeys = Object.keys(states); if (stateKeys.length !== productIds.length || !productIds.every((id) => states[id] === "pending" || states[id] === "applied" || states[id] === "conflict")) { return "productStates do not cover the product set one-to-one"; } if (!Array.isArray(p["directed"]) || !p["directed"].every((d0) => { if (typeof d0 !== "object" || d0 === null) return false; const d = d0; if (typeof d["id"] !== "string" || typeof d["intentRequestId"] !== "string" || typeof d["baseRev"] !== "string") return false; if (d["op"] !== "update" && d["op"] !== "delete") return false; if (typeof d["state"] !== "string" || !CONSOLIDATION_DIRECTED_STATES.has(d["state"])) return false; if (d["op"] === "update") { if (typeof d["plannedPostRev"] !== "string") return false; if (d["state"] === "pending" && (typeof d["entry"] !== "object" || d["entry"] === null)) return false; } else if (d["entry"] !== undefined) return false; return true; })) { return "malformed directed set"; } const intentIds = new Set(); if (!Array.isArray(p["intents"]) || !p["intents"].every((i0) => { if (typeof i0 !== "object" || i0 === null) return false; const i = i0; if (typeof i["requestId"] !== "string") return false; intentIds.add(i["requestId"]); return typeof i["state"] === "string" && CONSOLIDATION_INTENT_STATES.has(i["state"]); })) { return "malformed intents"; } if (!p["directed"].every((d) => intentIds.has(d.intentRequestId))) return "directed patch references an undeclared intent"; const freezeRefused = p["freezeRefusedInputIds"]; if (freezeRefused !== undefined && (!Array.isArray(freezeRefused) || !freezeRefused.every((x) => typeof x === "string"))) { return "malformed freezeRefusedInputIds"; } if (p["mintExposure"] !== undefined && p["mintExposure"] !== "partitioned") return "malformed mintExposure echo"; if ((p["v"] === 2) !== (freezeRefused !== undefined)) { return p["v"] === 2 ? "v2 plan without its freezeRefusedInputIds member" : "freezeRefusedInputIds on a v1 plan"; } if (!Array.isArray(p["audit"])) return "malformed audit trail"; return undefined; } export function readConsolidationPlan(controlDir, planId) { if (!isValidConsolidationPlanId(planId)) return { state: "absent" }; const path = planPath(controlDir, planId); if (!existsSync(path)) return { state: "absent" }; try { const raw = readStrictSidecar(join(controlDir, CONSOLIDATION_PLANS_DIR), `${planId}.json`, "memory consolidation plan"); const bad = planShapeInvalid(raw, planId); if (bad !== undefined) return { state: "corrupt", detail: bad }; return { state: "ok", plan: raw }; } catch (err) { return { state: "corrupt", detail: thrownText(err) }; } } export function listConsolidationPlanIds(controlDir) { const dir = join(controlDir, CONSOLIDATION_PLANS_DIR); if (!existsSync(dir)) return []; try { return readdirSync(dir) .filter((f) => f.endsWith(".json") && !f.endsWith(".corrupt.json")) .map((f) => f.slice(0, -".json".length)) .filter((id) => isValidConsolidationPlanId(id)); } catch { return []; } } export function quarantineCorruptPlan(controlDir, planId) { if (!isValidConsolidationPlanId(planId)) return undefined; const from = planPath(controlDir, planId); if (!existsSync(from)) return undefined; const name = `${planId}.corrupt.json`; try { renameSync(from, join(controlDir, CONSOLIDATION_PLANS_DIR, name)); return name; } catch { return undefined; } } function coerceIntentFile(raw) { if (raw === undefined) return { v: 1, rows: {} }; if (typeof raw !== "object" || raw === null || raw.v !== 1 || typeof raw.rows !== "object" || raw.rows === null) { throw new ControlPlaneCorruptError("memory consolidation intent-credential account has an unrecognized shape (fail-closed sidecar)"); } return raw; } export function recordIntentCredentials(controlDir, rows) { if (rows.length === 0) return []; return lockedStrictUpdate(controlDir, CONSOLIDATION_INTENTS_FILE, "memory consolidation intent-credential account", coerceIntentFile, (file) => { const written = []; for (const row of rows) { if (file.rows[row.requestId] !== undefined) continue; file.rows[row.requestId] = row; written.push(row.requestId); } return written.length > 0 ? { next: file, result: written } : { result: written }; }); } export function readIntentCredentials(controlDir) { const raw = readStrictSidecar(controlDir, CONSOLIDATION_INTENTS_FILE, "memory consolidation intent-credential account"); return coerceIntentFile(raw).rows; } export function deriveProductSlug(name, fallback) { const base = (name ?? "").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[-.]+|[-.]+$/g, ""); return base.length > 0 ? base : fallback; } export class ConsolidationRefusedError extends Error { code; reasons; occurrenceId; constructor(code, message, reasons = []) { super(reasons.length > 0 ? `${message}\n${reasons.map((r) => ` - ${r}`).join("\n")}` : message); this.code = code; this.reasons = reasons; this.occurrenceId = uuidv7(); } } export function productAddPatch(product) { return { op: "add", id: product.id, entry: product, guard: "absent" }; } export const CONSOLIDATION_RUN_STOP_REASONS = Object.freeze(["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed", "mint_invalidated", "purity_violation"]);