import type { ChatTransport, UIMessage, UIMessageChunk } from 'ai';
import type { AgentBackend } from './backend.js';
import type { AgentRunFailure } from './run-errors.js';
/** Identity surfaced by the backend's `meta` SSE frame / response headers. */
export interface AgentStreamMeta {
    runId: string;
    threadId: string;
}
/**
 * How the transport retries a stream that dropped mid-run. It reconnects only to a server that
 * numbers its frames (SSE `id:`) — without a cursor a reconnect would replay the run into the
 * message it is already writing.
 */
export interface ReconnectOptions {
    /** Attempts before giving up with an error chunk. Default 6. */
    maxAttempts?: number;
    /** Delay before the first attempt, doubled each attempt. Default 500 ms. */
    baseDelayMs?: number;
    /** Cap on the delay between attempts. Default 8000 ms. */
    maxDelayMs?: number;
}
/** Where a live stream stands. `reconnecting` → it dropped and the transport is retrying. */
export type StreamConnectionState = {
    status: 'live';
} | {
    status: 'reconnecting';
    runId: string;
    attempt: number;
    after: number;
}
/** The run ended while the client was away (the resume answered 404): reload the thread. */
 | {
    status: 'gone';
    runId: string;
}
/** Every attempt failed; the stream ended with an error chunk. */
 | {
    status: 'failed';
    runId: string;
};
export interface AgentChatTransportOptions {
    /**
     * What the transport talks to. Default: an {@link AgentClient} built from `baseUrl`, `headers`,
     * `getHeaders`, `credentials` and `fetch` below — which are ignored when a backend is given.
     */
    backend?: AgentBackend;
    /** Retry a dropped stream from its last frame. Default on; `false` turns it off. */
    reconnect?: ReconnectOptions | false;
    /** Fires as a stream drops, reconnects, or gives up. */
    onConnectionChange?: (state: StreamConnectionState) => void;
    /** The server's origin, e.g. `https://api.example.com`. Defaults to `''` (same origin). */
    baseUrl?: string;
    /** The agent's route prefix (`AgentModule`'s `path`, global prefix included). Default `'agent'`. */
    path?: string;
    /** Static headers merged into every request (e.g. a tenant ref). */
    headers?: Record<string, string>;
    /**
     * Resolved at request time — use for short-lived bearer tokens that
     * must not be captured once at construction. Merged over `headers`.
     */
    getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
    /** Forwarded to `fetch` so cookie-based auth/impersonation works. */
    credentials?: RequestCredentials;
    /** Named agent to run the turn (backend `agent` field). */
    agent?: string;
    /** One of the agent's personas to answer as (backend `persona` field). */
    persona?: string;
    /**
     * Extra body fields evaluated per send — the hook injects `threadId`
     * and `pageContext` through this. Returned object is spread into the
     * request body after the SDK's own `body`.
     */
    getBody?: () => Record<string, unknown>;
    /**
     * The run id to resume on mount, if any. Returning `undefined` makes
     * `reconnectToStream` resolve to `null` WITHOUT hitting the network —
     * this is the generalized fix for "useChat fires a doomed GET on mount
     * and surfaces its 404". Wire this to the thread's `activeRunId`.
     */
    getResumeRunId?: () => string | undefined;
    /**
     * Fires once `reconnectToStream` has attached to a run's stream, with that run's id. A resumed
     * stream replays frames but opens with no `meta` frame, so this is the only place a caller learns
     * which run it is now reading — what a Stop pressed after a reload has to cancel.
     */
    onResumeAttached?: (runId: string) => void;
    /** Fires whenever a stream emits its `meta` frame. */
    onMeta?: (meta: AgentStreamMeta) => void;
    /**
     * Fires synchronously at the very start of `sendMessages`/`reconnectToStream`, before any network
     * call — i.e. strictly before that attempt's own `onMeta` (if any) can fire. Lets a caller reset
     * "which run is THIS attempt settling" state without racing React's render/effect timing: a
     * consumer that also read `onMeta` to learn the current run id would otherwise risk misattributing
     * an early failure (no meta ever arrives) to a PRIOR attempt's id.
     */
    onAttemptStart?: () => void;
    /**
     * A resume found nothing streaming under the run id (the backend answered 404): the run already
     * ended. The SDK is told there is nothing to resume, so the caller is the one that has to reload
     * what the run wrote — `useAgentChat` re-reads the thread.
     */
    onResumeGone?: (runId: string) => void;
    /**
     * The run's stream ended with the backend's `event: error` frame — called with its `code` and
     * `message` right before the stream is closed with an error chunk. The chunk can only carry the
     * text; this is where the `code` reaches a caller, so it can word the failure itself.
     */
    onRunError?: (failure: AgentRunFailure) => void;
    /** Injectable for tests / non-browser runtimes. Defaults to global fetch. */
    fetch?: typeof fetch;
}
/**
 * AI SDK v7 `ChatTransport` for the Adonis agent backend. POSTs
 * `/agent/chat`, parses the backend's `meta` + `{delta}` + `done` SSE
 * frames, and re-emits them as the v7 UI-message chunk stream
 * (`start` → `text-start` → `text-delta`* → `text-end` → `finish`).
 *
 * The backend hydrates prior history from its store, so only the latest
 * user message text is sent each turn — keeping payloads tiny and
 * preventing the client from corrupting replayed history.
 *
 * Attachments (image/PDF for a vision-capable model) ride the per-send body:
 * `sendMessage({ text }, { body: { attachments: MessageAttachment[] } })`. They
 * flow to the backend as the turn's `attachments`, get persisted on the user
 * message, and are rendered as native model content parts. Optimistic display of
 * the user's own attachment thumbnails is the consumer's concern (it stages the
 * upload), the same way history rendering reads `StoredMessage.attachments`.
 *
 * Frames beyond text/reasoning/tools become AI SDK data parts (seen by `useChat`'s `onData`):
 *  - `ui`                 → `data-ui` part, `id` = the component id (a repeat id updates it in place)
 *  - `approval-requested` → `data-approval-requested` part keyed by the call id, plus the SDK's
 *                           native `tool-approval-request` (the part moves to `approval-requested`)
 *  - `approval-settled`   → `data-approval-settled` part keyed by the call id (who decided, through
 *                           what, remembered or not); the outcome still rides the call's own output
 *  - `title`, `cancelled`, `queue` → transient `data-title` / `data-cancelled` / `data-queue` (never
 *                           stored on the message). A send the server queued (`202`) is reported
 *                           as a transient `data-queue` too, carrying `queuedSend`
 *  - any other kind       → `data-<kind>` part carrying the frame minus `kind`, keyed by its `id`
 *                           when it has one — forwarded, never dropped
 * `parentId` on a tool frame rides the part's `toolMetadata` next to `toolKind`.
 */
