UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

801 lines • 54.6 kB
/**
 * The ORIGIN of a surviving `ask` — WHICH AUTHORITY raised it, or took it over — as one word from a
 * closed set; and, below it, the ask's CLASS SET, the mode × class arbitration table and the one predicate a
 * permission mode is read through.
 *
 * Why an axis of its own. The tool gate has several automatic ask-resolution lanes (the auto-mode
 * classifier today; the persisted-rule lane and the sandbox-admission leg are the same shape), and each
 * one's eligibility used to be written as a growing conjunction of NEGATIVES — "not an org ask, not a
 * hook ask, not a matched ask rule, not the classifier's own fallback, not the question tool, not a
 * marked call…" — one more conjunct per kind of ask the engine learned to mint. Every conjunct was
 * right; the FORM was the debt: a new kind of ask had to be remembered at every lane, and forgetting
 * one was a silent loosening (the new ask fell into whichever lane nobody had excluded it from).
 *
 * The replacement: the gate derives the origin ONCE from the facts its layers established
 * ({@link askOriginOf}) and stamps it on the surviving ask as the DISCLOSED word; the lanes read the ask's
 * class SET ({@link classifyAsk}) folded onto one row of a table that MUST name every class
 * ({@link MODE_ARBITRATION}, fenced by {@link AssertAllKeysHandled}). Adding a kind of ask is then: one
 * class here, one arm in the derivation, one row in the table — and the compiler refuses the build until
 * the row exists, so the default for an unconsidered class is a red build, never a silent allow.
 *
 * What is and is not an origin. An origin is WHO raised the question. Two facts that ride beside it
 * are deliberately not origins: `requiresRealApproval` says what may CLEAR the question (a governance
 * bit any origin can carry — a safety policy's own ask carries it with origin `policy`), and the
 * gate's `autoMode` seat says whether a classifier is WIRED at all (a property of the session, not of
 * the ask). Both stay where they are and are consumed beside the origin, not folded into it.
 */
import type { PermissionResult } from "./tool-policy.js";
/**
 * The closed set, in AUTHORITY order — the order {@link askOriginOf} resolves a fold in when several
 * facts hold on one call (a hook ask folded over a policy ask, an org stamp over either). The order
 * decides only the WORD; every lane table below is total over the set, so it can never turn an ask
 * one member would have excluded into one another member admits (the invariant is pinned).
 *
 *  - `content_question` — the reserved question tool: a content-ROUTING signal, not a permission
 *    question; it leaves the gate through the content channel, never through a permission decider.
 *  - `ancestor_marked` — the call is MARKED by an ancestor (an inherited approver-unavailable float, or
 *    a durable mandate floated down because this task can park it): it must reach the park with no
 *    synchronous decision-maker in between. The word names WHO raised it (an ancestor's mark), like every
 *    other member; whether the ask can be resolved is the park's business, not the origin's.
 *  - `org_unavailable` — the deployment is org-governed and could not read its snapshot: the fail-closed
 *    tighten, cleared by a person only ("does the org forbid this?" is unanswered, and no automatic
 *    lane may answer it).
 *  - `org_rule` — an organization policy rule asked (or stamped an existing ask): "no configuration
 *    clears this, a person nods".
 *  - `rule_store_unavailable` — the person's rule store WAS read, but the CALL could not be read against
 *    their standing deny/ask rows (the tightening lexer's `unreadable` word: an expansion where a rule
 *    reads a word, an unterminated quote, a syntax error — the answer's `reason`): whether one of those
 *    rows forbids this call is unknown, so an allow tightens to an ask a person must clear and no automatic
 *    lane may answer. A store that could not be READ is not this word: its rows take no part in the
 *    decision (the call is judged as if the person had no persisted rows) and the run carries one
 *    `permission.rule_store_unavailable` notice instead.
 *  - `hook` — a deployment's PreToolUse screening face raised or co-raised the ask; the word is
 *    engine-stamped at the fold and is the deployment's own per-call adjudication.
 *  - `ask_rule` — an EXPLICIT `ask` permission rule matched: a person's standing "ask me each time",
 *    an instruction rather than hesitation.
 *  - `denial_limit_fallback` — the auto-mode classifier reached its denial bound and handed the call
 *    to a person; the classifier stands BEFORE this ask, never after it.
 *  - `shell_gate_tighten` — the gate's own irreversibility tighten raised the ask over a tier the COARSE
 *    shell seat installed (the shape tier on every full shell, or the `shellGate: "always"` floor), not an
 *    explicit per-tool mark. The gate records that its own tighten spoke; a policy stamping
 *    `decisionReason: "safety"` on its own ask does not become one.
 *  - `safety_tighten` — the gate's own post-fold tightens over an EXPLICIT fact of the call: the tool's
 *    egress mark, its irreversibility mark, a peer-message referral in auto mode, a write onto the
 *    write-protected table. Same recording rule as above.
 *  - `policy` — everything else: a deployment `ToolPolicy` ask or a mode-default ask. This is the
 *    hesitation family — the one an armed classifier exists to resolve. The two tighten words are
 *    classifier-eligible too (arming auto mode is the deployment's explicit choice to let the
 *    classifier be the explicit ask-resolution for those classes); they are separate words so a deny
 *    observer or an approval card can say WHICH engine layer raised the question, which one word
 *    could not.
 */
export declare const ASK_ORIGINS: readonly ["content_question", "ancestor_marked", "org_unavailable", "org_rule", "rule_store_unavailable", "hook", "ask_rule", "denial_limit_fallback", "shell_gate_tighten", "safety_tighten", "policy"];
export type AskOrigin = (typeof ASK_ORIGINS)[number];
/** Whether a value is a member of the closed origin set. Exported because the word crosses process
 *  boundaries: a host validating one it received must not hand-roll the set. */
