/** * Returns the idle timeout used for provider streaming transports. * * `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` is accepted as a backward-compatible alias. * Set `PI_STREAM_IDLE_TIMEOUT_MS=0` to disable the watchdog. * * Providers that legitimately stream much slower than the global default can pass * `fallbackMs` to widen the floor used when neither env var nor caller option is set. * Caller options still take precedence; env overrides still trump the fallback. */ export declare function getStreamIdleTimeoutMs(fallbackMs?: number): number | undefined; /** * Returns the idle timeout used for OpenAI-family streaming transports. * * `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` takes precedence over the generic * `PI_STREAM_IDLE_TIMEOUT_MS` because some deployments tune OpenAI-compatible * backends separately from Anthropic/Gemini-style transports. * * Set `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS=0` to disable the watchdog. */ export declare function getOpenAIStreamIdleTimeoutMs(fallbackMs?: number): number | undefined; /** * Returns the timeout used while waiting for the first stream event. * The first token can legitimately take longer than later inter-event gaps, * so the default never undershoots the steady-state idle timeout. * * Set `PI_STREAM_FIRST_EVENT_TIMEOUT_MS=0` to disable the watchdog. * * Providers whose first response can legitimately take longer (heavy reasoning, * slow cold-start proxies) can pass `fallbackMs` to widen the floor used when * neither env var nor caller option is set. Caller options still take precedence; * env overrides still trump the fallback. */ export declare function getStreamFirstEventTimeoutMs(idleTimeoutMs?: number, fallbackMs?: number): number | undefined; /** * Returns the first-event timeout used for OpenAI-family streaming transports. * * Precedence: explicit `PI_OPENAI_STREAM_FIRST_EVENT_TIMEOUT_MS` (including a * `"0"` disable) wins outright. Otherwise the resolved idle (caller-supplied * `idleTimeoutMs` — which itself already encompasses per-call * `streamIdleTimeoutMs` or `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` resolved * upstream) floors the first-event budget so slow local OpenAI-compatible * servers are not undercut by a shorter `PI_STREAM_FIRST_EVENT_TIMEOUT_MS` * or the global default during prompt processing. * * Returns `undefined` when an explicit env knob disables the watchdog. */ export declare function getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs?: number, fallbackMs?: number): number | undefined; /** * Arms a clearable pre-response (time-to-first-byte) abort guard for a streaming * fetch, combined with the caller's signal. * * `AbortSignal.timeout(ms)` is an *absolute* wall-clock deadline: once handed to * `fetch` it keeps governing the request after the response headers arrive, so * it aborts an actively-streaming body the moment it fires — not just a stalled * pre-response request (issue #2422 regression: large `write` tool-call streams * died at the budget with `TimeoutError: The operation timed out.` despite * deltas actively flowing). This arms a `clearTimeout`-able timer instead; * callers MUST `clear()` as soon as `fetchWithRetry` resolves (headers in) so * the body stream is left to the iterator-level idle watchdog. The timer aborts * with a `TimeoutError` matching `AbortSignal.timeout`, so a genuine pre-response * stall behaves exactly as the prior code did — `fetchWithRetry` normalizes the * abort to "Request was aborted" either way (only a post-headers abort ever * surfaced the raw `"The operation timed out."`, which clearing now prevents). * * Returns the caller signal unchanged (and a no-op `clear`) when no positive * timeout is configured. */ export declare function armPreResponseTimeout(callerSignal: AbortSignal | undefined, timeoutMs: number | undefined): { signal: AbortSignal | undefined; clear: () => void; }; export interface IdleTimeoutIteratorOptions { idleTimeoutMs?: number; firstItemTimeoutMs?: number; errorMessage: string; firstItemErrorMessage?: string; onIdle?: () => void; onFirstItemTimeout?: () => void; /** * Optional semantic-progress predicate. Non-progress items are still yielded, * but they do not reset the idle deadline. This prevents provider * keepalive/no-op events from keeping a stalled tool call alive forever. */ isProgressItem?: (item: unknown) => boolean; /** * Cancel iteration as soon as this signal aborts. Required for caller-driven * cancellation (ESC) when the underlying transport does not surface signal * aborts to the iterator (HTTP/2 proxies, native sockets, mocked fetch). * Without this, the consumer sleeps on iterator.next() until the idle/first * -event watchdog fires — observable as the issue #912 "Working… forever" * symptom on the github-copilot provider. */ abortSignal?: AbortSignal; } /** * Yields items from an async iterable while enforcing a maximum idle gap between items. * * The first item may use a shorter timeout so stuck requests can be aborted and retried * before any user-visible content has streamed. */ export declare function iterateWithIdleTimeout(iterable: AsyncIterable, options: IdleTimeoutIteratorOptions): AsyncGenerator; export interface TerminalGraceIteratorOptions { /** * Epoch-ms timestamp at which the consumer observed a logically terminal * item (e.g. a chat-completions chunk carrying `finish_reason`), or * `undefined` while the stream is still mid-response. Read before every * pull, so the consumer can flip it between yields. */ finishedAtMs: () => number | undefined; /** * Post-terminal budget: how long after `finishedAtMs()` to keep draining * trailing items (e.g. a usage-only chunk or the `[DONE]` sentinel) before * ending the iteration cleanly. The deadline is fixed at * `finishedAtMs() + graceMs`; trailing items do not extend it, so * keepalive-only servers cannot hold the stream open. */ graceMs: number; /** * Invoked when the grace window closes with the source still open. Use it * to abort the underlying request: the source generator is typically parked * mid-`next()` (not at a yield), so a queued `.return()` alone cannot reach * the transport until that pending read settles. */ onGraceEnd?: () => void; } /** * Yields items from an async iterable until the consumer marks the stream * logically finished AND the source stays silent past a short grace window. * * Misbehaving OpenAI-compatible servers deliver the terminal chunk but never * send `[DONE]` nor close the connection; without this guard the consumer * hangs on `iterator.next()` until the idle watchdog converts an * already-successful turn into a timeout error. Grace expiry is a clean end * of iteration, never an error. */ export declare function iterateWithTerminalGrace(iterable: AsyncIterable, options: TerminalGraceIteratorOptions): AsyncGenerator;