import type { EngineId } from './types.js'; /** Which env var holds each engine's API key. */ export declare const ENGINE_KEY_ENV: Record; /** * Default judge model per engine. * * NOT simply the cheapest any more. The judge does competitor discovery, which * is factual RECALL, and cheap models do not recall - they fabricate. Measured * 2026-07-17 on a Turkish retailer (doremusic; ground truth verified on the * web): `gpt-4o-mini` returned 0/10 real rivals and missed Zuhal Müzik, the * obvious one, inventing a different plausible-sounding list on every run. * `gpt-5.4` returned Zuhal Müzik, MyDukkan and Cangöz Müzik, ranked first. * The judge is cheap to upgrade: 2 one-shot setup calls per brand (cached), * and the scoring judge only fires on ambiguous answers, capped at * `JUDGE_RATE_CAP` (it fired 0 times across every live run so far). */ export declare const DEFAULT_JUDGE_MODELS: Record; /** * Which provider serves a model id, including ids that are no longer a default. * * Callers used to reverse-look-up the engine by exact match against * {@link DEFAULT_JUDGE_MODELS} and silently fall back to "the first available * engine" on a miss. When a default changes, every id the tool previously told * users to pin misses - so `--judge gemini-2.5-flash` built an OpenAI adapter * and posted a Gemini model id to api.openai.com. Matching on the id's own * prefix keeps legacy and future ids routing to the provider that owns them. * * Returns undefined when the id belongs to no known provider; callers must * surface that as an honest error rather than guessing an engine. */ export declare function engineForModel(model: string): EngineId | undefined; /** The canonical set of engine ids, in a stable order. Single source of truth. */ export declare const ENGINE_ORDER: EngineId[]; /** Engines whose API key is present and non-empty, in a stable order. */ export declare function detectAvailableEngines(env: Record): EngineId[]; /** User-resolvable config keys. All optional; resolution fills what it can. */ export interface OptifeedConfig { judgeModel?: string; engines?: EngineId[]; maxCostUsd?: number; maxSetupCostUsd?: number; } /** Layers in ascending precedence: defaults < env < file < flags. */ export interface ConfigSources { defaults?: Partial; env?: Partial; file?: Partial; flags?: Partial; } /** * Merge config layers with precedence flags > file > env > defaults. A key that * is `undefined` in a higher layer does not clobber a lower layer's value. */ export declare function resolveConfig(sources: ConfigSources): OptifeedConfig; /** Raised when a judge model is needed but no engine keys are available. */ export declare class NoJudgeModelError extends Error { constructor(); } export interface ResolveJudgeModelInput { interactive: boolean; availableEngines: EngineId[]; /** A previously saved explicit choice; wins when present. */ savedJudgeModel?: string; /** Injected picker for the interactive path; receives candidate model ids. */ prompt?: (choices: string[]) => Promise; } export interface JudgeModelResolution { model: string; source: 'saved' | 'prompted' | 'fallback'; /** Human-readable note when we fell back without asking (CI/MCP/--yes). */ notice?: string; /** * Set when the resolved judge has a MEASURED quality problem, whatever the * selection path. Consumers must surface this - it reports that the run's * competitor list may be fabricated (rule #6). */ qualityWarning?: string; } /** * Judge models in order of MEASURED competitor-recall quality, best first. * * This deliberately replaces the old "cheapest available" rule. The judge does * factual RECALL, and that rule kept silently downgrading it: first to * `claude-haiku-4-5`, then - once anthropic's default was corrected - to * `gemini-flash-latest`, because each new cheap entry undercut the measured * choice. Fixing each instance never fixed the rule. * * Measured 2026-07-20 on the doremusic Turkish-retailer task (ground truth * verified on the web, 3 trials each through the real discovery path): * * | model | verified rivals | cross-trial stability | * | ------------------- | --------------- | --------------------- | * | gpt-5.4 | 4/4 | high | * | gemini-flash-latest | 4/4 | high | * | claude-sonnet-5 | 0 | none (no overlap) | * * Price proved ANTI-correlated with quality: `claude-sonnet-5` is the most * expensive candidate ($3/$15) and the only one that fabricated, inventing a * different plausible-sounding list on every run and never returning Zuhal * Müzik, the market leader both other models ranked first every time. * * gpt-5.4 leads gemini-flash-latest on track record (measured twice, and no * free-tier rate-limit history) rather than on recall, where they tied. * `sonar` is last: it bills per search, so it is expensive AND a poor fit for * a parametric recall task. * * Re-measure when a default here changes - a model id is not a quality claim. */ export declare const JUDGE_PREFERENCE: string[]; /** * Judges with a measured quality problem, keyed by model id. * * Surfaced whenever the model is resolved - including when the user pinned it * explicitly - because rule #6 forbids running a known-unreliable judge * silently. The run still proceeds: an Anthropic-only user must keep a working * zero-config path. */ export declare const JUDGE_QUALITY_WARNINGS: Record; /** * Resolve which judge model to use. Order: saved explicit choice → interactive * prompt → best available by measured recall (non-interactive fallback, with a * printed notice). Throws {@link NoJudgeModelError} when no engines are * available. */ export declare function resolveJudgeModel(input: ResolveJudgeModelInput): Promise; export interface ResolveStateDirInput { cwd: string; domain: string; homeDir: string; /** Whether `/.optifeed` can be created/written (injected fs check). */ isProjectWritable: boolean; } /** * State dir: `/.optifeed/` when the project dir is writable, else * `/.optifeed/`. Domain-scoped in both cases so several brands * can be checked from one directory without their caches colliding. Uses * POSIX-style joins (no fs access). */ export declare function resolveStateDir(input: ResolveStateDirInput): string;