export declare function isAskOrigin(v: unknown): v is AskOrigin;
/**
 * A compile-time fence: `T` must be `never`. Instantiate it with `Exclude<<closed set>, keyof <table>>`
 * beside a table that is meant to be TOTAL over the set — while every member has a row the argument is
 * `never` and the build is green; add a member without its row and the argument is that member's
 * literal, which does not satisfy the constraint, so the build is red AT THE FENCE with the missing
 * member spelled out in the error. The table's own `satisfies Partial<Record<…>>` clause guards the
 * other direction (a row for a word that is not in the set).
 */
export type AssertAllKeysHandled<T extends never> = T;
/** The facts about one call that only the gate holds — the derivation's inputs beyond the decision
 *  itself. Every member is a plain boolean/word read ONCE by the gate, so the derivation is a pure
 *  function that a test can drive arm by arm. */
export interface AskOriginFacts {
    /** The call is on the reserved question tool. */
    readonly contentQuestion: boolean;
    /** The call is marked unresolvable by an ancestor (see the gate's `isMarkedUnresolvable` seat). */
    readonly markedUnresolvable: boolean;
    /** Which org state, if any, spoke on this call — the gate's own record of its org layer, never a
     *  read-back off the decision (a policy can put `decisionReason: "org_rule"` on its own ask; the
     *  gate's cell cannot be written by a policy). */
    readonly org: "rule" | "unavailable" | undefined;
    /** The gate's own record of its persisted-rule lane's READ: the store was read, but THIS CALL could not be read
     *  against the person's deny/ask rows (the lane's answer carried the lexer's reason) — the origin
     *  `rule_store_unavailable`. Absent: the lane read the call, or its store could not be read and its rows took no
     *  part. Written only by the gate. */
    readonly ruleCallUnreadable?: boolean;
    /** Which of the gate's OWN post-fold stations minted the standing ask, if one did — again the gate's
     *  record of its own layer (a station replaces an `allow` wholesale, so at most one fires per pass),
     *  never a read-back of `decisionReason: "safety"` off a decision a policy could have composed. `unstated` is
     *  the last station: the mode's answer for a call to a tool that states no permission of its own when no layer
     *  stated the allow — its origin is `policy` (no tighten word), its class `unstated`. */
    readonly tightened: "shell_gate" | "safety" | "unstated" | undefined;
    /** The MANDATE mark inputs of this call — the tool SEAT's marks plus the gate's own per-call records
     *  ({@link MandateMarks}), read from the same two places the lane's own derivation reads them. The
     *  per-call PROBE member is deliberately not here: it is engine-stamped on the DECISION, and
     *  {@link ruleClearanceOf} reads it there, so one fact cannot arrive twice with two values. An inherited
     *  station passes the tool SEAT's marks of the task it runs in (its per-call records are the gate's, absent
     *  there); a station holding no seat at all passes `{}` — an honest empty, since the decision's own
     *  members still answer. */
    readonly mandateMarks: Omit<MandateMarks, "probeMandated">;
}
/**
 * The ONE derivation of an ask's origin, from the surviving decision plus the gate's own facts. The
 * arms are in {@link ASK_ORIGINS} order (strongest authority first); the members it reads off the
 * decision are the three the engine already treats as tighten-only provenance (`decisionReason:
 * "hook"` is engine-stamped at both fold arms; `matchedAskRule` and `denialLimitFallback` are stamped
 * by their mint sites and, when self-declared by a policy, can only opt that policy's own ask OUT of
 * automatic resolution — the safe direction). Nothing here reads a member that could loosen.
 */
export declare function askOriginOf(decision: Extract<PermissionResult, {
    action: "ask";
}>, facts: AskOriginFacts): AskOrigin;
/**
 * Stamp the ask's origin word — and withdraw the disclosures that speak for the TIGHTEN alone when some
 * layer above it is the reason this call is being asked about. Today that is one member: the read
 * boundary's directory offer, whose claim holds only while the tighten is the whole reason. Every higher
 * rung is a layer with a reason of its own — a standing `ask` row, a call unreadable against the rows, an org
 * verdict, a hook, the denial-limit fallback — which no directory grant answers, so the ask would stand
 * after the grant and the offer would have promised a clearing it cannot deliver (measured: `cat
 * <out-of-root>` under a standing `Bash(cat:*)` ask rule offered the directory and kept asking after it).
 * It lives at the one derivation rather than the three stamp sites: the withdrawal's input IS the origin
 * word, so a site that stamped without it would be a second, quieter answer to one question.
 * The same stamp writes the ask's CLASS members (`askClass` / `classes`, {@link askClassMembersOf} over the
 * `classification` the caller derived from this decision under these facts): the word and the class leave the
 * gate on one object, and a value a policy put on either is replaced.
 */
