/**
 * Keyword-coverage judge — baseline complement to the semantic concept
 * judge.
 *
 * Where {@link runSemanticConceptJudge} uses an LLM to read source code
 * and decide whether a concept is REALLY implemented (not just
 * keyword-mentioned), this judge does the cheap, deterministic version:
 * fetch the served preview, concatenate every linked CSS/JS asset, and
 * substring-match each expected concept's keywords against the
 * concatenated haystack. Optional `requiredElement` selector adds a
 * structural gate so "supply counter" can require an actual `<input>` or
 * `<table>`, not just a comment containing the word.
 *
 * Use both judges. Keyword coverage is a fast 0-cost gate — a stub page
 * with the right keywords passes here, fails the semantic judge. Score
 * divergence between the two is itself a signal: high keyword coverage
 * + low semantic = "the agent slapped the right words on the right
 * scaffold but didn't wire any of it up."
 *
 * Pure functions, soft-fail on fetch error, no LLM dependency.
 */
export interface KeywordConceptSpec {
    name: string;
    keywords: string[];
    /**
     * Optional CSS selector that must match in the HTML for the concept
     * to count as present. Tiny subset:
     *   - `tag`               (e.g. `form`)
     *   - `tag[attr="value"]` (e.g. `input[type="number"]`)
     *   - `tag[attr]`         (presence only)
     * Anything more complex is rejected with `null` (treated as
     * "unenforced", not "failed").
     */
    requiredElement?: string;
}
export interface KeywordCoverageFinding {
    concept: string;
    found: boolean;
    matchedKeywords: string[];
    /** True iff the optional requiredElement selector matched; null when no selector. */
    requiredElementPresent: boolean | null;
}
export interface KeywordCoverageResult {
    /** 0..1 share of concepts satisfied. */
    score: number;
    presentCount: number;
    totalCount: number;
    findings: KeywordCoverageFinding[];
    durationMs: number;
    /** Total bytes assembled across html + linked assets. */
    totalAssembledBytes: number;
    /** Soft-failure reason if the audit couldn't run. */
    error?: string;
}
export interface KeywordCoverageOptions {
    /** Override fetch implementation — for tests. */
    fetch?: typeof fetch;
    /** Per-asset fetch timeout (default 3s). */
    assetTimeoutMs?: number;
    /** Initial-HTML fetch timeout (default 5s). */
    htmlTimeoutMs?: number;
}
/**
 * Element-presence check using a tiny CSS-selector subset. Returns
 * null when the selector isn't supported — caller treats that as
 * "unenforced" rather than "failed."
 */
export declare function htmlContainsElement(html: string, selector: string): boolean | null;
/**
 * Pull every `<link rel=stylesheet href>` and `<script src>` from a
 * raw HTML body. Returns absolute URLs resolved against `baseUrl`.
 * Permissive regex — agent-authored markup doesn't always quote
 * attributes the same way.
 */
export declare function extractAssetUrls(html: string, baseUrl: string): string[];
/**
 * Score expected concepts against an already-fetched HTML payload + any
 * pre-fetched CSS/JS assets. Use when the runner has the bytes in hand
 * and doesn't want a fresh HTTP round-trip — e.g. sandbox runtime where
 * the preview content was fetched via curl from inside the container.
 */
export declare function runKeywordCoverageJudge(html: string, expectedConcepts: ReadonlyArray<KeywordConceptSpec>, assets?: ReadonlyArray<string>): KeywordCoverageResult;
/**
 * URL-fetch flavor — GET the preview, parallel-fetch every linked
 * stylesheet + script (with bounded timeouts, soft-fail individually),
 * then score via {@link runKeywordCoverageJudge}.
 */
export declare function runKeywordCoverageJudgeUrl(previewUrl: string, expectedConcepts: ReadonlyArray<KeywordConceptSpec>, options?: KeywordCoverageOptions): Promise<KeywordCoverageResult>;
//# sourceMappingURL=keyword-coverage-judge.d.ts.map