/**
 * `defineEvaluator()` + `evaluate()` — custom programmatic evaluators
 * (RFC 004 §4.4, closes #244).
 *
 * Not every check is an LLM judge or a chai assertion. Sometimes the
 * ground truth lives in code: a SQL result must match a golden row set, a
 * generated diff must apply cleanly, a JSON answer must validate against a
 * schema. A custom evaluator is a plain function that inspects the run and
 * returns a verdict — deterministic, free, and fully under the author's
 * control.
 *
 * ```js
 * const { defineEvaluator, test } = require('@opensearch-project/agent-health');
 *
 * defineEvaluator('sql-matches-golden', ({ result }) => {
 *   const rows = JSON.parse(result.agentOutput);
 *   return { pass: deepEqual(rows, GOLDEN), reasoning: 'row set comparison' };
 * });
 *
 * test('answers the revenue query', { prompt: '...' }, async ({ agent, evaluate }) => {
 *   const result = await agent.run();
 *   await evaluate(result, 'sql-matches-golden');
 * });
 * ```
 *
 * Evaluators are registered into a process-global registry by id (like a
 * shared helper library), and run **in-process** when the body calls
 * `evaluate()`. They record a `MatcherResult` with `method: 'evaluator'`,
 * gate the test by default, and support an `.observe()` variant that feeds
 * score/insights only — mirroring the `judge` fixture.
 */
import type { EvalResult } from './types.js';
import type { TracesAccessor } from '../matchers/traces.js';
import type { Verdict } from './judge.js';
/** Context handed to a custom evaluator. */
export interface EvaluatorContext {
    /** The agent run under evaluation. */
    result: EvalResult;
    /** Optional free-form criteria string passed at the call site. */
    criteria?: string;
    /** Run-scoped OTel traces, when available (same as `result.traces`). */
    traces?: TracesAccessor;
}
/** What a custom evaluator returns. */
export interface EvaluatorResult {
    /** Whether the check passed. */
    pass: boolean;
    /** Optional normalised score on the [0, 1] interval. */
    score?: number;
    /** Human-readable explanation (shown in the per-matcher breakdown). */
    reasoning?: string;
}
export type EvaluatorFn = (ctx: EvaluatorContext) => EvaluatorResult | Promise<EvaluatorResult>;
/**
 * Register a custom evaluator under `id`.
 *
 * Registering the **same function** under an id again is a no-op (a watched
 * file re-loading the identical module is fine). Registering a *different*
 * function under an id that's already taken throws: the registry is
 * process-global (shared like a helper library), so two `.eval.{js,ts}` files
 * defining `defineEvaluator('len-check', …)` with different bodies would
 * otherwise silently shadow each other based on load order. Fail loudly so the
 * collision is visible at authoring time. Use distinct ids (or a shared helper
 * module) when you need different implementations.
 */
export declare function defineEvaluator(id: string, fn: EvaluatorFn): void;
/** Look up a registered evaluator by id. @internal */
export declare function getEvaluator(id: string): EvaluatorFn | undefined;
/** Clear all registered evaluators (test isolation / per-load reset). @internal */
export declare function clearEvaluators(): void;
/**
 * The `evaluate` fixture: run a registered custom evaluator against a
 * result. Gate role by default; `.observe()` records score/insights only.
 */
export interface EvaluateFn {
    (result: EvalResult, evaluatorId: string, criteria?: string): Promise<Verdict>;
    observe(result: EvalResult, evaluatorId: string, criteria?: string): Promise<Verdict>;
}
export declare const evaluate: EvaluateFn;
//# sourceMappingURL=evaluators.d.ts.map