/**
 * Aggregate-level resolver for clarification responses.
 *
 *   Step 1 (deterministic, mandatory): sum_match math + date-range check
 *   Step 2 (LLM commentary, best-effort): per-file plain-English notes
 *
 * Step 1 is the source of truth. Step 2 enriches with qualitative commentary
 * but never overrides the verdict — if the LLM step fails, file_notes is empty
 * and the deterministic summary still flows through.
 */
import 'server-only';
import type { ClarificationExpectation } from '../types/clarification.js';
import type { ExtractedResponseFields, ResolutionCheck } from '../types/shared_data.js';
export interface ResolutionResponseFile {
    /** hazo_files file ID */
    file_id: string;
    /** Display filename — used in commentary prompts */
    file_name?: string;
    /** MIME type (e.g. 'application/pdf'). Required when sending file_b64. */
    mime_type?: string;
    /** Document type from classification (e.g. 'invoice', 'bank_statement'). */
    document_type?: string;
    /**
     * Plain-language description of the document from classification — e.g.
     * "settlement adjustment sheet for property purchase". Drives both the
     * deterministic content denylist and the LLM's file summary.
     */
    document_nature?: string;
    /** Structured fields pulled by autofill */
    extracted_fields?: ExtractedResponseFields;
    /**
     * Base64-encoded file bytes. When present, the LLM commentary step can
     * inspect the actual document for the relevance check (richer than
     * extracted_fields alone). Optional — falls back to extracted_fields-only
     * relevance assessment when omitted.
     */
    file_b64?: string;
    /** Optional OCR text the commentary step can reference */
    ocr_text?: string;
}
/** Per-file relevance verdict from the LLM commentary step. */
export type RelevanceVerdict = 'matches' | 'mismatch' | 'unclear';
export interface FileRelevance {
    verdict: RelevanceVerdict;
    reason: string;
}
export interface ResolutionInput {
    expectation: ClarificationExpectation;
    response_files: ResolutionResponseFile[];
}
export interface ResolutionHandlerOptions {
    /** Tolerance in major units (default 0.05 — five cents). */
    tolerance?: number;
    /** Per-attempt timeout for the LLM commentary step in ms (default 5000). */
    commentary_timeout_ms?: number;
    /** Optional LLM commentary callback. When omitted, file_notes stays empty. */
    commentary_fn?: (input: CommentaryInput) => Promise<CommentaryOutput | null>;
    /** Error sink — defaults to console.error */
    on_error?: (error: unknown, context: string) => void;
}
export interface CommentaryInput {
    expectation: ClarificationExpectation;
    files: Array<{
        file_id: string;
        file_name?: string;
        mime_type?: string;
        document_type?: string;
        document_nature?: string;
        extracted_fields?: ExtractedResponseFields;
        /** Base64 bytes when available — gives the LLM access to the document content. */
        file_b64?: string;
        ocr_text?: string;
    }>;
    /** Pre-deterministic verdict so the LLM can frame its commentary around it. */
    verdict: {
        sum_actual: number;
        sum_expected: number;
        delta: number;
    };
}
export interface CommentaryOutput {
    file_notes: Record<string, string>;
    /**
     * Per-file relevance verdict — keyed by file_id. The deterministic step
     * uses this to exclude mismatch files from the sum and flag unclear ones.
     * Files missing from this map default to 'matches' (no signal = trust).
     */
    relevance?: Record<string, FileRelevance>;
    overall_note?: string;
}
/**
 * Run the two-step resolver.
 *
 *   Step 1 — LLM commentary + relevance (best-effort, optional)
 *   Step 2 — deterministic math, gated by relevance verdicts
 *
 * Commentary runs first so its per-file relevance can influence which files
 * count toward the sum. When the LLM is unavailable or fails, relevance is
 * empty and the deterministic step runs with type-filter only — preserving
 * the prior behaviour as a graceful fallback.
 */
export declare function run_resolution(input: ResolutionInput, options?: ResolutionHandlerOptions): Promise<ResolutionCheck>;
export interface ResolutionRouteOptions extends ResolutionHandlerOptions {
}
export interface ResolutionRequestBody {
    expectation: ClarificationExpectation;
    response_files: ResolutionResponseFile[];
}
/**
 * Creates a POST route handler that wraps run_resolution. Mirrors the factory
 * pattern used by validation_handler / classification_handler / autofill_handler.
 */
export declare function create_resolution_route(options?: ResolutionRouteOptions): (request: Request) => Promise<Response>;
/** Minimal adapter shape — same as classification/validation handlers expect. */
interface HazoConnectAdapterLike {
    query(builder: unknown, method?: string, body?: unknown): Promise<unknown>;
    rawQuery(endpoint: string, options?: RequestInit): Promise<unknown>;
    getConfig(): Promise<unknown>;
}
export interface LlmCommentaryFnOptions {
    /** prompt area in hazo_prompts (e.g. 'thread_form'). */
    prompt_area: string;
    /** prompt key in hazo_prompts (e.g. 'resolve_clarification_relevance'). */
    prompt_key: string;
    /**
     * HazoConnectAdapter from the consuming app — passed to hazo_llm_api so it
     * can fetch the prompt text. Same adapter the classification/validation
     * handlers use.
     */
    hazo_connect_adapter?: HazoConnectAdapterLike;
    /**
     * Path to a SQLite DB for prompt lookup. Used when no adapter is provided.
     * Mirrors the classification handler's fallback.
     */
    sqlite_path?: string;
    /** Error sink — defaults to console.error. */
    on_error?: (err: unknown, ctx: string) => void;
}
/**
 * Build a commentary_fn that calls hazo_llm_api with the resolve_clarification_relevance
 * prompt, attaching every inspectable response file inline. Wire this into
 * `create_resolution_route({ commentary_fn: create_llm_commentary_fn(...) })`.
 *
 * The returned function is best-effort: any failure (LLM error, JSON parse
 * fail, missing prompt) returns `null` so the resolver falls back to the
 * deterministic-only verdict.
 */
export declare function create_llm_commentary_fn(options: LlmCommentaryFnOptions): (input: CommentaryInput) => Promise<CommentaryOutput | null>;
export {};
//# sourceMappingURL=resolution_handler.d.ts.map