UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

103 lines (102 loc) • 4.63 kB
import { describe, expandModPattern, MOD_SEATS, readOnSide, SEAT_TIER_TABLE, } from "./events.js"; import { resolveModBudgetMs } from "./budget.js"; export const MODS_INVALID_CODE = "config.mods_invalid"; export const MODS_REGISTRATION_REFUSED_CODE = "mods.registration_refused"; export const ENGINE_ORIGIN = Object.freeze({ plugin: "engine", tier: "core" }); export const MOD_TRACE_OUTCOMES = Object.freeze(["returned", "passed", "rejected", "caught", "expired", "kept", "skipped", "bypassed"]); export function modsInvalid(message) { return Object.assign(new Error(message), { code: MODS_INVALID_CODE }); } const TIER_ORDER = ["prepend", "user", "append"]; export function screenModSeats(seats, side, report) { const out = []; const names = new Set(); for (const seat of MOD_SEATS) { const list = seats[seat]; if (list === undefined) continue; if (!Array.isArray(list)) throw modsInvalid(`the ${seat} seat's mods must be a list of registrations, not ${describe(list)}`); for (const item of list) { const screened = screenOne(item, seat, side, report); if (names.has(screened.name)) throw modsInvalid(`two registrations are named "${screened.name}" — a registration name is unique in the run`); names.add(screened.name); out.push(screened); } } return out.sort((a, b) => TIER_ORDER.indexOf(a.tier) - TIER_ORDER.indexOf(b.tier)); } function isRegistration(v) { return typeof v === "object" && v !== null && typeof Reflect.get(v, "name") === "string" && typeof Reflect.get(v, "register") === "function"; } function screenOne(item, seat, side, report) { if (!isRegistration(item)) throw modsInvalid(`a ${seat} mods entry is ${describe(item)}; a registration has a name and a register function`); const name = item.name; if (name === "") throw modsInvalid(`a ${seat} registration has an empty name`); if (name === ENGINE_ORIGIN.plugin) throw modsInvalid(`a ${seat} registration is named "${name}", the engine's own origin word — choose another name`); const tier = tierOf(item.tier, seat, name); const uses = usesOf(item.uses, name); if (item.provenance !== undefined && typeof item.provenance !== "string") throw modsInvalid(`${name}: provenance is ${describe(item.provenance)}; it is a label (a string)`); const eventsBound = new Set(); for (const p of uses.events) for (const n of namesOf(p, name, "uses.events")) eventsBound.add(n); const callsBound = new Set(); for (const c of uses.calls) for (const n of namesOf(c, name, "uses.calls")) if (readOnSide(n, side)) callsBound.add(n); return { registration: item, name, seat, tier, budgetMs: resolveModBudgetMs(item.budgetMs, report, name), uses, ...(item.provenance !== undefined ? { provenance: item.provenance } : {}), eventsBound, callsBound, }; } function tierOf(declared, seat, name) { const row = SEAT_TIER_TABLE[seat]; const allowed = row.tiers; if (declared === undefined) { if (row.declared === "required") throw modsInvalid(`${name}: a ${seat} registration declares its tier (${allowed.join(", ")})`); return allowed[0]; } const tier = allowed.find((t) => t === declared); if (tier === undefined) throw modsInvalid(`${name}: a ${seat} registration's tier is ${allowed.join(" or ")}, not ${describe(declared)}`); return tier; } function usesOf(uses, name) { if (typeof uses !== "object" || uses === null) throw modsInvalid(`${name}: uses is ${describe(uses)}; it declares { events, calls }`); const events = stringList(Reflect.get(uses, "events"), name, "uses.events"); const calls = stringList(Reflect.get(uses, "calls"), name, "uses.calls"); return Object.freeze({ events, calls }); } function stringList(v, name, field) { if (!Array.isArray(v)) throw modsInvalid(`${name}: ${field} is ${describe(v)}; it is a list of event names`); const out = []; for (const x of v) { if (typeof x !== "string") throw modsInvalid(`${name}: ${field} holds ${describe(x)}; it is a list of event names`); out.push(x); } return Object.freeze(out); } function namesOf(pattern, name, field) { const expanded = expandModPattern(pattern); if ("refusal" in expanded) throw modsInvalid(`${name}: ${field}: ${expanded.refusal}`); return expanded.names; }