import { type VisualAtom } from "./tiling.js"; export interface CritiqueTile { index: number; /** Section label or "band N", for locating a finding. */ label: string; /** Document-space Y offset of the tile's top, so findings map to a place. */ scrollY: number; /** Document-space X offset (0 for full-width bands; element-relative for * selector tiles). Optional for backward compatibility — treat absent as 0. */ x?: number; width: number; height: number; /** Base64 PNG of the tile (no data: prefix). */ pngBase64: string; } export interface CritiqueFinding { tile: number; severity: "high" | "medium" | "low"; category: string; description: string; } /** * How much of the page the tiles actually cover. The tiler keeps at most * `maxTiles` bands and drops the rest, so on a tall page `capped` is true and * `reviewed_height_px` stops short of `page_height_px`. Callers that turn a * critique into a verdict must read this: a pass over the first 24 bands is * not a pass over the page. */ export interface CritiqueCoverage { page_height_px: number; reviewed_height_px: number; bands_total: number; bands_reviewed: number; capped: boolean; } export interface CritiqueResult { rule: "critique"; /** Number of tiles the page was cut into. */ tiles: number; /** Tile coverage of the page; absent only for results built without tiles. */ coverage?: CritiqueCoverage; /** Whether a provider was available. False → outcome "skipped". */ provider: boolean; findings: CritiqueFinding[]; outcome: "pass" | "fail" | "skipped"; /** Set when skipped or a provider call failed. */ error?: string; /** Host-reported provider provenance (route, attempts, fallbacks, usage). */ provider_meta?: Record; } /** * The host-injected model call. Given one tile and the rubric, return the * findings for that tile. Throwing is caught and surfaced as an error finding * so one bad tile never sinks the run. * * Optional properties let the host shape execution without widening the call * contract: `concurrency` caps how many tiles run at once (default 4 — tiles * are independent, and the wall-clock win is roughly the cap factor), and * `meta()` is read once after the run so route/usage provenance lands on the * result envelope. */ export type CritiqueProvider = ((input: { url: string; rubric: string; tile: CritiqueTile; }) => Promise) & { concurrency?: number; meta?: () => Record | undefined; /** Long-edge pixel budget of the routed model's vision input. The tiler * clamps band height to it so tiles are never downscaled by the provider. */ tileBudgetPx?: number; }; export declare const DEFAULT_CRITIQUE_RUBRIC: string; /** * Cut a page of height `pageHeight` into overlapping vertical bands. Pure math * so the caller (which owns the page) can screenshot each `clip` rect. The * overlap keeps a finding that straddles a seam visible in at least one tile. */ export declare function bandRects(pageHeight: number, pageWidth: number, bandHeight: number, overlap: number): Array<{ index: number; x: number; y: number; width: number; height: number; }>; /** * Cut tiles out of ONE full-page screenshot with pngjs. Playwright's own * `clip` is viewport-relative, so a band below the fold errors with "clipped * area outside the resulting image" — capturing the whole page once and * cropping in pixel space sidesteps that and keeps every tile at full * resolution. Pass `elementRects` for semantic (per-element) tiling; otherwise * the page is banded from the actual image height. Rects are clamped to the * image so an off-by-one never throws. * * When `atoms` are supplied (see tiling.ts), band seams snap into content * gaps instead of cutting at fixed offsets, and element rects taller than the * band budget are banded internally rather than shipped as one over-tall tile * the provider would downscale. Atom coordinates must be in the same pixel * space as the screenshot (identical at deviceScaleFactor 1; scale them if * the capture DPR differs). * * `hitRects` (document-space rectangles a deterministic gate already flagged) * add bands past the `maxTiles` cap: every full-page band that intersects a * hit rect and was dropped by the cap is cut as well, labelled `hit band N` * (N is the band's 1-based position in the full band list, and the tile's * `index` is that position minus one), and appended after the kept bands. * `coverage` stays honest about the kept prefix; `hitBands` counts the * extras. Ignored for selector tiling (`elementRects`), which has no cap to * look past in that sense. */ export declare function tilesFromFullPage(fullPageBuffer: Buffer, opts: { elementRects?: Array<{ label: string; x: number; y: number; width: number; height: number; }>; bandHeight?: number; overlap?: number; maxTiles: number; atoms?: VisualAtom[]; hitRects?: Array<{ x: number; y: number; width: number; height: number; }>; /** Cap on hit bands appended past `maxTiles` (default 50). */ maxHitBands?: number; }): { tiles: CritiqueTile[]; coverage: CritiqueCoverage; hitBands: number; }; /** * Validate + normalize a provider's raw output into CritiqueFindings. Tolerant * of a model returning a bare array or wrapping it in `{ findings: [...] }`, * and drops entries that are not shaped like a finding. */ export declare function normalizeFindings(raw: unknown, tile: number): CritiqueFinding[]; /** * Run the critique over pre-captured tiles with an injected provider. Findings * are collected across tiles; a provider throw becomes a `high` error finding * for that tile rather than aborting. Outcome is `fail` when any `high` finding * survives (the gate escalation lives in the caller). */ export declare function runCritique(args: { url: string; rubric: string; tiles: CritiqueTile[]; provider: CritiqueProvider | undefined; }): Promise; //# sourceMappingURL=critique.d.ts.map