@sema-agent/core
Version:
Stateless, task-oriented AI agent core
47 lines (46 loc) • 3.83 kB
TypeScript
import type { AskClass } from "../ask-origin.js";
import type { HumanReviewGateRow } from "../task-result.js";
import { type OnAsk, type PermissionResult, type ResolvedAsk, type ToolCallRequest } from "../tool-policy.js";
/** One inherited ask grant: a person's clean allow at an ancestor's live approver, on ONE presented shape, to ONE question
* (`classes`, the request's class set in `ASK_CLASSES` order) — it answers no stricter question. */
export interface InheritedAskGrant {
approver: unknown;
argsJson: string;
presented: unknown;
waitMs: number;
settled?: object;
classes: readonly AskClass[];
}
/** Does a grant for `granted` cover a question the gate classified as `asked`? `ordinary` — the plain hesitation every other
* class is a stricter form of — is covered by any card answered; every other class must have been on the card (the gate reads
* facts the station cannot hold, and a class those add was asked of nobody). Unclassified, or a card with no class ⇒ never. */
export declare function askGrantCovers(granted: readonly AskClass[], asked: readonly AskClass[] | undefined): boolean;
/** JSON.stringify throws on a cyclic value (a rewrite object carries no acyclicity guarantee) — `undefined` means "no
* comparable shape", which every consumer treats as a mismatch (skip the record / reuse ⇒ re-ask, the safe direction). */
export declare function askGrantShapeOf(args: unknown): string | undefined;
/**
* Duplicate-frame collapse, the CONSUME half: an inherited layer already resolved THIS call as a clean allow at the
* SAME approver function, on EXACTLY the byte-shape this ask now presents, for a question that covers this one — asking
* again is the 2-frame UX debt, not a second authority. Guards, all mismatches re-ask (⇒ `undefined`):
* · IDENTITY: the approver FUNCTION reference (never a name), compared through `askApproverIdentity`: a delegated
* child's approver is the ancestor's own wrapped with provenance — a different function object, the same operator
* (the wrapper grants no authority of its own); identity answers only "same consultation".
* · SHAPE: byte-equality against the presented SNAPSHOT; reuse re-emits that snapshot as the executed shape via
* `presentedInput` — the same binding as a main-gate approval (shown == executed, by construction).
* · CLASS: the gate's stamped `classes` is covered by the set the person answered ({@link askGrantCovers}); a class
* the gate's own judges add (a protected target, an out-of-root read) was not on the card. Not covered ⇒ the gate
* asks its own question and the grant stays (it answers what it answered; the call-site sweep ends it). The class
* set is also the AUTHORITY test (a hook's ask carries `hook`, a fallback `denial_limit_fallback`, a marked tool
* `operator_forced`, a protected write `write_protection`, an out-of-root read `probe`, the operator's shell floor
* `shell_floor` — a class no inherited station mints from its facts, so no station card answers it; the shell seat's classify-tier
* hesitation is `ordinary`, the one question a yes on the same bytes answers) — neither the ask's `decisionReason` nor
* its `mandate` word is read.
* · LEDGER: the ONE real consultation (timed on the wrapper arm) is booked here — one frame happened, one is booked.
* The grant lives within one gate evaluation (consumed on reuse, swept at the call site): a REOPENED checkpoint's
* re-adjudication never sees a stale grant.
*/
export declare function consumeInheritedAskGrant(grants: Map<string, InheritedAskGrant>, onAsk: OnAsk | undefined, humanReviewRef: {
count: number;
totalWaitMs: number;
gates: HumanReviewGateRow[];
}, decision: PermissionResult, req: ToolCallRequest): ResolvedAsk | undefined;