@sema-agent/core
Version:
Stateless, task-oriented AI agent core
46 lines (45 loc) • 3.46 kB
TypeScript
/**
* The one reading of a {@link ToolSpec} object. A spec is the caller's own mutable object graph, and the factory and the
* run's roster snapshot both turn it into something that outlives the moment it was read; each takes its copy through
* here, so a member is read once, by name, through an ordinary property read (an inherited, non-enumerable or
* Proxy-answered declaration included — how every phase reads a declaration), and the copy holds no object the caller can
* reach: declaration data (lists, schemas, faces, hints) is copied level by level — own string AND symbol keys, so a
* TypeBox schema keeps its kind marks — and frozen, and the only values kept by reference are the functions a ToolSpec
* declares as functions ({@link FUNCTION_MEMBERS}).
*
* A declaration the copy cannot take is refused, naming the member path, with the declaration door's code: a function inside
* data, an object with a prototype (a class instance, a Date, a Map), an accessor, a graph past the node or depth budget.
* A tool-name list is the one lenient place, and only at the places its judge reads one ({@link NAME_LIST_MEMBERS},
* {@link FACE_LIST_KEYS}): it is taken through the judge's own snapshot ({@link snapshotToolNameList}), so a container that
* cannot be read becomes the judge's failure marker and an entry the copy cannot take stands in as an inert value of the same
* JS type — the judge refuses either with the sentence it always had, without the caller's object ever being on the face.
*/
import type { ToolSpec } from "./tool-spec.js";
/** Every {@link ToolSpec} member, by name — the type's own key set, fenced both ways (a member added to `ToolSpec` and not
* listed here, or a name here that `ToolSpec` does not declare, is a compile error). */
export declare const TOOL_SPEC_MEMBERS: readonly string[];
/** One copy's ledger: the nodes charged so far (a value is a node; a container is charged for all its slots / keys when it is
* entered, before anything is allocated, and its children are not charged again), the source objects already copied (a graph
* that shares a sub-node copies it once and charges it once — strict and lenient copies kept apart) and the sources being
* copied now (a cycle is refused). */
export interface CopyState {
nodes: number;
readonly seen: WeakMap<object, object>;
readonly seenLenient: WeakMap<object, object>;
readonly inflight: Set<object>;
}
export declare const newCopyState: () => CopyState;
/** One member's value, as the face and the factory's copy hold it. `state` is the ledger of the whole spec / entry. */
export declare function frozenMember(name: string, value: unknown, state: CopyState): unknown;
/** A value under a key that is no ToolSpec member (a contract symbol, a decoration): the engine reads no declaration off it, so
* it is carried when it is plain data and left off the face when it is not. */
export declare function frozenExtra(key: string, value: unknown, state: CopyState): {
kept: true;
value: unknown;
} | {
kept: false;
};
/** A spec as it stands now: a frozen object holding each {@link TOOL_SPEC_MEMBERS} member as read once (a member the spec does
* not answer for stays absent). The caller keeping its own object and changing it afterwards — a member, or anything inside a
* list or a schema — changes nothing here. */
export declare function frozenToolSpec<T extends ToolSpec>(spec: T): Readonly<T>;