export declare function stampAskOrigin<T extends Extract<PermissionResult, {
    action: "ask";
}>>(decision: T, facts: AskOriginFacts, classification: AskClassification): T & {
    origin: AskOrigin;
} & AskClassMembers;
/**
 * CAN a persisted allow rule clear this ask — and, if not, which door closed? ONE derivation with TWO
 * readers, which is the whole point of the shape: the approval card's `ruleOffersAbsence` word and the
 * ask's own {@link import("./tool-policy.js").AskRequest.mandated} bit. They used to be two
 * enumerations of one boundary, and the drift was live — the card's lane speaks for one tool, only
 * where a rule store is wired and only on a task that can hold a rule, so the same mandated read asked
 * as a mandated question on the shell tool and as an apparently ordinary one on the monitor tool.
 *
 * "Allow rules silence the classifier's questions, never a mandated one." The doors, as a SET:
 *  · the MARKS family — {@link persistedRuleMandateOf} over the tool seat's marks and the gate's own
 *    per-call records (the read station's structural demotion, an unanswered probe, an operator's
 *    per-call shell doctrine, the tool's own egress / irreversibility marks, a write the protection
 *    judge matched or could not resolve);
 *  · GOVERNANCE (`requiresRealApproval`) — an org verdict, a safety policy's own demand, the
 *    classifier's denial-limit fallback: only judgment clears it;
 *  · a HOOK-raised ask — the deployment's own per-call screening authority, which the lane's clearing
 *    conjuncts refuse;
 *  · ANCESTOR authority (`inheritedUnresolved` / `ancestorResolved`) — the ask belongs to an ancestor's
 *    approver and this task's rule lane never adjudicates it;
 *  · the bit already stamped on the ask, which no layer below the mint may clear (tightening-only).
 *
 * And the SHADOW doors, which are not mandates: an allow-layer member already matched and is outranked
 * (`allowRuleShadowed`), or the person's explicit "ask me each time" rule matched
 * (`matchedAskRule`). A rule minted from such a card silences nothing, so the lane offers none — but
 * the reason is that a RULE is already speaking, not that the question is beyond rules, and a surface
 * that auto-answers by posture is not crossing a mandate when it answers one.
 *
 * FOUR named readings of the one pass, because four callers need four different things and taking
 * the wrong one is how this boundary drifts:
 *  · `word` — the DISCLOSED reason, first door wins, in the display order the card has always used
 *    (governance, the shadow, the hook, the ask rule, the two ancestor doors, then the marks). Display
 *    only. A structural door the offer factory owns (no lane wired, a tool it does not speak for, a
 *    task that cannot hold a rule) is decided BEFORE this and is deliberately silent;
 *  · `mandated` — the FACT the ask carries as its own member. Read off the whole mandate set, so which
 *    door is DISPLAYED cannot change it: an ask that is both shadowed and mandated is mandated;
 *  · `ruleMayClear` — the ENFORCEMENT reading the gate's clearing arms take. It is `mandated` plus the
 *    person's explicit `ask` rule, and deliberately NOT `word !== undefined`: `allowRuleShadowed`
 *    is an ACCOUNT of a refusal that already happened, display-only by contract, and reading it as a
 *    blocker would let a policy forwarding that metadata refuse a call nothing is blocking. A
 *    display word is never an authority;
 *  · `mandate` — WHICH table word the bit stands on, only when the MARKS family is its whole reason (no
 *    governance, hook or ancestor door, and no bit stamped without a word); absent, the bit's reason is
 *    not one word.
 */
export declare function ruleClearanceOf(ask: RuleClearanceFacts | undefined, marks: Omit<MandateMarks, "probeMandated">): {
    word?: "mandated" | "shadowed";
    mandated: boolean;
    ruleMayClear: boolean;
    mandate?: PersistedRuleMandate;
};
/** The facts one ask carries about whether a rule could clear it — the members {@link ruleClearanceOf}
 *  reads beside the tool seat's marks. Every one is either engine-stamped or, where a caller can
 *  self-declare it, tightening-only: a claim here can opt an ask OUT of automatic answering and never
 *  into it. */
export interface RuleClearanceFacts {
    /** The bit a gate already stamped (see {@link import("./tool-policy.js").AskRequest.mandated}). */
    mandated?: true;
    /** The table word a gate stamped beside that bit (see {@link import("./tool-policy.js").AskRequest.mandate}). */
    mandate?: PersistedRuleMandate;
    requiresRealApproval?: boolean;
    /** Allow-layer members matched and did not clear the ask (see {@link import("./allow-rule-shadow.js").AllowRuleShadow}). */
    allowRuleShadowed?: readonly import("./allow-rule-shadow.js").AllowRuleShadow[];
    decisionReason?: import("./tool-policy.js").DecisionReason;
    /** An EXPLICIT `ask` permission rule matched this call (DSL, stamped at the rule policy's three
     *  matched-ask exits — bare, covering and param). */
    matchedAskRule?: string;
    /** The ask was minted by the deployment's own governance layer (see {@link import("./tool-policy.js").PermissionResult}). */
    authoredBy?: "deployment";
    /** The engine-stamped probe mandate (the reversibility probe declared this call's demotion
     *  structural — for the built-in shell probe, a read outside the session's allowed directories). */
    probeMandated?: boolean;
    /** An ancestor's approver answered "unavailable" for this call, or a durable mandate was floated here. */
    inheritedUnresolved?: boolean;
    /** The ask is being resolved at an ANCESTOR's frozen approver (the three inherited-lane mint sites
     *  pass it literally) — this task's rule lane never adjudicates it. */
    ancestorResolved?: boolean;
    /** The classes of the contributors the policy fold carried under this ask (see {@link import("./tool-policy.js").PermissionResult});
     *  a member whose row no rule may clear keeps the question a person's. */
    foldedAskClasses?: readonly AskClass[];
}
/** The ask FACE's mandate members — the bit and its table word, spread at the four mint stations and the
 *  park row. The stations hand it the SAME facts object they hand the rule-offer factory, so the card's word
 *  and these members are readings of one derivation and cannot drift. The tool SEAT is not re-judged here:
 *  its marks were folded into the stamped bit and word at the gate, and a station minting a request from an
 *  ANCESTOR's decision holds no seat of this task's to judge. */
export declare function askMandateMember(ask: RuleClearanceFacts | undefined): {
    mandated?: true;
    mandate?: PersistedRuleMandate;
    authoredBy?: "deployment";
};
/** The clearance facts of an ask the engine did not construct (a policy's verdict), each member read ONCE into
 *  an engine-owned value — for a station that keeps the answer past its own side effects, where the verdict's
 *  accessors must not run again. The members a verdict may carry that narrow what may clear it. */
