@sema-agent/core
Version:
Stateless, task-oriented AI agent core
297 lines (296 loc) • 12.7 kB
JavaScript
import { describe } from "./events.js";
import { describeFault, isPlainRecord, MODS_PAYLOAD_MODE, receive } from "./receive.js";
export const MODS_VALUE_DEPTH = 1_000;
const HANDED_MODE = Object.freeze({ ...MODS_PAYLOAD_MODE, depth: MODS_VALUE_DEPTH + 1, past: "refuse" });
function payloadMode(isProxy) {
return isProxy === undefined ? HANDED_MODE : Object.freeze({ ...HANDED_MODE, isProxy });
}
const TOO_DEEP_TO_COMPARE = `too deep to compare (nested past ${MODS_VALUE_DEPTH} levels)`;
function faultClause(fault) {
if (fault.kind !== "too-deep")
return describeFault(fault);
return `a value nested past ${MODS_VALUE_DEPTH} levels${fault.path.length > 0 ? ` under "${fault.path[0]}"` : ""}`;
}
export const MOD_FAILURE_CAUSES = Object.freeze(["threw", "timeout", "violation", "reentry"]);
export const NEXT_IN_FLIGHT = Object.freeze({ inFlight: true });
const TOO_DEEP = Object.freeze({ tooDeep: true });
const OPEN = "open";
function sameModValue(a0, b0, paired, depth0, homes) {
const stack = [];
const named = (r) => Object.keys(r).filter((k) => r[k] !== undefined);
const meet = (a, b, depth) => {
if (a === b || (Number.isNaN(a) && Number.isNaN(b)))
return 0;
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null)
return false;
let row = paired.get(a);
const met = row?.get(b);
if (met !== undefined) {
const height = met === OPEN ? 1 : met;
if (depth + height - 1 > MODS_VALUE_DEPTH)
throw TOO_DEEP;
return height;
}
if (depth > MODS_VALUE_DEPTH)
throw TOO_DEEP;
if (row === undefined)
paired.set(a, (row = new Map()));
row.set(b, OPEN);
if (!homes.has(b))
homes.set(b, a);
if (Array.isArray(a) || Array.isArray(b)) {
if (!Array.isArray(a) || !Array.isArray(b) || Object.getPrototypeOf(a) !== Array.prototype || Object.getPrototypeOf(b) !== Array.prototype || a.length !== b.length)
return false;
}
else if (!isPlainRecord(a) || !isPlainRecord(b))
return false;
const ra = a;
const rb = b;
const names = named(ra);
if (names.length !== named(rb).length)
return false;
stack.push({ a: ra, b: rb, row, names, depth, next: 0, height: 1 });
return undefined;
};
const top = meet(a0, b0, depth0);
if (top !== undefined)
return top;
for (;;) {
const f = stack[stack.length - 1];
if (f.next < f.names.length) {
const k = f.names[f.next++];
if (!Object.hasOwn(f.b, k))
return false;
const below = meet(f.a[k], f.b[k], f.depth + 1);
if (below === false)
return false;
if (below !== undefined)
f.height = Math.max(f.height, below + 1);
continue;
}
f.row.set(f.b, f.height);
stack.pop();
const holder = stack[stack.length - 1];
if (holder === undefined)
return f.height;
holder.height = Math.max(holder.height, f.height + 1);
}
}
export function pinnedKeyChanged(pinned, received, passed) {
const compared = comparePinned(pinned, received, passed);
return "broke" in compared ? compared.broke : undefined;
}
function comparePinned(pinned, received, passed) {
const paired = new Map();
const homes = new Map();
for (const key of pinned) {
try {
if (sameModValue(received[key], passed[key], paired, 1, homes) === false)
return { broke: { key } };
}
catch (err) {
return { broke: { key, unproven: err === TOO_DEEP ? TOO_DEEP_TO_COMPARE : "the comparison could not walk it to the end" } };
}
}
return { homes };
}
function engineObjects(held) {
const found = new Set();
const stack = Object.values(held);
while (stack.length > 0) {
const v = stack.pop();
if (typeof v !== "object" || v === null || found.has(v) || !Object.isFrozen(v))
continue;
if (!isPlainRecord(v) && !(Array.isArray(v) && Object.getPrototypeOf(v) === Array.prototype))
continue;
found.add(v);
for (const k of Object.keys(v))
stack.push(v[k]);
}
return found;
}
export function engineObjectsOnce(held) {
let found;
return () => (found ??= engineObjects(held));
}
const unprovenCopy = (key, why) => `next() handed on a copy of the pinned key "${key}" that is ${why}; a copy is taken only when proven the same`;
export function handedOn(pinned, held, passed, engines, isProxy) {
const received = receiveRecord(passed, payloadMode(isProxy), engines);
if ("refusal" in received) {
const key = received.fault?.kind === "too-deep" ? received.fault.path[0] : undefined;
return { violation: key !== undefined && pinned.includes(key) ? unprovenCopy(key, TOO_DEEP_TO_COMPARE) : `next() takes the event object: ${received.refusal}` };
}
return pinnedHandOn(pinned, held, received.payload, engines);
}
export function pinnedHandOn(pinned, held, passed, engines) {
const compared = comparePinned(pinned, held, passed);
if ("broke" in compared) {
return { violation: compared.broke.unproven === undefined ? `next() changed the pinned key "${compared.broke.key}"` : unprovenCopy(compared.broke.key, compared.broke.unproven) };
}
if (pinned.every((k) => Object.hasOwn(passed, k) === Object.hasOwn(held, k) && Object.is(passed[k], held[k])))
return { payload: passed };
return { payload: rehomed(pinned, held, passed, compared.homes, engines ?? engineObjects(held)) };
}
function rehomed(pinned, held, passed, homes, engines) {
const swap = new Map();
for (const [copy, home] of homes)
if (copy !== passed && !engines.has(copy))
swap.set(copy, home);
const isContainer = (v) => typeof v === "object" && v !== null && !engines.has(v) && !swap.has(v) && (isPlainRecord(v) || (Array.isArray(v) && Object.getPrototypeOf(v) === Array.prototype));
const holders = new Map();
const walked = new Set([passed]);
const stack = [passed];
while (stack.length > 0) {
const node = stack.pop();
for (const k of Object.keys(node)) {
if (node === passed && pinned.includes(k))
continue;
const v = node[k];
if (v !== passed && !swap.has(v) && !isContainer(v))
continue;
const by = holders.get(v);
if (by === undefined)
holders.set(v, [node]);
else
by.push(node);
if (isContainer(v) && !walked.has(v)) {
walked.add(v);
stack.push(v);
}
}
}
const twins = new Map([[passed, {}]]);
const reach = [passed, ...swap.keys()];
while (reach.length > 0) {
for (const h of holders.get(reach.pop()) ?? []) {
if (twins.has(h))
continue;
twins.set(h, Array.isArray(h) ? [] : {});
reach.push(h);
}
}
const put = (to, k, v) => void Object.defineProperty(to, k, { value: v, enumerable: true, writable: true, configurable: true });
const moved = (v) => (typeof v !== "object" || v === null ? v : swap.has(v) ? swap.get(v) : (twins.get(v) ?? v));
for (const [from, to] of twins) {
const r = from;
for (const k of Object.keys(r)) {
if (from !== passed || !pinned.includes(k))
put(to, k, moved(r[k]));
else if (Object.hasOwn(held, k))
put(to, k, held[k]);
}
if (from === passed)
for (const k of pinned)
if (Object.hasOwn(held, k) && !Object.hasOwn(to, k))
put(to, k, held[k]);
if (Array.isArray(from) && Array.isArray(to))
to.length = from.length;
}
for (const to of twins.values())
Object.freeze(to);
return twins.get(passed);
}
export function answerViolation(answer, check, e, by, below) {
if (answer === undefined)
return "returned no answer (return next(e) to pass the event on)";
if (check === undefined)
return undefined;
try {
return check(answer, e, by, below);
}
catch {
return "returned an answer the station cannot read";
}
}
const denyOf = (a) => (typeof a === "object" && a !== null && Object.prototype.hasOwnProperty.call(a, "deny") ? Reflect.get(a, "deny") : undefined);
export function methodAnswerViolation(answer, _e, _by, below = []) {
if (!isPlainRecord(answer))
return "takes { value } or { deny }";
const hasValue = Object.prototype.hasOwnProperty.call(answer, "value");
const deny = denyOf(answer);
if (deny !== undefined) {
if (hasValue || typeof deny !== "string" || deny === "")
return "takes { value } or { deny: <reason> }";
return below.some((b) => denyOf(b) === undefined) ? "a deny after its next() was answered or while it still runs (deny in place of next)" : undefined;
}
if (!hasValue)
return "takes { value } or { deny }";
return below.includes(NEXT_IN_FLIGHT) ? "a value while its next() still runs (value in place of next)" : undefined;
}
export function matcherViolation(m) {
if (typeof m !== "object" || m === null || Array.isArray(m))
return `a matcher is an object of top-level fields, not ${describe(m)}`;
for (const [k, v] of Object.entries(m)) {
if (typeof v !== "string" && typeof v !== "number" && typeof v !== "boolean")
return `matcher field "${k}" is ${describe(v)}; a matcher value is a string, a number or a boolean`;
}
return undefined;
}
export function matcherMatches(m, e) {
if (m === undefined)
return true;
return Object.entries(m).every(([k, v]) => Object.prototype.hasOwnProperty.call(e, k) && e[k] === v);
}
function receiveRecord(v, mode, held) {
if (mode.isProxy !== undefined && typeof v === "object" && v !== null && mode.isProxy(v))
return { refusal: "the payload is a Proxy (refused before any of its traps runs)" };
if (!isPlainRecord(v))
return { refusal: `an event payload is a plain object, not ${describe(v)}` };
const r = receive(v, mode, held);
return r.ok ? { payload: Object.freeze(r.value) } : { refusal: `the payload holds ${faultClause(r.fault)}`, fault: r.fault };
}
export function receivePayload(v, isProxy, held) {
const r = receiveRecord(v, payloadMode(isProxy), held);
return "refusal" in r ? { refusal: r.refusal } : r;
}
export function receiveEvent(e) {
const r = receiveRecord(e, MODS_PAYLOAD_MODE);
return "refusal" in r ? { refusal: r.refusal } : r;
}
export function answerUpward(v) {
if (typeof v !== "object" || v === null || Object.isFrozen(v))
return v;
if (Array.isArray(v) && Object.getPrototypeOf(v) === Array.prototype)
return Object.freeze([...v]);
return isPlainRecord(v) ? Object.freeze({ ...v }) : v;
}
export function receiveAnswer(answer, engines, isProxy) {
const r = receive(answer, payloadMode(isProxy), engines);
return r.ok ? { answer: r.value } : { refusal: `answered ${faultClause(r.fault)}` };
}
const DETACHED_MODE = Object.freeze({ accessors: "refuse", depth: Infinity, others: "as-is" });
export function detachedCopy(e) {
const r = receive(e, DETACHED_MODE);
if (!r.ok)
throw new Error(`the observer's copy of the event could not be made: ${describeFault(r.fault)}`);
const copy = r.value;
const clones = new Map();
const walked = new Set([copy]);
const stack = [copy];
while (stack.length > 0) {
const node = stack.pop();
for (const k of Object.keys(node)) {
const v = node[k];
if (typeof v !== "object" || v === null)
continue;
if (isPlainRecord(v) || (Array.isArray(v) && Object.getPrototypeOf(v) === Array.prototype)) {
if (!walked.has(v)) {
walked.add(v);
stack.push(v);
}
continue;
}
if (!clones.has(v)) {
let own = v;
try {
own = structuredClone(v);
}
catch {
}
clones.set(v, own);
}
Object.defineProperty(node, k, { value: clones.get(v), enumerable: true, writable: true, configurable: true });
}
}
return copy;
}