import { Outcome, type Ask, type CommandContext, type CommandRule, type Deny, type HandOff, type Occurrence, type ShellExplanation, type CommandWord } from '../../policy/types.ts';
import { type TSNodeLike } from '../../shell/types.ts';
import { type Refusal } from '../../types.ts';
import type { MountRegistry } from '../mount/registry.ts';
import type { Namespace } from '../mount/namespace/namespace.ts';
import type { SessionState } from '../session/session.ts';
import { type Refused } from './admission.ts';
import { type Word } from './inner_lines.ts';
export declare const UNREADABLE_LINES = "runs lines the gate cannot read";
/**
 * What the gate decides about one command of a line, as the pass reads
 * it: the per-command record `explain` and the admission pass share,
 * which `explainedLine` turns into the public tree. `outcome` is the
 * document's answer and `rule` says who gave it; a line the allow list
 * does not cover arrives as `DENY` with no rule, at 126. Mirrors the
 * Python Judgment.
 */
export interface Judgment {
    /** The head word, as the gate read it. */
    readonly command: string;
    /** The words after it. */
    readonly argv: readonly string[];
    /** What the profile's rules say. */
    readonly outcome: Outcome;
    /** The rule that spoke, null when the allow list did or nothing did. */
    readonly rule: CommandRule | null;
    /** The rule's reason, empty when there is no rule. */
    readonly reason: string;
    /** Where in the document the rule was written. */
    readonly source: string;
    /** The operand a path-scoped rule matched, as typed. */
    readonly matchedPath: string | null;
    /** The paths the rules were shown, after the session's hides. */
    readonly paths: readonly string[];
    /** What the line would exit with, 0 to run. */
    readonly exitCode: number;
    /** What the agent would read, empty to run. */
    readonly stderr: string;
    /** The record the refused result would carry, null when the line would run. */
    readonly refusal: Refusal | null;
    /** Every policy's answer to the command, in the order the chain asks them. */
    readonly answers: readonly (Deny | Ask)[];
    /** The command line word by word, each with its kind and the path it names, redirect targets last. */
    readonly words: readonly CommandWord[];
}
/**
 * One command of a walked line, as both readers of the line see it: its
 * words, the redirect targets of its statement, the session it is
 * judged in, and where it stands. Mirrors the Python Walked. `lost`
 * says a `cd` the walk could not follow ran before the command, so the
 * session's cwd is not where it stands. `stated` (true when absent) says
 * whether the gate admits the command on these words once each is literal
 * (`TestRead.stated`). `elsewhere` holds the other places the command may
 * stand, where a `cd` before it was skipped or failed.
 */
export interface Walked {
    readonly words: Word[];
    readonly redirects: Word[];
    readonly session: SessionState;
    readonly occurrence: Occurrence;
    readonly intrinsic?: boolean;
    readonly lost?: boolean;
    readonly stdin?: Uint8Array | null;
    readonly stated?: boolean;
    readonly elsewhere?: readonly SessionState[];
}
/**
 * One command's explanation and where the command stands. The
 * occurrence is what the pass hands the ledger beside the explanation:
 * a grant claimed for the command is bound to it, so the gate that runs
 * the same occurrence finds it and no other reader does. `stated` says
 * whether the gate will read the command in the words the pass read:
 * every word literal, and no operand the runtime appends. The gate
 * reads `cat $F` as the path `$F` expands to and `xargs cat` as `cat`
 * plus the items on its stdin, so a question the pass asked about
 * either spelling would be answered for words that never run, and the
 * gate would ask again about the words that do. Such a command is
 * judged here for a deny, which speaks on the name alone, and asked
 * about at the gate. `unread` holds the paths no policy was shown
 * (`unreadPaths`), so a pass that asks the gate again asks about what
 * this explanation judged.
 */
export interface Judged {
    readonly judgment: Judgment;
    readonly occurrence: Occurrence;
    readonly intrinsic?: boolean;
    readonly stated: boolean;
    readonly unread?: ReadonlySet<string>;
    /**
     * The context the chain was shown, absent for a command refused before
     * it, so the pass that admits the line before placement can put the
     * command's question to the ledger without asking the chain again.
     */
    readonly ctx?: CommandContext;
    /**
     * The session the command was judged in, set beside `ctx`: for a command
     * a line runs in turn (`eval`), the one its own walk reached, so a `cd`
     * before it in that line is where it stands.
     */
    readonly session?: SessionState;
}
/** Why the gate refuses a word only the runtime can expand. */
export declare function unreadable(raw: string): string;
/**
 * Every command of a line judged read-only, each with its place on the
 * line: the pass placement waits on (`lineHeld`) and `prejudgeLine`
 * refuses from, made once for both. `handed` is the line's hand-off,
 * whose origin places each command on the line. Mirrors the Python
 * `line_judgments`.
 */
