@sema-agent/core
Version:
Stateless, task-oriented AI agent core
95 lines (94 loc) • 7.39 kB
TypeScript
/**
* The ONE receipt of a caller's object: how a value someone else's code built becomes a value the receiver owns.
* Every reader of a caller's object reads it here — a constructor's configuration (`new Runner`'s deps, a brain's
* config), a station's answer record, a mods handler's payload and answer — each with its own mode, never its own
* copy of the walk.
*
* The walk: a primitive is itself. A CONTAINER — an ordinary record (prototype `Object.prototype` or none) or an
* ordinary array — within `depth` levels is copied member by member off its own property descriptors, captured in
* one call before any of the caller's code can run; each member lands as an enumerable data property of the copy (a
* defined property, never an assignment, so an own `__proto__` key stays a plain key). Anything else — a function, an
* object of another prototype, a container past `depth` — is handed as it is, refused, or (an object of another
* prototype under `copy`) copied off its own members like a record, by `others`, which also says whether cycles and
* shared references keep their shape or are refused / walked again. A refusal refuses the whole receipt, or — under
* `contain` — only the member it was met in. The walk keeps its own stack (one frame per container being copied), so
* neither how deep a value nests nor how deep its caller already stands decides a verdict: only `depth` does.
*
* Zero-dependency on purpose (this folder compiles on its own): a Proxy cannot be recognised from here, so the
* host passes its probe (`isProxy`); with a probe, a Proxy is refused before anything is asked of it.
*/
/** How a receipt reads. */
export interface ReceiptMode {
/**
* `call-once`: every own string-keyed member is read — a data property's value, or an accessor's getter called exactly
* once on the object (a getter that throws refuses the receipt; one that moves the object's prototype refuses it, but
* under `others: "copy"`).
* `refuse`: only own enumerable string-keyed DATA properties are members; an accessor, a non-enumerable property or
* a symbol-keyed one refuses the receipt (no getter ever runs).
*/
readonly accessors: "call-once" | "refuse";
/** How many levels of containers are copied (`Infinity` = every level). */
readonly depth: number;
/** What a value that is not copied is (a function, another prototype, a container past `depth`, a symbol or a
* bigint): handed as it is, or a refusal of the receipt. Shared references keep their shape in the copy (one copy
* each); under `as-is` a cycle does too, under `refuse` a cycle is refused and a shared reference must fit `depth`
* where it sits each time it is met. `copy` is `refuse`, except that an object of another prototype within `depth`
* — a class instance, another realm's object or array, an array of a subclass — is copied as an ordinary record (an
* array as an ordinary array) off its OWN members: its prototype is not kept and no code of the prototype runs, so a
* prototype's getters and a built-in's internal state (a `Date`'s time, a `Map`'s entries) are not members and are
* not copied, while every own data member is (and an own accessor is read as `accessors` says; one that moves the
* object's prototype changes nothing the copy holds). A Proxy and a cycle are still refused. */
readonly others: "as-is" | "refuse" | "copy";
/** `refuse`: a container (an ordinary record or array) past `depth` refuses the receipt (`too-deep`) even where
* `others` hands other values as they are — a value of another kind there is still what `others` says — and a shared
* reference must fit `depth` where it sits each time it is met, by the height its first walk found (a walk that ran
* into a container still being walked — a cycle — counts that container as one level, and so does an object the
* reader holds, met directly or in what holds it): `depth` is a bound, never a copy depth. Absent ⇒ a container past
* `depth` is what `others` says. */
readonly past?: "refuse";
/** A fault met in a member — its getter threw, it is a value the receipt refuses, it holds a cycle, it runs too deep,
* or it could not be copied at all (`uncopyable`) — leaves THAT member out of its container's copy and is reported in the
* receipt's `faults` (where it was met); the walk goes on with the next member. A container refused where it was met
* is refused again wherever else it is met (never read twice). Only a fault of the received value itself refuses the
* receipt. Absent ⇒ the first fault refuses the receipt. */
readonly contain?: true;
/** Every container the copy makes is frozen (a received value cannot be changed after it was received). */
readonly freeze?: boolean;
/** The host's Proxy probe. */
readonly isProxy?: (value: object) => boolean;
}
/** Why a receipt was refused. `uncopyable` has two causes, and depth is neither (the walk keeps its own stack): reading
* the value threw where none of its getters runs — a Proxy's trap, on a receipt given no Proxy probe; what it threw is
* never read — or the value holds more objects than one receipt can index. */
export declare const RECEIPT_FAULTS: readonly ["proxy", "accessor", "hidden", "getter-threw", "prototype-moved", "not-data", "cycle", "too-deep", "uncopyable"];
export type ReceiptFaultKind = (typeof RECEIPT_FAULTS)[number];
/** A refusal and where in the value it was met (member names from the top). */
export interface ReceiptFault {
readonly kind: ReceiptFaultKind;
readonly path: readonly string[];
}
/** A receipt: the copy (with, under `contain`, every fault a member was left out for), or the refusal. */
export type Receipt = {
readonly ok: true;
readonly value: unknown;
readonly faults?: readonly ReceiptFault[];
} | {
readonly ok: false;
readonly fault: ReceiptFault;
};
/** Whether `v` is an ordinary record: an object whose prototype is `Object.prototype` or none (an array is not one). */
export declare function isPlainRecord(v: unknown): v is Record<string, unknown>;
/**
* Receive `value` by `mode`. `held` names objects the reader already owns: met in the walk, each is kept as itself — not
* received again, never walked — so a member handed back unchanged keeps its identity. The set is consulted as it is,
* never copied, so one set serves every receipt of the same owner.
*/
export declare function receive(value: unknown, mode: ReceiptMode, held?: ReadonlySet<object>): Receipt;
/** The mods bus's mode: a payload and an answer received whole — no accessor runs, no depth limit, cycles and shared
* references kept, a function or a class instance handed as it is, every copied container frozen (so a member the
* bus holds cannot be changed under it by a handler that holds it too). The engine's own event is received in it as it
* is; what a handler gives the bus — a payload, an answer, a `$` call's arguments — in it held to the bus's one depth
* (the site rules set `depth` and `past: "refuse"`). */
export declare const MODS_PAYLOAD_MODE: ReceiptMode;
/** One clause naming a fault for a sentence ("an accessor at input.deep.x"). */
export declare function describeFault(fault: ReceiptFault): string;