@sema-agent/core
Version:
Stateless, task-oriented AI agent core
101 lines (100 loc) • 5.29 kB
TypeScript
import { type ModBusSide, type ModEventName, type ModSeat } from "./events.js";
import { type ModAdmissionRecord, type ModDollarBase, type ModOrigin, type ModSeats } from "./registration.js";
import { type ModAnswerCheck, type ModEventPayload, type ModFailureCause, type ModMatcher, type ModSkip, type ProxyProbe } from "./site-rules.js";
/** A frame on the dispatch stack: a registration's own `$` call, or one of its handlers running. */
export type ModFrame = {
readonly kind: "dollar";
readonly plugin: string;
} | {
readonly kind: "handler";
readonly plugin: string;
readonly handler: number;
};
/** What a chain bottom (the core of an event) receives besides `e`. */
export interface ModBottomContext {
readonly frames: readonly ModFrame[];
readonly signal: AbortSignal;
readonly origin: ModOrigin;
}
/** A `$` method's chain bottom: the host's seat behind it. Its throw is the caller's throw. */
export type ModMethodBottom = (args: ModEventPayload, ctx: ModBottomContext) => unknown;
/**
* A `$` method on a bus: `run` is its chain bottom; `enter`, when the method has one, shapes the caller's (received)
* arguments into the event payload the chain sees — the one place a method's argument form is normalised before any
* handler reads it. Either one's throw is the caller's throw.
*/
export interface ModMethodSeat {
readonly run: ModMethodBottom;
readonly enter?: (args: ModEventPayload) => ModEventPayload;
}
export type ModMethodBottoms = Partial<Record<ModEventName, ModMethodSeat>>;
export type ModDispatchOutcome = {
readonly kind: "answered";
readonly answer: unknown;
readonly answeredBy?: string;
readonly skipped: readonly ModSkip[];
} | {
readonly kind: "failed";
readonly registration: string;
readonly cause: ModFailureCause;
readonly reason: string;
readonly skipped: readonly ModSkip[];
} | {
readonly kind: "aborted";
};
/** What a participant filter sees of one mounted handler: its registration, that registration's seat, and whether it only observes. */
export interface ModParticipant {
readonly plugin: string;
readonly seat: ModSeat;
readonly observe: boolean;
}
export interface ModDispatchOptions {
/** The core of the event: what the engine (or shell) does when the chain reaches the bottom. */
readonly bottom: (e: ModEventPayload, ctx: ModBottomContext) => unknown;
readonly signal?: AbortSignal;
/** The frames the event is raised under (a chain bottom hands its own `ctx.frames` on). */
readonly frames?: readonly ModFrame[];
readonly origin?: ModOrigin;
/** The raiser's reading of an answer; an answer it refuses is a violation of the site. */
readonly checkAnswer?: ModAnswerCheck;
/** Which mounted handlers take part (absent ⇒ every handler on the event). A handler it leaves out neither runs nor observes. */
readonly participant?: (p: ModParticipant) => boolean;
}
/** One handler as admission hung it — what a registration's `register` mounted, never its code. */
export interface ModMountedHandler {
readonly plugin: string;
readonly seat: ModSeat;
readonly pattern: string;
readonly matcher?: ModMatcher;
readonly observe: boolean;
/** Whether it was hung with a `.catch`. */
readonly caught: boolean;
}
export interface ModBus {
readonly records: readonly ModAdmissionRecord[];
/** Every mounted handler, in mount order (registrations in run order, each in the order its `register` hung them). */
mounted(): readonly ModMountedHandler[];
/** Whether any mounted handler hangs on `event` (among those `participant` admits) — no handler, no dispatch (the station skips the call). */
hasHandlers(event: ModEventName, participant?: (p: ModParticipant) => boolean): boolean;
dispatch(event: ModEventName, e: ModEventPayload, options: ModDispatchOptions): Promise<ModDispatchOutcome>;
}
export interface ModBusInput<D extends ModDollarBase = ModDollarBase> {
readonly side: ModBusSide;
readonly seats: ModSeats<D>;
readonly bottoms: ModMethodBottoms;
/** Where handler failures go (the host's error channel). Never throws back into the bus. */
readonly report: (err: Error, classification?: "observational-hook-verdict-ignored") => void;
/** The host's one rule for turning a THROWN value into text (a handler, a `.catch` or a `register` may throw
* anything). The bus has no rendering of its own: every failure reason it writes names the value this way. */
readonly describeThrown: (err: unknown) => string;
/** The host's Proxy probe: a Proxy a handler answers or hands on is refused before any of its traps runs (a violation
* of the site). Absent ⇒ a Proxy is read like any object, its traps run by the copy. */
readonly isProxy?: ProxyProbe;
readonly signal?: AbortSignal;
}
/**
* Screen both seats, then admit registrations one by one in run order: each is put to the
* `plugin.register` handlers of the registrations already admitted; a refused one is never registered
* (its code never runs). Throws `config.mods_invalid` for a registration the kernel cannot take.
*/
export declare function admitMods<D extends ModDollarBase = ModDollarBase>(input: ModBusInput<D>): Promise<ModBus>;