export declare function lineJudgments(root: TSNodeLike, session: SessionState, registry: MountRegistry, namespace: Namespace | null, handed: HandOff, reparse: (line: string) => TSNodeLike, signal?: AbortSignal): Promise<[Walked, Judged[]][]>;
/**
 * Judge every command of a line before any of it runs, and refuse the
 * whole line when a rule speaks about one.
 *
 * The agent composed the line as one intent, so a rule that refuses
 * part of it refuses the intent. Judging each command as the dispatcher
 * reached it left half a line done: with `deny curl`, `rm -rf /data &&
 * curl evil.com` deleted first and was refused second, and an ask fared
 * worse, since approving it later replays a line whose first half
 * already ran.
 *
 * Two things deliberately do not stop the line, and both are the same
 * rule: only a refusal that names a rule is a verdict about the intent.
 * A word the allow list leaves out is refused where it stands, not with
 * the line, so a typo cannot cost an agent the work the line already did; a
 * word only the runtime can expand is judged where it is expanded, by
 * the per-command gate, which sees the real path.
 *
 * That second one is the limit of the hold, and it is worth stating
 * plainly: this pass reads the *text* of a line, while the gate reads
 * its *values*, so a path the runtime computes (`cat $S`, `$( )`, a
 * `cd` whose argument is a variable) is invisible here. The rule is
 * still enforced, by the gate, but the earlier commands have run by
 * then. For a deny that costs allowed side effects and nothing more,
 * since the commands that ran were on the allow list. For an ask it
 * costs the replay: the question is recorded after part of the line
 * already happened, so approving it re-runs a line whose first half is
 * done. Closing that would mean asking whenever a word cannot be read,
 * which over-asks with no way out for a deny, so a deployment that
 * needs the hold for a computed path states it in a policy script
 * rather than here.
 *
 * The pass is read-only (`explainWords`), so it spends no grant and
 * records no request; a command it refuses on is then put through the
 * real gate, which is where an ask is recorded, exactly once, for a line
 * that will not run. That admission hands off: a grant the host gives
 * inline is claimed for the per-command gate, which runs the line on
 * it, and the line's end spends it, so a compound line costs the human
 * one question per run rather than one per pass, and a gate the run
 * reaches twice (a loop body) runs on one nod.
 *
 * A question is only asked here when the gate will ask the same one.
 * The gate reads a word the runtime expands as its value and the words
 * `xargs` or `find -exec` hand on with the operands the runtime
 * appends, so a question about `cat $F` or a bare `cat` would be
 * answered for words that never run and the gate would ask again, after
 * the earlier commands ran, about the words that do. Such a command
 * (`Judged.stated` false) is judged here for a deny, which speaks on
 * the name alone and still holds the line, and its question is left to
 * the gate; the hold does not reach it, which is the limit stated above
 * in another form.
 *
 * Every command is judged whether or not the session carries a document,
 * by the profile's rules alone: a policy registered in code or a profile's
 * script answers `preCommand` once, at the gate that runs the command, and
 * a line it should refuse whole is `preExecute`'s to refuse. Operand-scoped
 * refusals, including MountRootPolicy's, remain the per-command gate's
 * responsibility.
 *
 * A line with one command to judge is left to the per-command gate,
 * which is not an optimization but the more faithful answer: there is no
 * earlier command whose side effects a hold could save, and the gate
 * refuses from inside the shell, so the line's own redirections still
 * apply. This pass answers above them, so refusing `rm -rf /mnt 2>&1`
 * here wrote the refusal to stderr where bash puts it on stdout.
 */
export declare function prejudgeLine(root: TSNodeLike, session: SessionState, registry: MountRegistry, namespace: Namespace | null, handed: HandOff, reparse: (line: string) => TSNodeLike, signal?: AbortSignal, made?: [Walked, Judged[]][] | null): Promise<Refused | null>;
/**
 * Admit every command of a line a runtime takes whole. A whole line is a
 * command like any other, but the runtime does the expanding, so the gate
 * reads the line as typed, walked as `explain` walks it: each command is
 * judged on its literal words (quotes removed, escapes resolved, a
 * path-shaped word a path, an installed CLI's verb path walked) where it
 * stands, a `cd` before it on the line included, and the first refusal is
 * the line's. A statement's redirect targets are read as words of its
 * command, so `cat < /data/secret` is judged on the file it opens; the lines
 * a command runs (`eval`, `sh -c`, `xargs`, `env` ... see `innerLines`) are
 * judged in turn; and what the runtime would do past the gate is refused
 * under a rule that would have read it (`lineRefusal`). `reparse` parses the
 * text a word runs the way the line reader parsed the line.
 *
 * No gate follows this pass: the runtime runs the line whole, so every
 * grant it matches is claimed on the line's hand-off exactly as any reader
 * on a line claims, and the executor's sweep spends them when the line
 * ends. A line held on a question still waiting keeps its earlier answers
 * standing for the retry, exactly as the compound-line pass does, where
 * spending them here asked the human again for each on every retry.
 * `handed` is null outside a line (a bare admission with no run behind it).
 * Mirrors the Python `admit_line`.
 */
