@sema-agent/core
Version:
Stateless, task-oriented AI agent core
91 lines (90 loc) • 6.25 kB
TypeScript
/**
* The TIGHTEN-ONLY CLAMP SEATS an engine-built child spec carries from the run that builds it — and the
* ONE carrier every such constructor passes them through.
*
* Why a carrier and not a convention. The engine builds child specs in several places (the delegation
* lane, the observer sidecar, the wake/revival fold, the teacher's internal legs, the verify gate's two
* legs, the workflow lane), and each re-deriving per seat WHICH value is the tightening one lands a new
* seat one constructor at a time — the ones it does not reach staying invisible, since a `grep readFace`
* census cannot see a station that carries the face through a persisted seed or a synthesized fold.
*
* Two mechanical halves replace the census:
* · the COMPILER — {@link ChildClampSource} requires every seat as a PRESENT key (an `undefined` value
* is fine; a missing key is not), so adding a member to {@link CHILD_CLAMP_SEATS} reds `tsc` at every
* construction site until each one says what it carries there;
* · the SCAN — `scripts/verify-child-clamp-seats.mjs` reads {@link ENGINE_CHILD_SPEC_CONSTRUCTORS} and
* requires each registered constructor to go through this carrier, requires the carrier's own source
* to cover every seat, and requires every tighten-only seat `tightenTaskSpec` governs to be either
* carried here or listed in {@link CHILD_CLAMP_SEATS_NOT_CARRIED} with a reason.
*
* What "tighten-only" means for this seat list: the value that travels is the RESTRICTING one and only
* that one, so a parent that clamped nothing changes nothing about its children, and nothing a child can
* author lifts a clamp its parent ran under. That is why the carrier omits a key rather than writing a
* loosening value — a constructor may state its own stricter default and spread this after it.
*/
import type { ReadDenyEntry } from "../tools/fs/read-deny.js";
import type { TaskSpec } from "./types.js";
/**
* The clamp seats, as string literals (the closed set the scan and the wire-manifest reader both parse —
* a spread or an identifier here reads as an empty set).
*/
export declare const CHILD_CLAMP_SEATS: readonly ["handsReadOnly", "interactiveTools", "readFace", "writeFace", "readDenyPatterns"];
/** One clamp seat's name. */
export type ChildClampSeat = (typeof CHILD_CLAMP_SEATS)[number];
/**
* The seats `tightenTaskSpec` governs that this carrier deliberately does NOT carry, each with the reason
* — the "forgot" / "decided" distinction, kept where a new reader will trip over it. The scan requires
* every seat named in `tightenTaskSpec` to be in this table or in {@link CHILD_CLAMP_SEATS}.
*/
export declare const CHILD_CLAMP_SEATS_NOT_CARRIED: Readonly<Record<string, string>>;
/**
* The engine-built child-spec constructors, as `<repo-relative file>#<name>` — the closed registry the
* scan enumerates. A constructor that builds a spec for a child RUN belongs here; the scan requires each
* row to resolve to exactly one declaration IN ITS OWN FILE that calls {@link childClampSeats}.
*
* Why the file is half the key, and why a shape the scan cannot read is a RED rather than an absence: a
* registry keyed on a bare NAME asks a syntactic scan to enumerate every shape that binds a callable, and
* that list has no end — every shape it misses leaves the real constructor invisible while a namesake
* answers for it.
*/
export declare const ENGINE_CHILD_SPEC_CONSTRUCTORS: readonly ["src/agents/subagent.ts#buildChildSpec", "src/agents/subagent.ts#observerSpec", "src/agents/subagent.ts#resumeSpec", "src/agents/teacher.ts#inheritedDeploymentConfig", "src/agents/verify.ts#verifierSpec", "src/agents/verify.ts#fixLegSpec", "src/orchestration/workflow-primitives.ts#workflowChildClamps"];
/**
* The clamp source a construction site hands the carrier: EVERY seat as a present key. A site whose lane
* has no value for a seat writes `undefined` explicitly — which is the point, since that is a sentence
* a reader (and a diff) can see, unlike an absent key.
*/
export interface ChildClampSource {
/** The write-hands clamp: only `true` travels. */
handsReadOnly: boolean | undefined;
/** The hard-headless clamp: only `false` travels. */
interactiveTools: boolean | undefined;
/** The read containment face: only `"roots"` travels. */
readFace: TaskSpec["readFace"] | undefined;
/** The write containment face: only `"roots"` travels. */
writeFace: TaskSpec["writeFace"] | undefined;
/** The deny-set additions: an add-only axis, so a non-empty list travels as a spawn-time copy. */
readDenyPatterns: readonly ReadDenyEntry[] | undefined;
}
/**
* Project a parent's clamps onto a child spec — the ONE place that decides, per seat, which value is the
* tightening one and how it is copied. Spread the result onto the child spec; a seat with nothing to
* clamp contributes NO KEY, so a constructor may state its own stricter value above the spread and keep
* it (a `false` from the parent can never lift it).
*
* @contract child_spec.clamps_travel_whole — every child spec the ENGINE builds carries the whole of its
* parent's tighten-only clamp seats (`CHILD_CLAMP_SEATS`) through this one carrier: only the restricting
* value of each seat travels, arrays are copied at spawn, and a seat the parent did not clamp adds no key
* — so a clamp can never end one level deep, and a new seat cannot reach some constructors and not others.
*/
export declare function childClampSeats(source: ChildClampSource): Partial<TaskSpec>;
/**
* The OWN posture of an engine-built leg that never asks the person (the observer sidecar, the verifier): no
* human-facing tool is mounted AND the leg declares itself headless. Both, always together — the spec's posture
* outranks the one a leg inherits from its parent or its deployment, and the prepare door refuses an
* "interactive" posture paired with `interactiveTools: false`, so a leg that stated only the clamp was refused
* under every interactive parent. Spread ABOVE {@link childClampSeats}: a caller clamp can only tighten it.
*/
export declare function engineHeadlessLegSeats(): {
interactiveTools: false;
interactionPosture: "headless";
};