/**
 * The runner registry — the data-driven replacement for a mode if/else.
 *
 * Every concrete {@link Runner} is listed once in {@link RUNNERS}; selecting the
 * right one for an invocation is a linear scan that returns the first whose
 * {@link Runner.accepts | accepts} is true. Adding a new way to drive the agent
 * means appending an entry here — no dispatch ladder to edit.
 *
 * Note the registry covers only the *running* modes. The `help` and `version`
 * modes are pure, output-only concerns the boot/CLI layer renders directly from
 * the flag table and package metadata, so no runner accepts them and they never
 * reach {@link selectRunner}.
 */
import type { Invocation, Runner } from "./contract";
/**
 * The ordered list of runners. Order is significant only as a tie-break: the
 * three modes are mutually exclusive, so at most one accepts any invocation, but
 * the scan stops at the first match either way.
 */
export declare const RUNNERS: readonly Runner[];
/**
 * Thrown when no registered runner can handle an invocation. This is a launcher
 * bug (e.g. a non-running mode reached the registry), not a user error.
 */
export declare class NoRunnerError extends Error {
    constructor(mode: string);
}
/**
 * Pick the runner for an invocation: the first in {@link RUNNERS} that accepts
 * it. Throws {@link NoRunnerError} if none match — which should only happen for
 * the `help`/`version` modes the boot layer is expected to short-circuit before
 * ever calling here.
 */
export declare function selectRunner(invocation: Invocation): Runner;
