UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

150 lines (149 loc) • 10.9 kB
/** * Site rules: what a handler may hand on and answer. A handler that changes a pinned key on the way down, * or answers outside the station's answer form, breaks the site's contract — a VIOLATION, judged by the * failure column like a crash but never reported as one (no code crashed). */ import { type ModSeat, type ModTier } from "./events.js"; /** The host's Proxy probe (this folder cannot recognise a Proxy on its own): with one, a handler's Proxy is refused * before any of its traps runs; without one, it is read like any object. */ export type ProxyProbe = (value: object) => boolean; /** * The one depth the bus holds a handler's values to: how many container levels a value may have below a payload member * or an answer's top (1 = the member itself). Every value a handler gives the bus — what it hands to `next`, a `$` * call's arguments, its answer — is received in a mode bounded by it (a shared reference measured again where it sits * each time it is met), so a value nested deeper is refused by the first walk that meets it before that * walk goes any deeper. Every walk keeps its own stack, so neither the platform nor the caller's stack depth decides. * The pinned-key comparison is held to the same depth. A container is one level wherever it is met, so the verdict * does not depend on member order for a value without a cycle; through a cycle, depth is measured on the walk's way (a * container still being walked counts as one level where it is met again). The engine's own event is received at any * depth, and its objects — handed back anywhere in what a handler hands on, answers or passes to a `$` call — are taken * as themselves, never walked: one of its containers is one level where it sits, whatever it holds. */ export declare const MODS_VALUE_DEPTH = 1000; /** Why a handler did not answer for itself: it threw, it ran past its budget, it broke the site's contract, or it was * not run because the dispatch re-entered a frame it is part of (its `.catch` answers for it — CC 296 form, (cli296.js:242947) "hasCatch"). */ export declare const MOD_FAILURE_CAUSES: readonly ["threw", "timeout", "violation", "reentry"]; export type ModFailureCause = (typeof MOD_FAILURE_CAUSES)[number]; /** A handler the dispatch skipped (a failure on a skip event), and why. */ export interface ModSkip { readonly registration: string; readonly cause: ModFailureCause; readonly reason: string; } /** An event payload as the kernel carries it: an object, top level frozen by the bus. */ export type ModEventPayload = Readonly<Record<string, unknown>>; /** The registration whose handler gave an answer, as the station's answer check sees it. */ export interface ModAnswerer { readonly plugin: string; readonly seat: ModSeat; readonly tier: ModTier; } /** What an answer check reads, in `below`, for a `next` the handler started that has not answered yet (no `deny`, no `value`). */ export declare const NEXT_IN_FLIGHT: Readonly<{ inFlight: true; }>; /** The station's own reading of an answer: undefined when it is one the station takes, else why not. `below` is every * answer the handler's `next` resolved with, plus `NEXT_IN_FLIGHT` while its `next` is still in flight — * empty when it never called `next`. */ export type ModAnswerCheck = (answer: unknown, e: ModEventPayload, by: ModAnswerer, below: readonly unknown[]) => string | undefined; /** A matcher: top-level fields of `e` that must equal these values exactly. */ export type ModMatcher = Readonly<Record<string, string | number | boolean>>; /** Why a pinned key of what a handler handed on is not taken: it holds another value, or it is a copy the comparison * could not walk to the end (`unproven` says why) — refused all the same, since only a copy proven the same is taken. */ export interface PinnedKeyBreak { readonly key: string; readonly unproven?: string; } /** * The first pinned key `next` did not hand on as the engine's fact, or undefined. A pinned value is compared by value * ({@link sameModValue}): the engine's own fact handed on as is, or a copy of it, is not a change. */ export declare function pinnedKeyChanged(pinned: readonly string[], received: ModEventPayload, passed: ModEventPayload): PinnedKeyBreak | undefined; /** {@link engineObjects} of a dispatch's own event, found the first time it is asked and the same set every time after: * the event is frozen, so one finding serves every receipt of the dispatch. */ export declare function engineObjectsOnce(held: ModEventPayload): () => ReadonlySet<object>; /** * What the next handler reads when a handler hands `passed` to `next` (not the dispatch's own e): `passed` received * whole in the mode held to {@link MODS_VALUE_DEPTH} (every object of the dispatch's own e in it — `engines`, found once * per dispatch by {@link engineObjectsOnce} — kept as itself), then {@link pinnedHandOn}. A value nested past the depth * is refused by the receipt, before any deeper walk — under a pinned key in the comparison's own words (too deep to * compare), under any other member as the payload holding it. */ export declare function handedOn(pinned: readonly string[], held: ModEventPayload, passed: unknown, engines: ReadonlySet<object>, isProxy?: ProxyProbe): { readonly payload: ModEventPayload; } | { readonly violation: string; }; /** * What the next handler reads when a handler hands on `passed` (already received whole): the payload with every * pinned key the engine's own member — the same object `held` carries, or no member where `held` has none — so a * handler below a copy-forwarder reads the engine's fact, not the copy's shape (a member that is `undefined`, a * member order, a −0 the comparison does not count). A pinned key the copy did not hand on as the engine's fact * ({@link pinnedKeyChanged}) breaks the site instead. The comparison decides only whether to refuse; what travels down * for a pinned key is the engine's member, always. The payload keeps the shape it was received in: a reference inside * it to the payload itself is a reference to what is handed on (a cycle stays a cycle), and one to any object inside * what the handler handed on under a pinned key — the copy itself, a member of it, an element of one of its lists, at * any depth — is a reference to the engine's object it was proven the same as (a shared reference stays shared; an * object of the copy standing for two of the engine's goes to the one the comparison met first). `engines`: every * object of `held` (found here when not given). */ export declare function pinnedHandOn(pinned: readonly string[], held: ModEventPayload, passed: ModEventPayload, engines?: ReadonlySet<object>): { readonly payload: ModEventPayload; } | { readonly violation: string; }; /** Why a handler's answer breaks the site, or undefined. No answer at all is never an answer. */ export declare function answerViolation(answer: unknown, check: ModAnswerCheck | undefined, e: ModEventPayload, by: ModAnswerer, below: readonly unknown[]): string | undefined; /** * A `$` method's answer form: `{ value }` (the call's result) or `{ deny }` (the call throws). A `{ deny }` given after * the handler's `next` was answered with something that is not a deny — or while that `next` is still running — breaks * the form (the deny would stand in place of a call that already ran or is running) — a deny handed up from below is * no such thing. A `{ value }` given while that `next` is still running stands in place of the same running call, so it * breaks the form too; one given after `next` answered is the handler rewriting what it read, and stands. Counting the * running `next` is stricter than the reference, which reads answered runs only. */ export declare function methodAnswerViolation(answer: unknown, _e?: ModEventPayload, _by?: ModAnswerer, below?: readonly unknown[]): string | undefined; /** Why `m` is not a matcher, or undefined. */ export declare function matcherViolation(m: unknown): string | undefined; /** Whether every matcher field equals `e`'s own top-level field exactly. */ export declare function matcherMatches(m: ModMatcher | undefined, e: ModEventPayload): boolean; /** A record a handler gives the bus other than through `next` — a `$` call's arguments, and a method's `enter` shaping * of them — received held to {@link MODS_VALUE_DEPTH}; an object of `held` ({@link engineObjectsOnce} of what the bus * already holds: the dispatch the handler runs in, or the call's received arguments) is kept as itself, never walked. */ export declare function receivePayload(v: unknown, isProxy?: ProxyProbe, held?: ReadonlySet<object>): { readonly payload: ModEventPayload; } | { readonly refusal: string; }; /** The engine's own event at a dispatch's entry, received whole at any depth (one the receipt cannot copy whole — * reading it threw where none of its getters runs, or it holds more objects than one receipt can index — is refused, * never handed on shorter). */ export declare function receiveEvent(e: unknown): { readonly payload: ModEventPayload; } | { readonly refusal: string; }; /** * The bottom's answer as it starts up the chain: its top level frozen, so no handler can turn the answer it got from * `next` into another one (a deny in place of a result that already ran). A plain record or array the bottom made is * copied once and frozen; an answer already frozen (an engine sentinel) or not a plain container is itself. */ export declare function answerUpward(v: unknown): unknown; /** A handler's answer, received by the same receipt held to {@link MODS_VALUE_DEPTH} (an answer need not be a record; a * primitive is itself); an object of the dispatch's own event in it (`engines`, {@link engineObjectsOnce}) is kept as * itself, never walked. */ export declare function receiveAnswer(answer: unknown, engines: ReadonlySet<object>, isProxy?: ProxyProbe): { readonly answer: unknown; } | { readonly refusal: string; }; /** * A copy for an observe handler, made by the one receipt (its own stack, so no depth of the event decides whether an * observer runs — the event it observes was received at any depth too): every data container (a plain object, an * ordinary array) detached at any depth; then every other object the copy holds — a tool result's `Map`, `Date` or * typed array — detached by a structured clone of that object alone (one clone per object, so a shared one stays shared), * and one the clone refuses (a function, a handle, a class instance holding one) handed as is. The engine's own event * holds data properties only, so the copy is whole; a receipt that could not make it (a Proxy whose trap throws, on a bus * given no probe) is the observer's failure, reported like any other. */ export declare function detachedCopy(e: ModEventPayload): Record<string, unknown>;