/**
 * design/138 S2-C — the hands band's write-time CONTENT hook (single source; `src/tools/fs` re-exports
 * these so its import surface is unchanged — moved here by design/141 件2 because `RunnerDeps.hands`
 * carries the deployment-composable half of the hook). Called before EVERY `env.writeFile` in
 * Write/Edit/NotebookEdit with the resolved containment key and the exact final text; `{ ok:false }`
 * fails the tool with a structured error and NOTHING is written. A THROWING hook fails CLOSED.
 */
export interface BeforeWriteRequest {
    tool: "Write" | "Edit" | "NotebookEdit";
    /** The model-supplied path argument (for the error-message coordinate the model knows). */
    path: string;
    /** The resolved canonical containment key — what the hook should judge. */
    key: string;
    /** The FINAL full text about to be written (Edit/NotebookEdit: after application). */
    content: string;
}
export type BeforeWriteResult = {
    ok: true;
} | {
    ok: false;
    code: string;
    reason: string;
};
export type BeforeWriteHook = (req: BeforeWriteRequest) => BeforeWriteResult | undefined | Promise<BeforeWriteResult | undefined>;
/**
 * design/381 — the hands band's FIRST-TOUCH HISTORY hook, called before EVERY file mutation in
 * Write/Edit/NotebookEdit (after the `beforeWrite` content gate passed, immediately before the
 * final env write) with the resolved canonical containment key. The Runner wires it to
 * `FileHistoryStore.trackEdit` (backup-before-first-edit, the CC FileHistory trigger-point set
 * minus the bash simulation arm sema does not have — DV-7); later calls for an already-tracked
 * path are cheap store no-ops. `{ok:false}` carries the REFUSAL TEXT the tool must answer instead
 * of writing (DV-14 default: no durable first-touch state ⇒ the edit is refused, typed and loud —
 * any proceed-unprotected policy is resolved INSIDE the hook by the Runner, never by the band).
 * A THROWING hook fails CLOSED (same posture as the write gate). Absent hook ⇒ byte-identical
 * behavior (no history store wired).
 */
export interface TrackEditRequest {
    tool: "Write" | "Edit" | "NotebookEdit";
    /** The model-supplied path argument (for the error-message coordinate the model knows). */
    path: string;
    /** The resolved canonical containment key — the identity the history record is keyed on. */
    key: string;
    /** The tool call's abort signal, threaded into the store's env read. */
    signal?: AbortSignal;
}
export type TrackEditResult = {
    ok: true;
    /**
     * The compensating handle for the ordering the topology cannot avoid: the pre-image
     * capture runs BEFORE the write, so a write that then FAILS (lost create race, ENOSPC,
     * EACCES, read-only mount) would leave a first-touch record for an edit that never happened —
     * and an `existed-not` record makes an untouched path a rewind DELETE target, removing bytes
     * whoever writes them next (the user, a peer process) owns. Present ONLY when THIS call minted
     * the record (an already-tracked path belongs to the edit that first touched it and must never
     * be discarded); the band calls it on the write-failure arm of every mutation lane. `proof`
     * says which retraction rule applies: `"proven"` = the write's own error code guarantees
     * nothing was written, `"verify"` = ambiguous, so the history seat must re-read the path and
     * retract only if it still matches what the first touch recorded. It never throws and never
     * blocks the failure it accompanies — the original write error is the answer.
     */
    annul?: (proof: "proven" | "verify") => Promise<void>;
} | {
    ok: false;
    refusal: string;
};
export type TrackFileEditHook = (req: TrackEditRequest) => Promise<TrackEditResult>;
/**
 * The hands band's MUTATION-EDITED observation seat — the counterpart of {@link TrackEditRequest},
 * fired at the other end of the same lane. Called once per Write/Edit/NotebookEdit call whose FINAL
 * env write did not PROVABLY write nothing, at the single point every mutation lane funnels through,
 * under the same three-valued law as the first-touch record's own retraction:
 *  - a call the argument checks, the read-before-edit gate, the write gate or the first-touch
 *    history seat refused never fires it (nothing was written);
 *  - a call whose write FAILED with a code whose contract says nothing was written never fires it;
 *  - a call whose write failed AMBIGUOUSLY (a non-atomic env that truncated and then errored) or
 *    THREW fires it — exactly the arm where the retraction KEEPS the first-touch record because the
 *    file may really have changed. The two seats agree on purpose: a file whose baseline was kept
 *    for a possible modification must also be listed as possibly modified;
 *  - a `Bash` command that changed a file never fires it: bash does not run through this band's
 *    write lanes at all, which is the whole reason this seat is not a tool-name table.
 * PURE OBSERVATION: it returns nothing and it cannot refuse. A FAULT in it is contained in both
 * shapes an observer can fail in — a synchronous throw, and (the return type is `void`, but an
 * `async` function still type-checks there) a rejected promise, which is sunk rather than left
 * unhandled. Neither is awaited: an observer must never turn a landed write into a failed tool
 * answer, nor delay one.
 */
export interface FileEditedNotice {
    tool: "Write" | "Edit" | "NotebookEdit";
    /** The model-supplied path argument — the SAME coordinate the delegated-child projection's
     *  `SubagentEditedFile.path` carries, so the two seats can be read side by side. */
    path: string;
    /** The resolved canonical containment key the bytes actually landed on. */
    key: string;
}
export type FileEditedHook = (notice: FileEditedNotice) => void;
/**
 * design/141 件2 — the SAFE deployment-configurable subset of the hands toolkit ({@link RunnerDeps.hands}).
 * Only fields whose injection is purely additive for a deployment are here; Runner-internal orchestration
 * state (taskRegistry/detachHub/execClamp/cwdRef/mount coordination) is deliberately NOT configurable.
 */
export interface HandsBandOptions {
    /** Override the `bash_readonly` command allowlist (read-only band). Default: the built-in read-only set. */
    bashReadonlyAllow?: readonly string[];
    /** Co-Authored-By trailer for the bash git protocol. Default NONE ([c209] BREAKING: attribution is a
     *  deployment identity asset — the branded scenario sets `"Name <email>"`); `false` ≡ unset. */
    commitCoAuthor?: string | false;
    /** Read-tool image downsampler: inject your own, or `false` to force-disable (deterministic no-sharp).
     *  Omitted ⇒ auto-detect sharp. Same contract as the MCP-side {@link import("./mcp.js").ImageDownsampler}. */
    readImageDownsampler?: ((input: Buffer, mimeType: string) => Promise<import("./mcp.js").DownsampledImage | undefined>) | false;
    /** Deployment write gate — COMPOSED with (never replacing) the engine's MemoryEngine write-scan gate:
     *  this hook judges first (a rejection short-circuits), the engine gate then still runs in full. */
    beforeWrite?: BeforeWriteHook;
    /** RB-198 F1 (CC 220 `Zry`/`WZi.#m` parity): on a foreground Bash command's OWN timeout, an eligible
     *  command (see `canAutoBackground` — no `git` anywhere in the command, not a `sleep`-first-word wait) is
     *  adopted as a background task instead of being killed — CC's own posture ("received no response for
     *  Nms" would otherwise force a wasteful blind re-run of a non-idempotent long command). Default `false`
     *  (byte-compat): this is a deployment-level opt-in, deliberately NOT inferred from background-task
     *  plumbing (`detachHub`/`taskRegistry`) merely being present, since the Runner wires those unconditionally
     *  for every task — tying eligibility to their mere presence would silently flip the timeout outcome for
     *  every existing caller with no explicit signal at all. */
    autoBackgroundOnTimeout?: boolean;
}