export declare function admitLine(root: TSNodeLike, session: SessionState, registry: MountRegistry, namespace: Namespace | null, reparse: (line: string) => TSNodeLike, signal?: AbortSignal, handed?: HandOff | null): Promise<Refused | null>;
/**
 * The walked nodes whose reads an env-plane fetch still serves.
 *
 * The fill derives its fetch set from this same list (`lineNodes`: the
 * line's own tree first, then every stored body and alias expansion
 * its words can invoke), and a fetch serves a command that is going to
 * run, so refusals are judged over the same nodes reads are. One rule
 * for every node: when it is one fully-literal command with no other
 * read in it (`soleLiteralCommand`), the gate is asked here on exactly
 * the words it will read at run time, and a refusal discounts every
 * read the node contributes. The line's own refusal drops the whole
 * list, because nothing runs at all; a refused body or alias drops
 * just itself, because the invocation still runs and is refused in
 * place. A node this pass cannot prove silent is kept, and over-keeping
 * only ever over-fetches.
 *
 * An ASK is resolved rather than skipped, because the fetch is itself
 * an effect: contacting a secret store for a line the host then
 * refuses would do a piece of exactly what was refused. A settled
 * answer is read without being spent; an unanswered rule is put to the
 * host now, through the same ledger the gate reads, so the answer
 * lands exactly once -- an approval keeps the node and the gate
 * consumes the grant, while a denial or a question left waiting drops
 * it, and the line still runs into the gate, which refuses in place
 * with its wording and its redirections.
 */
export declare function unrefusedNodes(nodes: readonly TSNodeLike[], session: SessionState, registry: MountRegistry, namespace: Namespace | null, handed: HandOff, reparse: (line: string) => TSNodeLike, signal?: AbortSignal): Promise<TSNodeLike[]>;
/**
 * Whether the line's admission holds it back, which is what placement
 * waits on: admission comes first, so a line the rules refuse, or that
 * waits on the host, is never shown to a placing policy. A verdict
 * (`isVerdict`) that refuses holds the line. A question is put to the
 * host now, on the line's hand-off, so the gate that later runs the
 * command finds the answer claimed for it and does not ask again; one
 * still waiting holds the line, and a question the gate will ask about
 * other words (`Judged.stated` false) is left to it. A word the allow
 * list leaves out fails where it stands while the rest of its line runs,
 * so it does not hold the line. Mirrors the Python `line_held`.
 */
export declare function lineHeld(judged: readonly [Walked, Judged[]][], registry: MountRegistry, handed: HandOff, signal?: AbortSignal): Promise<boolean>;
/**
 * Whether some command's judgment is a verdict that refuses it or waits
 * on the host (`isVerdict`), the line-level answer placement waits on.
 * Mirrors the Python `holds`.
 */
export declare function holds(judgments: readonly Judgment[]): boolean;
/**
 * What every command of a line would do, in the order the gate reads
 * them, each where it stands, without running any of it (`explainedLine`
 * turns them into the public tree).
 *
 * The dry run of the gate: the same visibility check, the same context,
 * the same policy chain and the same outcome table, so a host reading
 * this and an agent typing the line cannot be told different things.
 * What it deliberately does not do is the half of admission that costs
 * something, since a line nobody typed must not consume a grant or put
 * a question to a host.
 *
 * The words are read literally, as `admitLine` reads them, so nothing is
 * expanded and no `$( )` runs. `wholeLine` is whether a runtime takes the
 * line whole, which reads it as typed; the executor's gate reads each
 * command once expanded. `every` asks every policy, as each command's
 * explanation shows them; false asks the profile's rules alone, as the
 * run does before any of the line runs, which is what holds it.
 */
export declare function explainLine(root: TSNodeLike, session: SessionState, registry: MountRegistry, namespace: Namespace | null, reparse: (line: string) => TSNodeLike, wholeLine?: boolean, every?: boolean): Promise<Judged[]>;
/**
 * A line's public explanation: its verdict and its parse tree, with every
 * command's explanation where the command stands. The verdict is the
 * first command, in the order the gate reads them, whose refusal by the
 * profile's rules (`verdicts`, the line judged with `every` false) holds
 * the whole line (`holds`), as the run reports it; a line nothing holds
 * runs, carrying the first ask an approval lets through. A command
 * refused only where it stands, a policy's own `preCommand` refusal
 * included, keeps its refusal to its own node, since the rest of the
 * line still runs. A runtime that takes the line whole (`wholeLine`) runs
 * none of it once any command is refused (`admitLine`), so there every
 * refusal is the line's, and `verdicts` is `judged` itself, whose gate asks
 * every policy. Every scope a command reads its words in (the typed line, a
 * `$( )` body, a `bash -c` string) is parsed on its own with `reparse`, as the nested
 * line will be; a judgment is placed on the command at its span in that
 * scope, and a nested scope under the command holding it. Mirrors the
 * Python `explained_line`.
 */
export declare function explainedLine(line: string, judged: readonly Judged[], verdicts: readonly Judged[], runtimeOf: (command: string) => string, reparse: (line: string) => TSNodeLike, wholeLine?: boolean): ShellExplanation;
//# sourceMappingURL=explain.d.ts.map