export declare function ownedClearanceFacts(d: Extract<PermissionResult, {
    action: "ask";
}>): RuleClearanceFacts;
/**
 * Which origins IMPLY the `requiresRealApproval` bit, and WHERE that implication is verified — the
 * consistency table {@link askOriginOf} reads. An ask of a must-carry origin exists so that no automatic
 * lane clears it (the org's asks: "no configuration clears this, a person nods"; the classifier's
 * fallback: no automatic lane may re-clear what the classifier already refused), so the bit must ride
 * it. The bit is NOT derived from this table: the write sites each have their own reason, and a
 * table-derived bit would have to either strip a safety policy's mandate or make every hook ask
 * non-budgetable. The table only refuses the one shape that is a defect: a must-carry origin without
 * the bit — and says where that refusal can honestly happen:
 *  - `"at_derivation"` — the origin is derived from a GATE fact only the engine writes (`facts.org`),
 *    so a missing bit is an engine defect and {@link askOriginOf} fails loud;
 *  - `"at_mint"` — the origin is derived from a decision MEMBER a policy may compose
 *    (`denialLimitFallback`: a self-declared one opts the policy's own ask out of automatic
 *    resolution, the safe direction, not a defect), so the invariant is pinned at the engine's two
 *    mint sites (the gate's fallback re-mint and the inherited station's) rather than thrown at
 *    derivation, where a policy's own object could trip it;
 *  - `false` — the origin says nothing about the bit: a `policy` ask may carry it (a safety policy's
 *    own mandate) or not, and both are legal.
 */
export declare const ORIGIN_IMPLIES_REAL_APPROVAL: {
    readonly content_question: false;
    readonly ancestor_marked: false;
    readonly org_unavailable: "at_derivation";
    readonly org_rule: "at_derivation";
    readonly rule_store_unavailable: "at_derivation";
    readonly hook: false;
    readonly ask_rule: false;
    readonly denial_limit_fallback: "at_mint";
    readonly shell_gate_tighten: false;
    readonly safety_tighten: false;
    readonly policy: false;
};
/** The fence over the consistency table: `never` while every origin has a row. */
export type RealApprovalTableCoversEveryOrigin = AssertAllKeysHandled<Exclude<AskOrigin, keyof typeof ORIGIN_IMPLIES_REAL_APPROVAL>>;
/**
 * The MANDATE family — the closed set of words for "this ask stands by STRUCTURE, so a standing allow
 * rule may not retire it". A DISCLOSURE vocabulary: the word rides the approval card and the park row
 * ({@link persistedRuleMandateOf} picks one), while what a live lane may do with the ask is decided by
 * the ask's CLASS SET ({@link classifyAsk}, {@link MODE_ARBITRATION}) — never by this one word.
 *
 *  - `operator_always` — the operator's `always` floor: per-call confirmation for shell commands.
 *  - `tool_marks` — the tool's own egress / irreversibility / interaction declaration.
 *  - `probe_mandate` — this call's reversibility probe declared the demotion STRUCTURAL.
 *  - `probe_unanswered` — the probe was consulted and did not answer: the boundary is UNKNOWN.
 *  - `write_protection` — the table covers this write's target. A write onto load-executed configuration
 *    re-authorizes the agent, so the question is a property of the TARGET, not of anyone's hesitation.
 *  - `write_protection_unresolved` — the judge could not RESOLVE the target: `probe_unanswered`'s
 *    doctrine one layer over.
 */
export declare const PERSISTED_RULE_MANDATES: readonly ["operator_always", "tool_marks", "probe_mandate", "probe_unanswered", "write_protection", "write_protection_unresolved"];
export type PersistedRuleMandate = (typeof PERSISTED_RULE_MANDATES)[number];
/** Membership test for {@link PersistedRuleMandate} — the screen a row reader applies to a stored word. */
export declare function isPersistedRuleMandate(v: unknown): v is PersistedRuleMandate;
/** WHY a matching persisted allow rule did not clear the ask, in the sentence a PERSON reads — one row per
 *  word (the ladder this replaces ended in a catch-all arm). Display prose; nothing reads it back to decide. */
declare const MANDATE_DISCLOSURE: {
    readonly probe_mandate: "the reversibility check declared this call structurally gated (the built-in shell check raises this for a read outside the directories allowed for this session) — it is cleared by confirming this call, never by a standing allow rule";
    readonly probe_unanswered: "the reversibility check did not answer for this call (it timed out or failed), so whether the call reads outside the directories allowed for this session is unknown — it is cleared by confirming this call, never by a standing allow rule";
    readonly operator_always: "this deployment mandates per-call confirmation for shell commands (shellGate: always)";
    readonly tool_marks: "this tool carries egress/irreversibility/interaction marks (a mandated confirmation a rule cannot clear)";
    readonly write_protection: "this write's target is on the deployment's write-protection table (the agent's own configuration is guarded one look per write) — it is cleared by confirming this call, never by a standing allow rule";
    readonly write_protection_unresolved: "the write-protection judge could not resolve this write's target, so whether it lands on the protected table is unknown — it is cleared by confirming this call: no standing rule and no automatic decider answers for it, though a wired approver seat can";
};
/** The fence over the disclosure table: `never` while every word has a row. */
export type DisclosureTableCoversEveryMandate = AssertAllKeysHandled<Exclude<PersistedRuleMandate, keyof typeof MANDATE_DISCLOSURE>>;
/** The disclosed reason for one word. `undefined` — no mandate — is the lane's OTHER refusal: a standing
 *  "ask me each time" rule matched, which outranks the remembered yes on its own. */
