/**
 * skillMatch — the DATA-matcher domain for skill-graph start rules (SG-A).
 *
 * One module owns everything a matcher is and does, so the three consumers can
 * never drift apart:
 *
 *   • what a matcher IS       — {@link SkillMatch} (author form) and
 *                               {@link SkillMatchData} (serializable description);
 *   • how it RUNS             — {@link compileMatch}: ONE compilation returns the
 *                               predicate that routes AND the data that describes it;
 *   • how two matchers RELATE — {@link compareMatchers}: what the check-up can
 *                               honestly prove (`overlapping-rules` /
 *                               `rules-shadowed-by-order`);
 *   • how it DRAWS            — {@link mermaidMatchCaption}: the entry-edge caption
 *                               `toMermaid()` uses when the author gave no label.
 *
 * Deliberately engine-type-free (the predicate context is structural —
 * `{ userMessage }` — which the engine's `InjectionContext` satisfies), so
 * `skillGraphCheckup.ts` can import it and keep its own "pure over strings"
 * law intact. Imported by `skillGraph.ts` (compile + caption) and
 * `skillGraphCheckup.ts` (data type + compare); imports nothing.
 */
/**
 * A DATA matcher on a start rule — the declarative alternative to a `when`
 * predicate. The two SYNC leaf members:
 *
 *   • a `RegExp` — tested against the user's message (`ctx.userMessage`). The
 *     stateful `g`/`y` flags are dropped at compile time (a sticky regex would
 *     alternate its answer across identical messages); the stored provenance
 *     carries the flags that actually run.
 *   • `{ keywords: [...] }` — case-insensitive; the rule matches when ANY keyword
 *     is present. Whole-word where sensible: a `\b` anchor is applied at each edge
 *     of the keyword that is an ASCII word character, so `refund` does not match
 *     "refunds", while `visa card` matches as a phrase and `v2?` matches literally.
 *
 * Being DATA is the point: a predicate is opaque code the library can only run,
 * while a matcher can be COMPARED by the check-up (`overlapping-rules`,
 * `rules-shadowed-by-order`), CAPTIONED by `toMermaid()` on the entry edge, and
 * STORED on the compiled skill's provenance (`metadata.skillGraph.match`, as
 * serializable {@link SkillMatchData}).
 *
 * The third member (SG-C) is the INTENT matcher:
 *   • `{ intent, examples }` — one sentence naming the intent ("customer wants
 *     a refund") plus ≥1 real user phrasings. Unlike the other two it compiles
 *     to NO message predicate: it cannot answer synchronously, so the entry
 *     carrying it compiles cursor-gated and the matching happens in the
 *     turn-start cascade (the RouteTurn stage), judged by the graph's
 *     configured classifier (`.classify(scorer)` / `start.classify`). A graph
 *     declaring one without a classifier is refused at build.
 *
 * The fourth member (9.20.0) is the CONJUNCTION:
 *   • `{ all: [...] }` — the rule matches only when EVERY member matches ("zone
 *     AND audit-shaped" as data, instead of a lookahead regex nobody can read
 *     back). Members are the SYNC data arms only — RegExp or `{ keywords }`:
 *     an intent inside `all` is refused at build (intents are judged by the
 *     classifier at turn start; they cannot compose into a synchronous AND —
 *     declare a separate intent rule instead). A nested `all` is FLATTENED
 *     (AND is associative, so the flattened form runs identically and the
 *     stored data describes exactly what runs — the same normalize-to-truth
 *     rule that drops a regex's stateful flags). An empty `all` is refused:
 *     it has nothing to check, so it could only surprise.
 *
 * Extensible by design: each non-RegExp member is an object discriminated by its
 * own required key, so a future matcher is a new arm — not a reshape. A shape
 * that is none of these is refused at build time, naming the forms that ARE
 * supported.
 */
export type SkillMatch = RegExp | {
    readonly keywords: readonly string[];
} | {
    readonly intent: string;
    readonly examples: readonly string[];
} | {
    readonly all: readonly SkillMatch[];
};
/**
 * The serializable description of a DATA matcher on a start rule (`match:` on
 * `start.rules` / `SkillEntryOptions.match`) — what the check-up compares, what
 * `toMermaid()` captions, and what rides the compiled skill's provenance. Pure
 * strings by design (it has to survive `structuredClone`): a `RegExp` matcher is
 * stored as its `source` + the `flags` that actually run.
 *
 * A discriminated union so a future member (e.g. an intent matcher) is a new
 * `kind` arm, not a reshape.
 *
 * The `'all'` arm carries the conjunction's members as `parts`. Compilation
 * GUARANTEES the parts are the leaf sync arms only (`'regex'` / `'keywords'`):
 * an intent member is refused, and a nested `all` is flattened before the data
 * is stored — so every consumer (the check-up's part comparison, the mermaid
 * caption) reads a flat AND of leaves, never a tree.
 */
export type SkillMatchData = {
    readonly kind: 'regex';
    readonly source: string;
    readonly flags: string;
} | {
    readonly kind: 'keywords';
    readonly keywords: readonly string[];
} | {
    readonly kind: 'intent';
    readonly intent: string;
    readonly examples: readonly string[];
} | {
    readonly kind: 'all';
    readonly parts: readonly SkillMatchData[];
};
/** The one field a compiled matcher reads. Structural on purpose — the engine's
 *  `InjectionContext` satisfies it, and this module never has to import it. */