export declare class AgentChatTransport implements ChatTransport<UIMessage> {
    private readonly options;
    private currentRunId;
    private currentThreadId;
    /**
     * One attempt at a time. The AI SDK keeps a single in-flight response per chat and decides
     * whether its streamed message REPLACES the list's last entry or is appended by comparing that
     * response's message id against the last entry alone. Two attempts writing into one chat
     * therefore append alternating copies of each other's message, and the list ends up holding
     * several entries under each id — which React renders as duplicate keys.
     */
    private attemptLive;
    private readonly backend;
    constructor(options?: AgentChatTransportOptions);
    /** Run id of the most recent stream — HITL approve/reject target this. */
    get runId(): string | undefined;
    /** Thread id of the most recent stream. */
    get threadId(): string | undefined;
    /**
     * True from the moment a send or resume attempt starts until its chunk stream terminates. A
     * caller that can refuse a turn BEFORE the SDK commits to one — `useAgentChat` wrapping
     * `sendMessage` — reads this; the transport itself can only refuse a resume, which is the one
     * attempt the SDK lets a transport decline.
     */
    get isAttemptLive(): boolean;
    sendMessages(options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0]): Promise<ReadableStream<UIMessageChunk>>;
    reconnectToStream(options: Parameters<ChatTransport<UIMessage>['reconnectToStream']>[0]): Promise<ReadableStream<UIMessageChunk> | null>;
    private captureHeaderMeta;
    private reconnectPolicy;
    private recordMeta;
    /**
     * Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.
     * Recognized frames:
     *  - `event: meta`  `data: {"runId","threadId"}`   → records identity
     *  - `data: <AgentStreamEvent JSON>`                → mapped to UI chunks (text / reasoning / tool /
     *                                                     `data-*`; see {@link AgentChatTransport} docs)
     *  - `event: done`  `data: {}`                      → terminates
     *  - `event: error` `data: {code,message}`          → error chunk
     *
     * A run is ONE UI message with N steps. Each `step-start`/`step-finish` pair brackets a model
     * call plus its tool execution; text and reasoning open lazily and close at the step boundary, so
     * tool-call cards (input streaming → output) render live between the prose.
     */
    private toChunkStream;
}
//# sourceMappingURL=agent-chat-transport.d.ts.map