import { type CostAccumulator, type CostSnapshot, type LLMProvider, type ResponseCache } from '../llm/index'; import { type CoverageLookup, type SourceFileInput } from '../pipeline/targeting'; import { type PrePassLogger } from '../pipeline/prepass'; import { type DroppedReplacement, type LlmMutatorMap } from '../pipeline/llm-map'; import type { NodeMutator } from '../mutators/index'; import type { LlmMutatorConfig } from '../config'; /** * Error thrown when a feature is enabled in config but not yet implemented in the * current milestone. Distinct class so callers (and tests) can match it precisely * rather than scraping a message. */ export declare class NotImplementedError extends Error { constructor(message: string); } /** * Error thrown when dynamicLLM is enabled with a network provider but the required * credentials are absent. Carries the offending provider for a precise message. */ export declare class MissingCredentialsError extends Error { /** The provider whose credentials were not found. */ readonly provider: string; constructor(message: string, provider: string); } /** * The gating plan {@link gateSwitches} returns — a pure description of what the * driver should do, independent of injection mode (`--ours-only`/`--augment`). */ export interface GatePlan { /** Inject the heuristic selection (true unless heuristics are disabled). */ runHeuristics: boolean; /** Run the dynamic-LLM pre-pass + inject the `LLMMutator` (Phase A: throws). */ runDynamicLLM: boolean; /** * Both switches are off: no custom mutators will be injected and the driver * should run STOCK Stryker unmodified. The driver MUST surface {@link warning}. */ bothOff: boolean; /** * A user-facing warning to emit, or `undefined` when none applies. Set when * both switches are off (the user gets vanilla behaviour and must be told). */ warning?: string; } /** * Collapse the two config switches into a {@link GatePlan}. PURE — it makes NO * credential check and NO injection; it only describes the decision. The driver * calls {@link assertLlmCredentials} and {@link buildLlmMutator} separately when * `runDynamicLLM` is true. */ export declare function gateSwitches(cfg: LlmMutatorConfig): GatePlan; /** * Fail FAST if dynamicLLM is enabled with a network provider whose credentials * are missing (functional-architecture §6 switch interplay). PURE except that it * reads the ambient environment (injectable for tests). Does NOT silently degrade * to heuristics-only — it throws {@link MissingCredentialsError} so the CLI can * print a clear error and exit non-zero. * * • `anthropic-agent-sdk` → reuse {@link resolveAuthEnv}, which throws when * `CLAUDE_CODE_OAUTH_TOKEN` is absent. We translate its * `AgentProviderError('missing_oauth_token')` into a uniform * {@link MissingCredentialsError}. * • `anthropic-api` → require `ANTHROPIC_API_KEY`. * • `openai` → require `OPENAI_API_KEY` (the real, metered OpenAI API). * • `openai-compatible` → NO required key: local servers (LM Studio / vLLM / * llama.cpp) run key-less. The provider still SENDS * `Authorization: Bearer ` when a key happens to be present, * but its absence is NOT a credential error here. * • `mock` (or dynamicLLM disabled) → no-op. * * @param cfg The parsed config. * @param env The ambient environment (defaults to `process.env`); injectable. */ export declare function assertLlmCredentials(cfg: LlmMutatorConfig, env?: Record): void; /** Injected dependencies for {@link buildLlmMutator} — all bun-mockable. */ export interface BuildLlmMutatorDeps { /** * The BUDGETED provider (cache + cost + ceiling enforcement). Injected so the * orchestrator is bun-testable with a MockProvider; run.ts builds the real * `createBudgetedProvider(createProvider(cfg), …)` and passes it. */ provider: LLMProvider; /** * The SAME cost accumulator the budgeted `provider` records into. The pre-pass * reads its snapshot for the final cost report; sharing one instance keeps the * provider's spend and the report in lockstep. */ costAccumulator: CostAccumulator; /** The absolute-path source files Stryker will mutate (read in run.ts). */ files: readonly SourceFileInput[]; /** * The cwd the map-builder resolves non-absolute fileNames against — must be * the dir Stryker keys absolute paths by (the project root). The targeting * already emits absolute fileNames, so this only normalizes defensively. */ cwd: string; /** Note logger (targeting notes, pre-pass stops, dropped near-equivalents). */ log?: PrePassLogger; /** Optional coverage probe; absent ⇒ no coverage signal (treat as eligible). */ coverageLookup?: CoverageLookup; /** Cooperative cancellation signal forwarded to the pre-pass. */ signal?: AbortSignal; /** * The SAME response cache the budgeted `provider` reads. When given, its key * listing drives MONOTONE targeting: every eligible function already cached * is always targeted (free), and the per-file / per-run caps bound only the * uncached ones. Absent ⇒ legacy targeting (every candidate counts as new). */ cache?: ResponseCache; /** * Frozen (cache-only) mode: uncached functions are not targeted at all (the * provider would answer them with no candidates). Default false. */ frozen?: boolean; } /** The outcome of {@link buildLlmMutator}: the injected mutators + run metadata. */ export interface BuildLlmMutatorResult { /** * The 17 LLM NodeMutators to push onto `allMutators` (sync map lookups): the * no-op `llm` wildcard plus one per `Llm`, in `LLM_MUTATOR_NAMES` * order, regardless of the map contents. */ mutators: readonly NodeMutator[]; /** The precomputed map (also drives the reporter's id→tag enrichment). */ map: LlmMutatorMap; /** Final LLM cost snapshot for the reporter. */ costSnapshot: CostSnapshot; /** Statement-shaped + near-equivalent drops, for the run log. */ droppedLog: DroppedReplacement[]; } /** * The REAL dynamic-LLM pre-pass orchestrator (M3) — replaces the Phase-A throw. * It runs entirely on the injected `LLMProvider` abstraction, so it is * bun-testable offline with a MockProvider (no Stryker, no network): * * 1. Gate 1/2 — `buildProposeTargets(files, cfg)` → EV-ranked function targets; * 2. Gate 3/4 — `runPrePass(provider, targets, cfg, …)` → filtered survivors * (cache + budget + diminishing-returns all enforced inside); * 3. build the precomputed `(absFileName, locKey) → ParsedEntry[]` map from the * survivors (`buildLlmMutatorMap`, statement-shaped drops logged); * 4. wrap it in the 17 sync `createLlmMutators(map)` NodeMutators (the no-op * `llm` wildcard + one per `Llm`, each serving its own entries). * * The driver calls {@link assertLlmCredentials} BEFORE constructing the provider, * so missing creds surface as a credentials error; this orchestrator assumes a * ready provider. It is async (the LLM work is the pre-pass); the RETURNED mutator * is synchronous (the seam invariant). * * @param cfg The parsed config (targeting + budget + diminishingReturns). * @param deps Injected provider, files, cwd, logger, coverage probe, signal. * @returns The injected mutator, the map, the cost snapshot, and the drop log. */ export declare function buildLlmMutator(cfg: LlmMutatorConfig, deps: BuildLlmMutatorDeps): Promise;