export declare function mandateDisclosureOf(mandate: PersistedRuleMandate | undefined): string;
/**
 * The mandate provenance of one call, judged from the SAME mark inputs the gate is driven with —
 * the single source for "could a persisted allow rule clear this ask?". Allow rules silence the
 * classifier's questions, never a mandated one, and this predicate is the mandated-family half of
 * that boundary (the real-approval/governance half rides the decision's own `requiresRealApproval`
 * bit, which the org layer stamps):
 *  · `probeMandated` — this CALL's own reversibility probe declared its demotion STRUCTURAL
 *    (`"probe_mandate"`), judged FIRST because it is the only per-call member here: the three below
 *    are properties of the TOOL and are true of every call on the seat, so a mandate that is true of
 *    this one call must not be shadowed by the tier that happens to carry it (#502: the built-in
 *    shell probe raises it for a listed reader naming a path outside the session's roots — a boundary
 *    the deployment declared, which is exactly what the shape tier alone cannot say);
 *  · `egress` — the tool's own external-write mark, judged next: it is the tool's declaration even
 *    when the shell seat also installed a tier on the same name;
 *  · `shellGated` + tier `"always"` — the operator's `always` floor, a per-call confirmation
 *    (`"operator_always"`); without the floor the shell seat installs the shape tier's `"maybe"`,
 *    and THOSE asks stay the rule lane's home turf (`undefined`) — that is the don't-ask-again main case, and the per-call member
 *    above is deliberately the ONLY thing that carves a mandate out of it;
 *  · a tool's OWN `"always"`/`"maybe"` irreversibility tier without the doctrine (`"tool_marks"`).
 *
 * The two per-call write-protection seats sit BELOW the probe words and ABOVE the seat marks, on the
 * ladder's rule: a word no lane may answer outranks the rest, and a fact about THIS call outranks a
 * property of the tool. Judged HERE, not in {@link irreversibilityMandateOf} which {@link safetyAxisOf}
 * shares: such a write is not an irreversible action, and its park stays budgetable.
 *
 * Two consumers, one derivation: the gate's silencing arm (a matching rule is disclosed as shadowed
 * instead of clearing the ask) and the runner's suggestion factory (a mandated ask offers no
 * "stop asking me this" option — a rule minted from it would never clear it). A drift between the
 * two would let a card offer a rule the lane then refuses to honor. The per-call member reaches both
 * the same way every other per-call fact does: the gate stamps it on the surviving ask, the
 * synchronous sites read it off the decision they spread, and the park leg threads its own parameter.
 */
export declare function persistedRuleMandateOf(marks: MandateMarks): PersistedRuleMandate | undefined;
/** The mark inputs of the two mandate derivations — properties of the tool SEAT (`egress`, `shellGated`,
 *  `irreversibility`) beside the two per-call facts the gate stamps. */
export interface MandateMarks {
    egress?: boolean;
    /** The tool's server declared that a call must be put to a person
     *  ({@link import("../engine/loop/types.js").AgentTool.mcpRequiresUserInteraction}): a mark of the tool's own, like `egress` — no
     *  persisted allow rule or standing grant clears the ask, and the class `requires_user_interaction` rides every ask on the call. */
    requiresUserInteraction?: boolean;
    shellGated?: boolean;
    irreversibility?: "never" | "maybe" | "always";
    /** The surviving ask's engine-stamped `probeMandated` — see
     *  {@link import("./types.js").ReversibilityVerdict.mandated}. */
    probeMandated?: boolean;
    /** The gate's own record that the write-protection judge covered this call and its table MATCHED
     *  (`GatePass.writeProtected`, written at the fourth post-fold tighten for a write tool's target and by the read station
     *  for a shell command's written targets). A GATE fact like the org words: it rides the pass, not the decision, so no
     *  layer outside the engine declares it. */
    writeProtected?: boolean;
    /** The same record for the judge's other non-clear verdict — consulted, could not RESOLVE the target.
     *  The pair is the write face's twin of `probeMandated` / `probeUnanswered`, mutually exclusive. */
    writeProtectionUnresolved?: boolean;
    /** The pass's own record that the probe was consulted and did not answer (`GatePass.probeUnanswered`):
     *  the boundary is unknown, which no standing yes may stand in for. The runner's suggestion factory does
     *  not see the pass and never passes it — a card may offer a rule the lane then discloses as shadowed
     *  (loud, never a silent override), the same drift window the `matchedAskRule` conjunct already has. */
    probeUnanswered?: boolean;
}
/**
 * The SAFETY AXIS of one ask — what makes its durable park NON-BUDGETABLE (`irreversible_ask`), judged
 * from the same mark inputs as the rule-lane mandate plus the gate's own record of which tighten minted the
 * ask. `egress` is the tool's static external-write mark, as it always was. `irreversible` is a mandate of
 * THIS call: the tool's own tier (`tool_marks` — the deployment declared the tool may be irreversible,
 * and a policy or hook asking first does not un-declare it), the operator's per-call doctrine
 * (`operator_always`), the probe's structural verdict (`probe_mandate` / `probe_unanswered`), OR — under a
 * shell-seat-installed `"maybe"` tier — the shell gate's own tighten having minted this ask (the doctrine's
 * probe judged THIS command not reversible). A policy ask on a shell-gated tool that the doctrine's probe
 * did not tighten and did not mandate is a plain ask: the tier was the probe's instrument, not a risk
 * label, and the park stays the budgetable kind. Before this derivation the static tier alone set the bit,
 * so every policy ask on the full shell face parked non-budgetable.
 *
 * Deliberately NOT the rule-lane predicate verbatim: a shell tighten's ask is rule-clearable (a person's
 * past yes to this command shape stands) yet non-budgetable (a budget's mechanical yes is not a person's).
 * The two derivations share every other row through {@link irreversibilityMandateOf}.
 */
export declare function safetyAxisOf(marks: MandateMarks & {
    readonly tightened: AskOriginFacts["tightened"];
}): {
    egress: boolean;
    irreversible: boolean;
    shell?: boolean;
} | undefined;
/**
 * The permission-mode closed set — the ONE key a caller sets (`TaskSpec.permissionMode`), spelled as the wire and
 * the reference implementation spell it. A root task's absent key reads `default`; a child's absent key inherits
 * its parent's effective mode ({@link clampPermissionMode}).
 */
export declare const PERMISSION_MODES: readonly ["default", "acceptEdits", "plan", "auto", "bypassPermissions", "dontAsk"];
export type PermissionMode = (typeof PERMISSION_MODES)[number];
/** Membership test for {@link PermissionMode} — the word crosses process boundaries (a request body, a checkpoint row). */
export declare function isPermissionMode(v: unknown): v is PermissionMode;
/**
 * The reference implementation's mode RANK, used for one thing only: a child's declared mode may not outrank
 * the ceiling its parent hands down (a declaration is adopted iff `rank[declared] <= rank[ceiling]`, and
 * `acceptEdits` / `auto` are incomparable). It is a clamping relation, not a per-class strictness order —
 * `default` and `dontAsk` share a rank, and whether a mode releases a given ask is {@link MODE_ARBITRATION}'s.
 */
