import type { StreamFn } from "../internal/llm.js";
/**
 * Tiered timeouts (design/17 §5B; re-cut along the segments of a call in 7.33.0). The task-level
 * `timeoutSec` only bounds the *whole* run; it cannot tell apart a gateway that never completes the
 * handshake, one that accepts the request and then holds the response headers while it works, one
 * that opens the stream and never emits a token, and one that goes quiet mid-answer. Four clocks,
 * one per segment, each named for the segment it bounds:
 *
 * - **connect**: TCP + TLS establishment inside `fetch` — the transport's own clock (undici's 10s by
 *   default), tuned on the process dispatcher the host installs; not a knob here (`transport.ts`).
 * - **first-byte**: the `fetch()` call → response headers. A stall here is this process cancelling
 *   its own request at a deadline it chose, and is announced as such.
 * - **first-token**: headers → the first content delta (a thinking delta counts).
 * - **idle**: silence between deltas once output has started.
 *
 * Every field resolves through the same chain: construction config (an explicit `0` is OFF) >
 * per-call advisory (`StreamOptions.stallTimeouts`) > the engine default constant below.
 */
/**
 * The largest delay a host timer can actually wait for. Past this `setTimeout` does not schedule far in
 * the future — it truncates the delay to 1ms and fires at once, which for a watchdog means the OPPOSITE
 * of what a deployment widening the bound asked for: every call aborted immediately, while the failure
 * text quotes the number that was requested. Any ms knob that reaches a timer is refused above it
 * (knob-hygiene #98); a watchdog that is meant to be off has its own `0` / `false` spelling.
 */
export declare const MAX_TIMER_DELAY_MS = 2147483647;
/**
 * Resolve one stall-timeout knob to its effective value, refusing invalid numbers loudly. `x && x > 0`
 * downstream reads NaN as "unset" — a deployment that SET a tighter watchdog and fumbled the units
 * ("120s" → Number → NaN) used to get every watchdog silently OFF, the dangerous direction (ruled
 * 2026-08-03, cross-repo robustness batch; same posture as the guardrail knob). `0` stays legal —
 * an explicit off. Absent stays absent. Above {@link MAX_TIMER_DELAY_MS} is refused for the mirror-image
 * reason: the host timer would turn "watch for 34 days" into "abort on arrival".
 */
export declare function resolveStallTimeoutMs(value: number | undefined, knob: string): number | undefined;
/**
 * The engine's DEFAULT watchdog values, one per segment of a model call — the third and last level of
 * the chain every brain reads (`BrainTimeoutConfig` field > per-call advisory `StreamOptions.stallTimeouts`
 * > these). The Runner supplies these same numbers as the advisory on every call, so a Brain supplied by
 * the deployment (BYOM) is told what the engine would arm; a first-party brain arms them itself when
 * neither a config field nor an advisory names the segment. A silent call is therefore always bounded by
 * something finer than the hour-scale {@link BRAIN_CALL_GUARDRAIL_DEFAULT_MS} (a measured production
 * frame once sat 680s with ZERO deltas) unless a deployment switched the watchdog for that segment off
 * by name — an explicit `0` is the only spelling of OFF. The TCP + TLS connect has no row here: it is
 * the transport's own clock (`transport.ts`), not a segment the engine can observe. 量纲:
 *   - first-byte 300s: the `fetch()` call → response headers. A self-hosted gateway that runs its
 *     prefill BEFORE writing headers is silent here for minutes and healthy; the 30s "connect" clock
 *     this replaces cut that prefill and re-sent the same body ten times. 300s is the reference
 *     implementation's window for a non-first-party route; the window grows with the request body
 *     ({@link firstByteWindow}) and the one retry is widened ({@link FIRST_BYTE_RETRY_WINDOW_FACTOR}).
 *   - first-token 600s: bounds the headers→first-delta wait, NOT the model's thought (the first
 *     THINKING delta already counts as the first token, design/17 O5), so a slow reasoner is safe. A
 *     minutes-scale bound is FALSE for a self-hosted server working through a long prefill AFTER
 *     headers: it emits nothing for 2–3 minutes while healthy, and cancelling it burns the prefill and
 *     re-sends a request that burns it again. The default transport keeps NO clock over this segment
 *     (the request carries `bodyTimeout: 0`), so this number is the bound actually granted.
 *     {@link import("../orchestration/workflow.js").WORKFLOW_AGENT_STALL_MS} is DERIVED from the
 *     first-byte lane at {@link STALL_FIRST_BYTE_MS} ({@link firstByteLaneKnobMs}) + this value, so the coarse agent-restart cannot sit UNDER a single
 *     silent provider call (a floor, not a guarantee of ordering — see that constant's comment).
 *   - idle 300s: mid-stream silence after output started — the reference implementation's idle floor
 *     (a self-hosted server can stall for minutes between deltas while a server-side tool runs; a provider
 *     keep-alive comment is not a delta and does not count).
 * ADVISORY: a brain whose construction-time {@link BrainTimeoutConfig} sets the corresponding field
 * keeps it (deployment sovereignty; see the `StreamOptions.stallTimeouts` priority chain).
 */
