import { b as AgentTransport, A as ApprovalDecision } from './agent-handle-rEWdERuv.js'; export { c as AgentClient, d as AgentClientOptions, e as AgentClientState, a as AgentHandle, f as AgentStreamInterruptedError, g as ApprovalAbortedError, C as ChannelPushSource, h as ChannelTransport, i as ChannelTransportOptions, j as ChannelTurnHandlers, I as InProcessApprovalRequestLike, k as InProcessAwaitApproval, l as InProcessRunInput, m as InProcessRunner, n as InProcessTransport, o as InProcessTransportOptions, P as PendingApproval, R as RequestContext, U as UseAgentStatus, p as agentHandle, q as isAgentHandle } from './agent-handle-rEWdERuv.js'; import { WireTransport, WireChunk, WireMessage } from '@theokit/presenter/wire'; export { WireTransport } from '@theokit/presenter/wire'; import '@theokit/sdk/errors'; /** Extra request headers — a static record OR a resolver called per request (for dynamic auth). */ type HeadersResolver = Record | (() => Record | undefined); interface HttpTransportOptions { /** Agent endpoint path or URL, e.g. `/api/agents/support`. */ api: string; /** * Extra request headers (e.g. auth). Static record OR a resolver evaluated on EVERY request — pass a * resolver when the value is dynamic (a rotating JWT), so a stale header is never sent. Merged UNDER * per-request headers. */ headers?: HeadersResolver; /** Override fetch (primarily for tests / non-browser hosts) — static; resolved once at construction. */ fetch?: typeof fetch; /** * Where the server-minted run id lives between requests — the key `reconnectToStream` needs * (usetheokit/theokit#387). * * Defaults to an in-memory cell, which is what this transport always did and what keeps a * reconnect working within one page lifetime. That default is also why a RELOAD could not * reconnect: the new page builds a fresh transport with an empty cell, so `reconnectToStream` * returns `null` before it reaches the network — while the server still holds the run in its * cache and would replay it. * * The medium is the CONSUMER's decision, not this package's. `sessionStorage` matches a run's * lifetime better than `localStorage`, and either would be a client library writing to browser * storage nobody asked it to write to, with privacy and SSR consequences. So the seam is injected * and the package stores nothing it was not handed a place for: * * ```ts * new HttpTransport({ * api: '/api/agents/support', * runIdStore: { * get: () => sessionStorage.getItem('theo.runId') ?? undefined, * set: (id) => { * if (id === undefined) sessionStorage.removeItem('theo.runId') * else sessionStorage.setItem('theo.runId', id) * }, * }, * }) * ``` * * Deliberately NOT included: reconnecting automatically on load. This makes a cached run * REACHABLE; reaching for it is a product decision nobody has asked for. */ runIdStore?: RunIdStore; } /** * Where the reconnect key is kept. * * Two methods rather than a storage object, so a consumer can back it with `sessionStorage`, a * cookie, a router param, or a test double — the transport never learns which. */ interface RunIdStore { get: () => string | undefined; set: (runId: string | undefined) => void; } /** * M41 (ADR-0050 D3) — `ChatTransport` over the web agent path. * * - `sendMessages`: `POST ` with the UIMessageStream `accept` + the `X-Theo-Action` CSRF header * (HTTP method + headers are identical to the pre-M41 `useAgent` fetch; the body shape is a superset — * `{ ...input, messages: [UIMessage] }` — which the server's dual-path parser accepts, so no * regression), captures the server-minted `x-theokit-run-id`, and returns `ReadableStream` * via `ai`'s own SSE parser (`responseToChunkStream`). * - `reconnectToStream`: `GET /runs//stream` (M37 durable transport); 404 → `null` (the run * completed / was evicted). A caller may pass a `Last-Event-ID` header to resume only the tail; by * default the server replays the run from the start and the client upserts by message id (idempotent). * - `approve`: `POST /approve/` (out-of-band HITL settle). * * Implemented directly (not by subclassing `DefaultChatTransport`) because reconnect keys on our * server-minted `runId` captured from a response header, which the base class does not expose — see * ADR-0050 D3. */ declare class HttpTransport implements AgentTransport { #private; constructor(options: HttpTransportOptions); sendMessages(options: Parameters[0]): Promise>; reconnectToStream(options: Parameters[0]): Promise | null>; approve(approvalId: string, decision: ApprovalDecision): Promise; } type UIMessage = WireMessage; type UIMessageChunk = WireChunk; /** * Read a TheoKit agent endpoint's `UIMessageStream` SSE `Response` into reconstructed assistant * messages. * * ## What changed, and why (plan `remove-ai-dependency`) * * This file used to be the ONLY runtime use of `ai` in the whole published surface — two * `await import('ai')` calls, measured in `@theokit/agents@7.0.0/dist/chunk-FCGL2PEC.js`. It now * uses TheoKit's own wire module, so installing `theokit` no longer pulls the ai-sdk. The FRAME * FORMAT is unchanged: an ai-sdk client still understands a TheoKit server and vice versa. * * The import is static again. It was dynamic only because `ai` was an OPTIONAL peer that an app * might not have installed; `@theokit/presenter` is a real dependency, so there is nothing to defer. * * ## The #136 workaround is gone with the thing it worked around * * `ai`'s reader swallows an `error` chunk unless the caller passes BOTH `onError` and * `terminateOnError` — eight lines of comment used to explain that trap here. Owning the reader * makes rejecting on `error` the DEFAULT, so the workaround has nothing left to work around. The * behaviour a consumer sees is identical; the regression test for #136 still guards it. * * `onMessage` is invoked on every reconstruction step with the latest snapshot of the assistant * message, so a caller (the `useAgent` hook) can render streaming updates. * * Because this path parses the bytes itself, its outcome also carries `framesDropped`: how many * frames the wire parser could not read and discarded. A turn that said nothing and a turn whose * frames were all unreadable both reconstruct to no text; that count is what tells them apart. */ declare function consumeUIMessageStream(response: Response, onMessage: (message: UIMessage) => void): Promise; /** * A UIMessageStream SSE `Response` → `ReadableStream`. This is precisely what a * `ChatTransport.sendMessages` returns, so `HttpTransport` builds on it directly. A body-less * response yields an empty stream. `onWarn` hears each frame the wire parser discards. */ declare function responseToChunkStream(response: Response, onWarn?: (message: string) => void): Promise>; /** * How a chunk stream ENDED — theokit#384. * * ## Why this exists at all * * The reader used to return `void`, which left its caller exactly two outcomes to choose between: * it threw, or it did not. A stream that simply STOPS — a socket closed mid-run — throws nothing, * so `AgentClient` read "did not throw" as "the agent finished" and settled a truncated answer in * `status: 'done'` with no error. The information needed to tell the two apart was on the wire and * nothing carried it out of the reader. * * ## What counts as an ending, and what does not * * The stream's own terminal chunk, `finish`, is the marker — NOT the SSE `[DONE]` sentinel: * * - `finish` is emitted by `presentUIMessageStream` on EVERY framework path, including the error * path, so it is the one terminator that exists on all three transports. `[DONE]` is written * only by the durable SSE encoder, so an in-process or channel stream has none to look for. * - `[DONE]` is also the WEAKER claim of the two. `durableUiMessageStreamResponse` flushes it from * a `finally` even when the source aborted mid-run, so a run cut on the SERVER carries `[DONE]` * and no `finish`. Keying on `finish` reports that case too; keying on `[DONE]` would miss it. * * `finish` also cannot lie in the other direction: nothing can truncate a run after its terminal * chunk has been written, so a stream that carried one carried a complete turn even if the socket * died a byte later. */ interface ChunkStreamOutcome { /** * `true` iff the terminal `finish` chunk crossed before the stream ended. * * `false` means the producer stopped talking mid-run: a dropped connection, a killed server, a * proxy timeout. It does NOT mean the run failed — a failure arrives as an `error` chunk, which * the reader raises instead of returning. */ readonly terminated: boolean; /** * How many chunks crossed. Diagnostic only, and it earns its place: it separates "the server * accepted the request and then said nothing" (`0`) from "the answer was cut mid-sentence" * (`n > 0`), which are different failures with different first suspects. */ readonly chunksReceived: number; /** * How many frames the wire parser discarded as unreadable (invalid JSON, an unknown variant). * Present only when this module parsed the bytes ({@link consumeUIMessageStream}); a caller * handing in an already-parsed chunk stream has no frames to count. */ readonly framesDropped?: number; } /** * Read a `ReadableStream` into reconstructed assistant messages. Shared by * {@link consumeUIMessageStream} (Response path) and the framework-agnostic `AgentClient` store * (transport path). `onMessage` fires on every reconstruction step so a caller can render * streaming updates. * * A provider failure (401/429/5xx) arrives as a `{ type: 'error', errorText }` chunk rather than a * thrown rejection — both the in-process runner and the SSE path emit it as data. The reader * rejects on it, so `AgentClient.#drive`'s existing catch surfaces it (`status='error'`). * * theokit#384 — it also RETURNS how the stream ended. See {@link ChunkStreamOutcome}. * * The observation happens in a `TransformStream` rather than inside `readMessageStream`, because a * bare `finish` reconstructs to nothing: the reader yields a snapshot for it only when it carries * `messageMetadata`, so the terminal chunk is invisible at the message level by design (measured * against the ai-sdk oracle — see `read-message-stream.ts`). Watching the CHUNKS is the only place * the terminator is observable without changing what the reader emits. The cost is one queue hop * per chunk on a path that already spends 3.274 ms per emit deriving the timeline (M86) — real, * and three orders of magnitude below the work it sits next to. */ declare function consumeChunkStream(stream: ReadableStream, onMessage: (message: UIMessage) => void): Promise; /** * M41/M42 — extract the turn text from the last user message's text parts. Shared by the transports * that hand a plain `message` string to an in-process/push runner (`InProcessTransport`, * `ChannelTransport`) rather than POSTing the `messages[]` array (`HttpTransport`). One definition (G12). */ declare function extractLastUserText(messages: readonly WireMessage[]): string; export { AgentTransport, ApprovalDecision, type ChunkStreamOutcome, type HeadersResolver, HttpTransport, type HttpTransportOptions, consumeChunkStream, consumeUIMessageStream, extractLastUserText, responseToChunkStream };