@sema-agent/core
Version:
Stateless, task-oriented AI agent core
67 lines (66 loc) • 4.51 kB
TypeScript
/**
* The hook seam's SEAT discipline and its disclosure vocabulary: which callback seats exist, what a
* deployment is allowed to put in one, and how a wiring disclosure names the ones that are wired.
*
* Kept beside the interface rather than inside it: `hooks.ts` owns the seam's BEHAVIOUR, and this module owns
* the seat as a CONFIGURATION VALUE — the same split `wiring-manifest.ts` keeps for the ask/question seats.
*/
import type { Hooks } from "./hooks.js";
import type { AssertAllKeysHandled } from "./ask-origin.js";
import type { SeamProvenance } from "./wiring-manifest.js";
/**
* The CALLBACK SEATS of {@link Hooks} as a word list — the vocabulary a wiring disclosure names a wired seam
* by (`WiringManifest.hooks[].family`), in declaration order. `preToolUseObservational` is deliberately NOT a
* member (it is a DECLARATION ABOUT `preToolUse`); `timeoutMs` is not a seat either.
*/
export declare const HOOK_FAMILIES: readonly ["preToolUse", "postToolUse", "userPromptSubmit", "stop", "postToolUseFailure", "postToolBatch", "preCompact", "postCompact", "stopFailure", "permissionDenied"];
/**
* One hook seat's name.
*
* @contract hooks.family_vocabulary — {@link HOOK_FAMILIES} is the only spelling of a hook seat's name on any
* disclosure face, and {@link HookFamiliesCoverEverySeat} makes a new callback member of {@link Hooks} red at
* `tsc` until it has a word there — a disclosure that omits a seat under-states what runs on the critical path.
*/
export type HookFamily = (typeof HOOK_FAMILIES)[number];
/** Every CALLBACK member of {@link Hooks} — derived from the interface, never a hand-kept second list. */
type HookCallbackSeat = {
[K in keyof Hooks]-?: NonNullable<Hooks[K]> extends (...args: never[]) => unknown ? K : never;
}[keyof Hooks];
/** The closed-set fence: `never` while every callback seat has a word, the MISSING SEAT'S NAME when one does
* not (the constraint form names it; a conditional type says only "something changed"). */
export type HookFamiliesCoverEverySeat = AssertAllKeysHandled<Exclude<HookCallbackSeat, HookFamily>>;
/** The typed refusal a hook seat holding a present NON-CALLBACK value raises at prepare. */
export declare const HOOKS_INVALID_CODE = "config.hooks_invalid";
/**
* Screen the resolved hook record's seats (`spec.hooks ?? deps.hooks`).
*
* @contract hooks.seat_value — a hook seat is a FUNCTION or it is absent. A PRESENT non-function
* (`preToolUse: null`, a string, a bare object) is a configuration error and refuses the whole prepare with
* {@link HOOKS_INVALID_CODE}, naming the family and the type received. It is not folded to "unwired": the tool
* gate arms on PRESENCE (`hooks?.preToolUse !== undefined`) and would invoke the non-function and fail closed
* on every call, while a callability-based disclosure reported no hooks at all. The sibling seams already
* refuse the same way (`resolveQuestionSeam` / `resolveElicitSeam`). `undefined` stays exactly what it was:
* an unwired seat, silent.
*/
export declare function assertHookSeatsCallable(hooks: Hooks | undefined, provenance: SeamProvenance): void;
/**
* One WIRED HOOK SEAT of this leg. `family` is the seat's name; `owner` is WHICH SEAT supplied the record, the
* `spec > deps` provenance the ask/question seams report. Deliberately no third owner word: core mounts NO
* engine-owned hooks, and a delegated child's inherited `preToolUse` rides the gate's constraint fold.
*/
export interface WiringManifestHookEntry {
family: HookFamily;
owner: SeamProvenance;
/** Only on the `preToolUse` entry, and only when the deployment DECLARED the face observational
* (`Hooks.preToolUseObservational`) — it still runs on every call but is not folded into a delegated
* child's inherited constraints. Absence means "not declared", never "declared false". */
observational?: true;
}
/**
* The wiring manifest's hook read face ({@link WiringManifestHookEntry}): one entry per CALLBACK seat the
* resolved record wires, in the vocabulary's declaration order. The VOCABULARY is the judge — families are
* walked, never the record's own keys. `owner` is a property of the RECORD, so every entry agrees. Callability
* and presence cannot diverge here: {@link assertHookSeatsCallable} has already refused the leg where they would.
*/
export declare function wiredHookEntries(hooks: Hooks | undefined, owner: SeamProvenance, preToolUseObservational: boolean): WiringManifestHookEntry[];
export {};