@sema-agent/core
Version:
Stateless, task-oriented AI agent core
103 lines (102 loc) • 4.63 kB
JavaScript
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;
}