@sema-agent/core
Version:
Stateless, task-oriented AI agent core
125 lines (124 loc) • 9.29 kB
TypeScript
/**
* 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";