import { a as AgentToolEventState, i as AgentToolEventMessage, l as AgentToolRunState, r as AgentToolEvent } from "../agent-tool-types-DSteYkkS.js"; import { n as createAgentToolEventState, t as applyAgentToolEvent } from "../agent-tools-eGTCdVZX.js"; import { JSONSchema7, Tool, ToolSet, UIMessage } from "ai"; import { Connection } from "agents"; //#region src/chat/message-builder.d.ts /** The parts array type from UIMessage */ type MessageParts = UIMessage["parts"]; /** A single part from the UIMessage parts array */ type MessagePart = MessageParts[number]; /** * Parsed chunk data from an AI SDK stream event. * This is the JSON-parsed body of a CF_AGENT_USE_CHAT_RESPONSE message, * or the `data:` payload of an SSE line. */ type StreamChunkData = { type: string; id?: string; delta?: string; text?: string; mediaType?: string; url?: string; sourceId?: string; title?: string; filename?: string; toolCallId?: string; toolName?: string; input?: unknown; inputTextDelta?: string; output?: unknown; state?: string; errorText?: string; /** When true, the output is preliminary (may be updated by a later chunk) */ preliminary?: boolean; /** Approval ID for tools with needsApproval */ approvalId?: string; providerMetadata?: Record; /** Whether the tool was executed by the provider (e.g. Gemini code execution) */ providerExecuted?: boolean; /** Payload for data-* parts (developer-defined typed JSON) */ data?: unknown; /** When true, data parts are ephemeral and not persisted to message.parts */ transient?: boolean; /** Message ID assigned by the server at stream start */ messageId?: string; /** Per-message metadata attached by start/finish/message-metadata chunks */ messageMetadata?: unknown; [key: string]: unknown; }; /** * Applies a stream chunk to a mutable parts array, building up the message * incrementally. Returns true if the chunk was handled, false if it was * an unrecognized type (caller may handle it with additional logic). * * Handles all common chunk types that both server and client need: * - text-start / text-delta / text-end * - reasoning-start / reasoning-delta / reasoning-end * - file * - source-url / source-document * - tool-input-start / tool-input-delta / tool-input-available / tool-input-error * - tool-output-available / tool-output-error * - step-start (aliased from start-step) * - data-* (developer-defined typed JSON blobs) * * @param parts - The mutable parts array to update * @param chunk - The parsed stream chunk data * @returns true if handled, false if the chunk type is not recognized */ declare function applyChunkToParts(parts: MessagePart[], chunk: StreamChunkData): boolean; /** * Returns true if `chunk` would be a no-op replay against the already-known * `parts` — i.e. some upstream is re-emitting events for a tool call that * the message has already advanced past. * * Used by stream broadcasters to suppress re-broadcasting these chunks to * connected clients. AI SDK v6's `updateToolPart` mutates an existing tool * part in place when a chunk arrives with a matching `toolCallId`, so a * replayed `tool-input-start` would clobber an `output-available` part back * to `input-streaming` on the client (issue #1404). * * Only returns true when re-broadcasting would *visibly regress* state on * a v6 client. Safe-by-construction chunk types (e.g. `tool-output-available` * carrying the same output the part already has) return false. * * Conditions: * - `tool-input-start` for a `toolCallId` that already exists in `parts`. * - `tool-input-delta` for a `toolCallId` whose existing part is no longer * `input-streaming`. * - `tool-input-available` for a `toolCallId` whose existing part is no * longer `input-streaming` (i.e. has already advanced to `input-available` * or any terminal state). */ declare function isReplayChunk(parts: MessagePart[], chunk: StreamChunkData): boolean; //#endregion //#region src/chat/sanitize.d.ts /** Maximum serialized message size before compaction (bytes). 1.8MB with headroom below SQLite's 2MB limit. */ declare const ROW_MAX_BYTES = 1800000; /** Measure UTF-8 byte length of a string. */ declare function byteLength(s: string): number; /** * Sanitize a message for persistence by removing ephemeral provider-specific * data that should not be stored or sent back in subsequent requests. * * 1. Strips OpenAI ephemeral fields (itemId, reasoningEncryptedContent) * 2. Filters truly empty reasoning parts (no text, no remaining providerMetadata) */ declare function sanitizeMessage(message: UIMessage): UIMessage; /** * Enforce SQLite row size limits by compacting tool outputs and text parts * when a serialized message exceeds the safety threshold (1.8MB). * * Compaction strategy: * 1. Compact tool outputs over 1KB (replace with summary) * 2. If still too big, truncate text parts from oldest to newest */ declare function enforceRowSizeLimit(message: UIMessage): UIMessage; //#endregion //#region src/chat/stream-accumulator.d.ts interface StreamAccumulatorOptions { messageId: string; continuation?: boolean; existingParts?: UIMessage["parts"]; existingMetadata?: Record; } type ChunkAction = { type: "start"; messageId?: string; metadata?: Record; } | { type: "finish"; finishReason?: string; metadata?: Record; } | { type: "message-metadata"; metadata: Record; } | { type: "tool-approval-request"; toolCallId: string; } | { type: "cross-message-tool-update"; updateType: "output-available" | "output-error"; toolCallId: string; output?: unknown; errorText?: string; preliminary?: boolean; } | { type: "error"; error: string; }; interface ChunkResult { handled: boolean; action?: ChunkAction; } declare class StreamAccumulator { messageId: string; readonly parts: UIMessage["parts"]; metadata?: Record; private _isContinuation; constructor(options: StreamAccumulatorOptions); applyChunk(chunk: StreamChunkData): ChunkResult; /** Snapshot the current state as a UIMessage. */ toMessage(): UIMessage; /** * Merge this accumulator's message into an existing message array. * Handles continuation (walk backward for last assistant), replacement * (update existing by messageId), or append (new message). */ mergeInto(messages: UIMessage[]): UIMessage[]; } //#endregion //#region src/chat/turn-queue.d.ts /** * TurnQueue — serial async queue with generation-based invalidation. * * Serializes async work via a promise chain, tracks which request is * currently active, and lets callers invalidate all queued work by * advancing a generation counter. * * Used by @cloudflare/ai-chat (full concurrency policy spectrum) and * @cloudflare/think (simple serial queue) to prevent overlapping * chat turns. */ type TurnResult = { status: "completed"; value: T; } | { status: "stale"; }; interface EnqueueOptions { /** * Generation to bind this turn to. Defaults to the current generation * at the time of the `enqueue` call. If the queue's generation has * advanced past this value by the time the turn reaches the front, * `fn` is not called and `{ status: "stale" }` is returned. */ generation?: number; } declare class TurnQueue { private _queue; private _generation; private _activeRequestId; private _countsByGeneration; get generation(): number; get activeRequestId(): string | null; get isActive(): boolean; enqueue(requestId: string, fn: () => Promise, options?: EnqueueOptions): Promise>; /** * Advance the generation counter. All turns enqueued under older * generations will be skipped when they reach the front of the queue. */ reset(): void; /** * Wait until the queue is fully drained (no pending or active turns). */ waitForIdle(): Promise; /** * Number of active + queued turns for a given generation. * Defaults to the current generation. */ queuedCount(generation?: number): number; private _decrementCount; } //#endregion //#region src/chat/client-tools.d.ts /** * Wire-format tool schema sent from the client. * Uses `parameters` (JSONSchema7) rather than AI SDK's `inputSchema` * because Zod schemas cannot be serialized over the wire. */ type ClientToolSchema = { /** Unique name for the tool */name: string; /** Human-readable description of what the tool does */ description?: Tool["description"]; /** JSON Schema defining the tool's input parameters */ parameters?: JSONSchema7; }; /** * Converts client tool schemas to AI SDK tool format. * * These tools have no `execute` function — when the AI model calls them, * the tool call is sent back to the client for execution. * * @param clientTools - Array of tool schemas from the client * @returns Record of AI SDK tools that can be spread into your tools object */ declare function createToolsFromClientSchemas(clientTools?: ClientToolSchema[]): ToolSet; //#endregion //#region src/chat/lifecycle.d.ts /** * Result passed to the `onChatResponse` lifecycle hook after a chat * turn completes. */ type ChatResponseResult = { /** The finalized assistant message from this turn. */message: UIMessage; /** The request ID associated with this turn. */ requestId: string; /** Whether this turn was a continuation of a previous assistant turn. */ continuation: boolean; /** How the turn ended. */ status: "completed" | "error" | "aborted"; /** Error message when `status` is `"error"`. */ error?: string; }; /** * Options accepted by programmatic entry points that drive a chat turn * (`saveMessages`, `continueLastTurn`). */ type SaveMessagesOptions = { /** * External `AbortSignal` for cancelling the turn from outside. * * When the signal aborts, the in-flight turn is cancelled exactly the * same way an internal `chat-request-cancel` WebSocket message would * cancel it: the inference loop's signal aborts, partially streamed * chunks are still persisted, and the resolved result reports * `status: "aborted"`. If the signal is already aborted when the * turn starts, no inference work is performed. * * Useful for bridging an external caller's abort intent into a turn * whose request id is generated server-side and not surfaced until * after completion — e.g. forwarding the AI SDK tool `execute`'s * `abortSignal` into a sub-agent's `saveMessages` call. See * [`cloudflare/agents#1406`](https://github.com/cloudflare/agents/issues/1406) * for the motivating use case. */ signal?: AbortSignal; }; /** * Result returned by programmatic entry points. * * - `"completed"` — the turn ran to completion. * - `"skipped"` — the turn was invalidated mid-flight, typically by a * `CHAT_CLEAR` protocol message that bumped the turn-queue * generation. * - `"aborted"` — the turn started but was cancelled before * completion, either by `MSG_CHAT_CANCEL` over the chat WebSocket or * by an external `AbortSignal` passed via {@link SaveMessagesOptions}. * Partial chunks streamed before the abort are still persisted. */ type SaveMessagesResult = { /** Server-generated request ID for the chat turn. */requestId: string; /** Whether the turn ran, was skipped, or was aborted. */ status: "completed" | "skipped" | "aborted"; }; /** * Context passed to the `onChatRecovery` hook when an interrupted chat * stream is detected after DO restart. */ type ChatRecoveryContext = { /** Stream ID from the interrupted stream. */streamId: string; /** Request ID from the interrupted stream. */ requestId: string; /** Partial text extracted from stored chunks. */ partialText: string; /** Partial message parts reconstructed from chunks. */ partialParts: MessagePart[]; /** Checkpoint data from `this.stash()` during the interrupted stream. */ recoveryData: unknown | null; /** Current persisted messages. */ messages: UIMessage[]; /** Custom body from the last chat request. */ lastBody?: Record; /** Client tool schemas from the last chat request. */ lastClientTools?: ClientToolSchema[]; /** * Epoch milliseconds when the underlying fiber was started. Compare * against `Date.now()` to suppress continuations for turns that have * been orphaned too long to safely replay. */ createdAt: number; }; /** * Options returned from `onChatRecovery` to control recovery behavior. */ type ChatRecoveryOptions = { /** Save the partial response from stored chunks. Default: true. */persist?: boolean; /** Schedule a continuation via `continueLastTurn()`. Default: true. */ continue?: boolean; }; /** * Controls how overlapping user submit requests behave while another * chat turn is already active or queued. * * - `"queue"` (default) — queue every submit and process them in order. * - `"latest"` — keep only the latest overlapping submit; superseded * submits still persist their user messages, but do not start their * own model turn. * - `"merge"` — coalesce overlapping submits into one model turn while * preserving the submitted user content. Exact persistence depends on * the chat package's message model. * - `"drop"` — ignore overlapping submits entirely (messages not * persisted). * - `{ strategy: "debounce", debounceMs? }` — trailing-edge latest with * a quiet window. * * Only applies to `submit-message` requests. Regenerations, tool * continuations, approvals, clears, programmatic `saveMessages`, and * `continueLastTurn` keep their existing serialized behavior. */ type MessageConcurrency = "queue" | "latest" | "merge" | "drop" | { strategy: "debounce"; debounceMs?: number; }; //#endregion //#region src/chat/submit-concurrency.d.ts type NormalizedMessageConcurrency = "queue" | "latest" | "merge" | "drop" | { strategy: "debounce"; debounceMs: number; }; type SubmitConcurrencyDecision = { action: "execute" | "drop"; strategy: NormalizedMessageConcurrency | null; submitSequence: number | null; debounceUntilMs: number | null; }; declare class SubmitConcurrencyController { private readonly options; private _submitSequence; private _latestOverlappingSubmitSequence; private _pendingEnqueueCount; private _resetEpoch; private _activeDebounceTimers; private _activeDebounceResolves; constructor(options: { defaultDebounceMs: number; }); get pendingEnqueueCount(): number; get overlappingSubmitCount(): number; decide(options: { concurrency: MessageConcurrency; isSubmitMessage: boolean; queuedTurns: number; }): SubmitConcurrencyDecision; /** * Mark a submit as accepted and in-flight between admission and turn * queue registration. Returns an idempotent `release()` function that * must be called when the submit either reaches the turn queue or is * abandoned. The returned function is bound to the controller's reset * epoch — releases from before the most recent `reset()` are no-ops, * so post-reset submits keep an accurate count. */ beginEnqueue(): () => void; isSuperseded(submitSequence: number | null): boolean; waitForTimestamp(timestampMs: number): Promise; cancelActiveDebounce(): void; reset(): void; waitForIdle(waitForQueueIdle: () => Promise): Promise; private normalize; } //#endregion //#region src/chat/broadcast-state.d.ts type BroadcastStreamState = { status: "idle"; } | { status: "observing"; streamId: string; accumulator: StreamAccumulator; }; type BroadcastStreamEvent = { type: "response"; streamId: string; /** Fallback message ID for a new accumulator (ignored if one exists for this stream). */ messageId: string; chunkData?: unknown; done?: boolean; error?: boolean; replay?: boolean; replayComplete?: boolean; continuation?: boolean; /** Required when continuation=true so the accumulator can pick up existing parts. */ currentMessages?: UIMessage[]; } | { type: "resume-fallback"; streamId: string; messageId: string; } | { type: "clear"; }; interface TransitionResult { state: BroadcastStreamState; messagesUpdate?: (prev: UIMessage[]) => UIMessage[]; isStreaming: boolean; } declare function transition(state: BroadcastStreamState, event: BroadcastStreamEvent): TransitionResult; //#endregion //#region src/chat/resumable-stream.d.ts /** * Minimal SQL interface matching Agent's this.sql tagged template. * Allows ResumableStream to work with the Agent's SQLite without * depending on the full Agent class. */ type SqlTaggedTemplate = { >(strings: TemplateStringsArray, ...values: (string | number | boolean | null)[]): T[]; }; declare class ResumableStream { private sql; private _activeStreamId; private _activeRequestId; private _streamChunkIndex; /** * Whether the active stream was started in this instance (true) or * restored from SQLite after hibernation/restart (false). An orphaned * stream has no live LLM reader — the ReadableStream was lost when the * DO was evicted. */ private _isLive; private _chunkBuffer; private _isFlushingChunks; private _lastCleanupTime; constructor(sql: SqlTaggedTemplate); get activeStreamId(): string | null; get activeRequestId(): string | null; hasActiveStream(): boolean; /** * Whether the active stream has a live LLM reader (started in this * instance) vs being restored from SQLite after hibernation (orphaned). */ get isLive(): boolean; /** * Start tracking a new stream for resumable streaming. * Creates metadata entry in SQLite and sets up tracking state. * @param requestId - The unique ID of the chat request * @returns The generated stream ID */ start(requestId: string): string; /** * Mark a stream as completed and flush any pending chunks. * @param streamId - The stream to mark as completed */ complete(streamId: string): void; /** * Mark a stream as errored and clean up state. * @param streamId - The stream to mark as errored */ markError(streamId: string): void; /** Maximum chunk body size before skipping storage (bytes). Prevents SQLite row limit crash. */ private static CHUNK_MAX_BYTES; /** * Buffer a stream chunk for batch write to SQLite. * Chunks exceeding the row size limit are skipped to prevent crashes. * The chunk is still broadcast to live clients (caller handles that), * but will be missing from replay on reconnection. * @param streamId - The stream this chunk belongs to * @param body - The serialized chunk body */ storeChunk(streamId: string, body: string): void; /** * Flush buffered chunks to SQLite in a single batch. * Uses a lock to prevent concurrent flush operations. */ flushBuffer(): void; /** * Send stored stream chunks to a connection for replay. * Chunks are marked with replay: true so the client can batch-apply them. * * Three outcomes: * - **Live stream**: sends chunks + `replayComplete` — client flushes and * continues receiving live chunks from the LLM reader. * - **Orphaned stream** (restored from SQLite after hibernation, no reader): * sends chunks + `done` and completes the stream. The caller should * reconstruct and persist the partial message from the stored chunks. * - **Completed during replay** (defensive): sends chunks + `done`. * * @param connection - The WebSocket connection * @param requestId - The original request ID * @returns The stream ID if the stream was orphaned and finalized, null otherwise. * When non-null the caller should reconstruct the message from chunks. */ replayChunks(connection: Connection, requestId: string): string | null; replayCompletedChunksByRequestId(connection: Connection, requestId: string): boolean; /** * Restore active stream state if the agent was restarted during streaming. * All streams are restored regardless of age — stale cleanup happens * lazily in _maybeCleanupOldStreams after recovery has had its chance. */ restore(): void; /** * Clear all stream data (called on chat history clear). */ clearAll(): void; /** * Drop all stream tables (called on destroy). */ destroy(): void; private _maybeCleanupOldStreams; /** @internal For testing only */ getStreamChunks(streamId: string): Array<{ body: string; chunk_index: number; }>; /** @internal For testing only */ getStreamMetadata(streamId: string): { status: string; request_id: string; } | null; /** @internal For testing only */ getAllStreamMetadata(): Array<{ id: string; status: string; request_id: string; created_at: number; }>; /** @internal For testing only */ insertStaleStream(streamId: string, requestId: string, ageMs: number): void; } //#endregion //#region src/chat/protocol.d.ts /** * Wire protocol message type constants for the cf_agent_chat_* protocol. * * These are the string values used on the wire between agent servers and * clients. Both @cloudflare/ai-chat (via its MessageType enum) and * @cloudflare/think use these values. */ declare const CHAT_MESSAGE_TYPES: { readonly CHAT_MESSAGES: "cf_agent_chat_messages"; readonly USE_CHAT_REQUEST: "cf_agent_use_chat_request"; readonly USE_CHAT_RESPONSE: "cf_agent_use_chat_response"; readonly CHAT_CLEAR: "cf_agent_chat_clear"; readonly CHAT_REQUEST_CANCEL: "cf_agent_chat_request_cancel"; readonly STREAM_RESUMING: "cf_agent_stream_resuming"; readonly STREAM_RESUME_ACK: "cf_agent_stream_resume_ack"; readonly STREAM_RESUME_REQUEST: "cf_agent_stream_resume_request"; readonly STREAM_RESUME_NONE: "cf_agent_stream_resume_none"; readonly TOOL_RESULT: "cf_agent_tool_result"; readonly TOOL_APPROVAL: "cf_agent_tool_approval"; readonly MESSAGE_UPDATED: "cf_agent_message_updated"; }; //#endregion //#region src/chat/continuation-state.d.ts /** * Minimal connection interface for sending WebSocket messages. * Matches the Connection type from agents without importing it. * Uses a permissive send signature so Connection (which extends * WebSocket with its own send overload) is structurally assignable. */ interface ContinuationConnection { readonly id: string; send(message: string): void; } interface ContinuationPending { connection: ContinuationConnection; connectionId: string; requestId: string; clientTools?: ClientToolSchema[]; body?: Record; errorPrefix: string | null; prerequisite: Promise | null; pastCoalesce: boolean; } interface ContinuationDeferred { connection: ContinuationConnection; connectionId: string; clientTools?: ClientToolSchema[]; body?: Record; errorPrefix: string; prerequisite: Promise | null; } declare class ContinuationState { pending: ContinuationPending | null; deferred: ContinuationDeferred | null; activeRequestId: string | null; activeConnectionId: string | null; awaitingConnections: Map; /** Clear pending state and awaiting connections (without sending RESUME_NONE). */ clearPending(): void; clearDeferred(): void; clearAll(): void; /** * Send STREAM_RESUME_NONE to all connections waiting for a * continuation stream to start, then clear the map. */ sendResumeNone(): void; /** * Flush awaiting connections by notifying each one via the provided * callback (typically sends STREAM_RESUMING), then clear. */ flushAwaitingConnections(notify: (conn: ContinuationConnection) => void): void; /** * Transition pending → active. Called when the continuation stream * actually starts. Moves request/connection IDs to active slots, * clears pending fields. */ activatePending(): void; /** * Transition deferred → pending. Called when a continuation turn * completes and there's a deferred follow-up waiting. * * Returns the new pending state (so the host can enqueue the turn), * or null if there was nothing deferred. */ activateDeferred(generateRequestId: () => string): ContinuationPending | null; } //#endregion //#region src/chat/abort-registry.d.ts /** * AbortRegistry — manages per-request AbortControllers. * * Shared between AIChatAgent and Think for chat turn cancellation. * Each request gets its own AbortController keyed by request ID. * Controllers are created lazily on first signal access. */ declare class AbortRegistry { private controllers; /** * Get or create an AbortController for the given ID and return its signal. * Creates the controller lazily on first access. */ getSignal(id: string): AbortSignal | undefined; /** * Get the signal for an existing controller without creating one. * Returns undefined if no controller exists for this ID. */ getExistingSignal(id: string): AbortSignal | undefined; /** * Cancel a specific request by aborting its controller. Optionally * propagate a reason — surfaces as `signal.reason` on the registry's * controller and through any `AbortError` it produces downstream. */ cancel(id: string, reason?: unknown): void; /** Remove a controller after the request completes. */ remove(id: string): void; /** Abort all pending requests and clear the registry. */ destroyAll(): void; /** Check if a controller exists for the given ID. */ has(id: string): boolean; /** Number of tracked controllers. */ get size(): number; /** * Link an external `AbortSignal` to the controller for `id`. When the * external signal aborts, the registry's controller is cancelled — * propagating the abort reason — exactly the same way an internal * cancel would (e.g. via a `chat-request-cancel` WebSocket message). * * This is the integration point for callers that drive a chat turn * programmatically and want to cancel it from outside without knowing * the internally-generated request id (e.g. the helper-as-sub-agent * pattern, where a parent's `AbortSignal` from the AI SDK tool * `execute` needs to land inside a `Think.saveMessages` call running * on a child DO). * * Behavior: * * - Passing `undefined` is a no-op and returns a no-op detacher, so * callers can unconditionally call this with `options?.signal`. * - If the external signal is already aborted, the registry's * controller is created (if needed) and cancelled synchronously. * - Otherwise a one-shot `abort` listener is attached. The returned * function detaches it. * * **Always call the returned detacher in a `finally` block** — the * external signal may outlive the request (a parent chat turn that * drives many helper turns reuses one signal across all of them) and * leaving listeners attached pins closures and grows the listener * list on each turn. * * @returns A detacher function. Call it after the request finishes * (success or failure) to remove the abort listener from `signal`. */ linkExternal(id: string, signal: AbortSignal | undefined): () => void; } //#endregion //#region src/chat/tool-state.d.ts /** * Tool State — shared update builders and applicator for tool part state changes. * * Used by both AIChatAgent and Think to apply tool results and approvals * to message parts. Each agent handles find-message, persist, and broadcast * in their own way; this module provides the state matching and update logic. */ /** * Describes an update to apply to a tool part. */ type ToolPartUpdate = { toolCallId: string; matchStates: string[]; apply: (part: Record) => Record; }; /** * Apply a tool part update to a parts array. * Finds the first part matching `update.toolCallId` in one of `update.matchStates`, * applies the update immutably, and returns the new parts array with the index. * * Returns `null` if no matching part was found. */ declare function applyToolUpdate(parts: Array>, update: ToolPartUpdate): { parts: Array>; index: number; } | null; /** * Build an update descriptor for applying a tool result. * * Matches parts in `input-available`, `approval-requested`, or `approval-responded` state. * Sets state to `output-available` (with output) or `output-error` (with errorText). */ declare function toolResultUpdate(toolCallId: string, output: unknown, overrideState?: "output-error", errorText?: string): ToolPartUpdate; /** * Build an update descriptor for applying a tool approval. * * Matches parts in `input-available` or `approval-requested` state. * Sets state to `approval-responded` (if approved) or `output-denied` (if denied). */ declare function toolApprovalUpdate(toolCallId: string, approved: boolean): ToolPartUpdate; //#endregion //#region src/chat/parse-protocol.d.ts /** * Protocol Message Parser — typed parsing of cf_agent_chat_* WebSocket messages. * * Parses raw WebSocket messages into a discriminated union of protocol events. * Both AIChatAgent and Think can use this instead of manual JSON.parse + type checking. */ /** * Discriminated union of all incoming chat protocol events. * * Each agent handles the events it cares about and ignores the rest. * Returns `null` for non-JSON messages or unrecognized types. */ type ChatProtocolEvent = { type: "chat-request"; id: string; init: { method?: string; body?: string; [key: string]: unknown; }; } | { type: "clear"; } | { type: "cancel"; id: string; } | { type: "tool-result"; toolCallId: string; toolName: string; output: unknown; state?: string; errorText?: string; autoContinue?: boolean; clientTools?: Array<{ name: string; description?: string; parameters?: unknown; }>; } | { type: "tool-approval"; toolCallId: string; approved: boolean; autoContinue?: boolean; } | { type: "stream-resume-request"; } | { type: "stream-resume-ack"; id: string; } | { type: "messages"; messages: unknown[]; }; /** * Parse a raw WebSocket message string into a typed protocol event. * * Returns `null` if the message is not valid JSON or not a recognized * protocol message type. Callers should fall through to the user's * `onMessage` handler when `null` is returned. * * @example * ```typescript * const event = parseProtocolMessage(rawMessage); * if (!event) return userOnMessage(connection, rawMessage); * * switch (event.type) { * case "chat-request": { ... } * case "clear": { ... } * case "tool-result": { ... } * } * ``` */ declare function parseProtocolMessage(raw: string): ChatProtocolEvent | null; //#endregion //#region src/chat/message-reconciler.d.ts /** * Reconcile incoming client messages against server state. * * 1. Merges server-known tool outputs into incoming messages that still * show stale states (input-available, approval-requested, approval-responded) * 2. Reconciles assistant IDs: exact match → content-key match → toolCallId match * * @param incoming - Messages from the client * @param serverMessages - Current server-side messages (source of truth) * @param sanitizeForContentKey - Function to sanitize a message before computing * its content key (typically strips ephemeral provider metadata) * @returns Reconciled messages ready for persistence */ declare function reconcileMessages(incoming: UIMessage[], serverMessages: readonly UIMessage[], sanitizeForContentKey?: (message: UIMessage) => UIMessage): UIMessage[]; /** * For a single message, resolve its ID by matching toolCallId against server state. * Prevents duplicate DB rows when client IDs differ from server IDs. * Tool call IDs are unique per conversation, so matching is safe regardless of state. */ declare function resolveToolMergeId(message: UIMessage, serverMessages: readonly UIMessage[]): UIMessage; /** * Content key for assistant messages used for dedup of identical short replies. * Returns JSON of sanitized parts, or undefined for non-assistant messages. */ declare function assistantContentKey(message: UIMessage, sanitize?: (message: UIMessage) => UIMessage): string | undefined; //#endregion export { AbortRegistry, type AgentToolEvent, type AgentToolEventMessage, type AgentToolEventState, type AgentToolRunState, type BroadcastStreamEvent, type BroadcastStreamState, type TransitionResult as BroadcastTransitionResult, CHAT_MESSAGE_TYPES, type ChatProtocolEvent, type ChatRecoveryContext, type ChatRecoveryOptions, type ChatResponseResult, type ChunkAction, type ChunkResult, type ClientToolSchema, type ContinuationConnection, type ContinuationDeferred, type ContinuationPending, ContinuationState, type EnqueueOptions, type MessageConcurrency, type MessagePart, type MessageParts, type NormalizedMessageConcurrency, ROW_MAX_BYTES, ResumableStream, type SaveMessagesOptions, type SaveMessagesResult, type SqlTaggedTemplate, StreamAccumulator, type StreamAccumulatorOptions, type StreamChunkData, SubmitConcurrencyController, type SubmitConcurrencyDecision, type ToolPartUpdate, TurnQueue, type TurnResult, applyAgentToolEvent, applyChunkToParts, applyToolUpdate, assistantContentKey, transition as broadcastTransition, byteLength, createAgentToolEventState, createToolsFromClientSchemas, enforceRowSizeLimit, isReplayChunk, parseProtocolMessage, reconcileMessages, resolveToolMergeId, sanitizeMessage, toolApprovalUpdate, toolResultUpdate }; //# sourceMappingURL=index.d.ts.map