export interface MatchableContext {
    readonly userMessage: string;
}
/**
 * The EVIDENCE a data matcher routed on (9.28.0) — the actual text out of the
 * USER MESSAGE that made the rule true. Recorded on the routing verdict
 * (`turn_routed.witness`, `cursorMove.witness`) so the record can say *because
 * the message said "…"* instead of only *a rule matched*.
 *
 * Three honesty rules, all enforced at capture:
 *   • the text comes from `ctx.userMessage` and nothing else — never a tool
 *     result, never system/assistant prose (a matcher only ever reads the user
 *     message, so this is a property of where it is captured, not a filter);
 *   • it is BOUNDED to {@link WITNESS_MAX_CHARS} characters, ellipsis included
 *     — a `/[\s\S]+/` rule must not paste the whole message into every record;
 *   • runs of whitespace collapse to one space and the ends are trimmed, so a
 *     multi-line match stays one quotable clause. That is the ONLY edit made to
 *     the matched substring, and it is the reason `text` is described as the
 *     matched text rather than the matched bytes.
 *
 * `keyword` is present only for the `{ keywords }` arm: WHICH declared keyword
 * hit, beside the text it hit on (they differ in case, and `text` may carry the
 * phrase's real spacing).
 *
 * A `when` predicate produces NO witness (opaque code — the library cannot know
 * what convinced it), and neither does an intent match (a scorer's evidence is
 * its scores, already recorded). Absent is the honest answer there.
 */
export interface RouteWitness {
    readonly text: string;
    readonly keyword?: string;
}
/** The witness bound — 80 characters, ellipsis included. A record is evidence,
 *  not a transcript. */
export declare const WITNESS_MAX_CHARS = 80;
/**
 * Compile a data matcher into its predicate + its serializable description — ONE
 * compilation, so the predicate that routes and the data the check-up compares can
 * never describe different matchers. Refuses a shape it cannot honor (a bare
 * string, an empty keyword list, an intent without examples), naming the forms
 * that ARE supported — accepting one and matching nothing would be the silent kind
 * of wrong.
 *
 * The INTENT arm compiles to `predicate: undefined` — it cannot answer
 * synchronously (a classifier judges it in the turn-start cascade), so the entry
 * carrying it is cursor-gated instead of rule-gated. `skillGraph.ts` is the only
 * caller that accepts a predicate-less compile, and it refuses `match: { intent }`
 * when no classifier is configured.
 *
 * The third member of the returned triple is `witness` (9.28.0): the SAME
 * compilation's evidence extractor — given a context the predicate accepted, it
 * returns {@link RouteWitness} (the matched text, bounded). It is a separate
 * function on purpose, so the hot path stays exactly what it was (a boolean
 * `.test`) and the evidence is paid for ONCE per turn, on the rule that actually
 * won. The intent arm has no witness (nothing matched synchronously).
 */
export declare function compileMatch(match: SkillMatch, where: string): {
    predicate: ((ctx: MatchableContext) => boolean) | undefined;
    data: SkillMatchData;
    witness?: (ctx: MatchableContext) => RouteWitness | undefined;
};
/**
 * Compare two DATA matchers, earlier vs later (declaration order). Returns only
 * what is PROVABLE:
 *   • `'shadows'`  — every message the later matches, the earlier matches too, so
 *     the later can never be chosen (identical regex; earlier keywords ⊇ later's);
 *   • `'overlaps'` — a witness class of messages matches both (a shared keyword),
 *     but each rule also has messages of its own;
 *   • `undefined`  — nothing provable here (different regex sources, a regex vs a
 *     keyword list): say nothing rather than guess.
 * `why` is the human clause the check-up splices into its problem message.
 *
 * The `'all'` arm (9.20.0) keeps the same law — claim only what part-coverage
 * proves. An `all` rule matches a SUBSET of each of its parts' messages, so:
 *   • a plain rule EARLIER that provably covers ONE part of a later `all` rule
 *     shadows it (every message the `all` matches satisfies that part, which
 *     the earlier already matches);
 *   • an `all` rule EARLIER shadows a later matcher when EVERY earlier part is
 *     provably covered by some later part — the later only ADDS constraints
 *     (identical part sets are the simplest case).
 * "Provably covers" is same-kind only: an identical regex, or a keyword
 * superset. No `'overlaps'` is ever claimed for a pair involving `all`: a
 * conjunction can be unsatisfiable (`all: [/^a/, /^b/]` matches nothing), so a
 * witness message cannot be asserted from the data — and the ordinary
 * specific-rule-first layout (the `all` rule above its plain fallback) is a
 * DESIGN, not a problem to warn about.
 */
export declare function compareMatchers(earlier: SkillMatchData, later: SkillMatchData): {
    relation: 'shadows' | 'overlaps';
    why: string;
} | undefined;
/**
 * Caption an entry edge with its data matcher, escaped for a mermaid `|…|` label
 * (`|` would end the label early; `"` would open a string — both become mermaid
 * entities). Keywords list at most three, then a count, so a wide router stays
 * readable; an `all` conjunction joins its parts with ` AND ` (parts are always
 * leaves — compilation flattened any nesting). Used only when the author gave no
 * explicit `label` — an explicit label always wins, byte-for-byte, exactly as
 * before.
 */
export declare function mermaidMatchCaption(m: SkillMatchData): string;
/**
 * The UNESCAPED caption — one grammar for naming a matcher, shared by the
 * mermaid label (which escapes it once, at the top) and by prose that quotes a
 * matcher back to its author (`skillExamples.ts`'s messages). Recursion for the
 * `all` arm happens here so the escaping stays a single outer pass.
 */
export declare function plainMatchCaption(m: SkillMatchData): string;
//# sourceMappingURL=skillMatch.d.ts.map