import type { AutoModeClassifyInput } from "./auto-mode.js";
import type { Message } from "../internal/llm.js";
/** The sentinel a deployment puts INSIDE a rule list to splice the CC default rules back in at that
 *  position (CC `XYt = "$defaults"`). Without it, a non-empty user list REPLACES the default section.
 *  DECLARED in `./auto-mode-defaults.js` (the leaf the #503 recipe canonicalizer can reach without
 *  loading these assets) and re-exported here, where every caller already looks for it. */
export { AUTO_MODE_DEFAULTS_SENTINEL } from "./auto-mode-defaults.js";
/** Deployment-authored rule overrides for the four paired sections of the permissions document.
 *  Each entry is ONE rule (rendered as a `- ` bullet); omit a field (or pass an empty array) to keep
 *  the CC default rules for that section verbatim. */
export interface AutoModeRules {
    allow?: readonly string[];
    softDeny?: readonly string[];
    hardDeny?: readonly string[];
    environment?: readonly string[];
}
export interface BuildAutoModePromptOptions {
    rules?: AutoModeRules;
    /** Settings-plane deny rules (tool[/argument-pattern] strings) — rendered into the
     *  `<settings_deny_rules>` slot as the CC circumvention-catching paragraph, after the engine's own lines. */
    settingsDenyRules?: readonly string[];
    /** The ENGINE's deny lines — its effective read-deny table as rules ({@link readDenyClassifierRules}).
     *  Rendered in the same paragraph, ahead of `settingsDenyRules`. A separate member (not pre-merged by the
     *  caller) because a persisted arming records the two halves separately: the deployment's half is compared
     *  against the redeeming deployment's own settings, the engine's half against the row's own table. */
    derivedDenyRules?: readonly DerivedDenyRule[];
    /** Extra session-context facts (e.g. the CC user-identity line) — appended as a
     *  `## Session Context` bullet block after the assembled document. */
    sessionContext?: readonly string[];
    /** design/385 §4.5 — the text spliced into the `<cross_session_messages_rule>` slot. Absent (the
     *  default, and every deployment without the cross-session lane) ⇒ the slot is blanked exactly as
     *  before; the lane mount passes `CROSS_SESSION_CLASSIFIER_RULE`. Callback form at the splice (the
     *  text may carry `$`). */
    crossSessionMessagesRule?: string;
}
/**
 * One engine deny line: a `Read(...)` rule in the engine's own path-rule grammar (`//` = the filesystem root,
 * `*` within one segment, a whole-segment `**` across segments, and a rule reaches the path it names and
 * everything under it — `parseRuleText` / `pathRuleReachOf`), plus whether ASCII letter case is ignored, which
 * that grammar cannot spell and the paragraph states beside the rule.
 */
export interface DerivedDenyRule {
    readonly rule: string;
    readonly anyCase?: true;
}
/**
 * The effective READ-deny table as the classifier's deny lines — one line per row, the row's reach in the
 * engine's own `Read(...)` path-rule grammar.
 *
 * A read-deny row is a run of path SEGMENTS matched anywhere in a path — the entry itself and everything under
 * it — and its `*` never crosses a `/` (read-deny.ts: `.ssh` refuses `~/.ssh/id_rsa` and `/srv/x/.ssh/config`
 * alike; a `**` segment matches exactly one segment). The rule grammar reaches the path a pattern names and
 * everything under it, so the line is `//**` + `/` + the row with every run of stars written `*` (one segment,
 * as in the row): `.ssh` ⇒ `Read(//**\/.ssh)`, `**\/secrets/**` ⇒ `Read(//**\/*\/secrets/*)`, `*` (every
 * path) ⇒ `Read(//**\/*)`. A row that names no segment at all (`""`, `/`) is not stated. A HOME-ANCHORED row
 * (`~/<run>`, read-deny.ts `READ_DENY_HOME_WORD`) is the rule grammar's home word with the same reach:
 * `~/.sema/settings.json` ⇒ `Read(~/.sema/settings.json)`.
 *
 * The text of a row can come from a task (a spawned task's author included) and it is spliced into a trusted
 * prompt, so a row is stated as a rule and nothing else: every character the grammar or the prompt must not
 * carry (see NOT_STATED) is written `*`, and an over-long pattern is cut and closed with `*` — both wider than
 * the row, never narrower, and the result still parses as a rule. No row can start a line, leave its code
 * span, form a tag or a second rule, or read as a sentence; the engine enforces the row as compiled either way.
 *
 * A row matched without regard to ASCII letter case (the deny set's default) carries `anyCase` when its stated
 * pattern has an ASCII letter. Duplicates are folded AFTER this encoding, first seen.
 */