export declare const STALL_FIRST_BYTE_MS = 300000;
export declare const STALL_FIRST_TOKEN_MS = 600000;
export declare const STALL_IDLE_MS = 300000;
/**
 * The retired names of the header-wait bound, each with the name that replaced it: the construction key and
 * the per-call advisory key. The type face no longer has them, but a type only refuses a LITERAL — a
 * spread object (`{ ...timeouts }`) or a JS caller still delivers the key, and a knob that arrives and is
 * then ignored is a deployment setting silently doing nothing. So their arrival is refused, naming the
 * replacement: a tripwire, not a second spelling (nothing reads the value).
 */
export declare const RETIRED_TIMEOUT_KNOBS: {
    readonly construction: {
        readonly connectTimeoutMs: "firstByteTimeoutMs";
    };
    readonly advisory: {
        readonly connectMs: "firstByteMs";
    };
};
/** Refuse a retired timeout key present on `holder` (see {@link RETIRED_TIMEOUT_KNOBS}); `where` names the
 *  object in the message (`"createOpenAIBrain config"`, `"StreamOptions.stallTimeouts"`). */
export declare function refuseRetiredTimeoutKnobs(holder: object | undefined, level: keyof typeof RETIRED_TIMEOUT_KNOBS, where: string): void;
/** The first-byte window grows with the request body: one second per this many bytes, rounded up (the
 *  reference implementation's allowance) — a large context on a slow uplink is still UPLOADING when a
 *  fixed window would call the gateway silent. */
export declare const FIRST_BYTE_UPLOAD_BYTES_PER_SECOND = 32768;
/** A first-byte stall is re-sent at most this many times by default ({@link BrainTimeoutConfig.firstByteRetries}),
 *  out of its OWN budget rather than `maxRetries` (the reference implementation: "normally one retry, not the
 *  session budget"); the shared ladder counts transport failures, and this is not one. */
export declare const FIRST_BYTE_RETRY_BUDGET = 1;
/** The first-byte lane's retry budget: absent ⇒ {@link FIRST_BYTE_RETRY_BUDGET}; `0` / `1` are themselves; anything
 *  else is refused (`config.stall_timeout_invalid`), never folded to the default or to `0`. */
export declare function resolveFirstByteRetries(value: unknown): 0 | 1;
/** The retry's knob is the first times this — the reference implementation widens its retry to a second
 *  knob's default (599s against a 300s first window); one knob here keeps that ratio instead. */
export declare const FIRST_BYTE_RETRY_WINDOW_FACTOR = 2;
/** The ONE resolution every watchdog knob reads: construction config (an explicit `0` is OFF) > per-call
 *  advisory > the engine default. A bad number at either level is refused ({@link resolveStallTimeoutMs});
 *  only an ABSENT value falls through. */
