UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

91 lines (90 loc) • 6.25 kB
/** * 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"; };