import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
import { type BeforeWriteHook, type FileEditedHook, type TrackFileEditHook } from "../../core/types.js";
import { type TaskRegistry } from "../../core/task-registry.js";
import { type ReadFileState } from "./safety.js";
import type { PdfModelCapabilities } from "./pdf.js";
export * from "./fs-shared.js";
export * from "./fs-pdf.js";
export * from "./fs-read.js";
export * from "./fs-write.js";
export * from "./fs-search-tools.js";
export * from "./bash-readonly-classifier.js";
export * from "./fs-bash.js";
export * from "./read-deny.js";
export { readDenyHomesOf } from "./read-deny-home.js";
import type { ShellReadAdmission } from "./shell-read-check.js";
export { BASH_CLASSIFY_EXTRA_ALLOW, bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
import { type ReadDenyEntry } from "./read-deny.js";
import { type ContainmentFace } from "./containment-face.js";
export * from "./containment-face.js";
import { type CwdRef, type ReadImageDownsamplerOption } from "./fs-shared.js";
/** Options for {@link createHandsToolkit}. */
export interface HandsToolkitOptions {
    /** CC --add-dir parity: extra allowed containment roots (canonical). Widens file-tool containment
     *  only; the primary root keeps the cwd/base role. The full `bash` (effect:write) is NOT constrained
     *  by these — it is deliberately unsandboxed and only uses them for its post-`cd` disclosure note.
     *  `bash_readonly` IS constrained by them (that face refuses a read outside the roots), so widening
     *  here widens what the read-only band may read. */
    additionalRoots?: readonly string[];
    /** The read-boundary whitelist: extra allowed containment roots for the READ faces only —
     *  read_file/grep/glob/repo_map and `bash_readonly` admit them; edit_file/write_file/notebook_edit
     *  and the full `bash`'s disclosure roots deliberately do NOT (widening reads must never silently
     *  widen writes). Read per call, never copied: a caller that grows the array it passed (the Runner's
     *  in-flight read-root grant) widens the structured read tools for their later calls. Caveat, the same tradeoff `additionalRoots` states:
     *  `bash_readonly`'s COMMAND allowlist is a deployment declaration — a
     *  deployment that puts a mutating command into `bashReadonlyAllow` has declared it read-safe,
     *  and that declaration applies inside every root, these included. Keep the allowlist genuinely
     *  non-mutating if these roots must stay read-only through the shell face too. */
    additionalReadRoots?: readonly string[];
    /** Mount a shell tool. Pass false for an env without a real shell (e.g. `StubExecutionEnv`) so a bash
     *  tool that can only error never reaches the model. Default false. */
    includeShell?: boolean;
    /** Verifier read-only boundary: mount ONLY effect:read hand tools — read_file/grep/glob
     *  (+ `bash_readonly` when `includeShell`). Drops edit_file/write_file and the full `bash`. Default false. */
    readOnly?: boolean;
    /** Override the `bash_readonly` command allowlist (default {@link BASH_READONLY_DEFAULT_ALLOW}). */
    bashReadonlyAllow?: readonly string[];
    /** The gate's shell read station's pre-spawn ADMISSION (`ShellReadAdmission`, shell-read-check.ts), handed to
     *  BOTH shell faces this band mounts: what a shell tool runs is what the station judged before approval, and
     *  the admission re-establishes the same candidates right before the spawn. The station itself — ONE parse of
     *  the command, its candidates established on the filesystem, judged by the read deny seat and the read
     *  roots — is the gate's (`ToolGateInput.shellReadCheck`, built by the Runner's hands phase for every shell
     *  face and every doctrine); this band judges nothing on its own.
     *
     *  ABSENT ⇒ no station judged this mount (a library-direct toolkit with no gate): the full shell runs
     *  unconfined as it always did, and `bash_readonly` confines NOTHING — its containment IS the station, so a
     *  consumer mounting the read-only band outside the Runner must run `runToolGate` with a station of its own
     *  (`createShellReadStation`). */
    shellReadAdmission?: ShellReadAdmission;
    /** The gate's file read station's ADMISSION (`FileReadStation.admitted`, file-read-check.ts), handed to Read / Grep /
     *  Glob: an out-of-root target the gate put to a question and then let through is read once, on the canonical key the
     *  station judged. ABSENT ⇒ no station (a library-direct toolkit): the tools refuse every out-of-root target. */
    readAdmission?: (toolCallId: string | undefined, canonicalKey: string) => boolean;
    /** Commit-message attribution trailer for the `bash` git protocol. Default NONE — attribution is a
     *  deployment identity asset, so the branded deployment sets it; pass `"Name <email>"` to advertise
     *  one, `false` (≡ unset) for no trailer section. */
    commitCoAuthor?: string | false;
    /** The caller's own {@link CwdRef} to track this task's logical cwd, instead of one
     *  created internally. The Runner passes its own so it can OBSERVE `cd` moves (`cwdRef.current` changes)
     *  and emit a `workspace_changed` event. Ignored in `readOnly` mode (no `cd`). Default: a fresh ref at root. */
    cwdRef?: CwdRef;
    /** Optional process-local registry for unified task_id (`b*`) handles. */
    taskRegistry?: TaskRegistry;
    /** Runner-owned owner/scope fallback used when a tool execute context is unavailable in tests. */
    taskOwner?: string;
    taskScope?: string;
    /** See createBashTool's taskOpts.sessionId: session-resident background commands. */
    sessionId?: string;
    /** The task lane of THE retain predicate (`retainsBackgroundProcesses`): the serving run's
     *  `TaskSpec.retainBackgroundProcesses`. The Runner fills it from the spec of the run it builds this band
     *  for; a direct mount passes its own declaration. Read by Bash's lifetime sentence (description +
     *  receipts) and stamped onto every background row Bash registers (stated). Absent ⇒ not stated: the rows
     *  leave the lane to the owning run's teardown. */
    retainBackgroundProcesses?: boolean;
    /** Mount TaskOutput/TaskStop directly from this band. Runner sets false and mounts the unified dispatcher once. */
    mountBackgroundTaskTools?: boolean;
    /** Completion-notification sink for finished background commands (threaded to createBashTool's
     *  onTaskNotification; the Runner wires the run-local injection lane here). */
    taskNotification?: (n: import("../../core/task-notification.js").TaskNotificationPayload, opts?: {
        priority?: "now" | "next" | "later";
    }) => void;
    /** The run-local per-tool-call detach hub (mid-flight ctrl+b → adopt as background). */
    detachHub?: import("../../core/tool-detach.js").ToolDetachHub;
    /** The Bash `timeout` parameter's DEFAULT budget, in ms — applied when the model omits `timeout`.
     *  Resolution (options over env over constant — the standing sema discipline: deployments configure
     *  the tool, not the process): this field ?? env `BASH_DEFAULT_TIMEOUT_MS` ?? 120000; invalid
     *  (non-finite / <= 0) values are discarded at every leg. See
     *  {@link import("./fs-shared.js").resolveBashTimeoutCaps} — the SAME resolver renders the tool
     *  description, so the advertised numbers always match the enforced ones. Absent ⇒ 120s. Applies to
     *  both shell legs (`bash` and `bash_readonly`). */
    bashDefaultTimeoutMs?: number;
    /** The Bash `timeout` parameter's engine CEILING, in ms — requests above it are clamped (the
     *  requested/capped disclosure names THIS value). Resolution: (this field ?? env
     *  `BASH_MAX_TIMEOUT_MS` ?? 600000), then floored at the resolved default — CC's own only-widen
     *  semantic: a ceiling below the default is a narrowing intent = invalid, ignored in favor of the
     *  default. Absent ⇒ 600s. */
    bashMaxTimeoutMs?: number;
    /** Structured sink for the timeout-cap discard announcements both shell legs emit at mount (see
     *  {@link import("../../core/types.js").EngineNotice} / `RunnerDeps.onNotice`). Wired, it REPLACES
     *  their `console.warn` line (a forwarding host must not show every fact twice); absent, the
     *  console line prints verbatim (last-resort loudness). Threaded per toolkit build — never a
     *  global. */
    onNotice?: (n: import("../../core/types.js").EngineNotice) => void;
    /** See createBashTool's taskOpts.oneShot / createTaskOutputTool's TaskToolOptions.oneShot for the
     *  full contract: this run has no later turn for an async background notification to land in.
     *  Threaded to both the Bash background-launch receipt and (when this band mounts TaskOutput itself,
     *  `mountBackgroundTaskTools: true`) the TaskOutput poll receipt. Absent ⇒ the unbranched wording. */
    oneShot?: boolean;
    /** CC `Zry`/`WZi.#m` parity: see createBashTool's taskOpts field of the same name for the full
     *  contract. Threaded from {@link HandsBandOptions.autoBackgroundOnTimeout} (a deployment-level
     *  opt-in) — NOT from `detachHub`/`taskRegistry` merely being present, which are wired unconditionally
     *  by the Runner for every task and so cannot double as an opt-in signal. Default undefined ⇒ `false`
     *  downstream. */
    autoBackgroundOnTimeout?: boolean;
    /** The run's tool-progress sink — see createBashTool's taskOpts field of the same name. Runner-wired
     *  (a late-bound cell behind the closure), absent for a standalone toolkit build ⇒ the shell's execute
     *  leg opens no streaming callbacks at all and its `env.exec` options are byte-identical to before. */
    onToolProgress?: (body: import("../../core/runner/contracts.js").ToolProgressBody) => void;
    /** Is a PERSON's input waiting for this run's next turn boundary — see createBashTool's taskOpts field
     *  of the same name. Runner-wired (reads the harness's own accepted-but-unserved account); absent ⇒ the
     *  shell never auto-backgrounds a running command, exactly as before. */
    queuedHumanInput?: () => boolean;
    /** Read-image downsampler seam ({@link ReadImageDownsamplerOption}). Default undefined = auto-detect
     *  the optional sharp dependency once per process; `false` disables it (deterministic no-sharp behavior). */
    readImageDownsampler?: ReadImageDownsamplerOption;
    /** PDF degradation chain: the SERVING model's PDF capability profile (prepare-task derives it via
     *  {@link import("./fs-pdf.js").pdfModelCapabilitiesOf}). A model without native document input gets pdftotext text extraction →
     *  rendered page images (vision) → an honest placeholder, instead of a brain-level placeholder. Default
     *  undefined = fully capable (native document block). */
    pdfModelCapabilities?: PdfModelCapabilities;
    /** Write-time content gate for Write/Edit/NotebookEdit (see {@link BeforeWriteHook}). The Runner
     *  wires the MemoryEngine's memory-domain scan here; absent ⇒ byte-identical behavior. */
    beforeWrite?: BeforeWriteHook;
    /** First-touch file-history hook for Write/Edit/NotebookEdit (see
     *  {@link TrackFileEditHook}): called after the content gates pass, immediately before the final
     *  env write, so the file's pre-edit state is durably recorded before it changes. The Runner
     *  wires it to `FileHistoryStore.trackEdit`; absent ⇒ byte-identical behavior (no history). */
    trackFileEdit?: TrackFileEditHook;
    /** The mutation lane's LANDED observation seat — fired once per Write/Edit/NotebookEdit call whose
     *  bytes reached disk (never for a refused/failed one, never for a Bash-authored change). Wired by
     *  the Runner to the per-run accumulator behind `TaskResult.editedFiles`; absent ⇒ nothing observes.
     *  Purely additive: it cannot refuse a write, and a fault in it is contained in both shapes (a
     *  synchronous throw and a rejected promise), neither of them awaited. */
    onFileEdited?: FileEditedHook;
    /** See createBashTool's taskOpts field of the same name: whether the Monitor tool is on this run's
     *  roster (the Runner mounts Monitor, this band never does). `false` drops the gh rate-limit hint's
     *  Monitor clause; absent ⇒ the full wording. */
    monitorToolActive?: boolean;
    /** ADDITIONS to the built-in sensitive-path READ deny set
     *  ({@link import("./read-deny.js").READ_FACE_DEFAULT_DENY_ENTRIES}), judged by the structured read
     *  faces (Read/Grep/Glob/RepoMap and their traversals) in BOTH containment modes. Add-only:
     *  additions union with the active built-ins, `[]` ≡ absent (union identity), and additions can
     *  never remove a built-in — the one removal channel is the deployment's built-in configuration
     *  ({@link readDenyBuiltinTiers} / {@link readDenyBuiltinExclude}). Bad entry shapes throw at wiring
     *  time. The write faces are untouched (their guard is createSensitivePathPolicy). */
    readDenyPatterns?: readonly ReadDenyEntry[];
    /** The deployment's built-in-table TIER selection ({@link import("./read-deny.js").ReadDenyBuiltinConfig}
     *  `tiers` key) — the switch for the built-in table:
     *  EXACTLY the listed tiers of {@link import("./read-deny.js").READ_FACE_BUILTIN_DENY_TABLE}
     *  are active (`[]` = none — explicit and legal); absent = the default selection
     *  ({@link import("./read-deny.js").READ_DENY_DEFAULT_TIERS}: `credentials` and the home-anchored `agent-config`).
     *  Composes with {@link readDenyBuiltinExclude} (tier selection first, then row removal) and never
     *  touches the additions seats. Unknown tier names throw at wiring time. Deployment seat ONLY —
     *  deliberately not a TaskSpec key, so no task can widen below its deployment. */
    readDenyBuiltinTiers?: readonly string[];
    /** Per-row removal from the built-in table by STABLE NAME (= the row's canonical pattern text, e.g.
     *  `".ssh"`, `".config/gcloud"`), applied after {@link readDenyBuiltinTiers} selection. Naming a row
     *  of an inactive tier is a satisfied intent (no-op); a name matching NO table row throws at wiring
     *  time (never silently ignored). Deployment seat ONLY, same clamp argument as the tiers key. */
    readDenyBuiltinExclude?: readonly string[];
    /** The spellings of the execution environment's home a home-anchored deny row (`~/…`) is placed under
     *  ({@link import("./read-deny-home.js").readDenyHomesOf}: the declared home and the account home, each with its canonical
     *  spelling). Absolute path strings only — any other shape throws at wiring time, naming the seat. The Runner passes the
     *  spellings its own judge was compiled with. A list given here is the band's home set as is: the account database is
     *  not asked and no home is added (each listed home is anchored at its spelling and, where the environment answers one,
     *  its canonical spelling), and the band's home views (a path spelled through a firmlink or a bind mount of a home,
     *  judged at the home) read this list and the environment's declared home; an entry that is not the declared home is probed once,
     *  under a short bound of its own, and a probe of it that fails costs that entry alone. The public shell judgment takes the
     *  same list as {@link import("./shell-read-check.js").ShellReadJudgeInput.readDenyHomes}. */
    readDenyHomes?: readonly string[];
    /** Where each home-anchored row's literal head leads beyond the home's spellings
     *  ({@link import("./read-deny-home.js").readDenyHomeLinksOf}, keyed by the row's pattern) — the Runner passes the
     *  locations its own judge was compiled with, so the band and the gate judge one placement.
     *  Unless a mount carries both, the band resolves the placement itself the way the Runner does (the homes it carries
     *  are its home set — the account is not asked and no home is added;
     *  {@link import("./read-deny-home.js").placeReadDenyAtHome}), and its read tools (Read / Grep / Glob / RepoMap) wait
     *  for that placement before they judge anything. A placement that fails leaves the home-anchored rows held anywhere.
     *  Carrying both is the engine's own handoff of ONE placement — its home views travel with it — so
     *  `readDenyHomes` must then be the very list that placement returned: any other list (a copy) holds the home-anchored
     *  rows at every location and is named on `console.warn`. */
    readDenyHomeLinks?: ReadonlyMap<string, readonly string[]>;
    /** The permission-rule rows of the deny table ({@link import("./read-deny.js").PathRuleDenyRow}, `ToolPolicy.pathDenyRules`):
     *  the read-face rows join the structured read faces' judge beside the segment rows. The Runner passes the leg's own. */
    pathDenyRules?: readonly import("./read-deny.js").PathRuleDenyRow[];
    /** The READ-face containment state for the structured read faces
     *  ({@link import("./containment-face.js").ContainmentFace}; resolved through the SAME
     *  {@link import("./containment-face.js").resolveContainmentFace} order prepare-task uses). Absent ⇒ the
     *  resolution order's default = "roots" (the engine never opens implicitly). "open" skips ONLY the
     *  roots containment judgment — the deny set, the UNC out-of-set refusal and the special-file type
     *  gates run in both faces. Beside `readOnly: true` the mount wins: this seat is deps-shaped (a
     *  deployment default, not a per-call assertion), so its "open" CLAMPS to "roots" and the clamp is
     *  ANNOUNCED (`config.read_face_deployment_clamped`, through `onNotice` / `console.warn`) — the loud
     *  refusal belongs to the TASK seat (`TaskSpec.readFace`), the one genuine per-call contradiction.
     *  Never affects the write faces — {@link writeFace} is a separate seat with its own default. */
    readFace?: ContainmentFace;
    /** The WRITE-face containment state for the structured write faces (Edit / Write / NotebookEdit)
     *  ({@link import("./containment-face.js").ContainmentFace}; resolved through the SAME
     *  {@link import("./containment-face.js").resolveContainmentFace} order — one body, one default,
     *  per seat). Absent ⇒ "roots" (today's behavior, byte-identical). "open" skips ONLY the roots
     *  containment judgment on the write band — the write-side sensitive-path policy, the blocked-device
     *  gate, the UNC out-of-set refusal and the read-before-edit requirement all still run, and the full
     *  `bash` face is unaffected either way (it was never fenced). Beside `readOnly: true` the mount
     *  wins the same way the read seat's does (that mount carries no write faces at all), announced as
     *  `config.write_face_deployment_clamped`. Never affects the read faces. */
    writeFace?: ContainmentFace;
    /** A runner's LIVE write face — its resolution, which reads the permission mode's face on every call. Present ⇒
     *  the write tools read it per call and {@link writeFace} is not consulted (the runner resolved and screened the
     *  seats itself); absent ⇒ the toolkit's own resolution of {@link writeFace}, fixed at creation. */
    writeFaceLive?: () => ContainmentFace;
    /** The session's reminder provenance mark: the band's engine-minted
     *  `<system-reminder>` open tags (the Read dedup/offset/empty reminders and the gh
     *  rate-limit hint) carry it as `mark="…"`, matching the system-prompt declaration the Runner
     *  renders for the same session. Threaded by prepare-task; a library-direct mount that omits it
     *  keeps bare tags — with no declaration in its prompt, a bare tag is the honest form. The tag is
     *  rendered by the mint home (core/reminder-mint.ts) — the content BODY next to it is
     *  byte-untouched. */
    reminderMark?: string;
    /** The per-run trailer/defuse trigger counters (observation seat):
     *  the Read text/notebook/PDF-text disclosure trailers bump `read.*` / `notebook.*` / `pdf.*`
     *  keys here. The form suffixes are enumerated once, on the consumer-facing contract
     *  (`TaskResult.stats.mechanisms.reminderDisclosures` in core/types.ts) — this map is the same
     *  open record and is not a second, narrower registry of them. Threaded by prepare-task, which folds the
     *  non-zero result into `TaskResult.stats.mechanisms.reminderDisclosures`; a library-direct
     *  mount may pass its own object or omit it (counting off — and the whole disclosure pipeline
     *  is off anyway when `reminderMark` is absent). */
    reminderDisclosureCounts?: import("../../core/reminder-disclosure.js").ReminderDisclosureCounts;
}
/** Whether the band's WRITE half (Edit / Write / NotebookEdit, and the full `bash`) mounts for these options — the ONE
 *  predicate {@link createHandsToolkit} branches on and prepare-task plans by (the memory phase must know, before the
 *  band exists, whether the instruction-taught write channel will be there). `readOnly` is the verifier boundary. */
export declare function handsWriteHalfMounts(opts: Pick<HandsToolkitOptions, "readOnly">): boolean;
/** The engine's background-output read exemption over one toolkit's seats: the exact registered output file of a
 *  background command, visible to the caller's own owner / scope / session, on THIS execution env — the file the
 *  launch receipt tells the model to Read. Shared by the Read tool's fence and the file read station, so the station
 *  puts no question where the fence would admit. */
export declare function backgroundOutputReadExemption(registry: TaskRegistry | undefined, seat: {
    readonly taskOwner?: string;
    readonly taskScope?: string;
    readonly sessionId?: string;
}, env: ExecutionEnv): ((canonicalKey: string, c?: {
    taskId?: string;
    principal?: string;
}) => boolean) | undefined;
/**
 * Build the per-task hand tool band over an injected env + fresh per-task read state.
 * `rootCanonical` is the already-canonicalized containment root (prepare-task resolves it once). Normal
 * mode mounts read/edit/write/grep/glob (+`bash` when `includeShell`); `readOnly` mode mounts only the
 * effect:read tools read/grep/glob (+`bash_readonly` when `includeShell`) — the verifier boundary. Subset
 * selection beyond that is the caller's job via the tool-filter.
 */
export declare function createHandsToolkit(env: ExecutionEnv, readFileState: ReadFileState, rootCanonical: string, opts?: HandsToolkitOptions): AgentTool[];
