import type { ExecutionEnv } from "../../internal/harness-types.js";
import type { ToolEffect } from "../types.js";
import type { NamedToolPolicy } from "../tool-policy.js";
import { type SessionPermissionRules, type SessionPolicyStore, type SessionRuleEntryField, type StoredSessionRules } from "../session-policy-store.js";
import type { PathRuleBases } from "../permission-rule-model.js";
/** The FULL path-confinable write-tool set: every catalog tool declaring a write path target — `Write` / `Edit`
 *  (target `file_path`) and `NotebookEdit`, whose write target is `notebook_path` and is extracted by the shared
 *  tool-aware {@link writeTargetPath} (notebook_path outranks a decoy `file_path` for that tool).
 *
 *  REF-B3 — the ONE spelling of "tools this codebase can confine by path". Consumers:
 *  {@link createSessionRulePolicy} (below, `allowDirs`) and `createFsWriteGatePolicy`
 *  (core/fs-write-gate-policy.ts), which used to rebuild `["Write", "Edit", "NotebookEdit"]` inline.
 *  A fourth spelling survives as a LITERAL in core/tool-policy.ts (the transcript-dir gate) because THIS
 *  module imports tool-policy.ts — that one is a documented coordinate held in sync by a source-scan pin
 *  (SW-10), not a silent copy.
 *
 *  SW-10 relax (ruled 2026-08-04): `NotebookEdit` was previously absent from the session-rule face's
 *  covered set, so an `allowDirs` confinement denied it OUTRIGHT via the "mutating but not path-confinable"
 *  arm — a capability GAP relative to the fs-write gate, which has always proven the tool confinable.
 *  Both faces now share this set and the same target extractor, so the session face confines notebooks by
 *  path instead of refusing them. The fail-closed deny for every OTHER non-confinable mutating tool
 *  (bash, deployment-authored write tools, unknown effects) is unchanged. */
export declare const PATH_CONFINABLE_WRITE_TOOLS: ReadonlySet<string>;
/** Path-segment-aware containment, FAMILY-AWARE ([K-PLATFORM-SWEEP] MAJOR#2: canonical keys are
 *  backslash-form on a win32 env — the "/"-hardcoded suffix rejected every legitimate write there).
 *  Compare in "/" form with drive-letter case folded; never keyed on process.platform (the env may be
 *  remote and of the OTHER family than the host).
 *
 *  REF-B3 — exported as the single source for the policy modules that fence WRITES by an allowlist of
 *  directories: fs-write-gate-policy.ts (`acceptDirs`
 *  /`exemptDirs`), plus the K-PLATFORM sweep tests. The line here used to read "exported ONLY for the
 *  K-PLATFORM sweep tests", which was already false when written (fs-write-gate-policy.ts imports it in
 *  production), and a since-retired skill fence carried a byte-identical private twin — comment included — so
 *  a platform-family fix had to be remembered at two coordinates.
 *
 *  The sibling implementation is `within()` in tools/fs/safety.ts (the fs-tool root fence). Those stay
 *  separate functions on purpose — that one is private to the fence and keyed on canonical roots — but
 *  they must never disagree on a verdict; the empty-root arm below is that alignment. */
export declare function isWithin(root: string, p: string): boolean;
/**
 * Where a session-rule layer takes the rules it enforces.
 *
 * @contract session_rules.every_gate_re_reads — a session-rule layer re-reads its rule source at EVERY
 * tool gate, not once at prepare: an operator's mid-run write (a new deny, a narrowed allowlist) binds the
 * very next call of the SAME run, on the same beat as the permission-rule store's per-call read. `seed` is
 * the prepare-time snapshot and stays the FLOOR — a read that fails, a row that was deleted, or a lagging
 * replica whose `rev` is below the snapshot's enforces the seed rather than loosening toward it — and it is
 * also what the prepare-time policy-name audit sees, so a rule written after prepare is enforced but not
 * audited. A source with no `read` is a constant one: the seed at every gate, which is what a caller that
 * hands a plain rules object gets.
 */
