UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

297 lines (296 loc) • 12.7 kB
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; }