UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

125 lines (124 loc) • 9.29 kB
/** * The static tool catalog's READ FACE (design/388 §2.1): `describeToolCatalog()` and the predicates * every former name table is now DERIVED from. Readable without an env, a deps object or a tool * instance — a host predicts a leg's roster from it (`predictMountedToolNames`), a policy module * derives its covered set from it, a shell renders it. * * Every predicate here answers over DEFINITIONS and folds to names with "any definition under this * name matches" — the rule that pins the two fail-open shapes a per-name table had (one name, two * definitions; the table could only hold one). * * Layer-0 vocabulary: depends on the leaf catalog and the face vocabulary only. */ import { type ToolCallPathTarget, type ToolCatalogEntry, type ToolMountTag, type ToolPathAccess } from "./tool-face.js"; /** The whole static catalog, one frozen row per definition (design/388 §2.1 `describeToolCatalog()`). * Replaces the retired name-keyed exports `HAND_TOOL_EFFECTS` / `DEFAULT_BUDGET_EXEMPT_TOOLS` / * `COMPACTABLE_TOOLS`: filter this instead (`e.mountedBy.includes("hands")`, `e.face.budgetExempt`, * `e.face.compactable`). */ export declare function describeToolCatalog(): readonly ToolCatalogEntry[]; /** Every definition under a wire name (a name may carry more than one — the two shells). */ export declare function catalogEntriesNamed(name: string): readonly ToolCatalogEntry[]; /** Every wire name the catalog knows, aliases included (a set — the two shells fold to one name). */ export declare function catalogToolNames(): ReadonlySet<string>; /** A-11/12/13/14 — tools this engine can CONFINE BY PATH on a write: `pathTarget.access ≠ read`. * Today {Write, Edit, NotebookEdit}. */ export declare function pathConfinableWriteToolNames(): ReadonlySet<string>; /** A-8 — every definition that names a path target at all (read or write): the file-tool glob lane. */ export declare function pathTargetingToolNames(): ReadonlySet<string>; /** The tools whose declared path target is a READ (a file read, a search root): the file read station's membership. */ export declare function readTargetToolNames(): ReadonlySet<string>; /** A-10 — the micro-compaction whitelist (`compactable: true`). */ export declare function compactableToolNames(): ReadonlySet<string>; /** A-5 — the aggregate-budget exemptions (`budgetExempt: true`). */ export declare function budgetExemptToolNames(): ReadonlySet<string>; /** The value a call's args carry under a path target: `param` first, then each alias, first non-empty string wins. */ export declare function pathTargetValue(args: unknown, pt: { param: string; aliases?: readonly string[]; }): string | undefined; /** The path target of a wire name — the FIRST definition's (every definition under one name declares * the same target; the conformance test pins that). */ /** A gated call as every fence sees it: the wire name, the args, and the OBJECT face a prepared leg stamped on the * request (absent on a BARE request — a policy invoked outside a Runner). */ export interface ToolCallLike { toolName: string; args: unknown; face?: { pathTarget?: ToolCallPathTarget; names?: readonly string[]; } | undefined; } type CallPathTarget = ToolCallPathTarget; /** * design/388 B6/B17 — the path target a PROTECTIVE fence reads (a deny-list judging a write's target: sensitive paths, * frozen paths, write protection, the transcript gate; edit tracking): the object face's own declaration first, the * catalog's declaration for the NAME as the FLOOR — for a bare request (a policy invoked outside a Runner) and for a * face that declares none. Reading the catalog for an undeclared same-name tool can only TIGHTEN a deny-list (it adds * a slot to judge — the pre-388 posture, never wider), which is exactly why this floor is wrong for a grant. * * The floor is a `member` seat over every name the call answers to (the face's `names`, else the call's name alone) in * the face's order — the name the call was made under first, then the wire name, then the other aliases — and the * first name the catalog declares a target for wins: a call made under a catalog name and resolved to a tool that * declares none keeps THAT name's slot, also when the tool answers to a second catalog name with another slot. * * #635: "can only tighten" is true of the SLOT and false of the RESOLUTION, so the floor hands over the first and * defaults the second (`pathTargetSlotOnly`). Telling a fence that a FOREIGN tool of the same name resolves its slot * against the project root is a claim about someone else's implementation: get it wrong and the judged path moves to * a different file, and the deny this floor exists to preserve simply misses. */ export declare function protectivePathTargetOf(call: ToolCallLike): CallPathTarget | undefined; /** The typed refusal a CONTAINMENT reader mints for a request that carries no face at all (#626 ② — design/388 B6's * bare-producer migration seam, closed). */ export declare const BARE_REQUEST_CODE = "policy.bare_request"; /** * The path target a CONTAINMENT fence reads (a grant admitting a write BY its path: session `allowDirs`, the fs-write * gate's accept/exempt dirs): the object * face's OWN declaration only — a same-name tool that declares no path target is an UNCONFIRMABLE write (the catalog's * slot may not be the one it writes; a decoy in that slot would buy an admission), so the fence fails closed. * * #626 ②: a BARE request (no face at all) is REFUSED, typed (`policy.bare_request`), never answered from the catalog * for the name. A grant is a claim about the tool's OWN slot, and only a prepared leg's roster can say which object * the call dispatches to — the catalog's declaration for a NAME is exactly the reading a same-name caller tool would * exploit. Inside a Runner every gated request carries the leg's face (the gate entry, the resume belts and the * pre-CAS edit recheck stamp it); a policy invoked outside a Runner stamps its own (`toolCallFaceOf(roster, name)`). * The PROTECTIVE floor above keeps reading the catalog for a bare request: tighten-only, a deny can only gain a slot. */ export declare function declaredPathTargetOf(call: ToolCallLike): CallPathTarget | undefined; /** Is this call a write a PROTECTIVE fence judges (a protected target whose access ≠ read)? The name sets are its static view. */ export declare function isProtectedWrite(call: ToolCallLike): boolean; /** Is this call a write a CONTAINMENT fence may ADMIT by its path (a declared target whose access ≠ read)? */ export declare function isDeclaredWrite(call: ToolCallLike): boolean; /** design/388 A-18 — the file-op access a compaction summary records for a LIVE tool name: only the FILE families' * path targets name a file (a search tool's `path` is a search ROOT, not a file the model read); every other name * records nothing (retired spellings in persisted transcripts read the frozen history table at the call site). */ export declare function fileOpAccessOf(name: string): ToolPathAccess | undefined; export declare function pathTargetOf(name: string): ToolCatalogEntry["face"]["pathTarget"]; /** * The rule vocabulary of a wire name from the STATIC catalog: `primaryParams` = the declared primaries, else * the path target's `param` + aliases; `params` = the declared vocabulary, else the path target's names — * and UNDEFINED when the catalog cannot know the full vocabulary (a definition whose schema is built inside * its factory and declares no `ruleFace.params`): the static half never claims a vocabulary it has not * read, so the rule compiler judges only what it knows and the leg's roster (which reads the schema) * judges the rest. `undefined` as a whole for a name with nothing to say. */ export declare function catalogRuleFaceOf(name: string): { primaryParams: string[]; params?: string[]; } | undefined; /** A-1 / server B-1 — the hand band's declared effects by name (any `hands` definition; the WRITE shell * wins the name when both are listed, matching the by-name table this replaces — the read-only shell's * own instance carries `effect: "read"` and the effect roster reports THAT). */ export declare function handBandEffects(): ReadonlyMap<string, NonNullable<ToolCatalogEntry["face"]["effect"]>>; /** A-3 / L4 — every `details.type` card the engine owns: the catalog's per-definition cards plus the * engine-minted ones (protocol channels, fs refusals). */ export declare function engineCardTypes(): ReadonlySet<string>; /** * L3 — the STATIC PREDICTION of a leg's core-mounted names for a set of active mount conditions: * every definition listed under at least one active tag. `scenario` and `optional` definitions mount * only when the vector names their tag (a host factory / a per-task opt-in decides them). Names are * folded (the two shells predict one `Bash`). */ export declare function predictMountedToolNames(active: ReadonlySet<ToolMountTag>): ReadonlySet<string>; /** The closed set as a Set, for callers that validate a tag read off the wire. */ export declare const TOOL_MOUNT_TAG_SET: ReadonlySet<string>; export { HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND } from "./tool-face.js";