@sema-agent/core
Version:
Stateless, task-oriented AI agent core
340 lines (339 loc) • 16.1 kB
JavaScript
import { types } from "node:util";
import { describeFault, isPlainRecord, receive } from "../core/mods/kernel/receive.js";
import { egressText } from "../core/untrusted-egress.js";
import { bandOf, isBandId } from "./bands.js";
import { SEMA_DEFAULT_CATALOG, catalogMessage } from "./catalog/index.js";
import { HOST_FORMS, HOST_ROLES, OVERLAY_OPS, PROMPT_FACT_NAMES, PROMPT_TRAITS, isWordOf, } from "./catalog/schema.js";
import { RENDERERS } from "./renderers.js";
import { canonicalWhen } from "./select.js";
export const OVERLAY_SOURCE_ORDER = Object.freeze(["bundled", "task", "center", "deploy"]);
export const ABSENT_THIS_RUN = "(absent this run)";
const SECTION_BY_ID = new Map(SEMA_DEFAULT_CATALOG.sections.map((s) => [s.id, s]));
const MESSAGE_TARGETS = (() => {
const m = new Map();
for (const s of SEMA_DEFAULT_CATALOG.sections) {
const ids = "message" in s.body ? [s.body.message] : "lines" in s.body ? s.body.lines.map((l) => l.message) : (RENDERERS[s.body.renderer].messages ?? []);
for (const id of ids)
m.set(id, { section: s });
}
return m;
})();
const MESSAGE_IDS = new Set(SEMA_DEFAULT_CATALOG.messages.map((m) => m.id));
export function sectionOfMessage(id) {
return MESSAGE_TARGETS.get(id)?.section;
}
const LOCKED = Object.freeze(SEMA_DEFAULT_CATALOG.sections
.filter((s) => s.band === "constitution" && !("renderer" in s.body))
.flatMap((s) => ("message" in s.body ? [s.body.message] : "lines" in s.body ? s.body.lines.map((l) => l.message) : []))
.flatMap((id) => catalogMessage(id).variants.map((v) => Object.freeze({ message: id, variant: v.id, text: v.text }))));
export function lockedEntries() {
return LOCKED;
}
export function lockedTextIn(text) {
const hits = [];
for (const e of LOCKED)
if (text.includes(e.text))
hits.push(`${e.message}#${e.variant}`);
return hits;
}
export function assembledRoleRefused(field, hits) {
const named = [...new Set(hits.map((h) => h.split("#")[0]))];
return Object.assign(new Error(`prompt.assembled_role_refused: ${field} contains, verbatim, the locked text of ${named.slice(0, 4).join(", ")}${named.length > 4 ? ` and ${named.length - 4} more` : ""} — the engine composes the locked sections itself; supply only the role layer (an already-assembled prompt is refused, never passed through)`), { code: "prompt.assembled_role_refused" });
}
const ID_GRAMMAR = /^[a-z0-9][a-z0-9/._-]*$/;
const OVERLAY_KEYS = new Set(["id", "op", "target", "text", "message", "when"]);
const OPS = new Set(OVERLAY_OPS);
const PROMPT_DATA = Object.freeze({ accessors: "refuse", depth: 4, others: "refuse", freeze: true, isProxy: (v) => types.isProxy(v) });
export function receivePromptData(raw) {
const r = receive(raw, PROMPT_DATA);
return r.ok ? { value: r.value } : { notData: describeFault(r.fault) };
}
export const PROMPT_BINDING_MAX_CHARS = 128;
export const PROMPT_BINDING_LIST_MAX = 64;
export function codePointsOver(s, max) {
if (s.length <= max)
return false;
if (s.length > 2 * max)
return true;
let n = 0;
for (const _ of s)
if (++n > max)
return true;
return false;
}
function bindingProblem(v) {
return typeof v !== "string" || v.trim() === "" || codePointsOver(v, PROMPT_BINDING_MAX_CHARS) ? `${str(v)} is not a non-blank string of at most ${PROMPT_BINDING_MAX_CHARS} characters (code points)` : undefined;
}
function bindingConditionProblem(b) {
if (!isPlainRecord(b))
return `"when.binding" is ${Array.isArray(b) ? "an array" : str(b)}, not { anyOf: [...] } or { noneOf: [...] }`;
const keys = Object.keys(b);
const k = keys[0];
if (keys.length !== 1 || (k !== "anyOf" && k !== "noneOf"))
return `"when.binding" carries ${keys.length === 0 ? "no list" : keys.map((x) => str(x)).join(", ")} (exactly one of anyOf, noneOf)`;
const list = b[k];
if (!Array.isArray(list) || list.length === 0)
return `"when.binding.${k}" is ${Array.isArray(list) ? "empty" : str(list)}, not a list naming at least one binding`;
if (list.length > PROMPT_BINDING_LIST_MAX)
return `"when.binding.${k}" names ${list.length} bindings (at most ${PROMPT_BINDING_LIST_MAX})`;
for (const name of list) {
const why = bindingProblem(name);
if (why !== undefined)
return `"when.binding.${k}": ${why}`;
}
if (new Set(list).size !== list.length)
return `"when.binding.${k}" names a binding twice`;
return undefined;
}
export function resolvePromptBinding(task) {
if (task.promptBinding === undefined)
return undefined;
const why = bindingProblem(task.promptBinding);
if (why !== undefined)
throw Object.assign(new Error(`TaskSpec.promptBinding ${why} (or absent)`), { code: "config.prompt_selector_invalid" });
return task.promptBinding;
}
function str(v, max = 160) {
if (typeof v !== "string")
return typeof v;
const e = egressText("same-principal", v);
return JSON.stringify(e.length > max ? `${e.slice(0, max)}…` : e);
}
function whenViolation(when) {
if (typeof when !== "object" || when === null || Array.isArray(when))
return `"when" is ${str(when)}, not an object`;
for (const k of Object.keys(when))
if (k !== "traits" && k !== "host" && k !== "facts" && k !== "binding")
return `"when" carries ${str(k)} (traits, host, facts, binding only)`;
const w = when;
if (w["traits"] !== undefined) {
const t = w["traits"];
if (typeof t !== "object" || t === null || Array.isArray(t))
return `"when.traits" is not an object`;
for (const [k, list] of Object.entries(t)) {
if (k !== "allOf" && k !== "noneOf")
return `"when.traits" carries ${str(k)} (allOf, noneOf only)`;
if (!Array.isArray(list))
return `"when.traits.${k}" is not an array`;
for (const name of list)
if (!isWordOf(PROMPT_TRAITS, name))
return `"when.traits.${k}" names ${str(name)}, not a trait (${PROMPT_TRAITS.join(", ")})`;
}
}
if (w["host"] !== undefined) {
const h = w["host"];
if (typeof h !== "object" || h === null || Array.isArray(h))
return `"when.host" is not an object`;
for (const [k, v] of Object.entries(h)) {
if (k === "role" ? !isWordOf(HOST_ROLES, v) : k === "form" ? !isWordOf(HOST_FORMS, v) : true) {
return `"when.host" carries ${str(k)} = ${str(v)} (role: ${HOST_ROLES.join(" | ")}; form: ${HOST_FORMS.join(" | ")})`;
}
}
}
if (w["facts"] !== undefined) {
const f = w["facts"];
if (typeof f !== "object" || f === null || Array.isArray(f))
return `"when.facts" is not an object`;
for (const [k, v] of Object.entries(f)) {
if (!isWordOf(PROMPT_FACT_NAMES, k))
return `"when.facts" names ${str(k)}, not a fact`;
if (typeof v !== "boolean")
return `"when.facts.${k}" is ${str(v)}, not a boolean`;
}
}
if (w["binding"] !== undefined)
return bindingConditionProblem(w["binding"]);
return undefined;
}
function textsOf(o) {
if (o.text !== undefined)
return [o.text];
if (o.message !== undefined && MESSAGE_IDS.has(o.message))
return catalogMessage(o.message).variants.map((v) => v.text);
return [];
}
export function validateOverlay(raw, ctx) {
const fail = (why) => ({ violation: why });
const received = receivePromptData(raw);
if ("notData" in received)
return fail(`an overlay is data (plain objects, arrays, strings, booleans); it holds ${received.notData}`);
const r = received.value;
if (!isPlainRecord(r))
return fail(`an overlay is an object, got ${Array.isArray(r) ? "an array" : str(r)}`);
for (const k of Object.keys(r)) {
if (OVERLAY_KEYS.has(k))
continue;
const rewrite = k === "scope" ? `; an overlay names its binding as "when": { "binding": { "anyOf": [<binding>] } }` : "";
return fail(`an overlay carries ${str(k)} (id, op, target, text, message, when only)${rewrite}`);
}
const { id, op, target, text, message, when } = r;
if (typeof id !== "string" || id.length === 0 || codePointsOver(id, 128) || !ID_GRAMMAR.test(id))
return fail(`overlay id ${str(id)} is not ^[a-z0-9][a-z0-9/._-]*$ of at most 128 characters`);
if (id.startsWith("core/") && ctx.sugar !== true)
return fail(`overlay id ${str(id)}: the core/ namespace is the engine's own`);
if (typeof op !== "string" || !OPS.has(op))
return fail(`overlay "${id}": op ${str(op)} is not one of ${OVERLAY_OPS.join(", ")}`);
if (typeof target !== "string" || target.length === 0)
return fail(`overlay "${id}": target ${str(target)} is not an id`);
const carries = op !== "drop";
if (!carries && (text !== undefined || message !== undefined))
return fail(`overlay "${id}": a drop carries no text or message`);
if (carries && (text === undefined) === (message === undefined))
return fail(`overlay "${id}": a ${op} carries exactly one of text, message`);
if (text !== undefined && (typeof text !== "string" || text.trim() === ""))
return fail(`overlay "${id}": text is ${typeof text === "string" ? "blank" : str(text)}`);
if (message !== undefined && (typeof message !== "string" || !MESSAGE_IDS.has(message)))
return fail(`overlay "${id}": message ${str(message)} is not a catalog message`);
if (when !== undefined) {
const why = whenViolation(when);
if (why !== undefined)
return fail(`overlay "${id}": ${why}`);
}
let band;
let where;
if (op === "add") {
if (!isBandId(target))
return fail(`overlay "${id}": an add targets a band; ${str(target)} is not one`);
if (bandOf(target).addRank === undefined)
return fail(`overlay "${id}": band "${target}" takes no additions`);
if (SECTION_BY_ID.has(id) || MESSAGE_IDS.has(id))
return fail(`overlay "${id}": an added entry cannot take a catalog id`);
band = target;
where = `band "${target}"`;
}
else if (op === "drop") {
const section = SECTION_BY_ID.get(target);
const added = ctx.lowerAdds?.get(target);
if (section !== undefined)
band = section.band;
else if (added !== undefined)
band = added;
else
return fail(`overlay "${id}": drop target ${str(target)} is neither a catalog section nor an entry a lower source added`);
where = `"${target}" (band "${band}")`;
}
else {
const placed = MESSAGE_TARGETS.get(target);
if (placed === undefined)
return fail(`overlay "${id}": ${op} target ${str(target)} is not a message the catalog places`);
band = placed.section.band;
where = `"${target}" (band "${band}")`;
}
const row = bandOf(band);
if (!row.ops[ctx.source].includes(op)) {
return fail(`overlay "${id}": a ${ctx.source} overlay may not ${op} ${where} (the band grants ${ctx.source} ${row.ops[ctx.source].length > 0 ? row.ops[ctx.source].join(", ") : "nothing"})`);
}
if (ctx.event !== undefined && row.modsEvent !== ctx.event)
return fail(`overlay "${id}": ${where} is written through ${row.modsEvent ?? "no mods event"}, not ${ctx.event}`);
const normalWhen = canonicalWhen(when);
const overlay = Object.freeze({
id,
op: op,
target,
...(text !== undefined ? { text: text } : {}),
...(message !== undefined ? { message: message } : {}),
...(normalWhen !== undefined ? { when: normalWhen } : {}),
});
for (const t of textsOf(overlay)) {
const hits = lockedTextIn(t);
if (hits.length > 0)
return fail(`overlay "${id}": its text contains, verbatim, the locked text of ${[...new Set(hits.map((h) => h.split("#")[0]))].join(", ")}`);
}
return { overlay };
}
export function foldOverlays(ordered, selectedText, applies, overlayText) {
const messageText = new Map();
const writers = new Map();
const dropped = new Map();
const added = new Map();
const addedBy = new Map();
const addCount = new Map();
const superseded = [];
const chains = new Map();
const supersede = (w, target, by) => {
superseded.push(Object.freeze({ id: w.id, source: w.origin.source, by: w.origin.by, target, supersededBy: by }));
if (w.link !== undefined)
w.link.superseded = true;
};
const applied = (key, o, origin) => {
const link = { source: origin.source, overlay: o.id, op: o.op };
chains.set(key, [...(chains.get(key) ?? []), link]);
return { id: o.id, origin, link };
};
const sectionMessages = (sectionId) => [...MESSAGE_TARGETS.entries()].filter(([, t]) => t.section.id === sectionId).map(([m]) => m);
for (const { overlay: o, origin } of ordered) {
if (!applies(o))
continue;
const me = { id: o.id, origin };
const text = o.op === "drop" ? "" : overlayText(o);
if (text === undefined) {
supersede(me, o.target, ABSENT_THIS_RUN);
continue;
}
if (o.op === "add") {
const prior = addedBy.get(o.id);
if (prior !== undefined)
supersede(prior, o.target, o.id);
const band = o.target;
const k = addCount.get(band) ?? 0;
addCount.set(band, k + 1);
added.set(o.id, Object.freeze({ id: o.id, band, rank: bandOf(band).addRank + k * 0.001, text, origin }));
addedBy.set(o.id, applied(o.id, o, origin));
continue;
}
if (o.op === "drop") {
const entry = addedBy.get(o.target);
if (entry !== undefined) {
supersede(entry, o.target, o.id);
added.delete(o.target);
addedBy.delete(o.target);
continue;
}
if (!SECTION_BY_ID.has(o.target)) {
supersede(me, o.target, ABSENT_THIS_RUN);
continue;
}
for (const m of sectionMessages(o.target)) {
for (const w of writers.get(m) ?? [])
supersede(w, m, o.id);
writers.delete(m);
messageText.delete(m);
}
dropped.set(o.target, applied(o.target, o, origin));
continue;
}
const section = MESSAGE_TARGETS.get(o.target).section;
const drop = dropped.get(section.id);
if (o.op === "append" && drop !== undefined) {
supersede(me, o.target, drop.id);
continue;
}
if (selectedText(o.target) === undefined && !messageText.has(o.target)) {
supersede(me, o.target, ABSENT_THIS_RUN);
continue;
}
if (drop !== undefined) {
supersede(drop, section.id, o.id);
dropped.delete(section.id);
}
const writer = applied(section.id, o, origin);
if (o.op === "replace") {
for (const w of writers.get(o.target) ?? [])
supersede(w, o.target, o.id);
writers.set(o.target, [writer]);
messageText.set(o.target, { text, origin, replaced: true });
}
else {
const base = messageText.get(o.target)?.text ?? selectedText(o.target);
writers.set(o.target, [...(writers.get(o.target) ?? []), writer]);
messageText.set(o.target, { text: `${base}\n\n${text}`, origin, replaced: messageText.get(o.target)?.replaced ?? false });
}
}
return {
messageText,
dropped: new Set(dropped.keys()),
added: [...added.values()],
superseded,
chains: new Map([...chains].map(([k, links]) => [k, Object.freeze(links.map((l) => Object.freeze({ ...l })))])),
};
}