export declare function resolveWatchdogMs(configured: number | undefined, advisory: number | undefined, engineDefault: number, knob: string): number;
/** One armed first-byte window, with the two numbers its announcement quotes. */
export interface FirstByteWindow {
    /** The knob's value for THIS attempt (the configured value, widened on the retry). */
    knobMs: number;
    /** The upload allowance added for the request body, ms (0 for an empty body). */
    allowanceMs: number;
    /** What is actually armed: `knobMs + allowanceMs`, capped at {@link MAX_TIMER_DELAY_MS}. `0` = off. */
    windowMs: number;
}
/** The first-byte window for one attempt; `retryIndex` = first-byte stalls already suffered (0 first),
 *  each widening the knob by {@link FIRST_BYTE_RETRY_WINDOW_FACTOR}. A knob of `0` (OFF) stays off. */
export declare function firstByteWindow(knobMs: number | undefined, bodyBytes: number, retryIndex: number): FirstByteWindow;
/**
 * The most a first-byte lane armed at `knobMs` waits on the gateway in total before it gives up: the probe
 * window and every widened retry its budget allows ({@link FIRST_BYTE_RETRY_BUDGET},
 * {@link FIRST_BYTE_RETRY_WINDOW_FACTOR}) — read off {@link firstByteWindow}, the same function that arms
 * each attempt, so the sum cannot drift from what is armed. The knob part only: the upload allowance
 * depends on the request body and the backoff between attempts is not a wait on the gateway, so the lane's
 * real wall time is this plus both — a LOWER bound.
 */
export declare function firstByteLaneKnobMs(knobMs: number): number;
export interface BrainTimeoutConfig {
    /**
     * `fetch()` call → response headers, ms: the window a gateway has to START answering (until 7.33.0 a
     * knob named `connectTimeoutMs` bounded this wait; it is gone — the connect clock is the transport's). Absent ⇒
     * {@link STALL_FIRST_BYTE_MS}; the armed window grows with the request body ({@link firstByteWindow});
     * a stall earns ONE retry at a widened window ({@link firstByteRetries}), then fails naming this knob.
     * Fires as a `stall` (this process cancelled its own request), never as a transport fault. `0` ⇒ off.
     */
    firstByteTimeoutMs?: number;
    /**
     * The first-byte lane's own retry budget: `1` (absent ⇒ {@link FIRST_BYTE_RETRY_BUDGET}) or `0` — the first
     * window's expiry is the call's terminal, for a host whose own failover ladder should take a silent gateway after
     * one window. Independent of `maxRetries` both ways; construction only; any other value fails the call naming this
     * knob. The derived floors ({@link firstByteLaneKnobMs}) read the default.
     */
    firstByteRetries?: 0 | 1;
    /**
     * After the stream opens, cancel it if no content delta (text / thinking / tool input) arrives
     * within this many ms → `BrainError("network", "first-token timeout")`. For reasoning models the
     * **first thinking delta also counts** as the first token, so a long-thinking model isn't killed
     * for being slow to its first *visible* word (design/17 O5). Absent ⇒ {@link STALL_FIRST_TOKEN_MS};
     * `0` ⇒ off.
     */
    firstTokenTimeoutMs?: number;
    /**
     * **Idle / mid-stream stall watchdog** (1.40.1, design/24 — gap found in the Claude Code source study).
     * After the first delta, re-armed on every delta; if the stream then goes silent for this many ms
     * (a gateway that hung mid-output) → `reader.cancel()` + `BrainError("network", "stream idle timeout")`.
     * Complements `firstTokenTimeoutMs`: that one tolerates a long *first* token (reasoning), this one
     * catches a stall *after* output has started, where a healthy stream emits steadily. Absent ⇒
     * {@link STALL_IDLE_MS}; `0` ⇒ off.
     */
    idleTimeoutMs?: number;
}
/**
 * A per-attempt abort controller that fires at the first-byte deadline while still forwarding an
 * outer (task) abort. The returned `signal` governs the **whole** request including the streamed body
 * — so once headers arrive, call `clearTimer()` (NOT `dispose()`) to stop the deadline while keeping
 * the outer-abort link alive for the body read; call `dispose()` only when the request is fully done
 * or being discarded.
 */