export declare const PERMISSION_MODE_RANK: {
    readonly plan: 0;
    readonly default: 1;
    readonly dontAsk: 1;
    readonly acceptEdits: 2;
    readonly auto: 3;
    readonly bypassPermissions: 4;
};
/** The fence over the rank table: `never` while every mode has a rank. */
export type RankTableCoversEveryMode = AssertAllKeysHandled<Exclude<PermissionMode, keyof typeof PERMISSION_MODE_RANK>>;
/**
 * Clamp a child's DECLARED mode under its parent's ceiling. An absent declaration inherits the ceiling; a
 * declaration the rank table does not admit under the ceiling is folded back to the ceiling (`clamped: true`
 * — the caller discloses it, never silently). `bypassPermissions` is adopted only under a `bypassPermissions`
 * parent, and the incomparable pair (`acceptEdits` ↔ `auto`) is never adopted across.
 */
export declare function clampPermissionMode(declared: PermissionMode | undefined, ceiling: PermissionMode): {
    effective: PermissionMode;
    clamped: boolean;
};
/** WHO put a tool's egress / irreversibility mark on the seat — the deployment's own declaration (`spec.tools`),
 *  a protocol server's self-report (MCP annotations), or the A2A default (every peer skill is an external write). */
export type ToolMarkSource = "deployment" | "mcp_annotation" | "a2a_default";
/**
 * The ask CLASS set — WHY a surviving ask exists, as the set of authorities that stand behind it. One ask can
 * belong to several classes at once (a hook door and an org verdict on one call); a class is derived from the
 * FACTS the gate's layers established ({@link classifyAsk}), never from the disclosed `origin` / `mandate`
 * words, which are single-winner projections and hide the members behind them. The array ORDER decides one
 * thing only: which member is the DISCLOSED word (`askClass`) when several hold — the door-before-word,
 * most-immune-first order; every lane reads the folded row ({@link foldArbitrationRow}), never the word.
 * `shell_floor` is the operator's `shellGate: "always"` floor on a shell-running seat — the question the `operator_always`
 * mandate word discloses, as a class of its own so a set that holds another forced question still holds it.
 * `requires_user_interaction` is the SERVER's declaration on the called tool (`_meta["anthropic/requiresUserInteraction"] === true`): a
 * permission question, unlike `content_question` (the reserved question tool's content-routing signal) — it carries that class's arbitration
 * row cell by cell, because the reference implementation judges the declaration ahead of its allow-rule lookup and its bypass release.
 * `unstated` is the mode's answer for a call nobody stated: a tool whose permission seat is `passthrough` (it states no
 * permission of its own — a remote peer's tool) reached the end of the fold with an allow no layer stated.
 */
export declare const ASK_CLASSES: readonly ["content_question", "hook", "ancestor", "unverifiable_delete", "transcript_write", "org_rule", "governance_unavailable", "rule_unreadable", "ask_rule", "denial_limit_fallback", "requires_user_interaction", "operator_forced", "shell_floor", "write_protection_unresolved", "probe", "write_protection", "tool_marks_mcp", "tool_marks_a2a", "policy_rra", "artifact_shared_read", "mode", "unstated", "ordinary"];
export type AskClass = (typeof ASK_CLASSES)[number];
/** Membership test for {@link AskClass} — the word crosses the wire on `tool_end.gate.disposition`. */
export declare function isAskClass(v: unknown): v is AskClass;
/** The four switches a class carries, one row per class ({@link MODE_ARBITRATION}). */
export interface ModeArbitrationRow {
    /** The ask still reaches a person under `bypassPermissions` (the reference implementation's `bypassImmune`). */
    readonly bypassImmune: boolean;
    /** The auto-mode classifier may answer it. */
    readonly classifierEligible: boolean;
    /** The sandbox-admission leg may admit it (containment holds the effect). */
    readonly sandboxAdmission: boolean;
    /** A persisted allow rule may clear it. */
    readonly ruleMayClear: boolean;
    /** The reference implementation's rule / safety check answers `ask` for it, so a hook's ask beside it is not the hook's
     *  forced decision: the call runs the full permission pipeline (the reference's `hookAskFloor`) — under `dontAsk`, a
     *  refusal rather than a person's question. */
    readonly hookAskFloor: boolean;
}
/**
 * The mode × class table. `bypassImmune` is the one new column (nothing said it before); the classifier and
 * sandbox columns are the fold of the two former per-word lane tables (only `probe` moved: the classifier may
 * now answer it — the read boundary's unestablishable spelling is fed to it as a deny-table row instead), and
 * `ruleMayClear` restates {@link ruleClearanceOf}'s conclusion per class (a real-approval bit is never rule-clearable).
 * `mode` is the permission mode's OWN question (`plan`'s two sentences, `decisionReason: "mode"`): the reference
 * implementation puts it to a person past every allow rule and never to an automatic lane, and its bypass conversion
 * releases it like any other non-immune ask. `unstated` is the reference implementation's passthrough conversion: an allow
 * rule or a bypass mode clears it, auto mode's classifier answers it, and a sandbox admits nothing on its behalf (the
 * tool's effect lands at a remote peer, outside any containment the leg declares). `hookAskFloor` marks the classes the reference's rule / safety check answers
 * `ask` for — an ask rule, a question the tool needs a person for, a dangerous removal, a protected or unresolvable write
 * target, the session transcript, another's shared artifact.
 */
