import type { Driver, DriverEvent } from './driver/index.js';
import { type FrameworkEvent } from './events.js';
/** Inputs to {@link emitSessionStart}. */
export interface SessionStartOptions {
    emit: (event: FrameworkEvent) => void;
    driver: Driver;
    /** The workspace the run works in. */
    cwd: string;
    /** The session link, literal or templated with `{sessionId}`. */
    sessionLink?: string | undefined;
    /** The model id the driver was started with (#1438), recorded on the event per leg. */
    model?: string | undefined;
}
/**
 * Emit the run's opening `session` event. A literal link is shown right away; a
 * templated one (`.../{sessionId}`) can only resolve once the driver reports its
 * session id, so it waits for the `session-update` from {@link createDriverEventHandler}.
 */
export declare function emitSessionStart(opts: SessionStartOptions): void;
/** Inputs to {@link createDriverEventHandler}. */
export interface DriverEventHandlerOptions {
    emit: (event: FrameworkEvent) => void;
    /** The session link template, when the caller configured one. */
    sessionLink?: string | undefined;
    /** The run's spend cap (#322). Omitted = uncapped. */
    budgetUsd?: number | undefined;
    /**
     * Answers "has the account reached its quota boundary?" between turns (#879).
     * Returns the label of the window that reached it, or null while there is room.
     */
    consumptionGate?: (() => string | null) | undefined;
    /** Tripped when the budget cap is crossed. */
    budgetController: AbortController;
    /** Tripped when the consumption gate reports a window is spent. */
    consumptionController: AbortController;
}
/** What {@link createDriverEventHandler} hands back. */
export interface DriverEventHandler {
    /** Wire this as the driver session's `onEvent`. */
    onDriverEvent: (event: DriverEvent) => void;
    /** The window that reached the boundary, once the consumption gate has fired. */
    consumptionTrip: () => string | undefined;
}
/**
 * Watch the driver's black box (#165) and turn it into the run's stream: surface the
 * real session id as `session-update` once known (that is the honest handle a UI links
 * to, and it changes per prompt, so re-emit), fold each turn's usage into the run total,
 * and trip the two self-stops.
 *
 * Both stops fire *after* the turn that crossed them: its cost is already spent, so the
 * point is to stop the next one. Each is signalled once, and the run's `AbortSignal.any`
 * composition carries it downstream. An agent that reports no price leaves `costUsd`
 * undefined and so can never trip the budget cap (#540). A consumption gate that throws
 * is treated as "carry on": an unreadable quota must not stop the work (#519), and the
 * gate is answered from a cached reading because a live one spawns the agent CLI (~5s).
 */
export declare function createDriverEventHandler(opts: DriverEventHandlerOptions): DriverEventHandler;
/** Inputs to {@link createRunControls}. */
export interface RunControlsOptions {
    emit: (event: FrameworkEvent) => void;
    /** The caller's abort signal (Stop button / Ctrl+C / control channel), if any. */
    signal?: AbortSignal | undefined;
    sessionLink?: string | undefined;
    budgetUsd?: number | undefined;
    consumptionGate?: (() => string | null) | undefined;
}
/** The run's abort plumbing plus its driver-event sink. */
export interface RunControls extends DriverEventHandler {
    /** The composed signal every driver turn runs under. */
    runSignal: AbortSignal;
    /** Trips a clean stop once this run has spent its budget cap (#322). */
    budgetController: AbortController;
    /** Trips a clean pause once the account's quota window is spent (#529). */
    consumptionController: AbortController;
    /** Trips a clean stop when the user declines a plan (#358); inert on the direct path. */
    declineController: AbortController;
}
/**
 * Compose the run's signal and wire its driver-event handler in one place. The caller's
 * signal is OR'd (via {@link AbortSignal.any}) with three self-stops — the budget cap
 * (#322), a spent consumption window (#529), and a declined plan (#358) — so anything
 * downstream that watches `runSignal` stops the same way regardless of which fired.
 * Shared by the build (`run.ts`) and direct-prompt (`prompt-run.ts`) paths.
 */
export declare function createRunControls(opts: RunControlsOptions): RunControls;
/** Inputs to {@link endStopDetail}. */
export interface StopDetailOptions {
    /** The error the run's turn loop threw. */
    err: unknown;
    /** The caller's own signal, to tell a caller stop from a self-stop. */
    signal?: AbortSignal | undefined;
    budgetController: AbortController;
    consumptionController: AbortController;
    declineController: AbortController;
    consumptionTrip: () => string | undefined;
    budgetUsd?: number | undefined;
    /**
     * Leave a resume note when the run paused on a consumption limit, returning where
     * it will resume from. Injected (not imported) so this module stays free of the
     * todo loop it would otherwise import in a cycle.
     */
    leaveResumeNote: () => Promise<string | undefined>;
}
/**
 * Classify why a run's turn loop threw and render the `end` event's `detail`. A caller
 * interrupt, a budget cap (#322), a declined plan (#358), or a spent consumption window
 * (#529) are all clean stops; anything else is a real failure. The resume note is written
 * here (once `paused` is known) rather than at the trip, because it is file I/O racing the
 * run unwinding. Shared so the two run paths can never disagree on what "stopped" means.
 */
export declare function endStopDetail(opts: StopDetailOptions): Promise<{
    stopped: boolean;
    detail: string;
}>;
//# sourceMappingURL=run-telemetry.d.ts.map