export declare function createAttemptController(firstByteWindowMs: number | undefined, outerSignal: AbortSignal | undefined): {
    signal: AbortSignal;
    timedOut: () => boolean;
    clearTimer: () => void;
    /** Abort this attempt's signal. The signal handed to `doFetch` is how an injected fetch wrapper
     *  learns it may release its transport, so an attempt being ABANDONED (its error body cut short by
     *  a bound, its response discarded before a retry) must say so rather than just dropping the
     *  reference. Idempotent; a no-op once the response is fully consumed. */
    abort: () => void;
    dispose: () => void;
};
/**
 * Default ceiling on how long the ENGINE waits for a single brain call (ruled 2026-08-03: one hour).
 * Deliberately far above any legitimate call: this is a wedged-run backstop, not a latency policy —
 * a deployment that wants a tight per-call bound sets the fine tier above ({@link BrainTimeoutConfig},
 * or the advisory {@link STALL_FIRST_BYTE_MS} family the engine supplies by default). All of these clocks
 * run in parallel with the task's own `limits.timeoutSec`; whichever expires first owns the run.
 */
export declare const BRAIN_CALL_GUARDRAIL_DEFAULT_MS = 3600000;
/** Terminal `TaskResult.errorCode` for a run the guardrail stopped waiting on. */
export declare const BRAIN_CALL_GUARDRAIL_ERROR_CODE = "brain.call_guardrail_timeout";
/** Thrown at every await point of a brain call the guardrail has given up on. */
export declare class BrainCallGuardrailTimeoutError extends Error {
    /** Read by the runner's terminal-code chain (`errorCodeOf`) — never matched on the message text. */
    readonly code = "brain.call_guardrail_timeout";
    readonly limitMs: number;
    constructor(limitMs: number);
}
/**
 * The guardrail knob's value space: a positive number of milliseconds, or `false`/`0` to switch the
 * guardrail OFF entirely (a deployment that owns its own outer bound). Absent ⇒
 * {@link BRAIN_CALL_GUARDRAIL_DEFAULT_MS}.
 */
export type BrainCallGuardrailKnob = number | false;
/**
 * Resolve the knob to the effective limit, or `undefined` when the guardrail is off.
 *
 * FAIL-LOUD on a value that is neither: a negative / NaN / Infinity setting is a configuration bug,
 * and silently folding it to a default (or to "off") is how an operator ends up believing a wedged
 * run is bounded when nothing bounds it.
 */
export declare function resolveBrainCallGuardrailMs(knob: BrainCallGuardrailKnob | undefined): number | undefined;
/** Records the FIRST guardrail expiry of a run, so the runner can give the task a typed terminal. */
export interface BrainCallGuardrailRef {
    /** Set once, by the first call the guardrail gave up on. Absent ⇒ the guardrail never fired. */
    timedOut?: BrainCallGuardrailTimeoutError;
}
/**
 * Wrap a {@link StreamFn} so the ENGINE never waits longer than `limitMs` for one brain call.
 *
 * Every await point of the call is bounded: the `stream()` promise itself (a brain that never
 * returns a stream), each iterator step (a brain that trickles events forever), and `result()` (a
 * brain that opens a stream and never terminates it). The guardrail's clock starts when the call is
 * issued and stops when the call ends — it bounds ONE call, not the turn and not the task.
 *
 * `return()` on the wrapped iterator forwards the inner cleanup WITHOUT awaiting it: the whole point
 * is that the engine stops waiting on a brain, so releasing the iterator must not re-introduce a wait
 * on the same hung code.
 */
export declare function withBrainCallGuardrail(streamFn: StreamFn, limitMs: number, ref: BrainCallGuardrailRef): StreamFn;