export declare function readDenyClassifierRules(entries: readonly {
    readonly pattern: string;
    readonly caseSensitive?: boolean;
}[], rules?: readonly {
    readonly tool: string;
    readonly pattern: string;
}[]): DerivedDenyRule[];
/**
 * The `<settings_deny_rules>` paragraph for one arming, from the engine's facts — the effective deny table
 * (`readDeny`: the compiled matcher's entries, built-in tiers + deployment + task additions; `readDenyRules`: its
 * permission-rule rows, both faces) and the deployment's `settingsDenyRules` — exactly as {@link buildAutoModePrompt}
 * splices it. Empty inputs ⇒ "".
 */
export declare function renderClassifierDenyRules(input: {
    readonly readDeny?: readonly {
        readonly pattern: string;
        readonly caseSensitive?: boolean;
    }[];
    readonly readDenyRules?: readonly {
        readonly tool: string;
        readonly pattern: string;
    }[];
    readonly settingsDenyRules?: readonly string[];
}): string;
/**
 * Assemble the classifier SYSTEM prompt (CC `KRg`, content-equivalent single string — CC splits the
 * session-context tail into a second uncached block purely as a prompt-cache optimization).
 * Every `.replace` uses the CALLBACK form: the assets and user rules are full of `$`-patterns
 * (`$USER/...`) that a string replacement would corrupt via `$&`/`$'` substitution (CC does the same).
 */
export declare function buildAutoModePrompt(options?: BuildAutoModePromptOptions): string;
export interface AutoModeWindowOptions {
    /** Newest transcript entries INCLUDED (default 40). Zero includes none — the classifier judges the
     *  action with no run history, and the omission line still names every entry left out. */
    maxEntries?: number;
    /** Per-entry excerpt cap in characters (default 2_000 — tool results and long turns are truncated
     *  head-first with a `[… N chars truncated]` marker; the classifier weighs shape over bulk). */
    maxCharsPerEntry?: number;
}
/**
 * Render the transcript window half of the classifier's user prompt: the newest `maxEntries` messages,
 * oldest→newest, each labeled by lane. Plain data — see the module header for the declared v1 gap vs
 * CC's entry-formatter stack.
 *
 * `maxEntries` counts the entries INCLUDED, so a bound of zero includes none and says so. That case is
 * handled explicitly rather than left to `slice`: `slice(-0)` is `slice(0)`, i.e. the WHOLE transcript,
 * which inverts a declared bound into "unbounded" — and silently, because the omitted count would then
 * be zero and the omission header would not render either. The recipe canonicalizer admits zero for both
 * window knobs deliberately (its sibling knobs use a minimum of one), so a deployment can genuinely
 * declare "classify the action with no transcript context"; a negative bound is not a coherent window
 * either and takes the same arm rather than `slice`'s "drop the oldest N".
 */
export declare function renderAutoModeWindow(messages: readonly Message[], options?: AutoModeWindowOptions): string;
/**
 * Render the pending action (the classify subject). The 207 process's step 1 keys on "the last tool
 * call in the transcript" — this block IS that entry for our assembly, placed last in the user prompt.
 */
export declare function renderAutoModeAction(input: AutoModeClassifyInput): string;
