@sema-agent/core
Version:
Stateless, task-oriented AI agent core
138 lines (137 loc) • 8.01 kB
TypeScript
import { type BandId, type CatalogSection, type OverlayOp, type OverlaySource, type VariantWhen } from "./catalog/schema.js";
/** One overlay: an operation on a target, carrying text (or a catalog message reference). */
export interface PromptOverlay {
/** `<namespace>/<name>`; the `core/` namespace is the task sugar's alone. Names an added entry. */
readonly id: string;
readonly op: OverlayOp;
/** A message id (`replace` / `append`), a band id (`add`), or a section / added-entry id (`drop`). */
readonly target: string;
/** The overlay's text (`replace` / `append` / `add`); exclusive with `message`. */
readonly text?: string;
/** A catalog message whose selected variant is the overlay's text; exclusive with `text`. */
readonly message?: string;
/** The overlay applies to this run only when this holds (the variant selector's own vocabulary, the binding
* included) — the one applicability condition of every source class. */
readonly when?: VariantWhen;
}
/** The source classes in application order (later wins). `bundled` is the catalog itself and writes no overlay. */
export declare const OVERLAY_SOURCE_ORDER: readonly ["bundled", "task", "center", "deploy"];
/** Who contributed an overlay: its source class and, within it, the contributor (`sugar`, a mods registration, the center). */
export interface OverlayOrigin {
readonly source: OverlaySource;
readonly by: string;
}
export interface SourcedOverlay {
readonly overlay: PromptOverlay;
readonly origin: OverlayOrigin;
}
/** An overlay a later one on the same target overrode (or a drop removed), or one that changed nothing this run. */
export interface SupersededOverlay {
readonly id: string;
readonly source: OverlaySource;
readonly by: string;
readonly target: string;
/** The overlay id that won, or {@link ABSENT_THIS_RUN}. */
readonly supersededBy: string;
}
/** `supersededBy` of an overlay that changed nothing this run: the selector chose no variant of its target, or of
* the catalog message it takes its text from. */
export declare const ABSENT_THIS_RUN = "(absent this run)";
/** The section a message id is placed by, when the catalog places it. */
export declare function sectionOfMessage(id: string): CatalogSection | undefined;
export interface LockedEntry {
readonly message: string;
readonly variant: string;
readonly text: string;
}
/** Every constitution entry the guard compares: each message a constitution section places, every variant, whole. */
export declare function lockedEntries(): readonly LockedEntry[];
/** The constitution entries (`message#variant`) whose canonical text `text` contains verbatim. */
export declare function lockedTextIn(text: string): string[];
/** The task sugar's one violation: a role-layer text that carries locked text (an already-assembled prompt). */
export declare function assembledRoleRefused(field: string, hits: readonly string[]): Error;
/** Receive a source's prompt data ({@link PROMPT_DATA}): its frozen copy, or the clause naming what in it is not data. */
export declare function receivePromptData(raw: unknown): {
readonly value: unknown;
} | {
readonly notData: string;
};
/** The longest binding a task may name (`TaskSpec.promptBinding`), counted in Unicode code points (`[...s].length`) — not
* in UTF-16 code units; every binding a `when.binding` list names is held to the same bound. Exported so a host screens
* with the engine's number and the engine's unit. Code points are the one unit of every character bound of the prompt
* artifact verifier ({@link codePointsOver}: a binding, an overlay id, `sourceRevision`, `evidence.intent`, a
* reference), so a character outside the BMP (an emoji, a rare CJK ideograph) counts one, not two; the overlay text
* budgets are UTF-8 bytes and are named so. */
export declare const PROMPT_BINDING_MAX_CHARS = 128;
/** The most bindings one `when.binding` list names — it bounds what a publisher can make the conflict check walk. */
export declare const PROMPT_BINDING_LIST_MAX = 64;
/** Whether `s` holds more than `max` Unicode code points — the one length unit of the prompt artifact verifier's
* character bounds. A string of at most `max` code units fits (a code point is one or two of them); one of more than
* twice `max` does not; only a string in between is counted — counted, never copied whole. */
export declare function codePointsOver(s: string, max: number): boolean;
/**
* The run's binding (`TaskSpec.promptBinding`): the scenario a `when.binding` condition reads, on an overlay of any
* source class. Its one writer is the task (a deployment names its scenario); absent ⇒ undefined. A value outside the
* grammar is refused by name (`config.prompt_selector_invalid`), never read as absent.
*/
export declare function resolvePromptBinding(task: {
readonly promptBinding?: unknown;
}): string | undefined;
/** What a validation needs besides the overlay: its source class, which mods event (if any) carries it, and the
* entries lower classes added (the only entries a `drop` may name besides a built-in section). */
export interface OverlayValidationContext {
readonly source: OverlaySource;
/** The mods event the overlay arrived on; absent for the sugar and the center. */
readonly event?: "prompt.compose";
/** The request-body sugar may use the `core/` namespace; nothing else may. */
readonly sugar?: boolean;
/** Ids of entries lower source classes added (`add`) and the band each landed in. */
readonly lowerAdds?: ReadonlyMap<string, BandId>;
}
/**
* Validate one overlay for one source class. Returns the overlay (a frozen copy of its known members, its `when` in
* normal form — {@link canonicalWhen}) or why it is a violation: shape, namespace, op, text/message, `when`, target existence, band authorization (and the band's mods
* write port), and the locked-text guard.
*/
export declare function validateOverlay(raw: unknown, ctx: OverlayValidationContext): {
readonly overlay: PromptOverlay;
} | {
readonly violation: string;
};
/** One added entry as the fold leaves it. */
export interface AddedEntry {
readonly id: string;
readonly band: BandId;
readonly rank: number;
readonly text: string;
readonly origin: OverlayOrigin;
}
/** One overlay in a section's history, in application order; `superseded` once a later one overrode it. The
* composition puts the catalog's own text (`bundled`) first for a catalog section. */
export interface ChainLink {
readonly source: "bundled" | OverlaySource;
readonly overlay?: string;
readonly op?: OverlayOp;
readonly superseded?: true;
}
/** The fold's result: per-message text the overlays set, dropped sections, added entries, superseded overlays. */
export interface OverlayFold {
/** Message id → its text after the overlays (absent ⇒ the selected variant's text stands). */
readonly messageText: ReadonlyMap<string, {
readonly text: string;
readonly origin: OverlayOrigin;
readonly replaced: boolean;
}>;
/** Built-in section ids a drop removed. */
readonly dropped: ReadonlySet<string>;
readonly added: readonly AddedEntry[];
readonly superseded: readonly SupersededOverlay[];
/** Section id (or added-entry id) → the overlays applied to it, in order, superseded ones marked. */
readonly chains: ReadonlyMap<string, readonly ChainLink[]>;
}
/**
* Apply sourced overlays, already validated and already in application order, over the selected texts.
* `selectedText(messageId)` answers the text the selector chose (undefined ⇒ the message is absent this run);
* `applies(overlay)` answers its `when`.
*/
export declare function foldOverlays(ordered: readonly SourcedOverlay[], selectedText: (messageId: string) => string | undefined, applies: (o: PromptOverlay) => boolean, overlayText: (o: PromptOverlay) => string | undefined): OverlayFold;