/** * Native tool-call permission gate for the TypeScript engine. * * The TypeScript sibling of the Rust reference engine's `permission.rs`. A * {@link PermissionHook} runs the pure, deterministic {@link decide} classifier * on every tool call and blocks (throws) on a **Deny**. An **Ask** is routed to * a human approver (the existing {@link HumanGate} seam) when one is wired, and * **fails closed** (blocks) when it is not. * * The classification model is ported natively from smooth's `auto_mode` / * `smooth-narc::judge`. This is the security-critical core and is exhaustively * tested — including adversarial compound-command and credential-path inputs. * * A stored grant ({@link PermissionGrants}) auto-approves a matching `Ask` * without prompting, and answering "approve always" persists a new grant. A * grant can only upgrade an `Ask` — it can **never** waive a `Deny` * circuit-breaker. A consumer-supplied {@link DenyPolicy} is evaluated FIRST and * is a circuit-breaker too: no grant waives it and no mode downgrades it. */ import type { DenyPolicy } from './denyPolicy.js'; import type { HumanGate } from './humanGate.js'; import { PermissionGrants } from './permissionGrants.js'; import type { ToolCall, ToolHook } from './agent.js'; export type { ToolCall, ToolHook }; /** * How aggressively the hook enforces. Mirrors the Rust engine's `AutoMode` (a * trimmed Claude Code `auto-mode` set). Selected via the `SMOOTH_AUTO_MODE` env * var through {@link autoModeFromEnv}. */ export declare enum AutoMode { /** Read-only allow, mutating ask, dangerous deny. Default. */ Ask = "ask", /** Like {@link AutoMode.Ask} but filesystem-edit (`Write`) tools auto-approve. Mirrors `acceptEdits`. */ AcceptEdits = "accept-edits", /** Like {@link AutoMode.Ask} but an unmatched verdict is a deny (fail-closed). Headless / CI posture (`dontAsk`). */ DenyUnmatched = "deny-unmatched", /** Allow everything **except** the hard circuit-breakers. Escape hatch (`bypassPermissions`, which keeps its breakers). */ Bypass = "bypass" } /** Parse a `SMOOTH_AUTO_MODE` value. Unknown / unset ⇒ {@link AutoMode.Ask}. */ export declare function autoModeFromValue(v: string | undefined): AutoMode; /** Read the mode from the process `SMOOTH_AUTO_MODE` environment variable. */ export declare function autoModeFromEnv(): AutoMode; /** The pure verdict returned by {@link decide}. A discriminated union on `kind`. */ export type Verdict = { kind: 'allow'; } | { kind: 'deny'; reason: string; } | { kind: 'ask'; reason: string; }; /** Match a domain against a suffix list (exact or subdomain), case-insensitive. */ export declare function domainMatchesSuffixList(domain: string, suffixes: readonly string[]): boolean; /** * Split a shell command line into subcommands on the operators that sequence * independent commands: `&&`, `||`, `;`, `|`, `&`, and newlines. Command / * process substitution (`$(…)`, `` `…` ``, `<(…)`) is surfaced as its own * segment so it can't ride in on a safe outer command. * * ponytail: substring split, not a real shell lexer — upgrade only if quoting * edge-cases (`echo "a && b"`) start mattering for policy. */ export declare function splitCompound(command: string): string[]; /** Pull a bare hostname out of a URL-ish or `host:port` token. */ export declare function hostFromToken(tok: string): string | undefined; /** Extract candidate hostnames from a single (already split) net-tool subcommand. */ export declare function extractHosts(subcommand: string): string[]; /** * Strip leading transparent wrappers and any leading `sudo` from a single * subcommand, returning the remaining command text. Used by the deny policy so a * rule anchored on the real binary (`aws …`) still matches `sudo aws …` / * `timeout 5 aws …`. */ export declare function stripWrappersAndSudo(subcommand: string): string; /** Category a tool falls into, derived from its name. Drives the default posture for non-bash tools. */ export type Category = 'bash' | 'network' | 'write' | 'safe' | 'unknown'; export declare function toolCategory(name: string): Category; /** * The pure, deterministic permission decision. No async, no I/O — the * security-critical core, tested exhaustively. * * `args` is the raw tool-call argument object; the relevant field is pulled per * category (`cmd` for bash, `path` for writes, `url`/`host` for network). */ export declare function decide(mode: AutoMode, toolName: string, args: Record): Verdict; /** * A {@link ToolHook} that enforces {@link decide} on every tool call. * * **`Ask` routing**: with an approver wired via {@link PermissionHook.withApprover} * (the existing {@link HumanGate} seam), an `Ask` verdict prompts a human and * blocks until they approve; with no approver it **fails closed** (throws). * {@link AutoMode.Bypass} / {@link AutoMode.AcceptEdits} downgrade eligible asks * inside {@link decide} before they reach the approver. A `Deny` always blocks * and is never routed to the human — circuit-breakers are not waivable. A * consumer {@link DenyPolicy}, when attached, is evaluated FIRST and is a * circuit-breaker of the same tier. */ export declare class PermissionHook implements ToolHook { private readonly autoMode; private approver?; private grants?; private denyPolicyRef?; constructor(autoMode?: AutoMode); /** Build a hook reading the mode from `SMOOTH_AUTO_MODE` (default `Ask`). */ static fromEnv(): PermissionHook; /** * Wire an interactive approver. When set, an `Ask` verdict consults the * {@link HumanGate} and blocks on its response — approve lets the call run, * anything else blocks it. Answering with `remember: true` * ({@link approveAlways}) persists a grant when {@link withGrants} is set. */ withApprover(gate: HumanGate): this; /** * Wire the in-memory allow-list. A matching grant auto-approves an `Ask` * *before* prompting; an `approve always` answer adds a fresh grant so the * next identical `Ask` is silent. The consumer owns persisting `grants`. */ withGrants(grants: PermissionGrants): this; /** * Attach a consumer {@link DenyPolicy}. Purely additive: with none attached * enforcement is identical to before. When set it is evaluated **first** — a * match is a hard deny (circuit-breaker tier) no grant can waive and no * {@link AutoMode} can downgrade. */ withDenyPolicy(policy: DenyPolicy): this; /** The mode this hook enforces. */ get mode(): AutoMode; preCall(call: ToolCall): Promise; /** Add an approve-always grant to the in-memory allow-list. */ private persistGrant; } //# sourceMappingURL=permission.d.ts.map