export declare const MODE_ARBITRATION: {
    readonly content_question: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: true;
    };
    readonly hook: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly ancestor: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly unverifiable_delete: {
        readonly bypassImmune: true;
        readonly classifierEligible: true;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: true;
    };
    readonly transcript_write: {
        readonly bypassImmune: false;
        readonly classifierEligible: true;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: true;
    };
    readonly org_rule: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly governance_unavailable: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly rule_unreadable: {
        readonly bypassImmune: false;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly ask_rule: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: true;
    };
    readonly denial_limit_fallback: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly requires_user_interaction: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: true;
    };
    readonly operator_forced: {
        readonly bypassImmune: true;
        readonly classifierEligible: true;
        readonly sandboxAdmission: true;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly shell_floor: {
        readonly bypassImmune: true;
        readonly classifierEligible: true;
        readonly sandboxAdmission: true;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly write_protection_unresolved: {
        readonly bypassImmune: true;
        readonly classifierEligible: false;
        readonly sandboxAdmission: true;
        readonly ruleMayClear: false;
        readonly hookAskFloor: true;
    };
    readonly probe: {
        readonly bypassImmune: false;
        readonly classifierEligible: true;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly write_protection: {
        readonly bypassImmune: false;
        readonly classifierEligible: true;
        readonly sandboxAdmission: true;
        readonly ruleMayClear: false;
        readonly hookAskFloor: true;
    };
    readonly tool_marks_mcp: {
        readonly bypassImmune: false;
        readonly classifierEligible: true;
        readonly sandboxAdmission: true;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly tool_marks_a2a: {
        readonly bypassImmune: false;
        readonly classifierEligible: true;
        readonly sandboxAdmission: true;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly policy_rra: {
        readonly bypassImmune: true;
        readonly classifierEligible: true;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly artifact_shared_read: {
        readonly bypassImmune: false;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: true;
    };
    readonly mode: {
        readonly bypassImmune: false;
        readonly classifierEligible: false;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: false;
        readonly hookAskFloor: false;
    };
    readonly unstated: {
        readonly bypassImmune: false;
        readonly classifierEligible: true;
        readonly sandboxAdmission: false;
        readonly ruleMayClear: true;
        readonly hookAskFloor: false;
    };
    readonly ordinary: {
        readonly bypassImmune: false;
        readonly classifierEligible: true;
        readonly sandboxAdmission: true;
        readonly ruleMayClear: true;
        readonly hookAskFloor: false;
    };
};
/** The fence over the arbitration table: `never` while every class has a row. */
export type ArbitrationTableCoversEveryClass = AssertAllKeysHandled<Exclude<AskClass, keyof typeof MODE_ARBITRATION>>;
/** The classes whose own mint puts the `requiresRealApproval` bit on the ask — a bit one of them explains is not
 *  the bare bit `policy_rra` names. The mark classes (probe / write protection / tool marks / operator forced / the
 *  shell floor) mint no bit and explain none — except `requires_user_interaction`, whose declaration the origin stamp
 *  turns into the bit on every ask of the tool. */
export declare const RRA_EXPLAINERS: ReadonlySet<AskClass>;
/** The classes a permission mode can RELEASE (the `bypassImmune: false` rows) — the only words that can appear on
 *  `GateDisposition.released.askClass`; every other class reaches a person under every mode. */
export declare const RELEASABLE_ASK_CLASSES: readonly AskClass[];
/**
 * The closed set of `reason` words a mode release carries (`GateDisposition.released.reason`, the `released` arm of
 * {@link Arbitration}): each is a releasable class the release record names on its own — `rule_unreadable`, a release
 * over a person's rule the call could not be read against. A release carries the first member (array order) its class
 * set holds, and none when it holds no member. {@link arbitrateByMode} and {@link isModeReleaseReason} read one copy
 * of this list taken at load (a caller that writes into the exported array changes neither), so a new reason is one
 * word here.
 */
export declare const MODE_RELEASE_REASONS: readonly ["rule_unreadable"];
export type ModeReleaseReason = (typeof MODE_RELEASE_REASONS)[number];
/** Membership test for {@link ModeReleaseReason} — the word crosses the wire on `tool_end.gate.disposition.released.reason`. */
export declare function isModeReleaseReason(v: unknown): v is ModeReleaseReason;
/**
 * The facts the class derivation reads beside the decision: the origin facts the gate holds, plus the tool
 * seat's mark PROVENANCE ({@link ToolMarkSource}; a mark with no source reads as the deployment's, fail-closed),
 * the A2A isolation switch (a set switch turns the A2A default into an operator's mandate), and — at an
 * ancestor station — the classes the ancestor's own ask carries.
 */
export interface AskClassFacts extends AskOriginFacts {
    readonly toolMarkSources?: ReadonlySet<ToolMarkSource>;
    readonly a2aIsolatePeers?: boolean;
    readonly ancestorClasses?: ReadonlySet<AskClass>;
}
/** The classification the arbitration reads: the whole set, the disclosed word (the set's first member in
 *  {@link ASK_CLASSES} order) and the row folded across the set. */
export interface AskClassification {
    readonly classes: ReadonlySet<AskClass>;
    readonly askClass: AskClass;
    readonly row: ModeArbitrationRow;
}
/** The two members an ask's classification puts on the approval request and the park row: the disclosed word and
 *  the whole set, the set as an array in {@link ASK_CLASSES} order (so `classes[0] === askClass`). */
export interface AskClassMembers {
    readonly askClass: AskClass;
    readonly classes: readonly AskClass[];
}
/** The ONE projection of a classification onto {@link AskClassMembers} — every station that carries an ask's class
 *  spells it through this, so the word and the set cannot be read from two derivations. The array is a fresh frozen
 *  copy per call: an approver that holds it cannot reach the park row's copy or the next card's. */
export declare function askClassMembersOf(c: AskClassification): AskClassMembers;
/** The screen a boundary applies before it writes the pair where a host reads it back (the park row): both or
 *  neither, and only a well-formed pair — every member of `classes` in the closed set, no repeat, and `askClass` its
 *  first member in {@link ASK_CLASSES} order. The copy returned is fresh and frozen, never the caller's array. */
export declare function screenAskClassMembers(askClass: unknown, classes: unknown): AskClassMembers | undefined;
/**
 * The DECISION half of the class derivation — what ONE ask says about itself through its own members, read
 * per contributing ask at the policy fold so a member the fold does not copy (a guard mark, the unreadable-rule
 * fact) is not lost behind the ask that won the slot. The bare-bit rules are applied over THIS ask's own set:
 * `requiresRealApproval` with no explaining class is `policy_rra`; a self-declared `mandated` with no other
 * class is `operator_forced` (a deployment probe's own mandate, read conservatively). Returns the specific
 * classes only — an empty set is "nothing but hesitation", which the caller reads as `ordinary`.
 */
export declare function decisionAskClassesOf(d: Extract<PermissionResult, {
    action: "ask";
}>): Set<AskClass>;
/** Fold a class set onto one row: `bypassImmune` and `hookAskFloor` by OR (any immune member keeps the ask; any floored
 *  member floors a hook's ask), the three permissive switches by AND (one refusing member withholds the lane). An empty
 *  set is `ordinary`. */
export declare function foldArbitrationRow(classes: ReadonlySet<AskClass>): ModeArbitrationRow;
/**
 * The ONE class derivation: the decision half ({@link decisionAskClassesOf}) joined with the gate's own facts —
 * the question tool, an ancestor's mark, the org layer's two arms, the persisted lane's unreadable call, the tool
 * seat's marks with their provenance, the read probe and the write-protection judge's records — then the bare
 * real-approval rule re-read over the WHOLE set (an org verdict explains a bit the policy did not mint). The
 * derivation reads facts and members, never the `origin` / `mandate` words.
 */
export declare function classifyAsk(d: Extract<PermissionResult, {
    action: "ask";
}>, facts: AskClassFacts): AskClassification;
/** The refusal the model reads when an ask reaches a run with no approver: the reference implementation's sentence
 *  (2.1.280) — the call did NOT run because this session has no approval surface — with the run's own fact beside it
 *  and the ask's prompt as what was requested. The gate appends the caller-side remedy. */
export declare function headlessDenialMessage(toolName: string, askMessage: string): string;
/** The state of the approver seat a remedy is written for: `absent` — no approver is wired for the leg; `unavailable` — one is
 *  wired and answered that nobody is reachable for this ask. Absent in the input reads as `absent` (the posture the
 *  function was written for). */
export type HeadlessApproverSeat = "absent" | "unavailable";
/**
 * The ONE sentence a headless auto-deny (an ask that reached its final deny because no approver is wired) adds
 * after its refusal: the caller-side change that would have let this call run. Read off the effective mode, the
 * ask's folded class row and whether the call is a covered write (the mode write gate's own question):
 *  · a class no permission mode releases (`bypassImmune`) — only a person can answer it: wire an approver;
 *  · the plan mode's own question (`mode`) — leave plan (steer the mode, or approve the plan) or wire an approver;
 *  · the write gate's own ask under `default` / `auto` — `acceptEdits` lets writes inside the working
 *    directories through without asking;
 *  · the same ask under `acceptEdits` — the target lies outside the working directories: add its directory to
 *    `additionalDirectories`;
 *  · any other releasable ask — `bypassPermissions` releases it (disclosed on the call).
 * Every arm also names the approver, in the clause of the seat's own state ({@link HeadlessApproverSeat}): none wired ⇒
 * wire one; one wired that answered `unavailable` for this ask ⇒ make a person reachable through it (a wired approver is
 * never told to be wired). The refusal's tool text and the `delegation.ask_unresolvable` notice both carry this sentence —
 * they call this function, neither spells it.
 */
export declare function headlessApprovalRemedy(input: {
    readonly mode: PermissionMode | undefined;
    readonly classification: AskClassification | undefined;
    readonly coveredWrite: boolean;
    readonly unplacedOptions?: {
        readonly program: "cp" | "mv";
    };
    readonly approverSeat?: HeadlessApproverSeat;
}): string;
/** The reference implementation's sentence (2.1.280) for a `cp` / `mv` the mode will not answer because it carries option
 *  words — the approval card's message under `acceptEdits` and the head of the headless remedy. */
export declare function fileCommandOptionsSentence(program: "cp" | "mv"): string;
/** What the mode arbitration decided for one surviving ask. `ask` hands it on unchanged (park / approver); `accepted` is
 *  the mode answering an ordinary question itself (`acceptEdits` on one of its file commands — an allow of the mode's own
 *  policy, like its in-root write gate, and no release: nothing is disclosed as released);
 *  `released` is an allow the mode granted (disclosed on `tool_end.gate.disposition.released`); `denied_by_mode`
 *  is a refusal the mode made (`deniedBy: "mode"`). */
export type Arbitration = {
    kind: "ask";
} | {
    kind: "accepted";
    mode: "acceptEdits";
} | {
    kind: "released";
    mode: PermissionMode;
    askClass: AskClass;
    classes: readonly AskClass[];
    origin?: AskOrigin;
    mandate?: PersistedRuleMandate;
    reason?: ModeReleaseReason;
} | {
    kind: "denied_by_mode";
    mode: "bypassPermissions" | "dontAsk";
    askClass: AskClass;
    classes: readonly AskClass[];
};
/**
 * The ONE mode read point. `dontAsk` refuses every ask — one value for every class — but a hook's that no member of the
 * set floors (`hookAskFloor`: a hook's ask beside a rule's or a safety check's ask is refused, as the reference's full
 * pipeline refuses it; a hook's ask alone is the reference's forced decision, which never reaches that conversion);
 * `bypassPermissions` refuses a write whose protection could not be judged (a person cannot be asked, and
 * releasing would be looser than the allow the judge already refuses), keeps every immune class and releases the
 * rest; `acceptEdits` answers an ORDINARY ask itself on a call the caller vouches is one of the mode's file commands
 * (`call.fileCommand`, `accepted`); `plan`, `default` and `auto` hand every ask on (plan's own questions were raised by
 * its write gate; the reference implementation refuses nothing at this point under plan — auto's classifier reads the
 * row at its own station). Reads the class SET and the folded row — never the disclosed word.
 */
export declare function arbitrateByMode(mode: PermissionMode, c: AskClassification, disclosure?: {
    origin?: AskOrigin;
    mandate?: PersistedRuleMandate;
}, call?: {
    readonly fileCommand?: boolean;
}): Arbitration;
export {};