UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

89 lines (88 loc) • 5.08 kB
import { assertContainmentFaceValue } from "../tools/fs/containment-face.js"; import { assertContextSettingsValue } from "./context-settings.js"; import { READ_DENY_DEPLOYMENT_SEATS, screenReadDenySeat } from "./read-face-seats.js"; import { deliverEngineNotice } from "./engine-notice.js"; import { uuidv7 } from "../internal/harness.js"; export const SWAPPABLE_DEP_SEATS = Object.freeze(["models", "tiers", "readFace", "writeFace", "contextSettings", "readDenyPatterns", "readDenyBuiltinTiers", "readDenyBuiltinExclude"]); const SWAPPABLE_DEP_SEAT_SET = new Set(SWAPPABLE_DEP_SEATS); export function isSwappableDepSeat(k) { return SWAPPABLE_DEP_SEAT_SET.has(k); } export const SWAP_SEAT_NOTICE = { models: "config.models_swapped", tiers: "config.models_swapped", readFace: "config.read_face_swapped", writeFace: "config.write_face_swapped", contextSettings: "config.context_settings_swapped", readDenyPatterns: "config.read_deny_swapped", readDenyBuiltinTiers: "config.read_deny_swapped", readDenyBuiltinExclude: "config.read_deny_swapped", }; export function screenSwappableDeps(next) { if (next === null || typeof next !== "object" || Array.isArray(next)) { throw new Error(`swapDeps: the candidate must be a plain object naming the seats to swap (got ${next === null ? "null" : Array.isArray(next) ? "array" : typeof next}) — the current deps stay in force`); } const keys = Object.keys(next); const unknown = keys.filter((k) => !isSwappableDepSeat(k)); if (unknown.length > 0) { throw new Error(`swapDeps: ${unknown.map((k) => JSON.stringify(k)).join(", ")} is not a hot-swappable seat (the closed set is ${SWAPPABLE_DEP_SEATS.join(" | ")}) — the current deps stay in force`); } const present = SWAPPABLE_DEP_SEATS.filter((seat) => Object.hasOwn(next, seat)); if (present.length === 0) { throw new Error(`swapDeps: the candidate names no seat (an empty swap is a caller defect, not a no-op) — the current deps stay in force`); } const raw = next; const candidate = {}; for (const seat of present) { const v = raw[seat]; switch (seat) { case "models": if (v === null || typeof v !== "object" || Array.isArray(v)) { throw new Error(`swapDeps: models must be a plain Record<string, Model> (got ${v === null ? "null" : Array.isArray(v) ? "array" : typeof v}) — the current generation stays in force`); } candidate.models = { ...v }; break; case "tiers": if (!present.includes("models")) { throw new Error(`swapDeps: tiers is a binding over models — pass the catalog it binds in the same swap (\`{ models, tiers }\`); the current generation stays in force`); } if (v !== undefined && (v === null || typeof v !== "object" || Array.isArray(v))) { throw new Error(`swapDeps: tiers must be a plain Record<string, ModelRef> or undefined (got ${v === null ? "null" : Array.isArray(v) ? "array" : typeof v}) — the current generation stays in force`); } candidate.tiers = v === undefined ? undefined : { ...v }; break; case "readFace": candidate.readFace = assertContainmentFaceValue(v, "read", "deps"); break; case "writeFace": candidate.writeFace = assertContainmentFaceValue(v, "write", "deps"); break; case "contextSettings": candidate.contextSettings = assertContextSettingsValue(v, "swapDeps"); break; case "readDenyPatterns": case "readDenyBuiltinTiers": case "readDenyBuiltinExclude": candidate[seat] = screenReadDenySeat(seat, v); break; default: { const _exhaustive = seat; throw new Error(`swapDeps: unhandled seat ${String(_exhaustive)}`); } } } return { seats: present, candidate: candidate }; } export function announceReadDenySwap(onNotice, seats, inForce) { const named = READ_DENY_DEPLOYMENT_SEATS.filter((s) => seats.includes(s)); if (named.length === 0) return; const patterns = inForce.readDenyPatterns?.length ?? null; const tiers = inForce.readDenyBuiltinTiers !== undefined ? [...inForce.readDenyBuiltinTiers] : null; const exclude = inForce.readDenyBuiltinExclude?.length ?? null; deliverEngineNotice(onNotice, { code: "config.read_deny_swapped", message: `deployment read-deny declaration swapped (${named.join(", ")}): ${patterns ?? "no"} addition(s), built-in tiers ${tiers === null ? "(default)" : `[${tiers.join(", ")}]`}, ${exclude ?? "no"} built-in row removal(s); in-flight legs keep the deny face they prepared under, new legs compile against the new declaration`, detail: { seats: named, patterns, tiers, exclude, occurrenceId: uuidv7() }, }); }