export interface SessionRuleSource {
    /** The prepare-time snapshot: the audit's view, and the floor no live read may loosen. */
    seed: SessionPermissionRules;
    /** The per-call read, keyed on the call being adjudicated, and bounded by the gate's own signal. Absent
     *  ⇒ the seed is the whole source. */
    read?: (toolCallId: string, signal?: AbortSignal) => Promise<SessionPermissionRules>;
}
/**
 * The LIVE half of {@link SessionRuleSource}: re-read `(sessionId, principal)` from the store on each
 * adjudicated call, floored by `seed`.
 *
 * ONE law, two consumers (this task's own session rules and each inherited ancestor's), where the policy
 * chain used to carry a hand-written live-preferring read for ancestors only and a frozen snapshot for the
 * task's own rules. Direction, unchanged from that read and stated once here: a whole-row replacement
 * includes RELAXATION — an operator's authorized post-prepare rule change propagates in BOTH directions;
 * what the floor forbids is an UNAUTHORIZED loosening (a lagging replica, a deleted row, an unreachable
 * store), and nothing task-side can edit the rules row itself.
 *
 * The floor RATCHETS to the highest-rev row this layer has actually served, and a live row replaces it only
 * on a STRICTLY GREATER `rev`. Two reasons, and the second is why "greater or equal" is not good enough:
 *  · pinning the floor to the prepare snapshot would make "a failed read never loosens" false in the one
 *    sequence that matters — a row tightened mid-run, enforced at one gate, then unreadable at the next
 *    would fall back PAST the tightening to the snapshot the run started with;
 *  · `rev` is per-ROW, and both bundled backends compute it as `(prior?.rev ?? 0) + 1`, so a row that is
 *    DELETED and written again starts over at 1. At `>=`, a recreated row whose rev merely TIES the
 *    enforced one would replace it — an unauthorized loosening dressed as an update. At `>`, a tie enforces
 *    the floor, which is a no-op for the honest case (equal rev is the same row) and fail-closed for the
 *    recycled one. Closed since 7.25.0 by the STORE's lineage identity (`StoredSessionRules.gen`): a
 *    recreated row is another generation and is never compared to the enforced one, whatever its rev, and
 *    a backend without lineages (gen 0 throughout) keeps the `>` protection above.
 *
 * The per-call handoff mirrors the permission-rule lanes': the first reader of a `toolCallId` takes the read
 * and hands it on, a second reader of the SAME call claims that one snapshot, and an unclaimed entry is
 * evicted first-in-first-out past a bound. It is a cost device, never a semantic one — a reader that cannot
 * claim simply reads again. Its firing path here is the fold itself: `policyLayers` re-appends the narrowing
 * layers AFTER a rewrite-capable caller layer, so one adjudication asks this layer twice under one id, and
 * the leading (original args) and trailing (rewritten args) passes must judge ONE set of rules.
 *
 * Two bounds keep a handed entry from ever being a STALE one, because `toolCallId` is not always unique —
 * the turn-boundary read-visibility query reuses one synthetic id for every path it screens and for every
 * turn of the run. The entry is taken ONCE, and a claimed snapshot is served only while its `rev` still
 * stands at the enforced floor; a claim from a later adjudication, whose floor has moved on, falls through
 * to a fresh read. A real call's id is the provider's: unique within one assistant response only (`brain/tool-call-id.ts`).
 */
export declare function createLiveSessionRules(store: SessionPolicyStore, floor: {
    sessionId: string;
    principal?: string;
    seed: StoredSessionRules;
}, hooks?: {
    /** A rule source this layer could not read, once per distinct reason. */
    onUnreadable?: (reason: string) => void;
    /** The store row this layer now enforces, each time the floor ADVANCES. Never called for the synthetic
     *  empty seed: it reports rows the store actually served, which is what a child may inherit. */
    onEnforced?: (row: StoredSessionRules) => void;
}): (toolCallId: string, signal?: AbortSignal) => Promise<SessionPermissionRules>;
/**
 * Build the per-session deny-narrowing policy. For each tool call:
 *  - `toolDeny` → deny the calls its entries reach — the one list matcher's deny reading (`judgeAllowEntries`): a tool name
 *    under any name the call answers to, a protocol namespace, a shell command rule over every program the command line runs
 *    (a line it cannot read against the rule is refused too), a path rule over every view of the call's target.
 *  - `toolAllow` (if set) → deny any call no entry admits (the matcher's allow reading in the `narrowing` posture: every
 *    segment of a command line named by an entry; never the allow layer's auto-mode strip), naming the uncovered segment.
 *  - an entry that reads as nothing → in no row (it guards and grants nothing; the prepare-time audit names it, or
 *    `onEarlierGrammar(entries, bucket, grammar)`, once per distinct set, when its snapshot's own earlier grammar reads it so).
 *  - a snapshot that cannot be read (its shape; a `toolDeny` entry no grammar reads) → deny every call, never quoting it.
 *  - `allowDirs` (if set) → a WRITE tool must land inside one of the dirs (resolved via `canonicalizeTarget`,
 *    so a symlink can't carry across a write out); a write-capable tool that cannot be path-confined (e.g. `bash`)
 *    is DENIED while `allowDirs` is set (fail-closed).
 * The policy structurally emits no ask. Returns `allow` when nothing constrains the call. `env`/`rootPath` are the task's real
 * exec env + root, so matching sees the actual filesystem (a remote/E2B task resolves in its container). A relative TARGET
 * prefers the call's live tracked cwd ({@link ToolCallRequest.cwd}) when the engine stamps one; `allowDirs` entries always
 * resolve against `rootPath`, and a path rule's pattern against `anchors` (the leg's root and home — absent, `rootPath`
 * alone, so a home-relative pattern is unreadable and refuses in the deny direction).
 *
 * The rules are taken from a {@link SessionRuleSource} at EVERY call
 * ({@link SessionRuleSource} carries the law); a plain rules object is the constant source, which is what
 * every caller outside the policy chain hands, and what a layer whose rules cannot change under it needs.
 */
export declare function createSessionRulePolicy(rules: SessionPermissionRules | SessionRuleSource, opts: {
    env: ExecutionEnv;
    rootPath?: string;
    toolEffects?: ReadonlyMap<string, ToolEffect>;
    anchors?: PathRuleBases;
    onEarlierGrammar?: (entries: readonly string[], field: SessionRuleEntryField, grammar: number) => void;
}): NamedToolPolicy;
