@tanstack/ai-client 0.35.1 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"chat-client.js","names":[],"sources":["../../src/chat-client.ts"],"sourcesContent":["import {\n EventType,\n StreamProcessor,\n convertSchemaToJsonSchema,\n generateMessageId,\n isStandardSchema,\n mergeMetadata,\n normalizeToUIMessage,\n parseWithStandardSchema,\n restoreInboundChunk,\n tanstackMetadata,\n} from '@tanstack/ai/client'\nimport {\n ByokBlockedError,\n ByokMissingError,\n ByokUnresolvedProviderError,\n} from '@tanstack/ai/byok'\nimport { byokFallbackProviderId } from './byok/client'\nimport {\n prepareResolvedByokHeaders,\n resolveByokProviderId,\n} from './byok/resolve'\nimport { createNoOpChatDevtoolsBridge } from './devtools-noop'\nimport {\n fetcherToConnectionAdapter,\n getChunkRunId,\n normalizeConnectionAdapter,\n} from './connection-adapters'\nimport { ChatPersistor } from './client-persistor'\nimport { ClearedStreamTracker } from './cleared-stream-tracker'\nimport { normalizeMessagesDates } from './message-date-normalizer'\nimport { InterruptManager } from './interrupt-manager'\nimport type {\n AnyClientTool,\n ContentPart,\n InterruptDefinition,\n InterruptSubmissionError,\n ModelMessage,\n RunAgentResumeItem,\n StreamChunk,\n} from '@tanstack/ai/client'\nimport type { ByokClient } from './byok'\nimport type {\n ChatHydrateOptions,\n ChatHydrationResult,\n ConnectionAdapter,\n SubscribeConnectionAdapter,\n} from './connection-adapters'\nimport type {\n ChatClientEventEmitter,\n ChatClientRunEventContext,\n} from './events'\nimport type {\n AIDevtoolsChatSnapshot,\n ChatDevtoolsBridge,\n ChatDevtoolsBridgeOptions,\n} from './devtools'\nimport type {\n BoundInterrupts,\n ChatClientOptions,\n ChatClientState,\n ChatFetcher,\n ChatInterruptState,\n ResolvableChatInterrupt,\n ChatPendingInterrupt,\n ChatResumeSnapshot,\n ChatResumeState,\n ConnectionStatus,\n MessagePart,\n MultimodalContent,\n QueueBusyReason,\n QueueOption,\n QueueStrategy,\n QueuedMessage,\n SendMessageOptions,\n SubagentHandle,\n ToolCallPart,\n UIMessage,\n WhenBusy,\n} from './types'\nimport type {\n InterruptManagerChangeSource,\n InterruptManagerSubmission,\n} from './interrupt-manager'\n\n/** Internal queue entry — public {@link QueuedMessage} plus optional per-send body. */\ninterface InternalQueuedMessage extends QueuedMessage {\n body?: Record<string, any>\n}\n\nfunction assertUniqueInterruptDefinitions(\n interrupts:\n | ReadonlyArray<InterruptDefinition<any, any, any, any>>\n | undefined,\n): void {\n const ids = new Set<string>()\n for (const interrupt of interrupts ?? []) {\n if (ids.has(interrupt.id)) {\n throw new Error(`Duplicate interrupt definition id: ${interrupt.id}`)\n }\n ids.add(interrupt.id)\n }\n}\n\ntype ChatClientUpdateOptionsWithoutContext<\n TTools extends ReadonlyArray<AnyClientTool>,\n TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =\n readonly [],\n> = {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n /** @deprecated Use `forwardedProps` instead. */\n body?: Record<string, any>\n forwardedProps?: Record<string, any>\n byok?: ByokClient\n byokProvider?: () => string | undefined\n tools?: TTools\n interrupts?: TInterrupts\n queue?: QueueOption\n onResponse?: (response?: Response) => void | Promise<void>\n onChunk?: (chunk: StreamChunk) => void\n onFinish?: (message: UIMessage) => void\n onError?: (error: Error) => void\n onSubscriptionChange?: (isSubscribed: boolean) => void\n onConnectionStatusChange?: (status: ConnectionStatus) => void\n onSessionGeneratingChange?: (isGenerating: boolean) => void\n onQueueChange?: (queue: Array<QueuedMessage>) => void\n onResumeStateChange?: (\n resumeState: ChatResumeState | null,\n pendingInterrupts: BoundInterrupts<TTools, TInterrupts>,\n ) => void\n /**\n * Fires whenever the id of the run in flight changes: the new id when a run\n * starts (including a rejoin), `null` when it settles.\n */\n onRunIdChange?: (runId: string | null) => void\n onInterruptStateChange?: (\n state: ChatInterruptState<TTools, TInterrupts>,\n context: { source: 'hydrate' | 'live' },\n ) => void\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n}\n\ntype ClientToolResult = {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n}\n\nfunction resolveTransport(transport: {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n}): ConnectionAdapter {\n const { connection, fetcher } = transport\n if (connection && fetcher) {\n throw new Error(\n 'ChatClient: pass either `connection` or `fetcher`, not both.',\n )\n }\n if (connection) return connection\n if (fetcher) return fetcherToConnectionAdapter(fetcher)\n throw new Error('ChatClient: either `connection` or `fetcher` is required.')\n}\n\nfunction connectionDrainsOnSend(connection: ConnectionAdapter): boolean {\n return 'connect' in connection\n}\n\nfunction isIntermediateToolTurn(chunk: StreamChunk): boolean {\n if (chunk.type !== 'RUN_FINISHED') return false\n if (chunk.outcome?.type === 'interrupt') return false\n const extra = chunk as StreamChunk & { finishReason?: unknown }\n if (extra.finishReason !== undefined) {\n return extra.finishReason === 'tool_calls'\n }\n return tanstackMetadata(chunk)?.finishReason === 'tool_calls'\n}\n\nexport interface NormalizedQueueConfig {\n whenBusy: WhenBusy\n drain: 'fifo' | 'batch'\n onOverflow: 'reject' | 'drop-oldest'\n maxSize?: number\n strategy?: QueueStrategy\n}\n\nexport function normalizeQueueOption(\n option: QueueOption | undefined,\n): NormalizedQueueConfig {\n const base: NormalizedQueueConfig = {\n whenBusy: 'queue',\n drain: 'fifo',\n onOverflow: 'reject',\n }\n if (!option) return base\n if (typeof option === 'string') return { ...base, whenBusy: option }\n if (typeof option === 'function') return { ...base, strategy: option }\n\n const maxSize = option.maxSize\n if (maxSize !== undefined) {\n if (!Number.isInteger(maxSize) || maxSize < 0) {\n throw new Error(\n 'ChatClient: queue.maxSize must be a non-negative integer',\n )\n }\n }\n\n return {\n whenBusy: option.whenBusy ?? 'queue',\n drain: option.drain ?? 'fifo',\n onOverflow: option.onOverflow ?? 'reject',\n ...(maxSize !== undefined ? { maxSize } : {}),\n }\n}\n\n/**\n * Merge a run of queued messages into a single send for `drain: 'batch'`.\n * All-string content is joined with newlines; mixed/multimodal content is\n * flattened into a single `ContentPart` array. The last item's `body` wins.\n * Object-form metadata is merged last-write-wins per key.\n */\nfunction mergeQueuedMessages(items: Array<InternalQueuedMessage>): {\n content: string | MultimodalContent\n body?: Record<string, any>\n} {\n const body = items.at(-1)?.body\n const stringContents: Array<string> = []\n for (const item of items) {\n if (typeof item.content !== 'string') {\n break\n }\n stringContents.push(item.content)\n }\n if (stringContents.length === items.length) {\n return {\n content: stringContents.join('\\n'),\n ...(body !== undefined ? { body } : {}),\n }\n }\n const parts: Array<ContentPart> = []\n let metadata: Record<string, any> | undefined\n for (const item of items) {\n if (typeof item.content === 'string') {\n parts.push({ type: 'text', content: item.content })\n continue\n }\n if (typeof item.content.content === 'string') {\n parts.push({ type: 'text', content: item.content.content })\n } else {\n parts.push(...item.content.content)\n }\n metadata = mergeMetadata(metadata, item.content.metadata)\n }\n return {\n content: {\n content: parts,\n ...(metadata !== undefined ? { metadata } : {}),\n },\n ...(body !== undefined ? { body } : {}),\n }\n}\n\n/**\n * Extract a boolean approval decision from an AG-UI resume payload, if present.\n * Tool-approval resolutions carry `{ approved: boolean, ... }`; generic\n * interrupt payloads do not.\n */\nfunction readApprovalApproved(payload: unknown): boolean | undefined {\n if (\n payload === null ||\n typeof payload !== 'object' ||\n Array.isArray(payload)\n ) {\n return undefined\n }\n if (!('approved' in payload) || typeof payload.approved !== 'boolean') {\n return undefined\n }\n return payload.approved\n}\n\nfunction readResumeState(\n snapshot: ChatResumeSnapshot,\n): ChatResumeState | undefined {\n const value: unknown = snapshot\n if (\n value === null ||\n typeof value !== 'object' ||\n !('resumeState' in value)\n ) {\n return undefined\n }\n const resumeState = value.resumeState\n if (\n resumeState === null ||\n typeof resumeState !== 'object' ||\n !('threadId' in resumeState) ||\n typeof resumeState.threadId !== 'string' ||\n resumeState.threadId.length === 0 ||\n !('runId' in resumeState) ||\n typeof resumeState.runId !== 'string' ||\n resumeState.runId.length === 0\n ) {\n return undefined\n }\n return { threadId: resumeState.threadId, runId: resumeState.runId }\n}\n\n/**\n * How long a reload rejoin waits for its first chunk before giving up. A durable\n * backend keeps a from-start join open waiting for a producer; without this\n * bound a stale pointer to an unknown/evicted run would pin the UI loading for\n * the backend's full first-chunk deadline (tens of seconds). Kept short so the\n * client decides \"reachable or not\" quickly.\n */\nconst REJOIN_CONNECT_DEADLINE_MS = 2000\n\n/**\n * Chunk types that (re)build the assistant message on a rejoin. The hydrated\n * in-flight partial is dropped only when one of these arrives — never on\n * `RUN_STARTED` alone — so a rejoin that connects but delivers no content cannot\n * leave an empty assistant bubble.\n */\nconst REJOIN_REBUILD_TRIGGERS = new Set<string>([\n 'TEXT_MESSAGE_START',\n 'TEXT_MESSAGE_CONTENT',\n 'REASONING_MESSAGE_CONTENT',\n 'TOOL_CALL_START',\n 'MESSAGES_SNAPSHOT',\n // Drop the hydrated card before this chunk creates it again. A subagent\n // turn may have no parent text, so the text triggers arrive too late.\n 'SUBAGENT_STARTED',\n])\n\nfunction rebuildsAssistantMessage(chunk: StreamChunk): boolean {\n if (chunk.type === 'REASONING_ENCRYPTED_VALUE') {\n return (\n chunk.subtype === 'message' &&\n typeof chunk.encryptedValue === 'string' &&\n chunk.encryptedValue.length > 0\n )\n }\n if (chunk.type === 'STEP_FINISHED') {\n return 'signature' in chunk && Boolean(chunk.signature)\n }\n return REJOIN_REBUILD_TRIGGERS.has(chunk.type)\n}\n\ntype SubagentCard = Extract<UIMessage['parts'][number], { type: 'subagent' }>\n\n/** Every subagent card in the messages, nested cards included. */\nfunction collectSubagentParts(\n messages: ReadonlyArray<UIMessage>,\n): Array<SubagentCard> {\n const cards: Array<SubagentCard> = []\n for (const message of messages) {\n for (const part of message.parts) {\n if (part.type !== 'subagent') continue\n cards.push(part)\n cards.push(...collectSubagentParts(part.subagent.messages))\n }\n }\n return cards\n}\n\nfunction readSubagentRunId(chunk: StreamChunk) {\n if ('subagentRunId' in chunk && typeof chunk.subagentRunId === 'string') {\n return chunk.subagentRunId\n }\n return undefined\n}\n\n// Cap parent-chain walks so a cyclic replayed parentRunId cannot loop.\nconst MAX_RUN_LINEAGE_DEPTH = 64\n\n/**\n * A deep copy of plain data. Dates stay Dates. Function-valued properties are\n * dropped.\n */\nfunction copyForDevtools<T>(value: T): T\nfunction copyForDevtools(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(copyForDevtools)\n if (value instanceof Date) return new Date(value.getTime())\n if (value === null || typeof value !== 'object') return value\n const out: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) {\n if (typeof item !== 'function') out[key] = copyForDevtools(item)\n }\n return out\n}\n\nexport class ChatClient<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TContext = unknown,\n TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =\n any,\n> {\n private readonly processor: StreamProcessor\n private readonly subagentHandles = new Map<string, SubagentHandle>()\n private readonly stoppedSubagentIds = new Set<string>()\n private connection: SubscribeConnectionAdapter\n private uniqueId: string\n private threadId: string\n // Durable chat persistence (optional): messages + resume snapshot as one\n // combined record, so a full page reload restores the transcript, rehydrates\n // pending interrupts, and rejoins an in-flight run. Clear-during-stream\n // suppression is always on via ClearedStreamTracker so `clear()` works\n // without a storage adapter.\n private readonly persistor?: ChatPersistor\n private readonly clearedStreamTracker = new ClearedStreamTracker()\n private currentRunId: string | null = null\n // Interrupt-resume tracking: the run/thread of the most recent interrupted\n // run, so approvals/client-tool results can be sent back. Cleared when the\n // run terminates. This is STATE (interrupt) resume, not delivery/cursor.\n private lastResume: ChatResumeState | null = null\n // Replay can deliver a parent pause after a descendant already finished.\n private readonly runParents = new Map<string, string>()\n private readonly supersededInterruptRunIds = new Set<string>()\n // The in-flight run id already handed to `resumeInFlightRun`, so a persisted\n // run is rejoined at most once even when both the sync read and the async\n // hydrate surface the same resume pointer.\n private rejoinedRunId: string | null = null\n private readonly interruptManager: InterruptManager<TTools, TInterrupts>\n private activeInterruptSubmission: InterruptManagerSubmission | undefined\n private interruptSubmissionFailure:\n | { errors: ReadonlyArray<InterruptSubmissionError> }\n | undefined\n private readonly joinedRunWaiters = new Map<string, () => void>()\n // When set, the next streamResponse() continues this interrupted run instead\n // of starting a fresh run (consumed once).\n private pendingResumeParentRunId: string | null = null\n private pendingResumeThreadId: string | null = null\n private pendingResumeItems: Array<RunAgentResumeItem> | null = null\n private activeResumeThreadId: string | null = null\n private activeResumeRunId: string | null = null\n // Track the legacy `body` option and the canonical `forwardedProps`\n // option as separate slots so that `updateOptions({ forwardedProps })`\n // doesn't wipe a previously-set `body` (and vice versa). They are\n // merged on every send, with `forwardedProps` winning on key collision.\n private bodyOption: Record<string, any> = {}\n private forwardedPropsOption: Record<string, any> = {}\n private byok: ByokClient | undefined\n private byokProvider: (() => string | undefined) | undefined\n private context: TContext | undefined = undefined\n private pendingMessageBody: Record<string, any> | undefined = undefined\n private queueConfig: NormalizedQueueConfig\n private messageQueue: Array<InternalQueuedMessage> = []\n /**\n * True from the moment `sendMessage` claims the client until its\n * `streamResponse` settles. Closes the race where concurrent callers both\n * see `isLoading === false`, both append a user message, and only one stream\n * actually runs (leaving stranded user messages with no reply).\n */\n private sendInFlight = false\n /**\n * True while `drainQueue` is delivering queued messages. Concurrent\n * `sendMessage` calls during a drain are treated as busy and follow\n * `whenBusy` (default: queue).\n */\n private messageQueueDraining = false\n /**\n * Set by `whenBusy: 'interrupt'` so an in-progress FIFO drain loop stops\n * before starting the next queued item (the interrupting send owns the client).\n */\n private stopMessageQueueDrain = false\n /**\n * Sync claim held for the duration of `deliverMessage` so concurrent\n * deliverers cannot both append a user message before only one stream runs.\n */\n private deliverClaim = false\n private isLoading = false\n private isSubscribed = false\n private error: Error | undefined = undefined\n private status: ChatClientState = 'ready'\n private connectionStatus: ConnectionStatus = 'disconnected'\n private abortController: AbortController | null = null\n private readonly clientToolsRef: { current: Map<string, AnyClientTool> }\n private readonly devtoolsBridge: ChatDevtoolsBridge\n /**\n * Alias for `this.events`. The bridge installs an\n * emitter that auto-attaches run/thread context and auto-emits a\n * snapshot after every event, so chat-client only ever calls\n * `this.events.X(...)` exactly like it did before devtools landed.\n */\n private readonly events: ChatClientEventEmitter\n private currentStreamId: string | null = null\n private currentMessageId: string | null = null\n private readonly postStreamActions: Array<() => Promise<void>> = []\n // Track pending client tool executions to await them before stream finalization\n private readonly pendingToolExecutions: Map<string, Promise<void>> = new Map()\n private activeClientTools: Map<string, AnyClientTool> | null = null\n private activeContext: TContext | undefined = undefined\n // Flag to deduplicate continuation checks during action draining\n private continuationPending = false\n private subscriptionAbortController: AbortController | null = null\n private processingResolve: (() => void) | null = null\n /**\n * `connect()` adapters push the full HTTP body into the subscribe queue, then\n * wait until that queue is idle. After `send()` returns, every chunk from this\n * request has been processed. Subscribe/send sockets do not drain that way.\n */\n private connectionDrainsOnSend = false\n private errorReportedGeneration: number | null = null\n private streamGeneration = 0\n private continuationGeneration = 0\n // Generation of the run that opened the current stream. Public\n // `addToolResult` must use this, not the live counter: `stop()` increments\n // the live counter, so a post-stop call would otherwise look current.\n private streamContinuationGeneration = 0\n // Tracks whether a queued checkForContinuation was skipped because\n // continuationPending was true (chained approval scenario)\n private continuationSkipped = false\n private draining = false\n private sessionGenerating = false\n private readonly activeRunIds = new Set<string>()\n /** Latched by `dispose()`; stops any late async callback starting new work. */\n private disposed = false\n /** The error a failed mount hydration set, cleared by the next successful one. */\n private hydrationError: Error | undefined\n /** Whether a view is currently watching. See `attach` / `detach`. */\n private tailing = false\n /** Constructor inputs `attach()` needs on every re-attach, not just the first. */\n private readonly rejoinRunId: string | null | undefined\n private readonly cachesMessages: boolean\n /**\n * Newest-window size from `history.pageSize`. Only set when\n * `persistence === true`.\n */\n private readonly historyPageSize: number | undefined\n private hasOlderMessages = false\n private olderMessagesCursor: string | undefined\n private readonly knownServerMessageIds = new Set<string>()\n private loadOlderMessagesInFlight = false\n private historyGeneration = 0\n private devtoolsMounted = false\n\n private readonly callbacksRef: {\n current: {\n onResponse: (response?: Response) => void | Promise<void>\n onChunk: (chunk: StreamChunk) => void\n onFinish: (message: UIMessage) => void\n onError: (error: Error) => void\n onMessagesChange: (messages: Array<UIMessage>) => void\n onLoadingChange: (isLoading: boolean) => void\n onErrorChange: (error: Error | undefined) => void\n onStatusChange: (status: ChatClientState) => void\n onSubscriptionChange: (isSubscribed: boolean) => void\n onConnectionStatusChange: (status: ConnectionStatus) => void\n onSessionGeneratingChange: (isGenerating: boolean) => void\n onQueueChange: (queue: Array<QueuedMessage>) => void\n onResumeStateChange: (\n resumeState: ChatResumeState | null,\n pendingInterrupts: BoundInterrupts<TTools, TInterrupts>,\n ) => void\n onRunIdChange: (runId: string | null) => void\n onInterruptStateChange: (\n state: ChatInterruptState<TTools, TInterrupts>,\n context: { source: 'hydrate' | 'live' },\n ) => void\n onCustomEvent: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n }\n }\n\n constructor(options: ChatClientOptions<TTools, TContext, TInterrupts>) {\n assertUniqueInterruptDefinitions(options.interrupts)\n // Do not mint a random thread id during construct. Framework hooks build\n // this client during render (SSR included). The wire/devtools identity is\n // `threadId`; it is assigned here when the caller passed one, or later in\n // `ensureThreadId()` from attach / mount / send.\n this.threadId = options.threadId || ''\n this.uniqueId = this.threadId\n // `persistence` is `false`/omitted (ephemeral, in-memory), `true`\n // (server-authoritative: cache nothing client-side, hydrate the thread from\n // the server by `threadId` on mount), or a storage adapter\n // (client-authoritative: cache the transcript plus resume pointer). Only the\n // server-authoritative mode turns transcript caching off; that is what gates\n // the mount hydration and keeps a client record from shadowing server history.\n let cachesMessages = true\n if (options.persistence === true) {\n if (!options.threadId) {\n throw new Error(\n '[TanStack AI] persistence needs a stable `threadId` to key on. Pass a threadId from your app (for example support-42).',\n )\n }\n cachesMessages = false\n this.historyPageSize = options.history?.pageSize\n } else if (options.persistence) {\n // A storage adapter: keep the combined record (transcript + resume pointer)\n // in the browser. Persistence keys on `threadId` (the conversation\n // identity) so a reload with the same `threadId` finds the same record.\n if (!options.threadId) {\n throw new Error(\n '[TanStack AI] persistence needs a stable `threadId` to key on. Pass a threadId from your app (for example support-42).',\n )\n }\n this.persistor = new ChatPersistor(\n options.persistence,\n options.threadId,\n (messages) => this.processor.setMessages(messages),\n (snapshot) => this.applyPersistedResume(snapshot),\n )\n this.historyPageSize = undefined\n } else {\n this.historyPageSize = undefined\n }\n // Both `body` (deprecated) and `forwardedProps` populate the AG-UI\n // `RunAgentInput.forwardedProps` wire field. They are stored\n // separately so `updateOptions` can replace one without touching the\n // other; the merge happens at send time, with `forwardedProps`\n // winning on key collision.\n this.bodyOption = options.body || {}\n this.forwardedPropsOption = options.forwardedProps || {}\n this.byok = options.byok\n this.byokProvider = options.byokProvider\n this.context = options.context\n this.queueConfig = normalizeQueueOption(options.queue)\n const transport = resolveTransport(options)\n this.connectionDrainsOnSend = connectionDrainsOnSend(transport)\n this.connection = normalizeConnectionAdapter(transport)\n\n // Build client tools map\n this.clientToolsRef = { current: new Map() }\n if (options.tools) {\n for (const tool of options.tools) {\n this.clientToolsRef.current.set(tool.name, tool)\n }\n }\n\n this.devtoolsBridge = (\n options.devtoolsBridgeFactory ?? createNoOpChatDevtoolsBridge\n )(this.buildDevtoolsBridgeOptions(options.devtools))\n this.events = this.devtoolsBridge.events\n\n this.callbacksRef = {\n current: {\n onResponse: options.onResponse || (() => {}),\n onChunk: options.onChunk || (() => {}),\n onFinish: options.onFinish || (() => {}),\n onError: options.onError || (() => {}),\n onMessagesChange: options.onMessagesChange || (() => {}),\n onLoadingChange: options.onLoadingChange || (() => {}),\n onErrorChange: options.onErrorChange || (() => {}),\n onStatusChange: options.onStatusChange || (() => {}),\n onSubscriptionChange: options.onSubscriptionChange || (() => {}),\n onConnectionStatusChange:\n options.onConnectionStatusChange || (() => {}),\n onSessionGeneratingChange:\n options.onSessionGeneratingChange || (() => {}),\n onQueueChange: options.onQueueChange || (() => {}),\n onResumeStateChange: options.onResumeStateChange || (() => {}),\n onRunIdChange: options.onRunIdChange || (() => {}),\n onInterruptStateChange: options.onInterruptStateChange || (() => {}),\n onCustomEvent: options.onCustomEvent || (() => {}),\n },\n }\n\n this.interruptManager = new InterruptManager<TTools, TInterrupts>({\n ...(options.tools !== undefined ? { tools: options.tools } : {}),\n ...(options.interrupts !== undefined\n ? { interrupts: options.interrupts }\n : {}),\n submit: (submission) => this.submitInterruptBatch(submission),\n onChange: (source) => this.notifyResumeStateChange(source),\n })\n\n // In-memory rehydrate of interrupt descriptors (e.g. after a page reload\n // when the host supplies a snapshot). Durable storage of that snapshot is\n // a persistence-stack concern — not wired here.\n if (options.initialResumeSnapshot) {\n this.applyResumeSnapshot(options.initialResumeSnapshot)\n }\n\n // Create StreamProcessor with event handlers.\n // Use conditional spreads so we don't pass `undefined` into\n // `StreamProcessorOptions` fields under `exactOptionalPropertyTypes`.\n const persistedState = this.persistor?.readInitial()\n const syncPersistedState =\n persistedState instanceof Promise ? undefined : persistedState\n // A persistor exists only in client-authoritative mode, so a synchronously\n // read record's transcript is the conversation; adopt it over host\n // `initialMessages`. (Server-authoritative mode has no persistor and instead\n // hydrates from the server on mount, keyed by threadId.)\n const initialMessages = syncPersistedState\n ? syncPersistedState.messages\n : options.initialMessages\n // A durable snapshot read synchronously from storage wins over the\n // in-memory `initialResumeSnapshot` fallback applied above. A snapshot with\n // pending interrupts rehydrates the interrupt UI; a bare in-flight run is\n // rejoined after the processor is ready (see `rejoinRunId` below).\n let rejoinRunId: string | null = null\n if (syncPersistedState?.resume) {\n const snapshot = syncPersistedState.resume\n const hasPendingInterrupts =\n Array.isArray(snapshot.pendingInterrupts) &&\n snapshot.pendingInterrupts.length > 0\n if (hasPendingInterrupts) {\n // Interrupts are run-scoped state, restored from the cached snapshot.\n this.applyResumeSnapshot(snapshot)\n } else if (snapshot.resumeState.runId) {\n // A bare in-flight run pointer drives a client-authoritative rejoin.\n rejoinRunId = snapshot.resumeState.runId\n }\n }\n // A host-supplied `initialResumeSnapshot` carrying a bare in-flight run is\n // rejoined too, not just its interrupts (which `applyResumeSnapshot` above\n // already restored). This is how a server-authoritative app hands a FRESH\n // client an in-flight run to tail — e.g. opening the thread on a second\n // device / browser, where hydration reports the active run id but no local\n // resume pointer exists. A run named by the persisted store wins.\n if (!rejoinRunId && options.initialResumeSnapshot) {\n const snapshot = options.initialResumeSnapshot\n const hasPendingInterrupts =\n Array.isArray(snapshot.pendingInterrupts) &&\n snapshot.pendingInterrupts.length > 0\n if (!hasPendingInterrupts && snapshot.resumeState.runId) {\n rejoinRunId = snapshot.resumeState.runId\n }\n }\n\n this.processor = new StreamProcessor({\n ...(options.streamProcessor?.chunkStrategy\n ? { chunkStrategy: options.streamProcessor.chunkStrategy }\n : {}),\n ...(initialMessages ? { initialMessages } : {}),\n events: {\n onMessagesChange: (messages) => {\n // Restored or replaced messages bring their own cards. Give each one\n // its live handle before anyone reads the messages.\n this.syncSubagentHandles()\n this.persistor?.notifyMessagesChanged(messages)\n this.callbacksRef.current.onMessagesChange(messages)\n // The bridge only snapshots on status changes, and the panel draws a\n // child card from the snapshot, so without this the card would update\n // only when the parent run ends.\n this.queueDevtoolsSnapshot()\n },\n onStreamStart: () => {\n this.setStatus('streaming')\n const assistantMessageId =\n this.processor.getCurrentAssistantMessageId()\n if (!assistantMessageId) {\n return\n }\n const messages = this.processor.getMessages()\n const assistantMessage = messages.find(\n (m: UIMessage) => m.id === assistantMessageId,\n )\n if (assistantMessage) {\n this.currentMessageId = assistantMessage.id\n this.events.messageAppended(\n assistantMessage,\n this.currentStreamId || undefined,\n )\n }\n },\n onStreamEnd: (message: UIMessage) => {\n this.callbacksRef.current.onFinish(message)\n this.setStatus('ready')\n // Resolve the processing-complete promise so streamResponse can continue\n this.resolveProcessing()\n },\n onError: (error: Error) => {\n this.reportStreamError(error)\n },\n onTextUpdate: (messageId: string, content: string) => {\n // Emit text update to devtools\n if (this.currentStreamId) {\n this.events.textUpdated(this.currentStreamId, messageId, content)\n }\n },\n onThinkingUpdate: (messageId: string, content: string) => {\n // Emit thinking update to devtools\n if (this.currentStreamId) {\n this.events.thinkingUpdated(\n this.currentStreamId,\n messageId,\n content,\n undefined,\n )\n }\n },\n onStructuredOutputChange: (args) => {\n const streamId = this.devtoolsBridge.resolveStreamId()\n const eventName =\n args.phase === 'start'\n ? 'structured-output:started'\n : args.phase === 'complete'\n ? 'structured-output:completed'\n : args.phase === 'error'\n ? 'structured-output:errored'\n : 'structured-output:updated'\n\n this.currentMessageId = args.messageId\n this.events.structuredOutputChanged(\n eventName,\n streamId,\n args.messageId,\n {\n status: args.status,\n raw: args.raw,\n ...(args.partial !== undefined ? { partial: args.partial } : {}),\n ...(args.data !== undefined ? { data: args.data } : {}),\n ...(args.reasoning !== undefined\n ? { reasoning: args.reasoning }\n : {}),\n ...(args.errorMessage !== undefined\n ? { errorMessage: args.errorMessage }\n : {}),\n ...(args.delta !== undefined ? { delta: args.delta } : {}),\n },\n )\n },\n onToolCallStateChange: (\n messageId: string,\n toolCallId: string,\n state: string,\n args: string,\n ) => {\n // Get the tool name from the messages\n const messages = this.processor.getMessages()\n const message = messages.find((m: UIMessage) => m.id === messageId)\n const toolCallPart = message?.parts.find(\n (p: MessagePart): p is ToolCallPart =>\n p.type === 'tool-call' && p.id === toolCallId,\n )\n const toolName = toolCallPart?.name || 'unknown'\n\n // Emit tool call state change to devtools\n if (this.currentStreamId) {\n this.events.toolCallStateChanged(\n this.currentStreamId,\n messageId,\n toolCallId,\n toolName,\n state,\n args,\n )\n }\n },\n onToolCall: (args: {\n toolCallId: string\n toolName: string\n input: any\n }) => {\n // Handle client-side tool execution automatically\n const clientTools =\n this.activeClientTools ?? this.clientToolsRef.current\n const clientTool = clientTools.get(args.toolName)\n const executeFunc = clientTool?.execute\n if (executeFunc) {\n const continuationGeneration = this.continuationGeneration\n // Capture the run context at execution-start so a tool whose\n // result lands AFTER the originating run finishes still reports\n // back against the originating run, not whatever run is\n // current when the result emits.\n const runEventContext =\n this.devtoolsBridge.getCurrentRunEventContext()\n // Create and track the execution promise\n const executionPromise = (async () => {\n try {\n const context =\n this.activeClientTools === null\n ? this.context\n : this.activeContext\n const output = await executeFunc(args.input, {\n toolCallId: args.toolCallId,\n context: context as TContext,\n emitCustomEvent: () => {},\n })\n await this.addToolResultForClientTool(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output,\n state: 'output-available',\n },\n clientTool,\n continuationGeneration,\n runEventContext,\n )\n } catch (error: any) {\n await this.addToolResultForClientTool(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output: null,\n state: 'output-error',\n errorText: error.message,\n },\n clientTool,\n continuationGeneration,\n runEventContext,\n )\n } finally {\n // Remove from pending when complete\n this.pendingToolExecutions.delete(args.toolCallId)\n }\n })()\n\n // Track the pending execution\n this.pendingToolExecutions.set(args.toolCallId, executionPromise)\n }\n },\n onApprovalRequest: (args: {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n }) => {\n const streamId = this.devtoolsBridge.resolveStreamId()\n const messageIdForApproval =\n this.findMessageIdForToolCall(args.toolCallId) ??\n this.currentMessageId ??\n ''\n\n this.events.approvalRequested(\n streamId,\n messageIdForApproval,\n args.toolCallId,\n args.toolName,\n args.input,\n args.approvalId,\n )\n },\n onCustomEvent: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => {\n // Server-side memory middleware transports its state as a `memory:state`\n // CUSTOM event (its own event bus never reaches this browser runtime).\n // Route it to the devtools bridge here — the designated custom-event\n // path — then still forward to the app's callback.\n if (eventType === 'memory:state') {\n this.devtoolsBridge.recordMemoryState(data)\n }\n if (\n eventType === 'compaction:started' ||\n eventType === 'compaction:state' ||\n eventType === 'compaction:ended'\n ) {\n this.devtoolsBridge.recordCompactionEvent(eventType, data)\n }\n if (eventType === 'skills:state') {\n this.devtoolsBridge.recordSkillsState(data)\n }\n this.callbacksRef.current.onCustomEvent(eventType, data, context)\n },\n },\n })\n // `initialMessages` do not fire a change event. Give their cards handles.\n this.syncSubagentHandles()\n\n this.persistor?.hydrateAsync(persistedState)\n\n this.rejoinRunId = rejoinRunId\n this.cachesMessages = cachesMessages\n // NO TAILING HERE, deliberately. Constructing a client must not open a\n // connection.\n //\n // A UI framework may build a client and then throw it away — React does it on\n // every double-invoked render, and the discarded instance is never mounted, so\n // nothing ever calls `detach()` or `dispose()` on it. When the constructor\n // opened a stream, that stream became unreachable and held one of the browser's\n // ~6 connections per origin until the page reloaded. Traced with CDP: connection\n // ids 1374/1396/1428/1437 were still held after eight thread switches, and a\n // later request waited 210 SECONDS for a free slot (`stallMs: 210752`).\n //\n // Guarding inside the client cannot fix that, because the leaking instance is\n // the one the framework discarded — every guard runs on the instance it kept.\n // Only \"idle until a view attaches\" makes a thrown-away client harmless.\n //\n // Callers therefore drive the lifecycle: `attach()` when a view mounts,\n // `detach()` when it unmounts. Every framework wrapper in this repo does.\n }\n\n /**\n * START TAILING: re-attach to an in-flight run so its chunks arrive here.\n *\n * Called by the constructor, and again by a UI wrapper every time its view\n * mounts. Idempotent — attaching while already attached does nothing — so the\n * constructor call and a wrapper's first mount cost one attach between them.\n *\n * Pairs with {@link detach}. The pair exists because tailing used to begin ONLY\n * in the constructor, which meant a view could never stop tailing and then\n * resume: unmount had to either keep the connection open or lose it for good.\n * Keeping it open is what starved the page — a browser allows ~6 connections per\n * origin, and one long-lived stream per view reaches that after a handful of\n * views, after which every other request queues (measured: an in-page fetch took\n * over two minutes while the same request from outside the browser took 17ms).\n */\n attach(): void {\n if (this.disposed || this.tailing) return\n this.ensureThreadId()\n this.tailing = true\n\n // Full page reload with an in-flight run persisted (synchronous store):\n // re-attach to it off the server's delivery-durability log so the stream\n // finishes here. Async stores rejoin from `applyPersistedResume` once the\n // hydrate resolves. Best-effort and non-blocking.\n if (this.rejoinRunId) {\n this.maybeRejoinInFlight(this.rejoinRunId)\n }\n\n // Server-authoritative (`persistence: true`): the client caches no transcript\n // and no run pointer — it re-hydrates from the server on mount, keyed by the\n // stable threadId. `hydrate` returns the stored transcript plus a cursor to\n // any in-flight run, which is tailed via the same joinRun path. This is what\n // makes reload AND a fresh device work with zero app glue (no loader/prop).\n if (!this.cachesMessages && this.connection.hydrate) {\n this.hydrateFromServer()\n }\n }\n\n /**\n * STOP TAILING: drop the connection, keep everything else.\n *\n * Called by a UI wrapper when its view unmounts. The transcript, the resume\n * pointer and the run id all stay, so a later {@link attach} repaints instantly\n * and re-tails from the durable log — nothing is lost, because the run keeps\n * going server-side and its log holds every chunk.\n *\n * Deliberately NOT `dispose()`: this client is expected back. And deliberately\n * not `stop()`, which means \"the user ended this run\" — detaching says only that\n * nobody is watching right now.\n *\n * `rejoinedRunId` is cleared so the next `attach` can re-join the same run;\n * without that reset the guard in {@link maybeRejoinInFlight} would treat the\n * run as already joined and the view would come back silent.\n */\n detach(): void {\n if (!this.tailing) return\n // BEFORE the abort, because `resumeInFlightRun`'s cleanup reads it: a join\n // aborted before its first chunk normally means \"this run is unreachable\" and\n // clears the resume pointer. A detach is not that — the run is fine and we\n // intend to come back — so the pointer must survive.\n this.tailing = false\n this.cancelInFlightStream({ setReadyStatus: true })\n this.rejoinedRunId = null\n }\n\n private applyResumeSnapshot(snapshot: ChatResumeSnapshot): void {\n const resumeState = readResumeState(snapshot)\n if (resumeState === undefined) {\n this.interruptManager.reset({ source: 'hydrate' })\n return\n }\n this.lastResume = resumeState\n const pendingInterrupts = Array.isArray(snapshot.pendingInterrupts)\n ? snapshot.pendingInterrupts\n : []\n if (pendingInterrupts.length === 0) {\n this.interruptManager.reset({ source: 'hydrate' })\n return\n }\n const generation = this.interruptGeneration(pendingInterrupts)\n this.interruptManager.hydrate(\n {\n threadId: resumeState.threadId,\n interruptedRunId: resumeState.runId,\n generation,\n interrupts: pendingInterrupts,\n },\n 'hydrate',\n )\n }\n\n /**\n * Apply a resume snapshot read from durable storage. Restores interrupt state,\n * and for a bare in-flight run (no pending interrupts) also rejoins it. This is\n * the async-store counterpart to the synchronous rejoin in the constructor:\n * `applyResumeSnapshot` alone only handles interrupts, so an async store\n * (`indexedDBPersistence`) would otherwise never rejoin a mid-stream run.\n */\n private applyPersistedResume(snapshot: ChatResumeSnapshot): void {\n this.applyResumeSnapshot(snapshot)\n const hasInterrupts =\n Array.isArray(snapshot.pendingInterrupts) &&\n snapshot.pendingInterrupts.length > 0\n const runId = snapshot.resumeState?.runId\n // A cached run pointer only reaches here through the persistor, which exists\n // only in client-authoritative mode, so a bare in-flight run rejoins here.\n // (Server-authoritative reconnect is resolved from the server by threadId in\n // `hydrateFromServer`.)\n if (!hasInterrupts && runId) {\n this.maybeRejoinInFlight(runId)\n }\n }\n\n /**\n * Rejoin a persisted in-flight run, guarded so it fires at most once and never\n * while another run is already active. Skipped when the connection is not\n * resumable (`joinRun` absent), so a non-durable transport is a no-op.\n */\n private maybeRejoinInFlight(runId: string): void {\n if (!this.connection.joinRun) return\n // A client with no view attached must never open a connection. `tailing` is\n // the load-bearing half: a view switch calls `detach()`, NOT `dispose()`, and\n // an in-flight hydration resolves a moment later and lands right here — so\n // guarding only on `disposed` let every switch open a fresh tail that nothing\n // would ever abort. Measured with CDP: connection ids 1366/1397/1429/1460 were\n // still held after eight switches, and a later request waited 97 SECONDS for a\n // slot (`stallMs: 97691`).\n if (this.disposed || !this.tailing) return\n if (this.rejoinedRunId === runId) return\n // A fresh send (or an already-running rejoin) owns the client; don't stomp it.\n if (this.isLoading || this.abortController) return\n this.rejoinedRunId = runId\n this.resumeInFlightRun(runId)\n }\n\n /**\n * Server-authoritative mount hydration (`persistence: true`). The client holds\n * no transcript and no run pointer; on mount it asks the server — keyed by the\n * stable threadId — for the stored transcript and whether a run is still\n * generating. The transcript repaints immediately; an in-flight run is tailed\n * through the same durability rejoin as a reload. Best-effort and\n * non-blocking: a failure leaves the client empty rather than throwing, and a\n * send that starts first owns the client (hydration then backs off).\n */\n private hydrateFromServer(): void {\n const hydrate = this.connection.hydrate\n if (!hydrate) return\n if (this.isLoading || this.abortController) return\n if (this.disposed) return\n const pageSize = this.historyPageSize\n const hydrateOptions: ChatHydrateOptions | undefined =\n pageSize === undefined ? undefined : { limit: pageSize }\n void (async () => {\n let result: ChatHydrationResult\n const generation = this.historyGeneration\n try {\n result = await hydrate(this.threadId, hydrateOptions)\n } catch (cause) {\n // Same staleness guard as the success path below: a failure from an\n // older attempt must not touch the state of a newer one.\n if (generation === this.historyGeneration) this.failHydration(cause)\n return\n }\n if (generation !== this.historyGeneration) return\n // NO VIEW IS WATCHING ANY MORE (it unmounted while this fetch was in\n // flight). Applying anything now is pointless, and one thing is actively\n // harmful: the branch below calls `maybeRejoinInFlight`, which opens a TAIL.\n // A tail started here belongs to a view that has gone, so nothing will ever\n // abort it, and a browser allows only ~6 connections per origin — so a\n // handful of switches starve the page and every later request queues.\n //\n // `!this.tailing` is the case that actually bites: a switch calls `detach()`,\n // not `dispose()`, so a `disposed`-only check let the leak straight through.\n if (this.disposed || !this.tailing) return\n // A send may have started while the fetch was in flight — don't stomp it.\n if (this.isLoading || this.abortController) return\n // A retry after a failed load succeeded: drop that failure, but not an\n // error something else set since.\n if (this.hydrationError && this.error === this.hydrationError) {\n this.setError(undefined)\n this.setStatus('ready')\n }\n this.hydrationError = undefined\n this.applyHydrationPage(result.page)\n if (result.messages.length > 0) {\n const windowMessages = normalizeMessagesDates(result.messages)\n this.processor.setMessages(windowMessages)\n this.rememberServerMessageIds(windowMessages)\n }\n if (result.interrupts && result.interrupts.pending.length > 0) {\n // Pending interrupt = the thread is paused awaiting a human decision, so\n // there is nothing to tail (no chunks stream until it resolves). Restore\n // the approval/wait from the SERVER — identical to reconstructing it from\n // a resume snapshot — so the reload re-prompts the decision and the resume\n // targets the run it paused. This is checked BEFORE `activeRun` on\n // purpose: a run that just paused can momentarily still read as `running`\n // on the server, so a racing hydrate reports both an `activeRun` cursor\n // AND the pending interrupt. Tailing that \"active\" run would drop the\n // approval card (and hang on a stream that never comes), so the interrupt\n // always wins.\n this.applyResumeSnapshot({\n resumeState: {\n threadId: this.threadId,\n runId: result.interrupts.runId,\n },\n pendingInterrupts: result.interrupts.pending,\n })\n } else if (result.activeRun?.runId) {\n this.maybeRejoinInFlight(result.activeRun.runId)\n }\n })()\n }\n\n /**\n * Surface a mount-hydration failure (`persistence: true`) on the observable\n * fields, mirroring `GenerationClient.failHydration`, so \"this thread failed\n * to load\" is distinguishable from \"this thread has no messages\" and the app\n * can show an error / offer a retry. A genuine miss — the server having no\n * record for a fresh thread — resolves normally and never reaches here; only a\n * thrown transport / authorize-gate error does.\n *\n * Skipped when the view unmounted (`!tailing`) or a `sendMessage` took\n * ownership while the hydrate GET was in flight, so a live run's state always\n * wins over a stale mount-time failure — same guard as the success path above.\n */\n private failHydration(cause: unknown): void {\n if (this.disposed || !this.tailing) return\n if (this.isLoading || this.abortController) return\n const error = cause instanceof Error ? cause : new Error(String(cause))\n // Mirror the send path: a BYOK key that is missing / locked must still\n // trigger the key-request flow on thread load, not just be reported.\n if (error instanceof ByokMissingError) {\n this.byok?.request(error.provider, 'missing')\n }\n if (error instanceof ByokBlockedError && error.reason === 'locked') {\n this.byok?.request(error.provider, 'locked')\n }\n this.hydrationError = error\n this.setStatus('error')\n this.setError(error)\n this.callbacksRef.current.onError(error)\n }\n\n mountDevtools(): void {\n this.ensureThreadId()\n if (this.devtoolsMounted) {\n return\n }\n\n this.devtoolsMounted = true\n this.devtoolsBridge.mountWithTools(this.processor.getMessages().length)\n }\n\n private ensureThreadId(): string {\n if (!this.threadId) {\n this.threadId = this.generateUniqueId('thread')\n }\n this.uniqueId = this.threadId\n return this.threadId\n }\n\n /**\n * Drain a runId-less RUN_ERROR that belongs to a cleared run the client is\n * still tracking. The persistor owns the cleared-run bookkeeping; the client\n * owns the active-run / session / processing state.\n */\n private drainIgnoredRunlessChunk(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_ERROR') return\n const runId = this.clearedStreamTracker.takeRunlessRunId()\n if (!runId) return\n this.activeRunIds.delete(runId)\n this.setSessionGenerating(this.activeRunIds.size > 0)\n this.resolveProcessing()\n }\n\n private retireIgnoredClearedTerminalChunk(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') return\n const runId =\n getChunkRunId(chunk) ?? this.clearedStreamTracker.takeRunlessRunId()\n if (!runId) return\n this.activeRunIds.delete(runId)\n this.setSessionGenerating(this.activeRunIds.size > 0)\n if (!getChunkRunId(chunk)) {\n this.resolveProcessing()\n }\n }\n\n private updateRunLifecycle(\n chunk: StreamChunk,\n options?: { resolveProcessing?: boolean },\n ): void {\n if (chunk.type === 'RUN_STARTED') {\n const chunkRunId = getChunkRunId(chunk) ?? chunk.runId\n const parentRunId =\n 'parentRunId' in chunk && typeof chunk.parentRunId === 'string'\n ? chunk.parentRunId\n : undefined\n if (parentRunId) {\n this.runParents.set(chunkRunId, parentRunId)\n if (this.supersededInterruptRunIds.has(chunkRunId)) {\n this.markLineageAnswered(parentRunId)\n }\n }\n this.activeResumeThreadId =\n 'threadId' in chunk && typeof chunk.threadId === 'string'\n ? chunk.threadId\n : this.activeResumeThreadId\n this.activeResumeRunId = chunkRunId\n // Late RUN_STARTED for a run that already finished is replay, not a live\n // turn. A new send can reuse a finished run id while isLoading is true.\n const isAnsweredReplayStart =\n this.supersededInterruptRunIds.has(chunkRunId) && !this.isLoading\n if (!isAnsweredReplayStart) {\n this.activeRunIds.add(chunkRunId)\n this.clearedStreamTracker.onRunStarted(chunkRunId)\n this.setSessionGenerating(true)\n }\n // Persist a live-run resume snapshot so a full page reload can rejoin this\n // in-flight run via joinRun. Only a persistor writes it, and a persistor\n // exists only in client-authoritative mode; server-authoritative reconnect\n // is resolved from the server by threadId in `hydrateFromServer`, so no\n // client-cached run pointer (which goes stale the moment a turn spans a\n // second run) is ever written. Interrupt/terminal handling overwrites or\n // clears it in observeInterruptState.\n if (\n this.persistor &&\n this.connection.joinRun &&\n !this.lastResume &&\n !isAnsweredReplayStart\n ) {\n this.persistResumeSnapshot({\n threadId: this.activeResumeThreadId ?? this.threadId,\n runId: chunkRunId,\n })\n }\n if (\n this.lastResume &&\n this.supersededInterruptRunIds.has(this.lastResume.runId)\n ) {\n this.lastResume = null\n this.persistor?.persistResumeSnapshot(null)\n this.interruptManager.reset()\n }\n return\n }\n\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') {\n return\n }\n\n const runId = getChunkRunId(chunk)\n if (runId) {\n this.activeRunIds.delete(runId)\n this.clearedStreamTracker.onRunSettled(runId)\n } else if (chunk.type === 'RUN_ERROR') {\n // RUN_ERROR without runId is a session-level error; clear all runs.\n this.activeRunIds.clear()\n this.clearedStreamTracker.onSessionRunError()\n }\n this.setSessionGenerating(this.activeRunIds.size > 0)\n const skipProcessingResolve =\n chunk.type === 'RUN_FINISHED' && isIntermediateToolTurn(chunk)\n if (options?.resolveProcessing !== false && !skipProcessingResolve) {\n this.resolveProcessing()\n }\n }\n\n /**\n * Track interrupt state off the stream's terminal events. A RUN_FINISHED with\n * an interrupt outcome records the pending interrupts + the run/thread to\n * resume; any other terminal event for the tracked/current run clears that\n * state. This is interrupt (state) resume — there is no delivery cursor.\n */\n private observeInterruptState(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') {\n return\n }\n\n if (this.activeInterruptSubmission && chunk.type === 'RUN_ERROR') {\n return\n }\n const runId = getChunkRunId(chunk)\n const threadId =\n 'threadId' in chunk && typeof chunk.threadId === 'string'\n ? chunk.threadId\n : this.activeResumeThreadId\n\n // Record answered lineage before hydrating a pause. Intermediate\n // tool_calls RUN_FINISHED is a mid-turn handoff, not an answered pause.\n if (\n runId &&\n !isIntermediateToolTurn(chunk) &&\n (chunk.type === 'RUN_ERROR' ||\n (chunk.type === 'RUN_FINISHED' && chunk.outcome?.type !== 'interrupt'))\n ) {\n this.markLineageAnswered(runId)\n }\n\n if (chunk.type === 'RUN_FINISHED' && chunk.outcome?.type === 'interrupt') {\n // Track the REQUEST run id (what the client sent) so a resume targets the\n // same run even when provider events carry their own run id.\n const interruptedRunId =\n this.currentRunId ?? runId ?? this.activeResumeRunId ?? ''\n // Replay re-emits a pause that a continuation already answered.\n if (\n interruptedRunId &&\n this.supersededInterruptRunIds.has(interruptedRunId)\n ) {\n return\n }\n this.lastResume = {\n threadId: threadId ?? this.threadId,\n runId: interruptedRunId,\n }\n this.interruptManager.hydrate(\n {\n threadId: this.lastResume.threadId,\n interruptedRunId,\n generation: this.interruptGeneration(chunk.outcome.interrupts),\n interrupts: chunk.outcome.interrupts,\n },\n 'live',\n )\n return\n }\n\n const isRunlessSessionError = chunk.type === 'RUN_ERROR' && !runId\n const isTrackedRunTerminal = Boolean(\n runId && this.lastResume?.runId === runId,\n )\n const isCurrentRunTerminal = Boolean(\n (runId && this.currentRunId === runId) ||\n (this.currentRunId && this.lastResume?.runId === this.currentRunId),\n )\n // Provider adapters sometimes stamp a different run id on continuation\n // events than the client-generated request id. RUN_STARTED updates\n // `activeResumeRunId`, so match that too.\n const isActiveStreamRunTerminal = Boolean(\n this.isLoading &&\n runId &&\n (runId === this.activeResumeRunId || runId === this.currentRunId),\n )\n const isCurrentStreamTerminal =\n this.isLoading && chunk.type === 'RUN_FINISHED' && !runId\n // A resume batch that finishes successfully (or with a non-interrupt\n // terminal) must always clear pending interrupts — even when the provider\n // run id does not correlate. Otherwise Approve works once but the UI\n // keeps showing a stale prompt and blocks follow-up turns.\n const isActiveInterruptSubmissionTerminal = Boolean(\n this.activeInterruptSubmission &&\n this.isLoading &&\n chunk.type === 'RUN_FINISHED' &&\n chunk.outcome?.type !== 'interrupt',\n )\n // Replay can deliver a parent pause after a descendant already finished,\n // and this terminal's run id may not match lastResume.\n const isLineageDescendantTerminal = Boolean(\n this.lastResume &&\n this.supersededInterruptRunIds.has(this.lastResume.runId),\n )\n if (\n isRunlessSessionError ||\n isTrackedRunTerminal ||\n isCurrentRunTerminal ||\n isActiveStreamRunTerminal ||\n isCurrentStreamTerminal ||\n isActiveInterruptSubmissionTerminal ||\n isLineageDescendantTerminal\n ) {\n this.lastResume = null\n // Run settled without an interrupt: drop the durable resume snapshot so a\n // later reload does not try to rejoin a finished run.\n this.persistor?.persistResumeSnapshot(null)\n this.interruptManager.reset()\n return\n }\n this.notifyResumeStateChange('live')\n }\n\n /** Walk parentRunId links and mark this run plus its ancestors as answered. */\n private markLineageAnswered(runId: string): void {\n let current: string | undefined = runId\n let hops = 0\n while (current !== undefined && hops < MAX_RUN_LINEAGE_DEPTH) {\n if (this.supersededInterruptRunIds.has(current)) return\n this.supersededInterruptRunIds.add(current)\n current = this.runParents.get(current)\n hops++\n }\n }\n\n /**\n * The interrupt-resume state for the active/interrupted run (its run/thread\n * ids), or null when there is nothing to resume. Apps can persist this to\n * resume interrupts across a full reload.\n */\n getResumeState(): ChatResumeState | null {\n return this.lastResume ? { ...this.lastResume } : null\n }\n\n /**\n * The id of the run this client has in flight — one it started via a send or\n * rejoined via `joinRun` — or null when there is none. Unlike\n * {@link getResumeState}, this tracks ordinary runs too, not only one that is\n * interrupted or being resumed. A run another client started and that arrives\n * over a live subscription is not this client's run and is not reported here.\n */\n getCurrentRunId(): string | null {\n return this.currentRunId\n }\n\n private setCurrentRunId(runId: string | null): void {\n if (this.currentRunId === runId) return\n this.currentRunId = runId\n this.callbacksRef.current.onRunIdChange(runId)\n }\n\n getInterruptState(): ChatInterruptState<TTools, TInterrupts> {\n return this.interruptManager.getState()\n }\n\n getInterrupts(): BoundInterrupts<TTools, TInterrupts> {\n return this.interruptManager.getInterrupts() as BoundInterrupts<\n TTools,\n TInterrupts\n >\n }\n\n /** @deprecated Use getInterrupts(). */\n getPendingInterrupts(): BoundInterrupts<TTools, TInterrupts> {\n return this.interruptManager.getInterrupts() as BoundInterrupts<\n TTools,\n TInterrupts\n >\n }\n\n resolveInterrupts(approved: boolean): void\n resolveInterrupts(\n resolver: (\n interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,\n ) => undefined,\n ): void\n resolveInterrupts(\n resolution:\n | boolean\n | ((\n interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,\n ) => undefined),\n ): void {\n // Branch so TypeScript can select the InterruptManager.resolve overloads.\n if (typeof resolution === 'boolean') {\n this.interruptManager.resolve(resolution)\n return\n }\n this.interruptManager.resolve(resolution)\n }\n\n cancelInterrupts(): void {\n this.interruptManager.cancel()\n }\n\n retryInterrupts(): void {\n this.interruptManager.retry()\n }\n\n /** Unsafe low-level resume escape hatch. Prefer bound interrupt methods. */\n resumeInterruptsUnsafe(\n resume: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ): Promise<boolean> {\n const target = state ?? this.lastResume\n if (!target) return Promise.resolve(false)\n return this.resumeInterruptsUnsafeForGeneration(\n resume,\n target,\n this.continuationGeneration,\n )\n }\n\n private resumeInterruptsUnsafeForGeneration(\n resume: Array<RunAgentResumeItem>,\n target: ChatResumeState,\n continuationGeneration: number,\n ): Promise<boolean> {\n if (continuationGeneration !== this.continuationGeneration) {\n return Promise.resolve(false)\n }\n // Auto-executed client tools resolve during the parent stream's\n // `pendingToolExecutions` wait — while `isLoading` is still true.\n // Defer the child continuation until that stream settles so we do not\n // race the parent cleanup or return a false \"could not start\" failure.\n if (this.isLoading) {\n return new Promise<boolean>((resolve, reject) => {\n this.queuePostStreamAction(async () => {\n try {\n resolve(\n await this.resumeInterruptsUnsafeForGeneration(\n resume,\n target,\n continuationGeneration,\n ),\n )\n } catch (error) {\n reject(error)\n }\n })\n })\n }\n this.pendingResumeThreadId = target.threadId\n this.pendingResumeParentRunId = target.runId\n this.pendingResumeItems = [...resume]\n return this.streamResponse()\n }\n\n /** @deprecated Use bound interrupt methods or resumeInterruptsUnsafe(). */\n resumeInterrupts(\n resume: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ): Promise<boolean> {\n return this.resumeInterruptsUnsafe(resume, state)\n }\n\n private async submitInterruptBatch(\n submission: InterruptManagerSubmission,\n ): Promise<void> {\n const continuationGeneration = this.continuationGeneration\n this.activeInterruptSubmission = submission\n this.interruptSubmissionFailure = undefined\n // Reflect approval decisions in the local message tree immediately so a\n // follow-up turn does not re-serialize tool-calls still stuck in\n // `approval-requested` (issue #532).\n for (const resolution of submission.resolutions) {\n const approved = readApprovalApproved(resolution.payload)\n if (approved === undefined) continue\n const approvalId = resolution.interruptId\n this.processor.addToolApprovalResponse(approvalId, approved)\n }\n const resumed = await this.resumeInterruptsUnsafeForGeneration(\n [...submission.resolutions],\n {\n threadId: submission.threadId,\n runId: submission.interruptedRunId,\n },\n continuationGeneration,\n ).finally(() => {\n // Only clear if this resume still owns the client: `stop()` may have\n // invalidated it while the submission was settling.\n if (this.activeInterruptSubmission === submission) {\n this.activeInterruptSubmission = undefined\n }\n })\n if (continuationGeneration !== this.continuationGeneration) return\n const failure = this.takeInterruptSubmissionFailure()\n if (failure !== undefined) {\n throw { errors: failure.errors }\n }\n if (!resumed) {\n throw new Error('Interrupt continuation could not be started.')\n }\n // Belt-and-suspenders: if the continuation stream finished successfully\n // but correlation failed to clear resume state, drop it now so the next\n // user turn is not blocked by a stale interrupt prompt.\n if (this.lastResume?.runId === submission.interruptedRunId) {\n this.lastResume = null\n this.interruptManager.reset()\n }\n }\n\n private takeInterruptSubmissionFailure():\n | { errors: ReadonlyArray<InterruptSubmissionError> }\n | undefined {\n const failure = this.interruptSubmissionFailure\n this.interruptSubmissionFailure = undefined\n return failure\n }\n\n private interruptGeneration(\n interrupts: ReadonlyArray<ChatPendingInterrupt>,\n ): number {\n let generation: number | undefined\n for (const interrupt of interrupts) {\n const candidate: unknown =\n interrupt.metadata?.['tanstack:interruptBinding']\n if (\n candidate === null ||\n typeof candidate !== 'object' ||\n !('generation' in candidate) ||\n typeof candidate.generation !== 'number' ||\n !Number.isInteger(candidate.generation) ||\n candidate.generation < 0\n ) {\n return 0\n }\n if (generation !== undefined && generation !== candidate.generation) {\n return 0\n }\n generation = candidate.generation\n }\n return generation ?? 0\n }\n\n private generateUniqueId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n\n private setIsLoading(isLoading: boolean): void {\n this.isLoading = isLoading\n this.callbacksRef.current.onLoadingChange(isLoading)\n this.events.loadingChanged(isLoading)\n }\n\n private setStatus(status: ChatClientState): void {\n this.status = status\n this.callbacksRef.current.onStatusChange(status)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setIsSubscribed(isSubscribed: boolean): void {\n this.isSubscribed = isSubscribed\n this.callbacksRef.current.onSubscriptionChange(isSubscribed)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setConnectionStatus(status: ConnectionStatus): void {\n this.connectionStatus = status\n this.callbacksRef.current.onConnectionStatusChange(status)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setSessionGenerating(isGenerating: boolean): void {\n if (this.sessionGenerating === isGenerating) return\n this.sessionGenerating = isGenerating\n this.callbacksRef.current.onSessionGeneratingChange(isGenerating)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private notifyResumeStateChange(source: InterruptManagerChangeSource): void {\n const resumeState = this.getResumeState()\n // Capture state before invoking callbacks so a synchronous nested change\n // cannot pair this publication's source with a later manager snapshot.\n const interruptState = this.interruptManager.getState()\n // Persist (or clear) the durable resume snapshot so a full page reload can\n // rehydrate pending interrupts and rejoin the run. Folded into the same\n // persistence adapter that stores messages (one record per chat).\n this.persistResumeSnapshot(resumeState)\n this.callbacksRef.current.onResumeStateChange(\n resumeState,\n interruptState.interrupts,\n )\n this.callbacksRef.current.onInterruptStateChange(interruptState, { source })\n }\n\n /**\n * Build the durable resume snapshot from the current resume state + pending\n * interrupt descriptors and hand it to the persistor (null clears it).\n */\n private persistResumeSnapshot(resumeState: ChatResumeState | null): void {\n if (!this.persistor) return\n if (!resumeState) {\n this.persistor.persistResumeSnapshot(null)\n return\n }\n const descriptors = this.interruptManager.getDescriptors()\n this.persistor.persistResumeSnapshot({\n resumeState,\n ...(descriptors.length > 0\n ? { pendingInterrupts: [...descriptors] }\n : {}),\n })\n }\n\n private resetSessionGenerating(options?: {\n preserveClearedStreamTracking?: boolean\n }): void {\n this.activeRunIds.clear()\n if (!options?.preserveClearedStreamTracking) {\n this.clearedStreamTracker.resetActiveRuns()\n }\n this.setSessionGenerating(false)\n }\n\n private setError(error: Error | undefined): void {\n this.error = error\n this.callbacksRef.current.onErrorChange(error)\n this.events.errorChanged(error?.message || null)\n }\n\n private buildDevtoolsBridgeOptions(\n devtools: ChatClientOptions['devtools'],\n ): ChatDevtoolsBridgeOptions {\n const client = this\n return {\n get hookId() {\n return client.uniqueId\n },\n get clientId() {\n return client.uniqueId\n },\n get threadId() {\n return client.threadId\n },\n metadata: {\n hookName: devtools?.hookName ?? 'useChat',\n outputKind: devtools?.outputKind ?? 'chat',\n ...(devtools?.framework ? { framework: devtools.framework } : {}),\n ...(devtools?.name ? { name: devtools.name } : {}),\n },\n getSnapshot: () => this.getDevtoolsSnapshot(),\n getTools: () => this.clientToolsRef.current.values(),\n getMessages: () => this.processor.getMessages(),\n setMessages: (messages: Array<UIMessage>) => {\n this.processor.setMessages(messages)\n },\n addToolResult: (toolCallId, output, errorText) => {\n this.processor.addToolResult(toolCallId, output, errorText)\n },\n generateId: (prefix) => this.generateUniqueId(prefix),\n }\n }\n\n private getDevtoolsSnapshot(): AIDevtoolsChatSnapshot {\n return {\n // The devtools store keeps the objects it gets, and a subagent handle\n // changes in place. A copy lets the panel see each new status and part.\n messages: copyForDevtools(this.processor.getMessages()),\n status: this.status,\n isLoading: this.isLoading,\n isSubscribed: this.isSubscribed,\n connectionStatus: this.connectionStatus,\n sessionGenerating: this.sessionGenerating,\n activeRunIds: Array.from(this.activeRunIds),\n queue: this.getQueue(),\n ...(this.error ? { error: this.error.message } : {}),\n }\n }\n\n private findMessageIdForToolCall(toolCallId: string): string | undefined {\n const messages = this.processor.getMessages()\n for (const message of messages) {\n const match = message.parts.find(\n (part: MessagePart): part is ToolCallPart =>\n part.type === 'tool-call' && part.id === toolCallId,\n )\n if (match) return message.id\n }\n return undefined\n }\n\n private abortSubscriptionLoop(): void {\n this.subscriptionAbortController?.abort()\n this.subscriptionAbortController = null\n }\n\n private resolveProcessing(): void {\n this.processingResolve?.()\n this.processingResolve = null\n }\n\n private cancelInFlightStream(options?: {\n setReadyStatus?: boolean\n abortSubscription?: boolean\n }): void {\n this.abortController?.abort()\n this.abortController = null\n if (options?.abortSubscription) {\n this.abortSubscriptionLoop()\n }\n this.resolveProcessing()\n this.setIsLoading(false)\n // Release deliver claim so an interrupting `deliverMessage` can append\n // after abort (the superseded deliver's finally also clears the claim).\n this.deliverClaim = false\n if (options?.setReadyStatus) {\n this.setStatus('ready')\n }\n }\n\n private reportStreamError(error: Error): void {\n const alreadyReported =\n this.errorReportedGeneration === this.streamGeneration\n this.setError(error)\n // Preserve request-level error semantics even if a RUN_ERROR arrives\n // slightly after loading flips false during stream teardown.\n if (\n this.isLoading ||\n this.status === 'submitted' ||\n this.status === 'streaming'\n ) {\n this.setStatus('error')\n }\n if (!alreadyReported) {\n this.errorReportedGeneration = this.streamGeneration\n this.callbacksRef.current.onError(error)\n }\n }\n\n /**\n * Start the background subscription loop.\n */\n private startSubscription(): void {\n this.subscriptionAbortController = new AbortController()\n const signal = this.subscriptionAbortController.signal\n\n this.consumeSubscription(signal)\n .catch((err) => {\n if (err instanceof Error && err.name !== 'AbortError') {\n this.setConnectionStatus('error')\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.reportStreamError(err)\n }\n // Resolve pending processing so streamResponse doesn't hang\n this.resolveProcessing()\n })\n .finally(() => {\n // Ignore stale loops that were superseded by a restart.\n if (this.subscriptionAbortController?.signal !== signal) {\n return\n }\n this.subscriptionAbortController = null\n if (!signal.aborted && this.isSubscribed) {\n this.setIsSubscribed(false)\n if (this.connectionStatus !== 'error') {\n this.setConnectionStatus('disconnected')\n }\n }\n })\n }\n\n /**\n * Consume chunks from the connection subscription.\n */\n private async consumeSubscription(signal: AbortSignal): Promise<void> {\n const stream = this.connection.subscribe(signal)\n for await (const chunk of stream) {\n if (signal.aborted) break\n await this.processIncomingChunk(chunk)\n }\n }\n\n /**\n * Re-attach to an in-flight run after a full page reload, replaying its stream\n * from the server's delivery-durability log via `joinRun` (which returns the\n * whole run so far, then tails live to completion).\n *\n * The log is the single source of truth for the run, so we rebuild the\n * in-flight assistant bubble from it rather than trying to reconcile the\n * server-hydrated partial with the replay: on the first chunk that actually\n * (re)builds a message we drop the hydrated in-flight assistant, and the\n * replay reconstructs one clean bubble. Dropping only on real content (not on\n * `RUN_STARTED`) means a rejoin that connects but delivers nothing can never\n * leave an empty bubble behind.\n *\n * Bounded connect: a durable backend keeps a from-start join open waiting for\n * a producer, so a stale pointer to an unknown/evicted run would otherwise pin\n * the UI in a loading state for the backend's full first-chunk deadline. We\n * give up after {@link REJOIN_CONNECT_DEADLINE_MS} if no chunk arrives and\n * clear the dead pointer so it does not retry on the next load.\n *\n * Replay chunks are processed WITHOUT the per-chunk yield the live path uses,\n * so the buffered prefix snaps in and only the genuinely-live tail streams at\n * network speed — a reload looks like the run continued, not like it re-typed.\n */\n private resumeInFlightRun(runId: string): void {\n const joinRun = this.connection.joinRun\n if (!joinRun) return\n const controller = new AbortController()\n this.abortController = controller\n this.setCurrentRunId(runId)\n // Record the resume state in-memory BEFORE replaying. Otherwise the\n // replayed `RUN_STARTED` (which carries the PROVIDER run id, not the\n // client/durability-log run id the pointer is keyed by) trips the\n // `!this.lastResume` guard in `updateRunLifecycle` and rewrites the\n // persisted pointer with the provider id — so a SECOND reload would\n // `joinRun` an id the log isn't keyed by and never re-attach.\n this.lastResume = { threadId: this.threadId, runId }\n this.streamContinuationGeneration = this.continuationGeneration\n this.setIsLoading(true)\n this.setStatus('streaming')\n void (async () => {\n let rebuilt = false\n let attached = false\n // Whether the join FAILED (a thrown non-abort error before any chunk), as\n // opposed to merely not delivering in time. Only a failure proves the\n // pointer dead — see the `finally`.\n let refused = false\n const connectTimer = setTimeout(() => {\n if (!attached) controller.abort()\n }, REJOIN_CONNECT_DEADLINE_MS)\n try {\n for await (const chunk of joinRun(runId, controller.signal)) {\n if (controller.signal.aborted) break\n if (!attached) {\n attached = true\n clearTimeout(connectTimer)\n }\n if (!rebuilt && rebuildsAssistantMessage(chunk)) {\n rebuilt = true\n this.dropTrailingInFlightAssistant()\n }\n await this.processIncomingChunk(chunk, { defer: false })\n }\n // Same contract as `streamResponse`: client tools may finish (and\n // queue a resume) while `isLoading` is still true. Wait for them\n // before teardown so `drainPostStreamActions` below sees the queue.\n if (this.pendingToolExecutions.size > 0) {\n await Promise.all(this.pendingToolExecutions.values())\n }\n } catch (error) {\n // Pre-attach failures (unknown/evicted run, connect deadline abort)\n // stay soft: keep the restored transcript. Post-attach transport/parser\n // failures are real stream errors and must surface so the UI is not\n // left truncated and silent.\n const isAbort =\n error instanceof Error &&\n (error.name === 'AbortError' || error.name === 'TimeoutError')\n if (!attached && !isAbort) refused = true\n if (attached && !isAbort) {\n this.reportStreamError(\n error instanceof Error ? error : new Error(String(error)),\n )\n }\n } finally {\n clearTimeout(connectTimer)\n if (!attached && refused && this.tailing && !this.disposed) {\n // The server REFUSED the join (unknown / evicted run): the pointer is\n // dead. Clear it so it does not retry and re-pin the UI on the next\n // load. The server's persisted transcript is still loaded.\n //\n // A connect-deadline abort (or an external abort) deliberately does\n // NOT clear it: the run may simply not have produced yet — a durable\n // run whose middleware is still booting a sandbox emits nothing for\n // a while — and clearing on a timeout would permanently orphan a run\n // that is still going. The pointer survives for the next load, which\n // costs that load one more bounded connect attempt.\n //\n // `tailing`/`disposed` guard the same pointer from the other side: a\n // DETACH aborts before the first chunk exactly like an unreachable run\n // does, and a refusal that lands after the view is gone belongs to\n // nobody. `refused` already spares the timeout case; these two spare\n // the \"no view is watching any more\" case, so the pointer only ever\n // dies for a client that is still looking at the run.\n this.lastResume = null\n this.persistor?.persistResumeSnapshot(null)\n }\n if (this.abortController === controller) {\n this.abortController = null\n this.setIsLoading(false)\n if (this.status === 'streaming') this.setStatus('ready')\n await this.drainPostStreamActions()\n }\n }\n })()\n }\n\n /**\n * Drop a hydrated, still-in-flight assistant turn so a resume replay can\n * rebuild it cleanly. Only touches a trailing assistant message (the shape a\n * reload-mid-stream leaves); a thread whose last turn is a user message (run\n * never produced, or already settled) is left untouched.\n */\n private dropTrailingInFlightAssistant(): void {\n const messages = this.processor.getMessages()\n const last = messages[messages.length - 1]\n if (last && last.role === 'assistant') {\n this.processor.setMessages(messages.slice(0, -1))\n }\n }\n\n private async processIncomingChunk(\n chunk: StreamChunk,\n options?: { defer?: boolean },\n ): Promise<void> {\n chunk = restoreInboundChunk(chunk)\n if (\n chunk.type === 'RUN_ERROR' &&\n this.isActiveInterruptSubmissionFailure(chunk)\n ) {\n const interruptErrors = tanstackMetadata(chunk)?.interruptErrors\n this.interruptSubmissionFailure = {\n errors: Array.isArray(interruptErrors) ? interruptErrors : [],\n }\n }\n if (this.connectionStatus === 'connecting') {\n this.setConnectionStatus('connected')\n }\n const shouldIgnore = this.clearedStreamTracker.shouldIgnoreChunk(chunk)\n if (shouldIgnore) {\n if (chunk.type === 'RUN_FINISHED' || chunk.type === 'RUN_ERROR') {\n if (getChunkRunId(chunk)) {\n this.updateRunLifecycle(chunk, { resolveProcessing: false })\n } else {\n this.drainIgnoredRunlessChunk(chunk)\n }\n this.retireIgnoredClearedTerminalChunk(chunk)\n this.resolveJoinedRun(chunk)\n }\n return\n }\n this.callbacksRef.current.onChunk(chunk)\n this.devtoolsBridge.observeChunk(chunk)\n const attributedId = readSubagentRunId(chunk)\n // A resumed child starts again under the same id, so its start lifts the stop.\n if (attributedId && chunk.type === EventType.SUBAGENT_STARTED) {\n this.stoppedSubagentIds.delete(attributedId)\n }\n if (attributedId && this.stoppedSubagentIds.has(attributedId)) {\n this.updateRunLifecycle(chunk)\n this.resolveJoinedRun(chunk)\n return\n }\n this.processor.processChunk(chunk)\n this.syncSubagentHandles()\n this.updateRunLifecycle(chunk)\n this.observeInterruptState(chunk)\n // Live path: yield a macrotask so the UI can paint. Skip when the page is\n // hidden. Browsers clamp setTimeout there, and that wait paces stream pull.\n // Replay passes defer: false so a backlog applies in one batch.\n if (\n options?.defer !== false &&\n (typeof document === 'undefined' || !document.hidden)\n ) {\n await new Promise((resolve) => setTimeout(resolve, 0))\n }\n this.resolveJoinedRun(chunk)\n }\n\n private isActiveInterruptSubmissionFailure(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n ): boolean {\n const submission = this.activeInterruptSubmission\n const errors = tanstackMetadata(chunk)?.interruptErrors\n if (!submission || !Array.isArray(errors) || errors.length === 0) {\n return false\n }\n const runId = getChunkRunId(chunk)\n if (runId !== undefined && runId !== this.currentRunId) return false\n if (\n typeof chunk.threadId === 'string' &&\n chunk.threadId !== submission.threadId\n ) {\n return false\n }\n return errors.every((error) => {\n if (\n error == null ||\n typeof error !== 'object' ||\n typeof error.threadId !== 'string' ||\n typeof error.interruptedRunId !== 'string' ||\n typeof error.generation !== 'number'\n ) {\n return false\n }\n return (\n error.threadId === submission.threadId &&\n error.interruptedRunId === submission.interruptedRunId &&\n error.generation === submission.generation\n )\n })\n }\n\n private resolveJoinedRun(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') return\n const runId = getChunkRunId(chunk)\n if (runId === undefined) return\n const resolve = this.joinedRunWaiters.get(runId)\n if (resolve === undefined) return\n this.joinedRunWaiters.delete(runId)\n resolve()\n }\n\n /**\n * Ensure subscription loop is running, starting it if needed.\n */\n private ensureSubscription(): void {\n if (!this.isSubscribed) {\n this.subscribe()\n return\n }\n if (\n !this.subscriptionAbortController ||\n this.subscriptionAbortController.signal.aborted\n ) {\n this.subscribe({ restart: true })\n }\n }\n\n /**\n * Create a promise that resolves when onStreamEnd fires.\n * Used by streamResponse to await processing completion.\n */\n private waitForProcessing(): Promise<void> {\n // Resolve any stale promise (e.g., from a previous aborted request)\n this.resolveProcessing()\n return new Promise<void>((resolve) => {\n this.processingResolve = resolve\n })\n }\n\n /**\n * Send a message and stream the response.\n * Supports both simple string content and multimodal content (images, audio, video, documents).\n *\n * @param content - The message content. Can be:\n * - A simple string for text-only messages\n * - A MultimodalContent object with content array and optional custom ID\n * @param body - Optional body parameters to merge with the client's base body for this request.\n * Uses shallow merge with per-message body taking priority.\n * @param sendOptions - Per-call overrides. `{ whenBusy }` overrides the\n * queue policy for this one send. `{ body }`\n * shallow-merges with `body` and with the chat-level\n * `body` / `forwardedProps`. `sendOptions.body` wins\n * on key collisions. Framework hooks forward this\n * object as their second argument.\n *\n * @example\n * ```ts\n * // Simple text message\n * await client.sendMessage('Hello!')\n *\n * // Text message with custom body params\n * await client.sendMessage('Hello!', { temperature: 0.7 })\n *\n * // Per-call whenBusy override\n * await client.sendMessage('Urgent', undefined, { whenBusy: 'interrupt' })\n *\n * // Per-call body via options. Same effect as the positional arg.\n * // This is the shape the framework hooks (`useChat`, `injectChat`) forward.\n * await client.sendMessage('Hello!', undefined, { body: { temperature: 0.7 } })\n *\n * // Multimodal message with image\n * await client.sendMessage({\n * content: [\n * { type: 'text', content: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/photo.jpg' } }\n * ]\n * })\n *\n * // Multimodal message with custom ID and body params\n * await client.sendMessage(\n * {\n * content: [\n * { type: 'text', content: 'Describe this audio' },\n * { type: 'audio', source: { type: 'data', value: 'base64...' } }\n * ],\n * id: 'custom-message-id'\n * },\n * { model: 'gpt-5.5' }\n * )\n * ```\n */\n async sendMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n sendOptions?: SendMessageOptions,\n ): Promise<void> {\n this.mountDevtools()\n const emptyMessage = typeof content === 'string' && !content.trim()\n if (emptyMessage) {\n return\n }\n if (this.hasPendingInterrupts()) {\n throw new Error(\n 'ChatClient: cannot send normal input while pending interrupts exist. Use resumeInterrupts() instead.',\n )\n }\n\n const resolvedBody = { ...body, ...sendOptions?.body }\n\n if (this.isSendBusy()) {\n const { action, id } = this.decideWhenBusy(content, sendOptions)\n if (action === 'drop') {\n return\n }\n if (action === 'queue') {\n this.enqueueMessage(content, resolvedBody, id)\n return\n }\n // 'interrupt': abort the current stream, then send now.\n // Unlike stop(), does not flush already-queued messages — they drain\n // after this interrupting send settles successfully.\n // Claim sendInFlight *before* cancelling so a concurrent send cannot\n // slip in between cancel and the deliver below.\n this.stopMessageQueueDrain = true\n this.sendInFlight = true\n this.cancelInFlightStream({ setReadyStatus: true })\n this.resetSessionGenerating()\n } else {\n this.sendInFlight = true\n }\n\n try {\n await this.deliverMessage(content, resolvedBody)\n } finally {\n this.sendInFlight = false\n }\n }\n\n /** True while interrupt descriptors still own continuation. */\n private hasPendingInterrupts(): boolean {\n return this.interruptManager.getDescriptors().length > 0\n }\n\n /** True while a stream is active, a send is claiming the client, or the queue is draining. */\n private isSendBusy(): boolean {\n return this.isLoading || this.sendInFlight || this.messageQueueDraining\n }\n\n private resolveBusyReason(): QueueBusyReason {\n if (this.isLoading) return 'streaming'\n if (this.messageQueueDraining) return 'draining'\n return 'sendInFlight'\n }\n\n /**\n * Append a user message and run the stream. Used by both direct sends and\n * queue drains — callers are responsible for busy/queue policy.\n *\n * Claims delivery synchronously before appending so concurrent callers\n * cannot both add a user message when only one stream can run.\n */\n private async deliverMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n ): Promise<boolean> {\n if (this.isLoading || this.deliverClaim) {\n return false\n }\n this.deliverClaim = true\n try {\n const normalizedContent = this.normalizeMessageInput(content)\n this.pendingMessageBody = body\n const userMessage = this.processor.addUserMessage(\n normalizedContent.content,\n normalizedContent.id,\n normalizedContent.metadata,\n )\n this.events.messageSent(userMessage.id, normalizedContent.content)\n return await this.streamResponse()\n } finally {\n this.deliverClaim = false\n }\n }\n\n /**\n * Resolve the effective action for a send that arrives while busy.\n * The returned `id` is the id that will be stored if the action is `queue`.\n */\n private decideWhenBusy(\n content: string | MultimodalContent,\n sendOptions?: SendMessageOptions,\n ): { action: WhenBusy; id: string } {\n const id = this.generateUniqueId('queued')\n if (sendOptions?.whenBusy) {\n return { action: sendOptions.whenBusy, id }\n }\n const { strategy, whenBusy } = this.queueConfig\n if (strategy) {\n const { action } = strategy({\n pending: {\n id,\n content,\n createdAt: Date.now(),\n },\n busyReason: this.resolveBusyReason(),\n queued: this.getQueue(),\n })\n return { action, id }\n }\n return { action: whenBusy, id }\n }\n\n private enqueueMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n id?: string,\n ): void {\n const { maxSize, onOverflow } = this.queueConfig\n if (maxSize !== undefined && this.messageQueue.length >= maxSize) {\n // maxSize 0 is a hard cap (never queue). drop-oldest cannot make room.\n if (onOverflow === 'reject' || maxSize === 0) {\n return\n }\n this.messageQueue.shift() // drop-oldest\n }\n this.messageQueue.push({\n id: id ?? this.generateUniqueId('queued'),\n content,\n createdAt: Date.now(),\n ...(body !== undefined ? { body } : {}),\n })\n this.emitQueueChange()\n }\n\n /**\n * Normalize the message input to extract content, optional id, and\n * optional metadata. String form has no metadata. Trims string content.\n */\n private normalizeMessageInput(input: string | MultimodalContent): {\n content: string | Array<ContentPart>\n id?: string\n metadata?: Record<string, any>\n } {\n if (typeof input === 'string') {\n return { content: input.trim() }\n }\n return {\n content: input.content,\n id: input.id,\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n }\n }\n\n /**\n * Append a message and stream the response\n */\n async append(message: UIMessage | ModelMessage): Promise<void> {\n this.mountDevtools()\n if (this.hasPendingInterrupts()) {\n throw new Error(\n 'ChatClient: cannot append normal input while pending interrupts exist. Use resumeInterrupts() instead.',\n )\n }\n // Normalize the message to ensure it has id and createdAt\n const normalizedMessage = normalizeToUIMessage(message, generateMessageId)\n\n // Skip system messages - they're handled via systemPrompts, not UIMessages\n if (normalizedMessage.role === 'system') {\n return\n }\n\n // Type assertion: after checking for system, we know it's user or assistant\n const uiMessage = normalizedMessage as UIMessage\n\n // Emit message appended event\n this.events.messageAppended(uiMessage)\n\n // Add to messages\n const messages = this.processor.getMessages()\n this.processor.setMessages([...messages, uiMessage])\n this.devtoolsBridge.emitSnapshot()\n\n // If stream is in progress, queue the response for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(async () => {\n await this.streamResponse()\n })\n return\n }\n\n await this.streamResponse()\n }\n\n /**\n * Stream a response from the LLM.\n * Returns true if the stream completed successfully, false on abort or error.\n */\n private async streamResponse(): Promise<boolean> {\n // Guard against concurrent streams - if already loading, skip\n if (this.isLoading) {\n return false\n }\n\n // Track generation so a superseded stream's cleanup doesn't clobber the new one\n const generation = ++this.streamGeneration\n this.streamContinuationGeneration = this.continuationGeneration\n // Native interrupt continuation is a fresh child run. The interrupted run\n // is carried as parentRunId and the complete resolution batch as resume.\n const resumeThreadId = this.pendingResumeThreadId\n const resumeParentRunId = this.pendingResumeParentRunId\n const resumeItems = this.pendingResumeItems\n this.pendingResumeThreadId = null\n this.pendingResumeParentRunId = null\n this.pendingResumeItems = null\n const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n this.setCurrentRunId(runId)\n this.activeResumeThreadId = resumeThreadId ?? this.threadId\n this.activeResumeRunId = runId\n\n this.setIsLoading(true)\n // Hand off from deliverClaim to isLoading so nested drain can call\n // deliverMessage after this stream settles (while the outer deliver\n // is still on the stack).\n this.deliverClaim = false\n this.setStatus('submitted')\n this.setError(undefined)\n this.errorReportedGeneration = null\n this.abortController = new AbortController()\n // Capture the signal immediately so that a concurrent stop() or\n // sendMessage() that reassigns this.abortController cannot cause\n // connect() to receive a stale or null signal.\n const signal = this.abortController.signal\n // Reset pending tool executions for the new stream\n this.pendingToolExecutions.clear()\n let streamCompletedSuccessfully = false\n let activeDevtoolsRunId: string | null = null\n let runTerminalEventEmitted = false\n\n try {\n // Get UIMessages with parts (preserves approval state and client tool results)\n const messages = this.messagesForSend(this.processor.getMessages())\n const clientTools = new Map(this.clientToolsRef.current)\n const runtimeContext = this.context\n\n // Call onResponse callback\n await this.callbacksRef.current.onResponse()\n\n // If the stream was cancelled during the onResponse await (e.g. stop()\n // from a callback or unmount, or reload() superseding this stream),\n // bail out before allocating waitForProcessing() — otherwise the\n // resolveProcessing() that ran during cancellation is a no-op and the\n // await processingComplete below would deadlock.\n if (signal.aborted) {\n return false\n }\n\n // Merge sources for the wire `forwardedProps` field, in priority\n // order (later spreads win):\n // 1. Legacy `body` option (deprecated).\n // 2. Canonical `forwardedProps` option (wins over `body`).\n // 3. Per-call body (`pendingMessageBody`: positional + sendOptions.body).\n // The AG-UI standard `threadId` is sent at the wire's top level for\n // run/conversation correlation, so we no longer auto-emit a separate\n // `conversationId` here — `chat({ threadId })` server-side covers the\n // same role for devtools/observability.\n const mergedBody = {\n ...this.bodyOption,\n ...this.forwardedPropsOption,\n ...this.pendingMessageBody,\n }\n\n // Clear the pending message body after use\n this.pendingMessageBody = undefined\n\n // Generate stream ID — assistant message will be created by stream events\n this.currentStreamId = this.generateUniqueId('stream')\n this.devtoolsBridge.setCurrentStreamId(this.currentStreamId)\n this.currentMessageId = null\n this.activeClientTools = clientTools\n this.activeContext = runtimeContext\n\n // Reset processor stream state for new response — prevents stale\n // messageStates entries (from a previous stream) from blocking\n // creation of a new assistant message (e.g. after reload).\n this.processor.prepareAssistantMessage()\n\n // Ensure subscription loop is running\n this.ensureSubscription()\n\n // Set up promise that resolves when onStreamEnd fires\n const processingComplete = this.waitForProcessing()\n\n // Build per-send run context for AG-UI compliance\n // Note: mergedBody already contains the merged this.body + pendingMessageBody\n // (pendingMessageBody was cleared above, so we use mergedBody as forwardedProps)\n // Convert each client tool's `inputSchema` (a Standard Schema:\n // Zod, ArkType, Valibot, etc.) to JSON Schema for the wire. Foreign\n // AG-UI servers consuming `RunAgentInput.tools[].parameters` expect\n // JSON Schema; sending a Standard Schema instance directly would\n // serialize to an unusable shape.\n let byokHeaders: Record<string, string> | undefined\n if (this.byok) {\n const provider = resolveByokProviderId(\n this.byokProvider,\n mergedBody.provider,\n byokFallbackProviderId(this.byok),\n )\n byokHeaders = await prepareResolvedByokHeaders(this.byok, provider)\n }\n\n const runContext = {\n threadId: resumeThreadId ?? this.threadId,\n runId,\n ...(resumeParentRunId !== null\n ? { parentRunId: resumeParentRunId }\n : {}),\n clientTools: Array.from(clientTools.values()).map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema\n ? convertSchemaToJsonSchema(t.inputSchema)\n : { type: 'object' },\n })),\n forwardedProps: { ...mergedBody },\n ...(resumeItems ? { resume: resumeItems } : {}),\n ...(byokHeaders ? { headers: byokHeaders } : {}),\n }\n this.devtoolsBridge.beginRun(runContext.runId, runContext.threadId)\n activeDevtoolsRunId = runContext.runId\n this.devtoolsBridge.emitRunLifecycle(\n 'run:created',\n runContext.runId,\n 'created',\n )\n this.devtoolsBridge.emitRunLifecycle(\n 'run:started',\n runContext.runId,\n 'started',\n )\n this.devtoolsBridge.emitSnapshot()\n\n // Send through normalized connection (pushes chunks to subscription queue)\n await this.connection.send(messages, mergedBody, signal, runContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- mutated asynchronously during await\n if (generation !== this.streamGeneration || signal.aborted) {\n return false\n }\n\n // connect() send() already waited until the subscribe queue was idle.\n // Kick the processing wait so a stream that ends on tool_calls (no\n // interrupt / stop) cannot hang. Subscribe/send sockets still wait for\n // a request-ending terminal below.\n if (this.connectionDrainsOnSend) {\n this.resolveProcessing()\n }\n\n // Wait for subscription loop to finish processing all chunks\n await processingComplete\n\n // If this stream was superseded (e.g. by reload()), bail out —\n // the new stream owns the processor and processingResolve now.\n if (generation !== this.streamGeneration) {\n return false\n }\n\n // A RUN_ERROR from the stream transitions status to error.\n // Do not treat this stream as a successful completion.\n if (this.status === 'error') {\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:errored',\n activeDevtoolsRunId,\n 'errored',\n this.error ? { error: this.error.message } : {},\n )\n runTerminalEventEmitted = true\n }\n return false\n }\n\n // Wait for pending client tool executions\n if (this.pendingToolExecutions.size > 0) {\n await Promise.all(this.pendingToolExecutions.values())\n }\n\n // Finalize (idempotent — may already be done by RUN_FINISHED handler)\n this.processor.finalizeStream()\n streamCompletedSuccessfully = true\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err))\n if (error.name === 'AbortError') {\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:cancelled',\n activeDevtoolsRunId,\n 'cancelled',\n )\n runTerminalEventEmitted = true\n }\n return false\n }\n if (error instanceof ByokMissingError) {\n this.byok?.request(error.provider, 'missing')\n }\n if (error instanceof ByokBlockedError && error.reason === 'locked') {\n this.byok?.request(error.provider, 'locked')\n }\n if (generation === this.streamGeneration) {\n this.reportStreamError(error)\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:errored',\n activeDevtoolsRunId,\n 'errored',\n { error: error.message },\n )\n runTerminalEventEmitted = true\n }\n }\n if (\n generation === this.streamGeneration &&\n (error instanceof ByokMissingError ||\n error instanceof ByokBlockedError ||\n error instanceof ByokUnresolvedProviderError)\n ) {\n throw error\n }\n } finally {\n // Only clean up if this is still the active stream.\n // A superseded stream (e.g. reload() started a new one) must not\n // clobber the new stream's abortController or isLoading state.\n if (generation === this.streamGeneration) {\n this.currentStreamId = null\n this.devtoolsBridge.setCurrentStreamId(null)\n this.currentMessageId = null\n this.setCurrentRunId(null)\n this.activeClientTools = null\n this.activeContext = undefined\n this.abortController = null\n this.setIsLoading(false)\n this.pendingMessageBody = undefined // Ensure it's cleared even on error\n\n if (activeDevtoolsRunId && !runTerminalEventEmitted) {\n if (streamCompletedSuccessfully) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:completed',\n activeDevtoolsRunId,\n 'completed',\n )\n } else if (signal.aborted) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:cancelled',\n activeDevtoolsRunId,\n 'cancelled',\n )\n }\n }\n\n // Drain any actions that were queued while the stream was in progress\n await this.drainPostStreamActions()\n\n if (streamCompletedSuccessfully) {\n this.rememberProcessorMessageIds()\n if (this.status !== 'ready') {\n // Terminal run, but onStreamEnd never fired: the processor had\n // no assistant message to emit it for (e.g. a bare\n // RUN_FINISHED{stop}, #421). The normal path already set\n // 'ready', so this is a no-op.\n this.setStatus('ready')\n }\n // Auto-send queued messages once the run fully settles. Skip if a\n // drain loop is already walking the queue (avoids nested re-entry).\n if (!this.messageQueueDraining) {\n await this.drainQueue()\n }\n } else {\n // Error/abort settle for the active generation: don't strand or\n // later mis-order queued messages. A failed turn flushes the queue\n // (consistent with stop()); it must NOT auto-drain into a likely\n // broken endpoint.\n this.flushQueue()\n }\n }\n }\n\n return streamCompletedSuccessfully\n }\n\n /**\n * Start the client subscription loop.\n * This controls the connection lifecycle independently from request lifecycle.\n */\n subscribe(options?: { restart?: boolean }): void {\n const restart = options?.restart === true\n if (this.isSubscribed && !restart) {\n return\n }\n\n if (this.isSubscribed && restart) {\n this.abortSubscriptionLoop()\n }\n\n this.setIsSubscribed(true)\n this.setConnectionStatus('connecting')\n this.startSubscription()\n }\n\n /**\n * Unsubscribe and fully tear down live behavior.\n * This aborts an in-flight request and the subscription loop.\n */\n unsubscribe(): void {\n this.cancelInFlightStream({\n setReadyStatus: true,\n abortSubscription: true,\n })\n this.discardPendingSends()\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.setConnectionStatus('disconnected')\n }\n\n /**\n * Reload the last assistant message\n */\n async reload(): Promise<void> {\n const messages = this.processor.getMessages()\n if (messages.length === 0) return\n\n // Find the last user message\n const lastUserMessageIndex = messages.findLastIndex(\n (m) => m.role === 'user',\n )\n\n if (lastUserMessageIndex === -1) return\n\n // Cancel any active stream before reloading\n if (this.isLoading) {\n this.cancelInFlightStream()\n }\n // Discard pending follow-ups so \"regenerate last answer\" does not also\n // auto-send messages that were typed during the previous stream.\n this.discardPendingSends()\n\n this.events.reloaded(lastUserMessageIndex)\n\n // Remove all messages after the last user message\n this.processor.removeMessagesAfter(lastUserMessageIndex)\n this.devtoolsBridge.emitSnapshot()\n\n // Resend\n await this.streamResponse()\n }\n\n /**\n * Stop the current stream\n */\n stop(): void {\n // Invalidate deferred work from the stopped continuation.\n // This aborts the local request. A durable server run keeps going.\n this.continuationGeneration++\n const hadLocalStream = this.abortController !== null\n this.cancelInFlightStream({ setReadyStatus: true })\n this.discardPendingSends()\n this.lastResume = null\n this.activeInterruptSubmission = undefined\n this.interruptManager.reset()\n if (hadLocalStream) {\n this.resetSessionGenerating()\n }\n this.events.stopped()\n }\n\n /**\n * Clear all messages\n */\n clear(): void {\n const hadLocalStream = this.abortController !== null\n this.clearedStreamTracker.snapshotClear({\n messages: this.processor.getMessages(),\n activeRunIds: this.activeRunIds,\n currentRunId: this.currentRunId,\n })\n // Always cancel in-flight work so clear works without message persistence.\n if (this.isLoading || hadLocalStream) {\n this.cancelInFlightStream({ setReadyStatus: true })\n this.resetSessionGenerating({ preserveClearedStreamTracking: true })\n } else if (this.activeRunIds.size > 0) {\n this.resetSessionGenerating({ preserveClearedStreamTracking: true })\n }\n // Suppress persisting the empty snapshot that clearMessages emits, then\n // remove the stored conversation outright.\n this.persistor?.beginClear()\n this.processor.clearMessages()\n this.resetHistoryPaging()\n this.discardPendingSends()\n this.persistor?.remove()\n this.stoppedSubagentIds.clear()\n this.lastResume = null\n this.interruptManager.reset()\n this.pendingResumeThreadId = null\n this.pendingResumeParentRunId = null\n this.pendingResumeItems = null\n this.setError(undefined)\n this.events.messagesCleared()\n }\n\n /**\n * Add the result of a client-side tool execution\n */\n async addToolResult(result: ClientToolResult): Promise<void> {\n const clientTool = this.clientToolsRef.current.get(result.tool)\n await this.addToolResultForClientTool(\n result,\n clientTool,\n this.streamContinuationGeneration,\n )\n }\n\n private async addToolResultForClientTool(\n result: ClientToolResult,\n clientTool: AnyClientTool | undefined,\n continuationGeneration: number,\n context?: ChatClientRunEventContext,\n ): Promise<void> {\n if (clientTool && result.state !== 'output-error') {\n try {\n result = {\n ...result,\n output: this.validateClientToolOutput(clientTool, result.output),\n }\n } catch (error: any) {\n result = {\n ...result,\n output: null,\n state: 'output-error',\n errorText: error.message,\n }\n }\n }\n\n this.events.toolResultAdded(\n result.toolCallId,\n result.tool,\n result.output,\n result.state || 'output-available',\n context,\n )\n\n if (continuationGeneration !== this.continuationGeneration) return\n\n // Always update local message state so the tool-call part is terminal in\n // the UI even when the AG-UI interrupt path owns server continuation.\n this.processor.addToolResult(\n result.toolCallId,\n result.output,\n result.state === 'output-error'\n ? result.errorText || 'Tool execution failed'\n : undefined,\n )\n this.devtoolsBridge.emitSnapshot()\n\n const resolvedViaInterrupt = this.interruptManager.resolveClientToolOutput(\n result.toolCallId,\n result.state === 'output-error'\n ? { error: result.errorText || 'Tool execution failed' }\n : result.output,\n )\n if (resolvedViaInterrupt) {\n // Interrupt manager stages/submits the resume batch (deferred until the\n // parent stream settles when still loading). Skip legacy continuation.\n return\n }\n\n // If stream is in progress, queue continuation check for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(() =>\n continuationGeneration === this.continuationGeneration\n ? this.checkForContinuation()\n : Promise.resolve(),\n )\n return\n }\n\n await this.checkForContinuation()\n }\n\n private validateClientToolOutput(\n clientTool: AnyClientTool,\n output: any,\n ): any {\n if (clientTool.outputSchema && isStandardSchema(clientTool.outputSchema)) {\n return parseWithStandardSchema(clientTool.outputSchema, output)\n }\n\n return output\n }\n\n /**\n * Respond to a tool approval request\n */\n async addToolApprovalResponse(response: {\n id: string // approval.id, not toolCallId\n approved: boolean\n }): Promise<void> {\n // Reflect the decision on the tool-call part so approval UIs that render\n // from `part.state` (the deprecated pre-interrupt pattern) clear the prompt\n // and show the response. The bound interrupt resolution below drives the\n // actual continuation; this keeps the legacy message-state surface in sync.\n this.processor.addToolApprovalResponse(response.id, response.approved)\n this.devtoolsBridge.emitSnapshot()\n\n if (\n this.interruptManager.resolveToolApprovalDecision(\n response.id,\n response.approved,\n )\n ) {\n return\n }\n // Find the tool call ID from the approval ID\n const messages = this.processor.getMessages()\n let foundToolCallId: string | undefined\n\n for (const msg of messages) {\n const toolCallPart = msg.parts.find(\n (p: MessagePart): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === response.id,\n )\n if (toolCallPart) {\n foundToolCallId = toolCallPart.id\n break\n }\n }\n\n if (foundToolCallId) {\n this.events.toolApprovalResponded(\n response.id,\n foundToolCallId,\n response.approved,\n )\n }\n\n // Add response via processor\n this.processor.addToolApprovalResponse(response.id, response.approved)\n this.devtoolsBridge.emitSnapshot()\n\n // If stream is in progress, queue continuation check for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(() => this.checkForContinuation())\n return\n }\n\n await this.checkForContinuation()\n }\n\n /**\n * Queue an action to be executed after the current stream ends\n */\n private queuePostStreamAction(action: () => Promise<void>): void {\n const continuationGeneration = this.continuationGeneration\n this.postStreamActions.push(async () => {\n if (continuationGeneration !== this.continuationGeneration) return\n await action()\n })\n }\n\n /**\n * Drain and execute all queued post-stream actions\n */\n private async drainPostStreamActions(): Promise<void> {\n if (this.draining) return\n this.draining = true\n try {\n let action: (() => Promise<void>) | undefined\n while ((action = this.postStreamActions.shift()) !== undefined) {\n await action()\n }\n } finally {\n this.draining = false\n }\n }\n\n /**\n * Check if we should continue the flow and do so if needed\n */\n private async checkForContinuation(): Promise<void> {\n // stop() bumps continuationGeneration without opening a new stream.\n if (this.streamContinuationGeneration !== this.continuationGeneration) {\n return\n }\n if (this.hasPendingInterrupts()) return\n\n // Prevent duplicate continuation attempts\n if (this.continuationPending || this.isLoading) {\n this.continuationSkipped = true\n return\n }\n\n if (this.shouldAutoSend()) {\n this.continuationPending = true\n this.continuationSkipped = false\n let succeeded = false\n try {\n succeeded = await this.streamResponse()\n } finally {\n this.continuationPending = false\n }\n // If a queued check was skipped while continuationPending was true\n // (e.g. a chained approval responded to during the stream), re-evaluate\n // now that the flag is cleared. Only replay after a successful stream —\n // aborted or errored streams should not trigger further continuation.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- mutated asynchronously during await\n if (this.continuationSkipped && succeeded) {\n this.continuationSkipped = false\n await this.checkForContinuation()\n }\n }\n }\n\n /**\n * Check if all tool calls are complete and we should auto-send.\n * Requires that there is at least one tool call in the last assistant message;\n * a text-only response has nothing to auto-send.\n */\n private shouldAutoSend(): boolean {\n // A pending interrupt owns the next send. Auto-continuing after a\n // completed server tool would start a sibling run and hide the card.\n if (this.lastResume) return false\n // Ownership follows the descriptors, not the submission handle. Generic\n // interrupts settle the resume stream through a post-stream action that\n // runs before `submitInterruptBatch`'s `finally` clears the handle, so\n // gating on the handle alone would strand a legacy client tool that the\n // native resume itself emitted (#1106).\n if (this.activeInterruptSubmission && this.hasPendingInterrupts()) {\n return false\n }\n if (this.interruptManager.getInterrupts().length > 0) return false\n const messages = this.processor.getMessages()\n const lastAssistant = messages.findLast(\n (m: UIMessage) => m.role === 'assistant',\n )\n if (!lastAssistant) return false\n const hasToolCalls = lastAssistant.parts.some(\n (p: MessagePart) => p.type === 'tool-call',\n )\n if (!hasToolCalls) return false\n return this.processor.areAllToolsComplete()\n }\n\n /**\n * Get current messages\n */\n getMessages(): Array<UIMessage<TTools>> {\n return this.processor.getMessages() as Array<UIMessage<TTools>>\n }\n\n getSubagents() {\n return [...this.subagentHandles.values()]\n }\n\n private devtoolsSnapshotQueued = false\n\n /** One devtools snapshot per microtask, however many chunks arrive. */\n private queueDevtoolsSnapshot(): void {\n if (this.devtoolsSnapshotQueued) return\n this.devtoolsSnapshotQueued = true\n queueMicrotask(() => {\n this.devtoolsSnapshotQueued = false\n this.devtoolsBridge.emitSnapshot()\n })\n }\n\n private syncSubagentHandles(): void {\n // Every card, nested ones included, gets a handle.\n const cards = collectSubagentParts(this.processor.getMessages())\n const present = new Set(cards.map((part) => part.subagent.id))\n // A card that left the messages (clear, reload) loses its handle.\n for (const id of this.subagentHandles.keys()) {\n if (!present.has(id)) this.subagentHandles.delete(id)\n }\n for (const part of cards) {\n const id = part.subagent.id\n const existing = this.subagentHandles.get(id)\n if (existing) {\n if (part.subagent === existing) continue\n // Keep the same live object, with exactly the card's fields. The\n // wire reads this object, so a field the card dropped goes too.\n const { stop } = existing\n for (const key of Object.keys(existing)) {\n Reflect.deleteProperty(existing, key)\n }\n Object.assign(existing, part.subagent, { stop })\n part.subagent = existing\n continue\n }\n const handle: SubagentHandle = {\n ...part.subagent,\n stop: () => {\n this.stopSubagent(id)\n },\n }\n this.subagentHandles.set(id, handle)\n part.subagent = handle\n }\n }\n\n private stopSubagent(id: string): void {\n const handle = this.subagentHandles.get(id)\n if (!handle || handle.status !== 'running') return\n this.stoppedSubagentIds.add(id)\n this.processor.processChunk({\n type: EventType.SUBAGENT_ERROR,\n subagentRunId: id,\n message: 'Stopped',\n timestamp: Date.now(),\n })\n this.syncSubagentHandles()\n handle.status = 'error'\n handle.error = { message: 'Stopped' }\n this.abortController?.abort()\n }\n\n /**\n * True when the last hydrate or older-page response said more messages exist.\n */\n getHasOlderMessages() {\n return this.hasOlderMessages\n }\n\n /**\n * Fetch the next older window and put it in front of the painted messages.\n *\n * No-op when there is no older page, no cursor, or a page load is already\n * running. A network failure rejects and leaves the painted messages as they\n * are; {@link getHasOlderMessages} stays true.\n */\n async loadOlderMessages() {\n const hydrate = this.connection.hydrate\n if (hydrate === undefined) return\n if (this.disposed) return\n if (!this.hasOlderMessages) return\n const cursor = this.olderMessagesCursor\n if (cursor === undefined) return\n if (this.loadOlderMessagesInFlight) return\n\n const hydrateOptions: ChatHydrateOptions = { before: cursor }\n if (this.historyPageSize !== undefined) {\n hydrateOptions.limit = this.historyPageSize\n }\n\n this.loadOlderMessagesInFlight = true\n const generation = this.historyGeneration\n try {\n const result = await hydrate(this.threadId, hydrateOptions)\n if (this.disposed) return\n if (generation !== this.historyGeneration) return\n const olderMessages = normalizeMessagesDates(result.messages)\n if (olderMessages.length === 0) {\n throw new Error('Older page was empty')\n }\n this.applyHydrationPage(result.page)\n this.processor.prependMessages(olderMessages)\n this.rememberServerMessageIds(olderMessages)\n } finally {\n this.loadOlderMessagesInFlight = false\n }\n }\n\n private resetHistoryPaging() {\n this.hasOlderMessages = false\n this.olderMessagesCursor = undefined\n this.knownServerMessageIds.clear()\n this.historyGeneration++\n }\n\n private applyHydrationPage(page: ChatHydrationResult['page']) {\n if (page?.truncated === true) {\n this.hasOlderMessages = true\n this.olderMessagesCursor = page.cursor\n return\n }\n this.hasOlderMessages = false\n this.olderMessagesCursor = undefined\n }\n\n private rememberServerMessageIds(messages: Array<UIMessage>) {\n const ids = messages.map((message) => message.id)\n for (const id of ids) {\n this.knownServerMessageIds.add(id)\n }\n }\n\n private rememberProcessorMessageIds() {\n if (this.historyPageSize === undefined) return\n this.rememberServerMessageIds(this.processor.getMessages())\n }\n\n private messagesForSend(messages: Array<UIMessage>) {\n if (this.historyPageSize === undefined) {\n return messages\n }\n const unknownMessages: Array<UIMessage> = []\n for (const message of messages) {\n const isKnown = this.knownServerMessageIds.has(message.id)\n if (isKnown) {\n continue\n }\n unknownMessages.push(message)\n }\n if (unknownMessages.length > 0) {\n return unknownMessages\n }\n // No new ids: reload has already dropped the old assistant, so this is\n // `[lastUser]`. Resume/continue still has the assistant, so the cutoff is\n // that assistant and the stored tool-call stays.\n const lastUserIndex = messages.findLastIndex(\n (message) => message.role === 'user',\n )\n if (lastUserIndex === -1) {\n return []\n }\n return messages.slice(lastUserIndex)\n }\n\n /**\n * True when an interrupt (or another direct send) claimed the client during\n * a drain. Read via a method so cross-await mutations are not constant-folded\n * by control-flow analysis.\n */\n private shouldAbortMessageQueueDrain(): boolean {\n return this.isLoading || this.stopMessageQueueDrain\n }\n\n /**\n * Deliver queued messages after a successful settle.\n * - `batch`: merge everything currently queued into one send, looping so\n * messages enqueued during that batch stream are not stranded.\n * - `fifo`: walk the queue in a loop, one stream at a time, until empty\n * (or until another send claims the client via interrupt).\n *\n * Uses `deliverMessage` directly so drains do not re-enter `sendMessage`'s\n * busy/queue policy (which would re-queue items and strand the rest).\n */\n private async drainQueue(): Promise<void> {\n // Note: do not gate on `sendInFlight`. Normal sends still hold\n // `sendInFlight` while `streamResponse`'s finally invokes drain; blocking\n // on it would permanently strand the queue.\n if (\n this.messageQueueDraining ||\n this.isLoading ||\n this.messageQueue.length === 0\n ) {\n return\n }\n\n this.messageQueueDraining = true\n this.stopMessageQueueDrain = false\n try {\n if (this.queueConfig.drain === 'batch') {\n while (this.messageQueue.length > 0) {\n if (this.shouldAbortMessageQueueDrain()) {\n return\n }\n const items = this.messageQueue.splice(0)\n this.emitQueueChange()\n const merged = mergeQueuedMessages(items)\n const completed = await this.deliverMessage(\n merged.content,\n merged.body,\n )\n // Failed/aborted deliver flushes the rest of the queue in streamResponse.\n if (!completed || this.shouldAbortMessageQueueDrain()) {\n return\n }\n }\n return\n }\n\n while (this.messageQueue.length > 0) {\n // Interrupt (or a new direct send) claimed the client — stop draining;\n // remaining items stay queued and will drain after that send settles.\n if (this.shouldAbortMessageQueueDrain()) {\n return\n }\n const next = this.messageQueue.shift()\n if (next === undefined) {\n return\n }\n this.emitQueueChange()\n const completed = await this.deliverMessage(next.content, next.body)\n // Failed/aborted deliver flushes the rest of the queue in streamResponse.\n if (!completed || this.shouldAbortMessageQueueDrain()) {\n return\n }\n }\n } finally {\n this.messageQueueDraining = false\n this.stopMessageQueueDrain = false\n }\n }\n\n /**\n * Drop any in-flight send claim and discard pending queued messages\n * (stop / error / clear / unsubscribe / reload).\n */\n private discardPendingSends(): void {\n this.sendInFlight = false\n this.flushQueue()\n }\n\n /**\n * Get the current send queue (messages held while a stream was in flight).\n */\n getQueue(): Array<QueuedMessage> {\n return this.messageQueue.map(({ id, content, createdAt }) => ({\n id,\n content,\n createdAt,\n }))\n }\n\n private emitQueueChange(): void {\n this.callbacksRef.current.onQueueChange(this.getQueue())\n this.devtoolsBridge.emitSnapshot()\n }\n\n /**\n * Remove a queued message by id before it drains.\n */\n cancelQueued(id: string): void {\n const index = this.messageQueue.findIndex((m) => m.id === id)\n if (index === -1) return\n this.messageQueue.splice(index, 1)\n this.emitQueueChange()\n }\n\n /**\n * Discard all pending queued messages (stop / error / clear / unsubscribe /\n * reload). Does not send them. Emits `onQueueChange([])` when anything was\n * removed.\n */\n private flushQueue(): void {\n if (this.messageQueue.length === 0) return\n this.messageQueue = []\n this.emitQueueChange()\n }\n\n /**\n * Get loading state\n */\n getIsLoading(): boolean {\n return this.isLoading\n }\n\n /**\n * Get current status\n */\n getStatus(): ChatClientState {\n return this.status\n }\n\n /**\n * Get whether the subscription loop is active\n */\n getIsSubscribed(): boolean {\n return this.isSubscribed\n }\n\n /**\n * Get current connection lifecycle status\n */\n getConnectionStatus(): ConnectionStatus {\n return this.connectionStatus\n }\n\n /**\n * Whether the shared session is actively generating.\n * Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).\n * Unlike `isLoading` (request-local), this reflects shared generation\n * activity visible to all subscribers (e.g. across tabs/devices).\n */\n getSessionGenerating(): boolean {\n return this.sessionGenerating\n }\n\n /**\n * Get current error\n */\n getError(): Error | undefined {\n return this.error\n }\n\n /**\n * Manually set messages\n */\n setMessagesManually(messages: Array<UIMessage<TTools>>): void {\n this.processor.setMessages(messages)\n this.devtoolsBridge.emitSnapshot()\n }\n\n /**\n * Update options refs (for use in React hooks to avoid recreating client)\n */\n updateOptions(options: ChatClientUpdateOptionsWithoutContext<TTools>): void\n updateOptions(\n options: ChatClientUpdateOptionsWithoutContext<TTools> &\n Pick<ChatClientOptions<TTools, TContext>, 'context'>,\n ): void\n updateOptions(\n options: ChatClientUpdateOptionsWithoutContext<TTools> & {\n context?: TContext | undefined\n },\n ): void {\n if (options.connection !== undefined || options.fetcher !== undefined) {\n const wasSubscribed = this.isSubscribed\n\n if (this.isLoading) {\n this.cancelInFlightStream({\n setReadyStatus: true,\n abortSubscription: true,\n })\n } else if (wasSubscribed) {\n this.abortSubscriptionLoop()\n }\n\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.setConnectionStatus('disconnected')\n const transport = resolveTransport({\n connection: options.connection,\n fetcher: options.fetcher,\n })\n this.connectionDrainsOnSend = connectionDrainsOnSend(transport)\n this.connection = normalizeConnectionAdapter(transport)\n\n if (wasSubscribed) {\n this.subscribe()\n }\n }\n // Replace each wire-payload slot independently so callers can update one\n // without wiping the other. Passing `undefined` for `body` or\n // `forwardedProps` leaves that slot unchanged; context is cleared when the\n // key is present with an `undefined` value.\n if (options.body !== undefined) {\n this.bodyOption = options.body\n }\n if (options.forwardedProps !== undefined) {\n this.forwardedPropsOption = options.forwardedProps\n }\n if (options.byok !== undefined) {\n this.byok = options.byok\n }\n if (options.byokProvider !== undefined) {\n this.byokProvider = options.byokProvider\n }\n if ('context' in options) {\n this.context = options.context\n }\n if (options.tools !== undefined) {\n this.interruptManager.updateTools(options.tools)\n this.clientToolsRef.current = new Map()\n for (const tool of options.tools) {\n this.clientToolsRef.current.set(tool.name, tool)\n }\n this.devtoolsBridge.notifyToolsChanged()\n }\n if (options.queue !== undefined) {\n this.queueConfig = normalizeQueueOption(options.queue)\n }\n if (options.onResponse !== undefined) {\n this.callbacksRef.current.onResponse = options.onResponse\n }\n if (options.onChunk !== undefined) {\n this.callbacksRef.current.onChunk = options.onChunk\n }\n if (options.onFinish !== undefined) {\n this.callbacksRef.current.onFinish = options.onFinish\n }\n if (options.onError !== undefined) {\n this.callbacksRef.current.onError = options.onError\n }\n if (options.onSubscriptionChange !== undefined) {\n this.callbacksRef.current.onSubscriptionChange =\n options.onSubscriptionChange\n }\n if (options.onConnectionStatusChange !== undefined) {\n this.callbacksRef.current.onConnectionStatusChange =\n options.onConnectionStatusChange\n }\n if (options.onSessionGeneratingChange !== undefined) {\n this.callbacksRef.current.onSessionGeneratingChange =\n options.onSessionGeneratingChange\n }\n if (options.onQueueChange !== undefined) {\n this.callbacksRef.current.onQueueChange = options.onQueueChange\n }\n if (options.onResumeStateChange !== undefined) {\n this.callbacksRef.current.onResumeStateChange =\n options.onResumeStateChange\n }\n if (options.onRunIdChange !== undefined) {\n this.callbacksRef.current.onRunIdChange = options.onRunIdChange\n }\n if (options.onInterruptStateChange !== undefined) {\n this.callbacksRef.current.onInterruptStateChange =\n options.onInterruptStateChange\n }\n if (options.onCustomEvent !== undefined) {\n this.callbacksRef.current.onCustomEvent = options.onCustomEvent\n }\n }\n\n dispose(): void {\n // FIRST, and latched: everything below is teardown, and an async callback that\n // lands mid-teardown must not start new work. In particular a hydration fetch\n // that resolves after this point must not open a tail — see `hydrateFromServer`.\n this.disposed = true\n // `unsubscribe()` below already aborts the in-flight stream (it calls\n // `cancelInFlightStream({ abortSubscription: true })`), so disposal does drop\n // an open tail. Verified by mutation: removing an extra abort here changes\n // nothing, because unsubscribe covers it.\n this.unsubscribe()\n this.devtoolsBridge.dispose()\n this.devtoolsMounted = false\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chat-client.js","names":[],"sources":["../../src/chat-client.ts"],"sourcesContent":["import {\n EventType,\n StreamProcessor,\n cloneAndDeepFreezeJson,\n convertSchemaToJsonSchema,\n generateMessageId,\n isStandardSchema,\n mergeMetadata,\n normalizeToUIMessage,\n restoreInboundChunk,\n tanstackMetadata,\n validateWithStandardSchema,\n} from '@tanstack/ai/client'\nimport {\n ByokBlockedError,\n ByokMissingError,\n ByokUnresolvedProviderError,\n} from '@tanstack/ai/byok'\nimport { byokFallbackProviderId } from './byok/client'\nimport {\n prepareResolvedByokHeaders,\n resolveByokProviderId,\n} from './byok/resolve'\nimport { createNoOpChatDevtoolsBridge } from './devtools-noop'\nimport {\n fetcherToConnectionAdapter,\n getChunkRunId,\n normalizeConnectionAdapter,\n} from './connection-adapters'\nimport { ChatPersistor } from './client-persistor'\nimport { ClearedStreamTracker } from './cleared-stream-tracker'\nimport { normalizeMessagesDates } from './message-date-normalizer'\nimport { InterruptManager } from './interrupt-manager'\nimport type {\n AnyClientTool,\n ContentPart,\n InterruptDefinition,\n InterruptSubmissionError,\n ModelMessage,\n RunAgentResumeItem,\n StreamChunk,\n} from '@tanstack/ai/client'\nimport type { ByokClient } from './byok'\nimport type {\n ChatHydrateOptions,\n ChatHydrationResult,\n ConnectionAdapter,\n SubscribeConnectionAdapter,\n} from './connection-adapters'\nimport type {\n ChatClientEventEmitter,\n ChatClientRunEventContext,\n} from './events'\nimport type {\n AIDevtoolsChatSnapshot,\n ChatDevtoolsBridge,\n ChatDevtoolsBridgeOptions,\n} from './devtools'\nimport type {\n BoundInterrupts,\n ChatClientOptions,\n ChatClientState,\n ChatFetcher,\n ChatInterruptState,\n ResolvableChatInterrupt,\n ChatPendingInterrupt,\n ChatResumeSnapshot,\n ChatResumeState,\n ConnectionStatus,\n MessagePart,\n MultimodalContent,\n QueueBusyReason,\n QueueOption,\n QueueStrategy,\n QueuedMessage,\n SendMessageOptions,\n SubagentHandle,\n ToolCallPart,\n UIMessage,\n WhenBusy,\n} from './types'\nimport type {\n InterruptManagerChangeSource,\n InterruptManagerSubmission,\n} from './interrupt-manager'\n\n/** Internal queue entry — public {@link QueuedMessage} plus optional per-send body. */\ninterface InternalQueuedMessage extends QueuedMessage {\n body?: Record<string, any>\n}\n\nconst STREAM_PROCESSING_BUDGET_MS = 8\n\ntype SchedulerWithYield = {\n yield?: () => Promise<void>\n}\n\nfunction yieldToHost(): Promise<void> {\n const { scheduler } = globalThis as typeof globalThis & {\n scheduler?: SchedulerWithYield\n }\n if (scheduler?.yield) return scheduler.yield()\n return new Promise((resolve) => setTimeout(resolve, 0))\n}\n\nfunction assertUniqueInterruptDefinitions(\n interrupts:\n | ReadonlyArray<InterruptDefinition<any, any, any, any>>\n | undefined,\n): void {\n const ids = new Set<string>()\n for (const interrupt of interrupts ?? []) {\n if (ids.has(interrupt.id)) {\n throw new Error(`Duplicate interrupt definition id: ${interrupt.id}`)\n }\n ids.add(interrupt.id)\n }\n}\n\ntype ChatClientUpdateOptionsWithoutContext<\n TTools extends ReadonlyArray<AnyClientTool>,\n TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =\n readonly [],\n> = {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n /** @deprecated Use `forwardedProps` instead. */\n body?: Record<string, any>\n forwardedProps?: Record<string, any>\n byok?: ByokClient\n byokProvider?: () => string | undefined\n tools?: TTools\n interrupts?: TInterrupts\n queue?: QueueOption\n onResponse?: (response?: Response) => void | Promise<void>\n onChunk?: (chunk: StreamChunk) => void\n onFinish?: (message: UIMessage) => void\n onError?: (error: Error) => void\n onSubscriptionChange?: (isSubscribed: boolean) => void\n onConnectionStatusChange?: (status: ConnectionStatus) => void\n onSessionGeneratingChange?: (isGenerating: boolean) => void\n onQueueChange?: (queue: Array<QueuedMessage>) => void\n onResumeStateChange?: (\n resumeState: ChatResumeState | null,\n pendingInterrupts: BoundInterrupts<TTools, TInterrupts>,\n ) => void\n /**\n * Fires whenever the id of the run in flight changes: the new id when a run\n * starts (including a rejoin), `null` when it settles.\n */\n onRunIdChange?: (runId: string | null) => void\n onInterruptStateChange?: (\n state: ChatInterruptState<TTools, TInterrupts>,\n context: { source: 'hydrate' | 'live' },\n ) => void\n onCustomEvent?: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n}\n\ntype ClientToolResult = {\n toolCallId: string\n tool: string\n output: any\n state?: 'output-available' | 'output-error'\n errorText?: string\n}\n\nfunction resolveTransport(transport: {\n connection?: ConnectionAdapter\n fetcher?: ChatFetcher\n}): ConnectionAdapter {\n const { connection, fetcher } = transport\n if (connection && fetcher) {\n throw new Error(\n 'ChatClient: pass either `connection` or `fetcher`, not both.',\n )\n }\n if (connection) return connection\n if (fetcher) return fetcherToConnectionAdapter(fetcher)\n throw new Error('ChatClient: either `connection` or `fetcher` is required.')\n}\n\nfunction connectionDrainsOnSend(connection: ConnectionAdapter): boolean {\n return 'connect' in connection\n}\n\nfunction isIntermediateToolTurn(chunk: StreamChunk): boolean {\n if (chunk.type !== 'RUN_FINISHED') return false\n if (chunk.outcome?.type === 'interrupt') return false\n const extra = chunk as StreamChunk & { finishReason?: unknown }\n if (extra.finishReason !== undefined) {\n return extra.finishReason === 'tool_calls'\n }\n return tanstackMetadata(chunk)?.finishReason === 'tool_calls'\n}\n\nexport interface NormalizedQueueConfig {\n whenBusy: WhenBusy\n drain: 'fifo' | 'batch'\n onOverflow: 'reject' | 'drop-oldest'\n maxSize?: number\n strategy?: QueueStrategy\n}\n\nexport function normalizeQueueOption(\n option: QueueOption | undefined,\n): NormalizedQueueConfig {\n const base: NormalizedQueueConfig = {\n whenBusy: 'queue',\n drain: 'fifo',\n onOverflow: 'reject',\n }\n if (!option) return base\n if (typeof option === 'string') return { ...base, whenBusy: option }\n if (typeof option === 'function') return { ...base, strategy: option }\n\n const maxSize = option.maxSize\n if (maxSize !== undefined) {\n if (!Number.isInteger(maxSize) || maxSize < 0) {\n throw new Error(\n 'ChatClient: queue.maxSize must be a non-negative integer',\n )\n }\n }\n\n return {\n whenBusy: option.whenBusy ?? 'queue',\n drain: option.drain ?? 'fifo',\n onOverflow: option.onOverflow ?? 'reject',\n ...(maxSize !== undefined ? { maxSize } : {}),\n }\n}\n\n/**\n * Merge a run of queued messages into a single send for `drain: 'batch'`.\n * All-string content is joined with newlines; mixed/multimodal content is\n * flattened into a single `ContentPart` array. The last item's `body` wins.\n * Object-form metadata is merged last-write-wins per key.\n */\nfunction mergeQueuedMessages(items: Array<InternalQueuedMessage>): {\n content: string | MultimodalContent\n body?: Record<string, any>\n} {\n const body = items.at(-1)?.body\n const stringContents: Array<string> = []\n for (const item of items) {\n if (typeof item.content !== 'string') {\n break\n }\n stringContents.push(item.content)\n }\n if (stringContents.length === items.length) {\n return {\n content: stringContents.join('\\n'),\n ...(body !== undefined ? { body } : {}),\n }\n }\n const parts: Array<ContentPart> = []\n let metadata: Record<string, any> | undefined\n for (const item of items) {\n if (typeof item.content === 'string') {\n parts.push({ type: 'text', content: item.content })\n continue\n }\n if (typeof item.content.content === 'string') {\n parts.push({ type: 'text', content: item.content.content })\n } else {\n parts.push(...item.content.content)\n }\n metadata = mergeMetadata(metadata, item.content.metadata)\n }\n return {\n content: {\n content: parts,\n ...(metadata !== undefined ? { metadata } : {}),\n },\n ...(body !== undefined ? { body } : {}),\n }\n}\n\n/**\n * Extract a boolean approval decision from an AG-UI resume payload, if present.\n * Tool-approval resolutions carry `{ approved: boolean, ... }`; generic\n * interrupt payloads do not.\n */\nfunction readApprovalApproved(payload: unknown): boolean | undefined {\n if (\n payload === null ||\n typeof payload !== 'object' ||\n Array.isArray(payload)\n ) {\n return undefined\n }\n if (!('approved' in payload) || typeof payload.approved !== 'boolean') {\n return undefined\n }\n return payload.approved\n}\n\nfunction readResumeState(\n snapshot: ChatResumeSnapshot,\n): ChatResumeState | undefined {\n const value: unknown = snapshot\n if (\n value === null ||\n typeof value !== 'object' ||\n !('resumeState' in value)\n ) {\n return undefined\n }\n const resumeState = value.resumeState\n if (\n resumeState === null ||\n typeof resumeState !== 'object' ||\n !('threadId' in resumeState) ||\n typeof resumeState.threadId !== 'string' ||\n resumeState.threadId.length === 0 ||\n !('runId' in resumeState) ||\n typeof resumeState.runId !== 'string' ||\n resumeState.runId.length === 0\n ) {\n return undefined\n }\n return { threadId: resumeState.threadId, runId: resumeState.runId }\n}\n\n/**\n * How long a reload rejoin waits for its first chunk before giving up. A durable\n * backend keeps a from-start join open waiting for a producer; without this\n * bound a stale pointer to an unknown/evicted run would pin the UI loading for\n * the backend's full first-chunk deadline (tens of seconds). Kept short so the\n * client decides \"reachable or not\" quickly.\n */\nconst REJOIN_CONNECT_DEADLINE_MS = 2000\n\n/**\n * Chunk types that (re)build the assistant message on a rejoin. The hydrated\n * in-flight partial is dropped only when one of these arrives — never on\n * `RUN_STARTED` alone — so a rejoin that connects but delivers no content cannot\n * leave an empty assistant bubble.\n */\nconst REJOIN_REBUILD_TRIGGERS = new Set<string>([\n 'TEXT_MESSAGE_START',\n 'TEXT_MESSAGE_CONTENT',\n 'REASONING_MESSAGE_CONTENT',\n 'TOOL_CALL_START',\n 'MESSAGES_SNAPSHOT',\n // Drop the hydrated card before this chunk creates it again. A subagent\n // turn may have no parent text, so the text triggers arrive too late.\n 'SUBAGENT_STARTED',\n])\n\nfunction rebuildsAssistantMessage(chunk: StreamChunk): boolean {\n if (chunk.type === 'REASONING_ENCRYPTED_VALUE') {\n return (\n chunk.subtype === 'message' &&\n typeof chunk.encryptedValue === 'string' &&\n chunk.encryptedValue.length > 0\n )\n }\n if (chunk.type === 'STEP_FINISHED') {\n return 'signature' in chunk && Boolean(chunk.signature)\n }\n return REJOIN_REBUILD_TRIGGERS.has(chunk.type)\n}\n\ntype SubagentCard = Extract<UIMessage['parts'][number], { type: 'subagent' }>\n\n/** Every subagent card in the messages, nested cards included. */\nfunction collectSubagentParts(\n messages: ReadonlyArray<UIMessage>,\n): Array<SubagentCard> {\n const cards: Array<SubagentCard> = []\n for (const message of messages) {\n for (const part of message.parts) {\n if (part.type !== 'subagent') continue\n cards.push(part)\n cards.push(...collectSubagentParts(part.subagent.messages))\n }\n }\n return cards\n}\n\nfunction readSubagentRunId(chunk: StreamChunk) {\n if ('subagentRunId' in chunk && typeof chunk.subagentRunId === 'string') {\n return chunk.subagentRunId\n }\n return undefined\n}\n\n// Cap parent-chain walks so a cyclic replayed parentRunId cannot loop.\nconst MAX_RUN_LINEAGE_DEPTH = 64\n\n/**\n * A deep copy of plain data. Dates stay Dates. Function-valued properties are\n * dropped.\n */\nfunction copyForDevtools<T>(value: T): T\nfunction copyForDevtools(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(copyForDevtools)\n if (value instanceof Date) return new Date(value.getTime())\n if (value === null || typeof value !== 'object') return value\n const out: Record<string, unknown> = {}\n for (const [key, item] of Object.entries(value)) {\n if (typeof item !== 'function') out[key] = copyForDevtools(item)\n }\n return out\n}\n\nexport class ChatClient<\n TTools extends ReadonlyArray<AnyClientTool> = any,\n TContext = unknown,\n TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> =\n any,\n> {\n private readonly processor: StreamProcessor\n private readonly subagentHandles = new Map<string, SubagentHandle>()\n private readonly stoppedSubagentIds = new Set<string>()\n private connection: SubscribeConnectionAdapter\n private uniqueId: string\n private threadId: string\n // Durable chat persistence (optional): messages + resume snapshot as one\n // combined record, so a full page reload restores the transcript, rehydrates\n // pending interrupts, and rejoins an in-flight run. Clear-during-stream\n // suppression is always on via ClearedStreamTracker so `clear()` works\n // without a storage adapter.\n private readonly persistor?: ChatPersistor\n private readonly clearedStreamTracker = new ClearedStreamTracker()\n private currentRunId: string | null = null\n // Interrupt-resume tracking: the run/thread of the most recent interrupted\n // run, so approvals/client-tool results can be sent back. Cleared when the\n // run terminates. This is STATE (interrupt) resume, not delivery/cursor.\n private lastResume: ChatResumeState | null = null\n // Replay can deliver a parent pause after a descendant already finished.\n private readonly runParents = new Map<string, string>()\n private readonly supersededInterruptRunIds = new Set<string>()\n // The in-flight run id already handed to `resumeInFlightRun`, so a persisted\n // run is rejoined at most once even when both the sync read and the async\n // hydrate surface the same resume pointer.\n private rejoinedRunId: string | null = null\n private readonly interruptManager: InterruptManager<TTools, TInterrupts>\n private activeInterruptSubmission: InterruptManagerSubmission | undefined\n private interruptSubmissionFailure:\n | { errors: ReadonlyArray<InterruptSubmissionError> }\n | undefined\n private readonly joinedRunWaiters = new Map<string, () => void>()\n // When set, the next streamResponse() continues this interrupted run instead\n // of starting a fresh run (consumed once).\n private pendingResumeParentRunId: string | null = null\n private pendingResumeThreadId: string | null = null\n private pendingResumeItems: Array<RunAgentResumeItem> | null = null\n private activeResumeThreadId: string | null = null\n private activeResumeRunId: string | null = null\n // Track the legacy `body` option and the canonical `forwardedProps`\n // option as separate slots so that `updateOptions({ forwardedProps })`\n // doesn't wipe a previously-set `body` (and vice versa). They are\n // merged on every send, with `forwardedProps` winning on key collision.\n private bodyOption: Record<string, any> = {}\n private forwardedPropsOption: Record<string, any> = {}\n private byok: ByokClient | undefined\n private byokProvider: (() => string | undefined) | undefined\n private context: TContext | undefined = undefined\n private pendingMessageBody: Record<string, any> | undefined = undefined\n private queueConfig: NormalizedQueueConfig\n private messageQueue: Array<InternalQueuedMessage> = []\n /**\n * True from the moment `sendMessage` claims the client until its\n * `streamResponse` settles. Closes the race where concurrent callers both\n * see `isLoading === false`, both append a user message, and only one stream\n * actually runs (leaving stranded user messages with no reply).\n */\n private sendInFlight = false\n /**\n * True while `drainQueue` is delivering queued messages. Concurrent\n * `sendMessage` calls during a drain are treated as busy and follow\n * `whenBusy` (default: queue).\n */\n private messageQueueDraining = false\n /**\n * Set by `whenBusy: 'interrupt'` so an in-progress FIFO drain loop stops\n * before starting the next queued item (the interrupting send owns the client).\n */\n private stopMessageQueueDrain = false\n /**\n * Sync claim held for the duration of `deliverMessage` so concurrent\n * deliverers cannot both append a user message before only one stream runs.\n */\n private deliverClaim = false\n private isLoading = false\n private isSubscribed = false\n private error: Error | undefined = undefined\n private status: ChatClientState = 'ready'\n private connectionStatus: ConnectionStatus = 'disconnected'\n private abortController: AbortController | null = null\n private readonly clientToolsRef: { current: Map<string, AnyClientTool> }\n private readonly devtoolsBridge: ChatDevtoolsBridge\n /**\n * Alias for `this.events`. The bridge installs an\n * emitter that auto-attaches run/thread context and auto-emits a\n * snapshot after every event, so chat-client only ever calls\n * `this.events.X(...)` exactly like it did before devtools landed.\n */\n private readonly events: ChatClientEventEmitter\n private currentStreamId: string | null = null\n private currentMessageId: string | null = null\n private readonly postStreamActions: Array<() => Promise<void>> = []\n // Track pending client tool executions to await them before stream finalization\n private readonly pendingToolExecutions: Map<string, Promise<void>> = new Map()\n private activeClientTools: Map<string, AnyClientTool> | null = null\n private activeContext: TContext | undefined = undefined\n // Flag to deduplicate continuation checks during action draining\n private continuationPending = false\n private subscriptionAbortController: AbortController | null = null\n private processingResolve: (() => void) | null = null\n /**\n * `connect()` adapters push the full HTTP body into the subscribe queue, then\n * wait until that queue is idle. After `send()` returns, every chunk from this\n * request has been processed. Subscribe/send sockets do not drain that way.\n */\n private connectionDrainsOnSend = false\n private errorReportedGeneration: number | null = null\n private streamGeneration = 0\n private continuationGeneration = 0\n // Generation of the run that opened the current stream. Public\n // `addToolResult` must use this, not the live counter: `stop()` increments\n // the live counter, so a post-stop call would otherwise look current.\n private streamContinuationGeneration = 0\n // Tracks whether a queued checkForContinuation was skipped because\n // continuationPending was true (chained approval scenario)\n private continuationSkipped = false\n private draining = false\n private sessionGenerating = false\n private readonly activeRunIds = new Set<string>()\n /** Latched by `dispose()`; stops any late async callback starting new work. */\n private disposed = false\n /** The error a failed mount hydration set, cleared by the next successful one. */\n private hydrationError: Error | undefined\n /** Whether a view is currently watching. See `attach` / `detach`. */\n private tailing = false\n /** Constructor inputs `attach()` needs on every re-attach, not just the first. */\n private readonly rejoinRunId: string | null | undefined\n private readonly cachesMessages: boolean\n /**\n * Newest-window size from `history.pageSize`. Only set when\n * `persistence === true`.\n */\n private readonly historyPageSize: number | undefined\n private hasOlderMessages = false\n private olderMessagesCursor: string | undefined\n private readonly knownServerMessageIds = new Set<string>()\n private loadOlderMessagesInFlight = false\n private historyGeneration = 0\n private devtoolsMounted = false\n\n private readonly callbacksRef: {\n current: {\n onResponse: (response?: Response) => void | Promise<void>\n onChunk: (chunk: StreamChunk) => void\n onFinish: (message: UIMessage) => void\n onError: (error: Error) => void\n onMessagesChange: (messages: Array<UIMessage>) => void\n onLoadingChange: (isLoading: boolean) => void\n onErrorChange: (error: Error | undefined) => void\n onStatusChange: (status: ChatClientState) => void\n onSubscriptionChange: (isSubscribed: boolean) => void\n onConnectionStatusChange: (status: ConnectionStatus) => void\n onSessionGeneratingChange: (isGenerating: boolean) => void\n onQueueChange: (queue: Array<QueuedMessage>) => void\n onResumeStateChange: (\n resumeState: ChatResumeState | null,\n pendingInterrupts: BoundInterrupts<TTools, TInterrupts>,\n ) => void\n onRunIdChange: (runId: string | null) => void\n onInterruptStateChange: (\n state: ChatInterruptState<TTools, TInterrupts>,\n context: { source: 'hydrate' | 'live' },\n ) => void\n onCustomEvent: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => void\n }\n }\n\n constructor(options: ChatClientOptions<TTools, TContext, TInterrupts>) {\n assertUniqueInterruptDefinitions(options.interrupts)\n // Do not mint a random thread id during construct. Framework hooks build\n // this client during render (SSR included). The wire/devtools identity is\n // `threadId`; it is assigned here when the caller passed one, or later in\n // `ensureThreadId()` from attach / mount / send.\n this.threadId = options.threadId || ''\n this.uniqueId = this.threadId\n // `persistence` is `false`/omitted (ephemeral, in-memory), `true`\n // (server-authoritative: cache nothing client-side, hydrate the thread from\n // the server by `threadId` on mount), or a storage adapter\n // (client-authoritative: cache the transcript plus resume pointer). Only the\n // server-authoritative mode turns transcript caching off; that is what gates\n // the mount hydration and keeps a client record from shadowing server history.\n let cachesMessages = true\n if (options.persistence === true) {\n if (!options.threadId) {\n throw new Error(\n '[TanStack AI] persistence needs a stable `threadId` to key on. Pass a threadId from your app (for example support-42).',\n )\n }\n cachesMessages = false\n this.historyPageSize = options.history?.pageSize\n } else if (options.persistence) {\n // A storage adapter: keep the combined record (transcript + resume pointer)\n // in the browser. Persistence keys on `threadId` (the conversation\n // identity) so a reload with the same `threadId` finds the same record.\n if (!options.threadId) {\n throw new Error(\n '[TanStack AI] persistence needs a stable `threadId` to key on. Pass a threadId from your app (for example support-42).',\n )\n }\n this.persistor = new ChatPersistor(\n options.persistence,\n options.threadId,\n (messages) => this.processor.setMessages(messages),\n (snapshot) => this.applyPersistedResume(snapshot),\n )\n this.historyPageSize = undefined\n } else {\n this.historyPageSize = undefined\n }\n // Both `body` (deprecated) and `forwardedProps` populate the AG-UI\n // `RunAgentInput.forwardedProps` wire field. They are stored\n // separately so `updateOptions` can replace one without touching the\n // other; the merge happens at send time, with `forwardedProps`\n // winning on key collision.\n this.bodyOption = options.body || {}\n this.forwardedPropsOption = options.forwardedProps || {}\n this.byok = options.byok\n this.byokProvider = options.byokProvider\n this.context = options.context\n this.queueConfig = normalizeQueueOption(options.queue)\n const transport = resolveTransport(options)\n this.connectionDrainsOnSend = connectionDrainsOnSend(transport)\n this.connection = normalizeConnectionAdapter(transport)\n\n // Build client tools map\n this.clientToolsRef = { current: new Map() }\n if (options.tools) {\n for (const tool of options.tools) {\n this.clientToolsRef.current.set(tool.name, tool)\n }\n }\n\n this.devtoolsBridge = (\n options.devtoolsBridgeFactory ?? createNoOpChatDevtoolsBridge\n )(this.buildDevtoolsBridgeOptions(options.devtools))\n this.events = this.devtoolsBridge.events\n\n this.callbacksRef = {\n current: {\n onResponse: options.onResponse || (() => {}),\n onChunk: options.onChunk || (() => {}),\n onFinish: options.onFinish || (() => {}),\n onError: options.onError || (() => {}),\n onMessagesChange: options.onMessagesChange || (() => {}),\n onLoadingChange: options.onLoadingChange || (() => {}),\n onErrorChange: options.onErrorChange || (() => {}),\n onStatusChange: options.onStatusChange || (() => {}),\n onSubscriptionChange: options.onSubscriptionChange || (() => {}),\n onConnectionStatusChange:\n options.onConnectionStatusChange || (() => {}),\n onSessionGeneratingChange:\n options.onSessionGeneratingChange || (() => {}),\n onQueueChange: options.onQueueChange || (() => {}),\n onResumeStateChange: options.onResumeStateChange || (() => {}),\n onRunIdChange: options.onRunIdChange || (() => {}),\n onInterruptStateChange: options.onInterruptStateChange || (() => {}),\n onCustomEvent: options.onCustomEvent || (() => {}),\n },\n }\n\n this.interruptManager = new InterruptManager<TTools, TInterrupts>({\n ...(options.tools !== undefined ? { tools: options.tools } : {}),\n ...(options.interrupts !== undefined\n ? { interrupts: options.interrupts }\n : {}),\n submit: (submission) => this.submitInterruptBatch(submission),\n onChange: (source) => this.notifyResumeStateChange(source),\n })\n\n // In-memory rehydrate of interrupt descriptors (e.g. after a page reload\n // when the host supplies a snapshot). Durable storage of that snapshot is\n // a persistence-stack concern — not wired here.\n if (options.initialResumeSnapshot) {\n this.applyResumeSnapshot(options.initialResumeSnapshot)\n }\n\n // Create StreamProcessor with event handlers.\n // Use conditional spreads so we don't pass `undefined` into\n // `StreamProcessorOptions` fields under `exactOptionalPropertyTypes`.\n const persistedState = this.persistor?.readInitial()\n const syncPersistedState =\n persistedState instanceof Promise ? undefined : persistedState\n // A persistor exists only in client-authoritative mode, so a synchronously\n // read record's transcript is the conversation; adopt it over host\n // `initialMessages`. (Server-authoritative mode has no persistor and instead\n // hydrates from the server on mount, keyed by threadId.)\n const initialMessages = syncPersistedState\n ? syncPersistedState.messages\n : options.initialMessages\n // A durable snapshot read synchronously from storage wins over the\n // in-memory `initialResumeSnapshot` fallback applied above. A snapshot with\n // pending interrupts rehydrates the interrupt UI; a bare in-flight run is\n // rejoined after the processor is ready (see `rejoinRunId` below).\n let rejoinRunId: string | null = null\n if (syncPersistedState?.resume) {\n const snapshot = syncPersistedState.resume\n const hasPendingInterrupts =\n Array.isArray(snapshot.pendingInterrupts) &&\n snapshot.pendingInterrupts.length > 0\n if (hasPendingInterrupts) {\n // Interrupts are run-scoped state, restored from the cached snapshot.\n this.applyResumeSnapshot(snapshot)\n } else if (snapshot.resumeState.runId) {\n // A bare in-flight run pointer drives a client-authoritative rejoin.\n rejoinRunId = snapshot.resumeState.runId\n }\n }\n // A host-supplied `initialResumeSnapshot` carrying a bare in-flight run is\n // rejoined too, not just its interrupts (which `applyResumeSnapshot` above\n // already restored). This is how a server-authoritative app hands a FRESH\n // client an in-flight run to tail — e.g. opening the thread on a second\n // device / browser, where hydration reports the active run id but no local\n // resume pointer exists. A run named by the persisted store wins.\n if (!rejoinRunId && options.initialResumeSnapshot) {\n const snapshot = options.initialResumeSnapshot\n const hasPendingInterrupts =\n Array.isArray(snapshot.pendingInterrupts) &&\n snapshot.pendingInterrupts.length > 0\n if (!hasPendingInterrupts && snapshot.resumeState.runId) {\n rejoinRunId = snapshot.resumeState.runId\n }\n }\n\n this.processor = new StreamProcessor({\n ...(options.streamProcessor?.chunkStrategy\n ? { chunkStrategy: options.streamProcessor.chunkStrategy }\n : {}),\n ...(initialMessages ? { initialMessages } : {}),\n events: {\n onMessagesChange: (messages) => {\n // Restored or replaced messages bring their own cards. Give each one\n // its live handle before anyone reads the messages.\n this.syncSubagentHandles()\n this.persistor?.notifyMessagesChanged(messages)\n this.callbacksRef.current.onMessagesChange(messages)\n // The bridge only snapshots on status changes, and the panel draws a\n // child card from the snapshot, so without this the card would update\n // only when the parent run ends.\n this.queueDevtoolsSnapshot()\n },\n onStreamStart: () => {\n this.setStatus('streaming')\n const assistantMessageId =\n this.processor.getCurrentAssistantMessageId()\n if (!assistantMessageId) {\n return\n }\n const messages = this.processor.getMessages()\n const assistantMessage = messages.find(\n (m: UIMessage) => m.id === assistantMessageId,\n )\n if (assistantMessage) {\n this.currentMessageId = assistantMessage.id\n this.events.messageAppended(\n assistantMessage,\n this.currentStreamId || undefined,\n )\n }\n },\n onStreamEnd: (message: UIMessage) => {\n this.callbacksRef.current.onFinish(message)\n this.setStatus('ready')\n // Resolve the processing-complete promise so streamResponse can continue\n this.resolveProcessing()\n },\n onError: (error: Error) => {\n this.reportStreamError(error)\n },\n onTextUpdate: (messageId: string, content: string) => {\n // Emit text update to devtools\n if (this.currentStreamId) {\n this.events.textUpdated(this.currentStreamId, messageId, content)\n }\n },\n onThinkingUpdate: (messageId: string, content: string) => {\n // Emit thinking update to devtools\n if (this.currentStreamId) {\n this.events.thinkingUpdated(\n this.currentStreamId,\n messageId,\n content,\n undefined,\n )\n }\n },\n onStructuredOutputChange: (args) => {\n const streamId = this.devtoolsBridge.resolveStreamId()\n const eventName =\n args.phase === 'start'\n ? 'structured-output:started'\n : args.phase === 'complete'\n ? 'structured-output:completed'\n : args.phase === 'error'\n ? 'structured-output:errored'\n : 'structured-output:updated'\n\n this.currentMessageId = args.messageId\n this.events.structuredOutputChanged(\n eventName,\n streamId,\n args.messageId,\n {\n status: args.status,\n raw: args.raw,\n ...(args.partial !== undefined ? { partial: args.partial } : {}),\n ...(args.data !== undefined ? { data: args.data } : {}),\n ...(args.reasoning !== undefined\n ? { reasoning: args.reasoning }\n : {}),\n ...(args.errorMessage !== undefined\n ? { errorMessage: args.errorMessage }\n : {}),\n ...(args.delta !== undefined ? { delta: args.delta } : {}),\n },\n )\n },\n onToolCallStateChange: (\n messageId: string,\n toolCallId: string,\n state: string,\n args: string,\n ) => {\n // Get the tool name from the messages\n const messages = this.processor.getMessages()\n const message = messages.find((m: UIMessage) => m.id === messageId)\n const toolCallPart = message?.parts.find(\n (p: MessagePart): p is ToolCallPart =>\n p.type === 'tool-call' && p.id === toolCallId,\n )\n const toolName = toolCallPart?.name || 'unknown'\n\n // Emit tool call state change to devtools\n if (this.currentStreamId) {\n this.events.toolCallStateChanged(\n this.currentStreamId,\n messageId,\n toolCallId,\n toolName,\n state,\n args,\n )\n }\n },\n onToolCall: (args: {\n toolCallId: string\n toolName: string\n input: any\n }) => {\n // Handle client-side tool execution automatically\n const clientTools =\n this.activeClientTools ?? this.clientToolsRef.current\n const clientTool = clientTools.get(args.toolName)\n const executeFunc = clientTool?.execute\n if (executeFunc) {\n const continuationGeneration = this.continuationGeneration\n // Capture the run context at execution-start so a tool whose\n // result lands AFTER the originating run finishes still reports\n // back against the originating run, not whatever run is\n // current when the result emits.\n const runEventContext =\n this.devtoolsBridge.getCurrentRunEventContext()\n // Create and track the execution promise\n const executionPromise = (async () => {\n try {\n const context =\n this.activeClientTools === null\n ? this.context\n : this.activeContext\n const output = await executeFunc(args.input, {\n toolCallId: args.toolCallId,\n context: context as TContext,\n emitCustomEvent: () => {},\n })\n await this.addToolResultForClientTool(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output,\n state: 'output-available',\n },\n clientTool,\n continuationGeneration,\n runEventContext,\n )\n } catch (error: any) {\n await this.addToolResultForClientTool(\n {\n toolCallId: args.toolCallId,\n tool: args.toolName,\n output: null,\n state: 'output-error',\n errorText: error.message,\n },\n clientTool,\n continuationGeneration,\n runEventContext,\n )\n } finally {\n // Remove from pending when complete\n this.pendingToolExecutions.delete(args.toolCallId)\n }\n })()\n\n // Track the pending execution\n this.pendingToolExecutions.set(args.toolCallId, executionPromise)\n }\n },\n onApprovalRequest: (args: {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n }) => {\n const streamId = this.devtoolsBridge.resolveStreamId()\n const messageIdForApproval =\n this.findMessageIdForToolCall(args.toolCallId) ??\n this.currentMessageId ??\n ''\n\n this.events.approvalRequested(\n streamId,\n messageIdForApproval,\n args.toolCallId,\n args.toolName,\n args.input,\n args.approvalId,\n )\n },\n onCustomEvent: (\n eventType: string,\n data: unknown,\n context: { toolCallId?: string },\n ) => {\n // Server-side memory middleware transports its state as a `memory:state`\n // CUSTOM event (its own event bus never reaches this browser runtime).\n // Route it to the devtools bridge here — the designated custom-event\n // path — then still forward to the app's callback.\n if (eventType === 'memory:state') {\n this.devtoolsBridge.recordMemoryState(data)\n }\n if (\n eventType === 'compaction:started' ||\n eventType === 'compaction:state' ||\n eventType === 'compaction:ended'\n ) {\n this.devtoolsBridge.recordCompactionEvent(eventType, data)\n }\n if (eventType === 'skills:state') {\n this.devtoolsBridge.recordSkillsState(data)\n }\n this.callbacksRef.current.onCustomEvent(eventType, data, context)\n },\n },\n })\n // `initialMessages` do not fire a change event. Give their cards handles.\n this.syncSubagentHandles()\n\n this.persistor?.hydrateAsync(persistedState)\n\n this.rejoinRunId = rejoinRunId\n this.cachesMessages = cachesMessages\n // NO TAILING HERE, deliberately. Constructing a client must not open a\n // connection.\n //\n // A UI framework may build a client and then throw it away — React does it on\n // every double-invoked render, and the discarded instance is never mounted, so\n // nothing ever calls `detach()` or `dispose()` on it. When the constructor\n // opened a stream, that stream became unreachable and held one of the browser's\n // ~6 connections per origin until the page reloaded. Traced with CDP: connection\n // ids 1374/1396/1428/1437 were still held after eight thread switches, and a\n // later request waited 210 SECONDS for a free slot (`stallMs: 210752`).\n //\n // Guarding inside the client cannot fix that, because the leaking instance is\n // the one the framework discarded — every guard runs on the instance it kept.\n // Only \"idle until a view attaches\" makes a thrown-away client harmless.\n //\n // Callers therefore drive the lifecycle: `attach()` when a view mounts,\n // `detach()` when it unmounts. Every framework wrapper in this repo does.\n }\n\n /**\n * START TAILING: re-attach to an in-flight run so its chunks arrive here.\n *\n * Called by the constructor, and again by a UI wrapper every time its view\n * mounts. Idempotent — attaching while already attached does nothing — so the\n * constructor call and a wrapper's first mount cost one attach between them.\n *\n * Pairs with {@link detach}. The pair exists because tailing used to begin ONLY\n * in the constructor, which meant a view could never stop tailing and then\n * resume: unmount had to either keep the connection open or lose it for good.\n * Keeping it open is what starved the page — a browser allows ~6 connections per\n * origin, and one long-lived stream per view reaches that after a handful of\n * views, after which every other request queues (measured: an in-page fetch took\n * over two minutes while the same request from outside the browser took 17ms).\n */\n attach(): void {\n if (this.disposed || this.tailing) return\n this.ensureThreadId()\n this.tailing = true\n\n // Full page reload with an in-flight run persisted (synchronous store):\n // re-attach to it off the server's delivery-durability log so the stream\n // finishes here. Async stores rejoin from `applyPersistedResume` once the\n // hydrate resolves. Best-effort and non-blocking.\n if (this.rejoinRunId) {\n this.maybeRejoinInFlight(this.rejoinRunId)\n }\n\n // Server-authoritative (`persistence: true`): the client caches no transcript\n // and no run pointer — it re-hydrates from the server on mount, keyed by the\n // stable threadId. `hydrate` returns the stored transcript plus a cursor to\n // any in-flight run, which is tailed via the same joinRun path. This is what\n // makes reload AND a fresh device work with zero app glue (no loader/prop).\n if (!this.cachesMessages && this.connection.hydrate) {\n this.hydrateFromServer()\n }\n }\n\n /**\n * STOP TAILING: drop the connection, keep everything else.\n *\n * Called by a UI wrapper when its view unmounts. The transcript, the resume\n * pointer and the run id all stay, so a later {@link attach} repaints instantly\n * and re-tails from the durable log — nothing is lost, because the run keeps\n * going server-side and its log holds every chunk.\n *\n * Deliberately NOT `dispose()`: this client is expected back. And deliberately\n * not `stop()`, which means \"the user ended this run\" — detaching says only that\n * nobody is watching right now.\n *\n * `rejoinedRunId` is cleared so the next `attach` can re-join the same run;\n * without that reset the guard in {@link maybeRejoinInFlight} would treat the\n * run as already joined and the view would come back silent.\n */\n detach(): void {\n if (!this.tailing) return\n // BEFORE the abort, because `resumeInFlightRun`'s cleanup reads it: a join\n // aborted before its first chunk normally means \"this run is unreachable\" and\n // clears the resume pointer. A detach is not that — the run is fine and we\n // intend to come back — so the pointer must survive.\n this.tailing = false\n this.cancelInFlightStream({ setReadyStatus: true })\n this.rejoinedRunId = null\n }\n\n private applyResumeSnapshot(snapshot: ChatResumeSnapshot): void {\n const resumeState = readResumeState(snapshot)\n if (resumeState === undefined) {\n this.interruptManager.reset({ source: 'hydrate' })\n return\n }\n this.lastResume = resumeState\n const pendingInterrupts = Array.isArray(snapshot.pendingInterrupts)\n ? snapshot.pendingInterrupts\n : []\n if (pendingInterrupts.length === 0) {\n this.interruptManager.reset({ source: 'hydrate' })\n return\n }\n const generation = this.interruptGeneration(pendingInterrupts)\n this.interruptManager.hydrate(\n {\n threadId: resumeState.threadId,\n interruptedRunId: resumeState.runId,\n generation,\n interrupts: pendingInterrupts,\n },\n 'hydrate',\n )\n }\n\n /**\n * Apply a resume snapshot read from durable storage. Restores interrupt state,\n * and for a bare in-flight run (no pending interrupts) also rejoins it. This is\n * the async-store counterpart to the synchronous rejoin in the constructor:\n * `applyResumeSnapshot` alone only handles interrupts, so an async store\n * (`indexedDBPersistence`) would otherwise never rejoin a mid-stream run.\n */\n private applyPersistedResume(snapshot: ChatResumeSnapshot): void {\n this.applyResumeSnapshot(snapshot)\n const hasInterrupts =\n Array.isArray(snapshot.pendingInterrupts) &&\n snapshot.pendingInterrupts.length > 0\n const runId = snapshot.resumeState?.runId\n // A cached run pointer only reaches here through the persistor, which exists\n // only in client-authoritative mode, so a bare in-flight run rejoins here.\n // (Server-authoritative reconnect is resolved from the server by threadId in\n // `hydrateFromServer`.)\n if (!hasInterrupts && runId) {\n this.maybeRejoinInFlight(runId)\n }\n }\n\n /**\n * Rejoin a persisted in-flight run, guarded so it fires at most once and never\n * while another run is already active. Skipped when the connection is not\n * resumable (`joinRun` absent), so a non-durable transport is a no-op.\n */\n private maybeRejoinInFlight(runId: string): void {\n if (!this.connection.joinRun) return\n // A client with no view attached must never open a connection. `tailing` is\n // the load-bearing half: a view switch calls `detach()`, NOT `dispose()`, and\n // an in-flight hydration resolves a moment later and lands right here — so\n // guarding only on `disposed` let every switch open a fresh tail that nothing\n // would ever abort. Measured with CDP: connection ids 1366/1397/1429/1460 were\n // still held after eight switches, and a later request waited 97 SECONDS for a\n // slot (`stallMs: 97691`).\n if (this.disposed || !this.tailing) return\n if (this.rejoinedRunId === runId) return\n // A fresh send (or an already-running rejoin) owns the client; don't stomp it.\n if (this.isLoading || this.abortController) return\n this.rejoinedRunId = runId\n this.resumeInFlightRun(runId)\n }\n\n /**\n * Server-authoritative mount hydration (`persistence: true`). The client holds\n * no transcript and no run pointer; on mount it asks the server — keyed by the\n * stable threadId — for the stored transcript and whether a run is still\n * generating. The transcript repaints immediately; an in-flight run is tailed\n * through the same durability rejoin as a reload. Best-effort and\n * non-blocking: a failure leaves the client empty rather than throwing, and a\n * send that starts first owns the client (hydration then backs off).\n */\n private hydrateFromServer(): void {\n const hydrate = this.connection.hydrate\n if (!hydrate) return\n if (this.isLoading || this.abortController) return\n if (this.disposed) return\n const pageSize = this.historyPageSize\n const hydrateOptions: ChatHydrateOptions | undefined =\n pageSize === undefined ? undefined : { limit: pageSize }\n void (async () => {\n let result: ChatHydrationResult\n const generation = this.historyGeneration\n try {\n result = await hydrate(this.threadId, hydrateOptions)\n } catch (cause) {\n // Same staleness guard as the success path below: a failure from an\n // older attempt must not touch the state of a newer one.\n if (generation === this.historyGeneration) this.failHydration(cause)\n return\n }\n if (generation !== this.historyGeneration) return\n // NO VIEW IS WATCHING ANY MORE (it unmounted while this fetch was in\n // flight). Applying anything now is pointless, and one thing is actively\n // harmful: the branch below calls `maybeRejoinInFlight`, which opens a TAIL.\n // A tail started here belongs to a view that has gone, so nothing will ever\n // abort it, and a browser allows only ~6 connections per origin — so a\n // handful of switches starve the page and every later request queues.\n //\n // `!this.tailing` is the case that actually bites: a switch calls `detach()`,\n // not `dispose()`, so a `disposed`-only check let the leak straight through.\n if (this.disposed || !this.tailing) return\n // A send may have started while the fetch was in flight — don't stomp it.\n if (this.isLoading || this.abortController) return\n // A retry after a failed load succeeded: drop that failure, but not an\n // error something else set since.\n if (this.hydrationError && this.error === this.hydrationError) {\n this.setError(undefined)\n this.setStatus('ready')\n }\n this.hydrationError = undefined\n this.applyHydrationPage(result.page)\n if (result.messages.length > 0) {\n const windowMessages = normalizeMessagesDates(result.messages)\n this.processor.setMessages(windowMessages)\n this.rememberServerMessageIds(windowMessages)\n }\n if (\n result.interrupts &&\n result.interrupts.pending.length > 0 &&\n (!result.activeRun?.runId ||\n result.activeRun.runId === result.interrupts.runId)\n ) {\n // Pending interrupt = the thread is paused awaiting a human decision, so\n // there is nothing to tail (no chunks stream until it resolves). Restore\n // the approval/wait from the SERVER — identical to reconstructing it from\n // a resume snapshot — so the reload re-prompts the decision and the resume\n // targets the run it paused. This is checked BEFORE `activeRun` on\n // purpose: a run that just paused can momentarily still read as `running`\n // on the server, so a racing hydrate reports both an `activeRun` cursor\n // AND the pending interrupt. Tailing that \"active\" run would drop the\n // approval card (and hang on a stream that never comes), so the interrupt\n // wins over that same run. A DIFFERENT active run is the continuation\n // that answered this interrupt: the server commits the answer only when\n // that run finishes, so join it (see the `else` branch below).\n this.applyResumeSnapshot({\n resumeState: {\n threadId: this.threadId,\n runId: result.interrupts.runId,\n },\n pendingInterrupts: result.interrupts.pending,\n })\n } else if (result.activeRun?.runId) {\n this.maybeRejoinInFlight(result.activeRun.runId)\n }\n })()\n }\n\n /**\n * Surface a mount-hydration failure (`persistence: true`) on the observable\n * fields, mirroring `GenerationClient.failHydration`, so \"this thread failed\n * to load\" is distinguishable from \"this thread has no messages\" and the app\n * can show an error / offer a retry. A genuine miss — the server having no\n * record for a fresh thread — resolves normally and never reaches here; only a\n * thrown transport / authorize-gate error does.\n *\n * Skipped when the view unmounted (`!tailing`) or a `sendMessage` took\n * ownership while the hydrate GET was in flight, so a live run's state always\n * wins over a stale mount-time failure — same guard as the success path above.\n */\n private failHydration(cause: unknown): void {\n if (this.disposed || !this.tailing) return\n if (this.isLoading || this.abortController) return\n const error = cause instanceof Error ? cause : new Error(String(cause))\n // Mirror the send path: a BYOK key that is missing / locked must still\n // trigger the key-request flow on thread load, not just be reported.\n if (error instanceof ByokMissingError) {\n this.byok?.request(error.provider, 'missing')\n }\n if (error instanceof ByokBlockedError && error.reason === 'locked') {\n this.byok?.request(error.provider, 'locked')\n }\n this.hydrationError = error\n this.setStatus('error')\n this.setError(error)\n this.callbacksRef.current.onError(error)\n }\n\n mountDevtools(): void {\n this.ensureThreadId()\n if (this.devtoolsMounted) {\n return\n }\n\n this.devtoolsMounted = true\n this.devtoolsBridge.mountWithTools(this.processor.getMessages().length)\n }\n\n private ensureThreadId(): string {\n if (!this.threadId) {\n this.threadId = this.generateUniqueId('thread')\n }\n this.uniqueId = this.threadId\n return this.threadId\n }\n\n /**\n * Drain a runId-less RUN_ERROR that belongs to a cleared run the client is\n * still tracking. The persistor owns the cleared-run bookkeeping; the client\n * owns the active-run / session / processing state.\n */\n private drainIgnoredRunlessChunk(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_ERROR') return\n const runId = this.clearedStreamTracker.takeRunlessRunId()\n if (!runId) return\n this.activeRunIds.delete(runId)\n this.setSessionGenerating(this.activeRunIds.size > 0)\n this.resolveProcessing()\n }\n\n private retireIgnoredClearedTerminalChunk(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') return\n const runId =\n getChunkRunId(chunk) ?? this.clearedStreamTracker.takeRunlessRunId()\n if (!runId) return\n this.activeRunIds.delete(runId)\n this.setSessionGenerating(this.activeRunIds.size > 0)\n if (!getChunkRunId(chunk)) {\n this.resolveProcessing()\n }\n }\n\n private updateRunLifecycle(\n chunk: StreamChunk,\n options?: { resolveProcessing?: boolean },\n ): void {\n if (chunk.type === 'RUN_STARTED') {\n const chunkRunId = getChunkRunId(chunk) ?? chunk.runId\n const parentRunId =\n 'parentRunId' in chunk && typeof chunk.parentRunId === 'string'\n ? chunk.parentRunId\n : undefined\n if (parentRunId) {\n this.runParents.set(chunkRunId, parentRunId)\n if (this.supersededInterruptRunIds.has(chunkRunId)) {\n this.markLineageAnswered(parentRunId)\n }\n }\n this.activeResumeThreadId =\n 'threadId' in chunk && typeof chunk.threadId === 'string'\n ? chunk.threadId\n : this.activeResumeThreadId\n this.activeResumeRunId = chunkRunId\n // Late RUN_STARTED for a run that already finished is replay, not a live\n // turn. A new send can reuse a finished run id while isLoading is true.\n const isAnsweredReplayStart =\n this.supersededInterruptRunIds.has(chunkRunId) && !this.isLoading\n if (!isAnsweredReplayStart) {\n this.activeRunIds.add(chunkRunId)\n this.clearedStreamTracker.onRunStarted(chunkRunId)\n this.setSessionGenerating(true)\n }\n // Persist a live-run resume snapshot so a full page reload can rejoin this\n // in-flight run via joinRun. Only a persistor writes it, and a persistor\n // exists only in client-authoritative mode; server-authoritative reconnect\n // is resolved from the server by threadId in `hydrateFromServer`, so no\n // client-cached run pointer (which goes stale the moment a turn spans a\n // second run) is ever written. Interrupt/terminal handling overwrites or\n // clears it in observeInterruptState.\n if (\n this.persistor &&\n this.connection.joinRun &&\n !this.lastResume &&\n !isAnsweredReplayStart\n ) {\n this.persistResumeSnapshot({\n threadId: this.activeResumeThreadId ?? this.threadId,\n runId: chunkRunId,\n })\n }\n if (\n this.lastResume &&\n this.supersededInterruptRunIds.has(this.lastResume.runId)\n ) {\n this.lastResume = null\n this.persistor?.persistResumeSnapshot(null)\n this.interruptManager.reset()\n }\n return\n }\n\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') {\n return\n }\n\n const runId = getChunkRunId(chunk)\n if (runId) {\n this.activeRunIds.delete(runId)\n this.clearedStreamTracker.onRunSettled(runId)\n } else if (chunk.type === 'RUN_ERROR') {\n // RUN_ERROR without runId is a session-level error; clear all runs.\n this.activeRunIds.clear()\n this.clearedStreamTracker.onSessionRunError()\n }\n this.setSessionGenerating(this.activeRunIds.size > 0)\n const skipProcessingResolve =\n chunk.type === 'RUN_FINISHED' && isIntermediateToolTurn(chunk)\n if (options?.resolveProcessing !== false && !skipProcessingResolve) {\n this.resolveProcessing()\n }\n }\n\n /**\n * Track interrupt state off the stream's terminal events. A RUN_FINISHED with\n * an interrupt outcome records the pending interrupts + the run/thread to\n * resume; any other terminal event for the tracked/current run clears that\n * state. This is interrupt (state) resume — there is no delivery cursor.\n */\n private observeInterruptState(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') {\n return\n }\n\n if (this.activeInterruptSubmission && chunk.type === 'RUN_ERROR') {\n return\n }\n const runId = getChunkRunId(chunk)\n const threadId =\n 'threadId' in chunk && typeof chunk.threadId === 'string'\n ? chunk.threadId\n : this.activeResumeThreadId\n\n // Record answered lineage before hydrating a pause. Intermediate\n // tool_calls RUN_FINISHED is a mid-turn handoff, not an answered pause.\n if (\n runId &&\n !isIntermediateToolTurn(chunk) &&\n (chunk.type === 'RUN_ERROR' ||\n (chunk.type === 'RUN_FINISHED' && chunk.outcome?.type !== 'interrupt'))\n ) {\n this.markLineageAnswered(runId)\n }\n\n if (chunk.type === 'RUN_FINISHED' && chunk.outcome?.type === 'interrupt') {\n // Track the REQUEST run id (what the client sent) so a resume targets the\n // same run even when provider events carry their own run id.\n const interruptedRunId =\n this.currentRunId ?? runId ?? this.activeResumeRunId ?? ''\n // Replay re-emits a pause that a continuation already answered.\n if (\n interruptedRunId &&\n this.supersededInterruptRunIds.has(interruptedRunId)\n ) {\n return\n }\n this.lastResume = {\n threadId: threadId ?? this.threadId,\n runId: interruptedRunId,\n }\n this.interruptManager.hydrate(\n {\n threadId: this.lastResume.threadId,\n interruptedRunId,\n generation: this.interruptGeneration(chunk.outcome.interrupts),\n interrupts: chunk.outcome.interrupts,\n },\n 'live',\n )\n return\n }\n\n const isRunlessSessionError = chunk.type === 'RUN_ERROR' && !runId\n const isTrackedRunTerminal = Boolean(\n runId && this.lastResume?.runId === runId,\n )\n const isCurrentRunTerminal = Boolean(\n (runId && this.currentRunId === runId) ||\n (this.currentRunId && this.lastResume?.runId === this.currentRunId),\n )\n // Provider adapters sometimes stamp a different run id on continuation\n // events than the client-generated request id. RUN_STARTED updates\n // `activeResumeRunId`, so match that too.\n const isActiveStreamRunTerminal = Boolean(\n this.isLoading &&\n runId &&\n (runId === this.activeResumeRunId || runId === this.currentRunId),\n )\n const isCurrentStreamTerminal =\n this.isLoading && chunk.type === 'RUN_FINISHED' && !runId\n // A resume batch that finishes successfully (or with a non-interrupt\n // terminal) must always clear pending interrupts — even when the provider\n // run id does not correlate. Otherwise Approve works once but the UI\n // keeps showing a stale prompt and blocks follow-up turns.\n const isActiveInterruptSubmissionTerminal = Boolean(\n this.activeInterruptSubmission &&\n this.isLoading &&\n chunk.type === 'RUN_FINISHED' &&\n chunk.outcome?.type !== 'interrupt',\n )\n // Replay can deliver a parent pause after a descendant already finished,\n // and this terminal's run id may not match lastResume.\n const isLineageDescendantTerminal = Boolean(\n this.lastResume &&\n this.supersededInterruptRunIds.has(this.lastResume.runId),\n )\n if (\n isRunlessSessionError ||\n isTrackedRunTerminal ||\n isCurrentRunTerminal ||\n isActiveStreamRunTerminal ||\n isCurrentStreamTerminal ||\n isActiveInterruptSubmissionTerminal ||\n isLineageDescendantTerminal\n ) {\n this.lastResume = null\n // Run settled without an interrupt: drop the durable resume snapshot so a\n // later reload does not try to rejoin a finished run.\n this.persistor?.persistResumeSnapshot(null)\n this.interruptManager.reset()\n return\n }\n this.notifyResumeStateChange('live')\n }\n\n /** Walk parentRunId links and mark this run plus its ancestors as answered. */\n private markLineageAnswered(runId: string): void {\n let current: string | undefined = runId\n let hops = 0\n while (current !== undefined && hops < MAX_RUN_LINEAGE_DEPTH) {\n if (this.supersededInterruptRunIds.has(current)) return\n this.supersededInterruptRunIds.add(current)\n current = this.runParents.get(current)\n hops++\n }\n }\n\n /**\n * The interrupt-resume state for the active/interrupted run (its run/thread\n * ids), or null when there is nothing to resume. Apps can persist this to\n * resume interrupts across a full reload.\n */\n getResumeState(): ChatResumeState | null {\n return this.lastResume ? { ...this.lastResume } : null\n }\n\n /**\n * The id of the run this client has in flight — one it started via a send or\n * rejoined via `joinRun` — or null when there is none. Unlike\n * {@link getResumeState}, this tracks ordinary runs too, not only one that is\n * interrupted or being resumed. A run another client started and that arrives\n * over a live subscription is not this client's run and is not reported here.\n */\n getCurrentRunId(): string | null {\n return this.currentRunId\n }\n\n private setCurrentRunId(runId: string | null): void {\n if (this.currentRunId === runId) return\n this.currentRunId = runId\n this.callbacksRef.current.onRunIdChange(runId)\n }\n\n getInterruptState(): ChatInterruptState<TTools, TInterrupts> {\n return this.interruptManager.getState()\n }\n\n getInterrupts(): BoundInterrupts<TTools, TInterrupts> {\n return this.interruptManager.getInterrupts() as BoundInterrupts<\n TTools,\n TInterrupts\n >\n }\n\n /** @deprecated Use getInterrupts(). */\n getPendingInterrupts(): BoundInterrupts<TTools, TInterrupts> {\n return this.interruptManager.getInterrupts() as BoundInterrupts<\n TTools,\n TInterrupts\n >\n }\n\n resolveInterrupts(approved: boolean): void\n resolveInterrupts(\n resolver: (\n interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,\n ) => undefined,\n ): void\n resolveInterrupts(\n resolution:\n | boolean\n | ((\n interrupt: ResolvableChatInterrupt<TTools, TInterrupts>,\n ) => undefined),\n ): void {\n // Branch so TypeScript can select the InterruptManager.resolve overloads.\n if (typeof resolution === 'boolean') {\n this.interruptManager.resolve(resolution)\n return\n }\n this.interruptManager.resolve(resolution)\n }\n\n cancelInterrupts(): void {\n this.interruptManager.cancel()\n }\n\n retryInterrupts(): void {\n this.interruptManager.retry()\n }\n\n /** Unsafe low-level resume escape hatch. Prefer bound interrupt methods. */\n resumeInterruptsUnsafe(\n resume: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ): Promise<boolean> {\n const target = state ?? this.lastResume\n if (!target) return Promise.resolve(false)\n return this.resumeInterruptsUnsafeForGeneration(\n resume,\n target,\n this.continuationGeneration,\n )\n }\n\n private resumeInterruptsUnsafeForGeneration(\n resume: Array<RunAgentResumeItem>,\n target: ChatResumeState,\n continuationGeneration: number,\n ): Promise<boolean> {\n if (continuationGeneration !== this.continuationGeneration) {\n return Promise.resolve(false)\n }\n // Auto-executed client tools resolve during the parent stream's\n // `pendingToolExecutions` wait — while `isLoading` is still true.\n // Defer the child continuation until that stream settles so we do not\n // race the parent cleanup or return a false \"could not start\" failure.\n if (this.isLoading) {\n return new Promise<boolean>((resolve, reject) => {\n this.queuePostStreamAction(async () => {\n try {\n resolve(\n await this.resumeInterruptsUnsafeForGeneration(\n resume,\n target,\n continuationGeneration,\n ),\n )\n } catch (error) {\n reject(error)\n }\n })\n })\n }\n this.pendingResumeThreadId = target.threadId\n this.pendingResumeParentRunId = target.runId\n this.pendingResumeItems = [...resume]\n return this.streamResponse()\n }\n\n /** @deprecated Use bound interrupt methods or resumeInterruptsUnsafe(). */\n resumeInterrupts(\n resume: Array<RunAgentResumeItem>,\n state?: ChatResumeState,\n ): Promise<boolean> {\n return this.resumeInterruptsUnsafe(resume, state)\n }\n\n private async submitInterruptBatch(\n submission: InterruptManagerSubmission,\n ): Promise<void> {\n const continuationGeneration = this.continuationGeneration\n this.activeInterruptSubmission = submission\n this.interruptSubmissionFailure = undefined\n // Reflect approval decisions in the local message tree immediately so a\n // follow-up turn does not re-serialize tool-calls still stuck in\n // `approval-requested` (issue #532).\n for (const resolution of submission.resolutions) {\n const approved = readApprovalApproved(resolution.payload)\n if (approved === undefined) continue\n const approvalId = resolution.interruptId\n this.processor.addToolApprovalResponse(approvalId, approved)\n }\n const resumed = await this.resumeInterruptsUnsafeForGeneration(\n [...submission.resolutions],\n {\n threadId: submission.threadId,\n runId: submission.interruptedRunId,\n },\n continuationGeneration,\n ).finally(() => {\n // Only clear if this resume still owns the client: `stop()` may have\n // invalidated it while the submission was settling.\n if (this.activeInterruptSubmission === submission) {\n this.activeInterruptSubmission = undefined\n }\n })\n if (continuationGeneration !== this.continuationGeneration) return\n const failure = this.takeInterruptSubmissionFailure()\n if (failure !== undefined) {\n throw { errors: failure.errors }\n }\n if (!resumed) {\n throw new Error('Interrupt continuation could not be started.')\n }\n // Belt-and-suspenders: if the continuation stream finished successfully\n // but correlation failed to clear resume state, drop it now so the next\n // user turn is not blocked by a stale interrupt prompt.\n if (this.lastResume?.runId === submission.interruptedRunId) {\n this.lastResume = null\n this.interruptManager.reset()\n }\n }\n\n private takeInterruptSubmissionFailure():\n | { errors: ReadonlyArray<InterruptSubmissionError> }\n | undefined {\n const failure = this.interruptSubmissionFailure\n this.interruptSubmissionFailure = undefined\n return failure\n }\n\n private interruptGeneration(\n interrupts: ReadonlyArray<ChatPendingInterrupt>,\n ): number {\n let generation: number | undefined\n for (const interrupt of interrupts) {\n const candidate: unknown =\n interrupt.metadata?.['tanstack:interruptBinding']\n if (\n candidate === null ||\n typeof candidate !== 'object' ||\n !('generation' in candidate) ||\n typeof candidate.generation !== 'number' ||\n !Number.isInteger(candidate.generation) ||\n candidate.generation < 0\n ) {\n return 0\n }\n if (generation !== undefined && generation !== candidate.generation) {\n return 0\n }\n generation = candidate.generation\n }\n return generation ?? 0\n }\n\n private generateUniqueId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n\n private setIsLoading(isLoading: boolean): void {\n this.isLoading = isLoading\n this.callbacksRef.current.onLoadingChange(isLoading)\n this.events.loadingChanged(isLoading)\n }\n\n private setStatus(status: ChatClientState): void {\n this.status = status\n this.callbacksRef.current.onStatusChange(status)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setIsSubscribed(isSubscribed: boolean): void {\n this.isSubscribed = isSubscribed\n this.callbacksRef.current.onSubscriptionChange(isSubscribed)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setConnectionStatus(status: ConnectionStatus): void {\n this.connectionStatus = status\n this.callbacksRef.current.onConnectionStatusChange(status)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private setSessionGenerating(isGenerating: boolean): void {\n if (this.sessionGenerating === isGenerating) return\n this.sessionGenerating = isGenerating\n this.callbacksRef.current.onSessionGeneratingChange(isGenerating)\n this.devtoolsBridge.emitSnapshot()\n }\n\n private notifyResumeStateChange(source: InterruptManagerChangeSource): void {\n const resumeState = this.getResumeState()\n // Capture state before invoking callbacks so a synchronous nested change\n // cannot pair this publication's source with a later manager snapshot.\n const interruptState = this.interruptManager.getState()\n // Persist (or clear) the durable resume snapshot so a full page reload can\n // rehydrate pending interrupts and rejoin the run. Folded into the same\n // persistence adapter that stores messages (one record per chat).\n this.persistResumeSnapshot(resumeState)\n this.callbacksRef.current.onResumeStateChange(\n resumeState,\n interruptState.interrupts,\n )\n this.callbacksRef.current.onInterruptStateChange(interruptState, { source })\n }\n\n /**\n * Build the durable resume snapshot from the current resume state + pending\n * interrupt descriptors and hand it to the persistor (null clears it).\n */\n private persistResumeSnapshot(resumeState: ChatResumeState | null): void {\n if (!this.persistor) return\n if (!resumeState) {\n this.persistor.persistResumeSnapshot(null)\n return\n }\n const descriptors = this.interruptManager.getDescriptors()\n this.persistor.persistResumeSnapshot({\n resumeState,\n ...(descriptors.length > 0\n ? { pendingInterrupts: [...descriptors] }\n : {}),\n })\n }\n\n private resetSessionGenerating(options?: {\n preserveClearedStreamTracking?: boolean\n }): void {\n this.activeRunIds.clear()\n if (!options?.preserveClearedStreamTracking) {\n this.clearedStreamTracker.resetActiveRuns()\n }\n this.setSessionGenerating(false)\n }\n\n private setError(error: Error | undefined): void {\n this.error = error\n this.callbacksRef.current.onErrorChange(error)\n this.events.errorChanged(error?.message || null)\n }\n\n private buildDevtoolsBridgeOptions(\n devtools: ChatClientOptions['devtools'],\n ): ChatDevtoolsBridgeOptions {\n const client = this\n return {\n get hookId() {\n return client.uniqueId\n },\n get clientId() {\n return client.uniqueId\n },\n get threadId() {\n return client.threadId\n },\n metadata: {\n hookName: devtools?.hookName ?? 'useChat',\n outputKind: devtools?.outputKind ?? 'chat',\n ...(devtools?.framework ? { framework: devtools.framework } : {}),\n ...(devtools?.name ? { name: devtools.name } : {}),\n },\n getSnapshot: () => this.getDevtoolsSnapshot(),\n getTools: () => this.clientToolsRef.current.values(),\n getMessages: () => this.processor.getMessages(),\n setMessages: (messages: Array<UIMessage>) => {\n this.processor.setMessages(messages)\n },\n addToolResult: (toolCallId, output, errorText) => {\n this.processor.addToolResult(toolCallId, output, errorText)\n },\n generateId: (prefix) => this.generateUniqueId(prefix),\n }\n }\n\n private getDevtoolsSnapshot(): AIDevtoolsChatSnapshot {\n return {\n // The devtools store keeps the objects it gets, and a subagent handle\n // changes in place. A copy lets the panel see each new status and part.\n messages: copyForDevtools(this.processor.getMessages()),\n status: this.status,\n isLoading: this.isLoading,\n isSubscribed: this.isSubscribed,\n connectionStatus: this.connectionStatus,\n sessionGenerating: this.sessionGenerating,\n activeRunIds: Array.from(this.activeRunIds),\n queue: this.getQueue(),\n ...(this.error ? { error: this.error.message } : {}),\n }\n }\n\n private findMessageIdForToolCall(toolCallId: string): string | undefined {\n const messages = this.processor.getMessages()\n for (const message of messages) {\n const match = message.parts.find(\n (part: MessagePart): part is ToolCallPart =>\n part.type === 'tool-call' && part.id === toolCallId,\n )\n if (match) return message.id\n }\n return undefined\n }\n\n private abortSubscriptionLoop(): void {\n this.subscriptionAbortController?.abort()\n this.subscriptionAbortController = null\n }\n\n private resolveProcessing(): void {\n this.processingResolve?.()\n this.processingResolve = null\n }\n\n private cancelInFlightStream(options?: {\n setReadyStatus?: boolean\n abortSubscription?: boolean\n }): void {\n this.abortController?.abort()\n this.abortController = null\n if (options?.abortSubscription) {\n this.abortSubscriptionLoop()\n }\n this.resolveProcessing()\n this.setIsLoading(false)\n // Release deliver claim so an interrupting `deliverMessage` can append\n // after abort (the superseded deliver's finally also clears the claim).\n this.deliverClaim = false\n if (options?.setReadyStatus) {\n this.setStatus('ready')\n }\n }\n\n private reportStreamError(error: Error): void {\n const alreadyReported =\n this.errorReportedGeneration === this.streamGeneration\n this.setError(error)\n // Preserve request-level error semantics even if a RUN_ERROR arrives\n // slightly after loading flips false during stream teardown.\n if (\n this.isLoading ||\n this.status === 'submitted' ||\n this.status === 'streaming'\n ) {\n this.setStatus('error')\n }\n if (!alreadyReported) {\n this.errorReportedGeneration = this.streamGeneration\n this.callbacksRef.current.onError(error)\n }\n }\n\n /**\n * Start the background subscription loop.\n */\n private startSubscription(): void {\n this.subscriptionAbortController = new AbortController()\n const signal = this.subscriptionAbortController.signal\n\n this.consumeSubscription(signal)\n .catch((err) => {\n if (err instanceof Error && err.name !== 'AbortError') {\n this.setConnectionStatus('error')\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.reportStreamError(err)\n }\n // Resolve pending processing so streamResponse doesn't hang\n this.resolveProcessing()\n })\n .finally(() => {\n // Ignore stale loops that were superseded by a restart.\n if (this.subscriptionAbortController?.signal !== signal) {\n return\n }\n this.subscriptionAbortController = null\n if (!signal.aborted && this.isSubscribed) {\n this.setIsSubscribed(false)\n if (this.connectionStatus !== 'error') {\n this.setConnectionStatus('disconnected')\n }\n }\n })\n }\n\n /**\n * Consume chunks from the connection subscription. Chunks are processed in\n * order; the loop yields to the host after each processing budget.\n */\n private async consumeSubscription(signal: AbortSignal): Promise<void> {\n const stream = this.connection.subscribe(signal)\n let chunkProcessingTime = 0\n for await (const chunk of stream) {\n if (signal.aborted) break\n const startedAt = performance.now()\n this.processIncomingChunk(chunk)\n chunkProcessingTime += performance.now() - startedAt\n if (chunkProcessingTime < STREAM_PROCESSING_BUDGET_MS) continue\n chunkProcessingTime = 0\n // Skip the yield when the page is hidden. Browsers clamp timers there,\n // and that wait paces stream pull.\n if (typeof document !== 'undefined' && document.hidden) continue\n await yieldToHost()\n }\n }\n\n /**\n * Re-attach to an in-flight run after a full page reload, replaying its stream\n * from the server's delivery-durability log via `joinRun` (which returns the\n * whole run so far, then tails live to completion).\n *\n * The log is the single source of truth for the run, so we rebuild the\n * in-flight assistant bubble from it rather than trying to reconcile the\n * server-hydrated partial with the replay: on the first chunk that actually\n * (re)builds a message we drop the hydrated in-flight assistant, and the\n * replay reconstructs one clean bubble. Dropping only on real content (not on\n * `RUN_STARTED`) means a rejoin that connects but delivers nothing can never\n * leave an empty bubble behind.\n *\n * Bounded connect: a durable backend keeps a from-start join open waiting for\n * a producer, so a stale pointer to an unknown/evicted run would otherwise pin\n * the UI in a loading state for the backend's full first-chunk deadline. We\n * give up after {@link REJOIN_CONNECT_DEADLINE_MS} if no chunk arrives and\n * clear the dead pointer so it does not retry on the next load.\n *\n * Replay chunks are processed WITHOUT the time-slice yield the live path uses,\n * so the buffered prefix snaps in and only the genuinely-live tail streams at\n * network speed — a reload looks like the run continued, not like it re-typed.\n */\n private resumeInFlightRun(runId: string): void {\n const joinRun = this.connection.joinRun\n if (!joinRun) return\n const controller = new AbortController()\n this.abortController = controller\n this.setCurrentRunId(runId)\n // Record the resume state in-memory BEFORE replaying. Otherwise the\n // replayed `RUN_STARTED` (which carries the PROVIDER run id, not the\n // client/durability-log run id the pointer is keyed by) trips the\n // `!this.lastResume` guard in `updateRunLifecycle` and rewrites the\n // persisted pointer with the provider id — so a SECOND reload would\n // `joinRun` an id the log isn't keyed by and never re-attach.\n this.lastResume = { threadId: this.threadId, runId }\n this.streamContinuationGeneration = this.continuationGeneration\n this.setIsLoading(true)\n this.setStatus('streaming')\n void (async () => {\n let rebuilt = false\n let attached = false\n // Whether the join FAILED (a thrown non-abort error before any chunk), as\n // opposed to merely not delivering in time. Only a failure proves the\n // pointer dead — see the `finally`.\n let refused = false\n const connectTimer = setTimeout(() => {\n if (!attached) controller.abort()\n }, REJOIN_CONNECT_DEADLINE_MS)\n try {\n for await (const chunk of joinRun(runId, controller.signal)) {\n if (controller.signal.aborted) break\n if (!attached) {\n attached = true\n clearTimeout(connectTimer)\n }\n if (!rebuilt && rebuildsAssistantMessage(chunk)) {\n rebuilt = true\n this.dropTrailingInFlightAssistant()\n }\n this.processIncomingChunk(chunk)\n }\n // Same contract as `streamResponse`: client tools may finish (and\n // queue a resume) while `isLoading` is still true. Wait for them\n // before teardown so `drainPostStreamActions` below sees the queue.\n if (this.pendingToolExecutions.size > 0) {\n await Promise.all(this.pendingToolExecutions.values())\n }\n } catch (error) {\n // Pre-attach failures (unknown/evicted run, connect deadline abort)\n // stay soft: keep the restored transcript. Post-attach transport/parser\n // failures are real stream errors and must surface so the UI is not\n // left truncated and silent.\n const isAbort =\n error instanceof Error &&\n (error.name === 'AbortError' || error.name === 'TimeoutError')\n if (!attached && !isAbort) refused = true\n if (attached && !isAbort) {\n this.reportStreamError(\n error instanceof Error ? error : new Error(String(error)),\n )\n }\n } finally {\n clearTimeout(connectTimer)\n if (!attached && refused && this.tailing && !this.disposed) {\n // The server REFUSED the join (unknown / evicted run): the pointer is\n // dead. Clear it so it does not retry and re-pin the UI on the next\n // load. The server's persisted transcript is still loaded.\n //\n // A connect-deadline abort (or an external abort) deliberately does\n // NOT clear it: the run may simply not have produced yet — a durable\n // run whose middleware is still booting a sandbox emits nothing for\n // a while — and clearing on a timeout would permanently orphan a run\n // that is still going. The pointer survives for the next load, which\n // costs that load one more bounded connect attempt.\n //\n // `tailing`/`disposed` guard the same pointer from the other side: a\n // DETACH aborts before the first chunk exactly like an unreachable run\n // does, and a refusal that lands after the view is gone belongs to\n // nobody. `refused` already spares the timeout case; these two spare\n // the \"no view is watching any more\" case, so the pointer only ever\n // dies for a client that is still looking at the run.\n this.lastResume = null\n this.persistor?.persistResumeSnapshot(null)\n }\n if (this.abortController === controller) {\n this.abortController = null\n this.setIsLoading(false)\n if (this.status === 'streaming') this.setStatus('ready')\n await this.drainPostStreamActions()\n }\n }\n })()\n }\n\n /**\n * Drop a hydrated, still-in-flight assistant turn so a resume replay can\n * rebuild it cleanly. Only touches a trailing assistant message (the shape a\n * reload-mid-stream leaves); a thread whose last turn is a user message (run\n * never produced, or already settled) is left untouched.\n */\n private dropTrailingInFlightAssistant(): void {\n const messages = this.processor.getMessages()\n const last = messages[messages.length - 1]\n if (last && last.role === 'assistant') {\n this.processor.setMessages(messages.slice(0, -1))\n }\n }\n\n private processIncomingChunk(chunk: StreamChunk): void {\n chunk = restoreInboundChunk(chunk)\n if (\n chunk.type === 'RUN_ERROR' &&\n this.isActiveInterruptSubmissionFailure(chunk)\n ) {\n const interruptErrors = tanstackMetadata(chunk)?.interruptErrors\n this.interruptSubmissionFailure = {\n errors: Array.isArray(interruptErrors) ? interruptErrors : [],\n }\n }\n if (this.connectionStatus === 'connecting') {\n this.setConnectionStatus('connected')\n }\n const shouldIgnore = this.clearedStreamTracker.shouldIgnoreChunk(chunk)\n if (shouldIgnore) {\n if (chunk.type === 'RUN_FINISHED' || chunk.type === 'RUN_ERROR') {\n if (getChunkRunId(chunk)) {\n this.updateRunLifecycle(chunk, { resolveProcessing: false })\n } else {\n this.drainIgnoredRunlessChunk(chunk)\n }\n this.retireIgnoredClearedTerminalChunk(chunk)\n this.resolveJoinedRun(chunk)\n }\n return\n }\n this.callbacksRef.current.onChunk(chunk)\n this.devtoolsBridge.observeChunk(chunk)\n const attributedId = readSubagentRunId(chunk)\n // A resumed child starts again under the same id, so its start lifts the stop.\n if (attributedId && chunk.type === EventType.SUBAGENT_STARTED) {\n this.stoppedSubagentIds.delete(attributedId)\n }\n if (attributedId && this.stoppedSubagentIds.has(attributedId)) {\n this.updateRunLifecycle(chunk)\n this.resolveJoinedRun(chunk)\n return\n }\n this.processor.processChunk(chunk)\n this.syncSubagentHandles()\n this.updateRunLifecycle(chunk)\n this.observeInterruptState(chunk)\n this.resolveJoinedRun(chunk)\n }\n\n private isActiveInterruptSubmissionFailure(\n chunk: Extract<StreamChunk, { type: 'RUN_ERROR' }>,\n ): boolean {\n const submission = this.activeInterruptSubmission\n const errors = tanstackMetadata(chunk)?.interruptErrors\n if (!submission || !Array.isArray(errors) || errors.length === 0) {\n return false\n }\n const runId = getChunkRunId(chunk)\n if (runId !== undefined && runId !== this.currentRunId) return false\n if (\n typeof chunk.threadId === 'string' &&\n chunk.threadId !== submission.threadId\n ) {\n return false\n }\n return errors.every((error) => {\n if (\n error == null ||\n typeof error !== 'object' ||\n typeof error.threadId !== 'string' ||\n typeof error.interruptedRunId !== 'string' ||\n typeof error.generation !== 'number'\n ) {\n return false\n }\n return (\n error.threadId === submission.threadId &&\n error.interruptedRunId === submission.interruptedRunId &&\n error.generation === submission.generation\n )\n })\n }\n\n private resolveJoinedRun(chunk: StreamChunk): void {\n if (chunk.type !== 'RUN_FINISHED' && chunk.type !== 'RUN_ERROR') return\n const runId = getChunkRunId(chunk)\n if (runId === undefined) return\n const resolve = this.joinedRunWaiters.get(runId)\n if (resolve === undefined) return\n this.joinedRunWaiters.delete(runId)\n resolve()\n }\n\n /**\n * Ensure subscription loop is running, starting it if needed.\n */\n private ensureSubscription(): void {\n if (!this.isSubscribed) {\n this.subscribe()\n return\n }\n if (\n !this.subscriptionAbortController ||\n this.subscriptionAbortController.signal.aborted\n ) {\n this.subscribe({ restart: true })\n }\n }\n\n /**\n * Create a promise that resolves when onStreamEnd fires.\n * Used by streamResponse to await processing completion.\n */\n private waitForProcessing(): Promise<void> {\n // Resolve any stale promise (e.g., from a previous aborted request)\n this.resolveProcessing()\n return new Promise<void>((resolve) => {\n this.processingResolve = resolve\n })\n }\n\n /**\n * Send a message and stream the response.\n * Supports both simple string content and multimodal content (images, audio, video, documents).\n *\n * @param content - The message content. Can be:\n * - A simple string for text-only messages\n * - A MultimodalContent object with content array and optional custom ID\n * @param body - Optional body parameters to merge with the client's base body for this request.\n * Uses shallow merge with per-message body taking priority.\n * @param sendOptions - Per-call overrides. `{ whenBusy }` overrides the\n * queue policy for this one send. `{ body }`\n * shallow-merges with `body` and with the chat-level\n * `body` / `forwardedProps`. `sendOptions.body` wins\n * on key collisions. Framework hooks forward this\n * object as their second argument.\n *\n * @example\n * ```ts\n * // Simple text message\n * await client.sendMessage('Hello!')\n *\n * // Text message with custom body params\n * await client.sendMessage('Hello!', { temperature: 0.7 })\n *\n * // Per-call whenBusy override\n * await client.sendMessage('Urgent', undefined, { whenBusy: 'interrupt' })\n *\n * // Per-call body via options. Same effect as the positional arg.\n * // This is the shape the framework hooks (`useChat`, `injectChat`) forward.\n * await client.sendMessage('Hello!', undefined, { body: { temperature: 0.7 } })\n *\n * // Multimodal message with image\n * await client.sendMessage({\n * content: [\n * { type: 'text', content: 'What is in this image?' },\n * { type: 'image', source: { type: 'url', value: 'https://example.com/photo.jpg' } }\n * ]\n * })\n *\n * // Multimodal message with custom ID and body params\n * await client.sendMessage(\n * {\n * content: [\n * { type: 'text', content: 'Describe this audio' },\n * { type: 'audio', source: { type: 'data', value: 'base64...' } }\n * ],\n * id: 'custom-message-id'\n * },\n * { model: 'gpt-5.5' }\n * )\n * ```\n */\n async sendMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n sendOptions?: SendMessageOptions,\n ): Promise<void> {\n this.mountDevtools()\n const emptyMessage = typeof content === 'string' && !content.trim()\n if (emptyMessage) {\n return\n }\n if (this.hasPendingInterrupts()) {\n throw new Error(\n 'ChatClient: cannot send normal input while pending interrupts exist. Use resumeInterrupts() instead.',\n )\n }\n\n const resolvedBody = { ...body, ...sendOptions?.body }\n\n if (this.isSendBusy()) {\n const { action, id } = this.decideWhenBusy(content, sendOptions)\n if (action === 'drop') {\n return\n }\n if (action === 'queue') {\n this.enqueueMessage(content, resolvedBody, id)\n return\n }\n // 'interrupt': abort the current stream, then send now.\n // Unlike stop(), does not flush already-queued messages — they drain\n // after this interrupting send settles successfully.\n // Claim sendInFlight *before* cancelling so a concurrent send cannot\n // slip in between cancel and the deliver below.\n this.stopMessageQueueDrain = true\n this.sendInFlight = true\n this.cancelInFlightStream({ setReadyStatus: true })\n this.resetSessionGenerating()\n } else {\n this.sendInFlight = true\n }\n\n try {\n await this.deliverMessage(content, resolvedBody)\n } finally {\n this.sendInFlight = false\n }\n }\n\n /** True while interrupt descriptors still own continuation. */\n private hasPendingInterrupts(): boolean {\n return this.interruptManager.getDescriptors().length > 0\n }\n\n /** True while a stream is active, a send is claiming the client, or the queue is draining. */\n private isSendBusy(): boolean {\n return this.isLoading || this.sendInFlight || this.messageQueueDraining\n }\n\n private resolveBusyReason(): QueueBusyReason {\n if (this.isLoading) return 'streaming'\n if (this.messageQueueDraining) return 'draining'\n return 'sendInFlight'\n }\n\n /**\n * Append a user message and run the stream. Used by both direct sends and\n * queue drains — callers are responsible for busy/queue policy.\n *\n * Claims delivery synchronously before appending so concurrent callers\n * cannot both add a user message when only one stream can run.\n */\n private async deliverMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n ): Promise<boolean> {\n if (this.isLoading || this.deliverClaim) {\n return false\n }\n this.deliverClaim = true\n try {\n const normalizedContent = this.normalizeMessageInput(content)\n this.pendingMessageBody = body\n const userMessage = this.processor.addUserMessage(\n normalizedContent.content,\n normalizedContent.id,\n normalizedContent.metadata,\n )\n this.events.messageSent(userMessage.id, normalizedContent.content)\n return await this.streamResponse()\n } finally {\n this.deliverClaim = false\n }\n }\n\n /**\n * Resolve the effective action for a send that arrives while busy.\n * The returned `id` is the id that will be stored if the action is `queue`.\n */\n private decideWhenBusy(\n content: string | MultimodalContent,\n sendOptions?: SendMessageOptions,\n ): { action: WhenBusy; id: string } {\n const id = this.generateUniqueId('queued')\n if (sendOptions?.whenBusy) {\n return { action: sendOptions.whenBusy, id }\n }\n const { strategy, whenBusy } = this.queueConfig\n if (strategy) {\n const { action } = strategy({\n pending: {\n id,\n content,\n createdAt: Date.now(),\n },\n busyReason: this.resolveBusyReason(),\n queued: this.getQueue(),\n })\n return { action, id }\n }\n return { action: whenBusy, id }\n }\n\n private enqueueMessage(\n content: string | MultimodalContent,\n body?: Record<string, any>,\n id?: string,\n ): void {\n const { maxSize, onOverflow } = this.queueConfig\n if (maxSize !== undefined && this.messageQueue.length >= maxSize) {\n // maxSize 0 is a hard cap (never queue). drop-oldest cannot make room.\n if (onOverflow === 'reject' || maxSize === 0) {\n return\n }\n this.messageQueue.shift() // drop-oldest\n }\n this.messageQueue.push({\n id: id ?? this.generateUniqueId('queued'),\n content,\n createdAt: Date.now(),\n ...(body !== undefined ? { body } : {}),\n })\n this.emitQueueChange()\n }\n\n /**\n * Normalize the message input to extract content, optional id, and\n * optional metadata. String form has no metadata. Trims string content.\n */\n private normalizeMessageInput(input: string | MultimodalContent): {\n content: string | Array<ContentPart>\n id?: string\n metadata?: Record<string, any>\n } {\n if (typeof input === 'string') {\n return { content: input.trim() }\n }\n return {\n content: input.content,\n id: input.id,\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n }\n }\n\n /**\n * Append a message and stream the response\n */\n async append(message: UIMessage | ModelMessage): Promise<void> {\n this.mountDevtools()\n if (this.hasPendingInterrupts()) {\n throw new Error(\n 'ChatClient: cannot append normal input while pending interrupts exist. Use resumeInterrupts() instead.',\n )\n }\n // Normalize the message to ensure it has id and createdAt\n const normalizedMessage = normalizeToUIMessage(message, generateMessageId)\n\n // Skip system messages - they're handled via systemPrompts, not UIMessages\n if (normalizedMessage.role === 'system') {\n return\n }\n\n // Type assertion: after checking for system, we know it's user or assistant\n const uiMessage = normalizedMessage as UIMessage\n\n // Emit message appended event\n this.events.messageAppended(uiMessage)\n\n // Add to messages\n const messages = this.processor.getMessages()\n this.processor.setMessages([...messages, uiMessage])\n this.devtoolsBridge.emitSnapshot()\n\n // If stream is in progress, queue the response for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(async () => {\n await this.streamResponse()\n })\n return\n }\n\n await this.streamResponse()\n }\n\n /**\n * Stream a response from the LLM.\n * Returns true if the stream completed successfully, false on abort or error.\n */\n private async streamResponse(): Promise<boolean> {\n // Guard against concurrent streams - if already loading, skip\n if (this.isLoading) {\n return false\n }\n\n // Track generation so a superseded stream's cleanup doesn't clobber the new one\n const generation = ++this.streamGeneration\n this.streamContinuationGeneration = this.continuationGeneration\n // Native interrupt continuation is a fresh child run. The interrupted run\n // is carried as parentRunId and the complete resolution batch as resume.\n const resumeThreadId = this.pendingResumeThreadId\n const resumeParentRunId = this.pendingResumeParentRunId\n const resumeItems = this.pendingResumeItems\n this.pendingResumeThreadId = null\n this.pendingResumeParentRunId = null\n this.pendingResumeItems = null\n const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n this.setCurrentRunId(runId)\n this.activeResumeThreadId = resumeThreadId ?? this.threadId\n this.activeResumeRunId = runId\n\n this.setIsLoading(true)\n // Hand off from deliverClaim to isLoading so nested drain can call\n // deliverMessage after this stream settles (while the outer deliver\n // is still on the stack).\n this.deliverClaim = false\n this.setStatus('submitted')\n this.setError(undefined)\n this.errorReportedGeneration = null\n this.abortController = new AbortController()\n // Capture the signal immediately so that a concurrent stop() or\n // sendMessage() that reassigns this.abortController cannot cause\n // connect() to receive a stale or null signal.\n const signal = this.abortController.signal\n // Reset pending tool executions for the new stream\n this.pendingToolExecutions.clear()\n let streamCompletedSuccessfully = false\n let activeDevtoolsRunId: string | null = null\n let runTerminalEventEmitted = false\n\n try {\n // Get UIMessages with parts (preserves approval state and client tool results)\n const messages = this.messagesForSend(this.processor.getMessages())\n const clientTools = new Map(this.clientToolsRef.current)\n const runtimeContext = this.context\n\n // Call onResponse callback\n await this.callbacksRef.current.onResponse()\n\n // If the stream was cancelled during the onResponse await (e.g. stop()\n // from a callback or unmount, or reload() superseding this stream),\n // bail out before allocating waitForProcessing() — otherwise the\n // resolveProcessing() that ran during cancellation is a no-op and the\n // await processingComplete below would deadlock.\n if (signal.aborted) {\n return false\n }\n\n // Merge sources for the wire `forwardedProps` field, in priority\n // order (later spreads win):\n // 1. Legacy `body` option (deprecated).\n // 2. Canonical `forwardedProps` option (wins over `body`).\n // 3. Per-call body (`pendingMessageBody`: positional + sendOptions.body).\n // The AG-UI standard `threadId` is sent at the wire's top level for\n // run/conversation correlation, so we no longer auto-emit a separate\n // `conversationId` here — `chat({ threadId })` server-side covers the\n // same role for devtools/observability.\n const mergedBody = {\n ...this.bodyOption,\n ...this.forwardedPropsOption,\n ...this.pendingMessageBody,\n }\n\n // Clear the pending message body after use\n this.pendingMessageBody = undefined\n\n // Generate stream ID — assistant message will be created by stream events\n this.currentStreamId = this.generateUniqueId('stream')\n this.devtoolsBridge.setCurrentStreamId(this.currentStreamId)\n this.currentMessageId = null\n this.activeClientTools = clientTools\n this.activeContext = runtimeContext\n\n // Reset processor stream state for new response — prevents stale\n // messageStates entries (from a previous stream) from blocking\n // creation of a new assistant message (e.g. after reload).\n this.processor.prepareAssistantMessage()\n\n // Ensure subscription loop is running\n this.ensureSubscription()\n\n // Set up promise that resolves when onStreamEnd fires\n const processingComplete = this.waitForProcessing()\n\n // Build per-send run context for AG-UI compliance\n // Note: mergedBody already contains the merged this.body + pendingMessageBody\n // (pendingMessageBody was cleared above, so we use mergedBody as forwardedProps)\n // Convert each client tool's `inputSchema` (a Standard Schema:\n // Zod, ArkType, Valibot, etc.) to JSON Schema for the wire. Foreign\n // AG-UI servers consuming `RunAgentInput.tools[].parameters` expect\n // JSON Schema; sending a Standard Schema instance directly would\n // serialize to an unusable shape.\n let byokHeaders: Record<string, string> | undefined\n if (this.byok) {\n const provider = resolveByokProviderId(\n this.byokProvider,\n mergedBody.provider,\n byokFallbackProviderId(this.byok),\n )\n byokHeaders = await prepareResolvedByokHeaders(this.byok, provider)\n }\n\n const runContext = {\n threadId: resumeThreadId ?? this.threadId,\n runId,\n ...(resumeParentRunId !== null\n ? { parentRunId: resumeParentRunId }\n : {}),\n clientTools: Array.from(clientTools.values()).map((t) => ({\n name: t.name,\n description: t.description,\n parameters: t.inputSchema\n ? convertSchemaToJsonSchema(t.inputSchema)\n : { type: 'object' },\n })),\n forwardedProps: { ...mergedBody },\n ...(resumeItems ? { resume: resumeItems } : {}),\n ...(byokHeaders ? { headers: byokHeaders } : {}),\n }\n this.devtoolsBridge.beginRun(runContext.runId, runContext.threadId)\n activeDevtoolsRunId = runContext.runId\n this.devtoolsBridge.emitRunLifecycle(\n 'run:created',\n runContext.runId,\n 'created',\n )\n this.devtoolsBridge.emitRunLifecycle(\n 'run:started',\n runContext.runId,\n 'started',\n )\n this.devtoolsBridge.emitSnapshot()\n\n // Send through normalized connection (pushes chunks to subscription queue)\n await this.connection.send(messages, mergedBody, signal, runContext)\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- mutated asynchronously during await\n if (generation !== this.streamGeneration || signal.aborted) {\n return false\n }\n\n // connect() send() already waited until the subscribe queue was idle.\n // Kick the processing wait so a stream that ends on tool_calls (no\n // interrupt / stop) cannot hang. Subscribe/send sockets still wait for\n // a request-ending terminal below.\n if (this.connectionDrainsOnSend) {\n this.resolveProcessing()\n }\n\n // Wait for subscription loop to finish processing all chunks\n await processingComplete\n\n // If this stream was superseded (e.g. by reload()), bail out —\n // the new stream owns the processor and processingResolve now.\n if (generation !== this.streamGeneration) {\n return false\n }\n\n // A RUN_ERROR from the stream transitions status to error.\n // Do not treat this stream as a successful completion.\n if (this.status === 'error') {\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:errored',\n activeDevtoolsRunId,\n 'errored',\n this.error ? { error: this.error.message } : {},\n )\n runTerminalEventEmitted = true\n }\n return false\n }\n\n // Wait for pending client tool executions\n if (this.pendingToolExecutions.size > 0) {\n await Promise.all(this.pendingToolExecutions.values())\n }\n\n // Finalize (idempotent — may already be done by RUN_FINISHED handler)\n this.processor.finalizeStream()\n streamCompletedSuccessfully = true\n } catch (err: unknown) {\n const error = err instanceof Error ? err : new Error(String(err))\n if (error.name === 'AbortError') {\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:cancelled',\n activeDevtoolsRunId,\n 'cancelled',\n )\n runTerminalEventEmitted = true\n }\n return false\n }\n if (error instanceof ByokMissingError) {\n this.byok?.request(error.provider, 'missing')\n }\n if (error instanceof ByokBlockedError && error.reason === 'locked') {\n this.byok?.request(error.provider, 'locked')\n }\n if (generation === this.streamGeneration) {\n this.reportStreamError(error)\n if (activeDevtoolsRunId) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:errored',\n activeDevtoolsRunId,\n 'errored',\n { error: error.message },\n )\n runTerminalEventEmitted = true\n }\n }\n if (\n generation === this.streamGeneration &&\n (error instanceof ByokMissingError ||\n error instanceof ByokBlockedError ||\n error instanceof ByokUnresolvedProviderError)\n ) {\n throw error\n }\n } finally {\n // Only clean up if this is still the active stream.\n // A superseded stream (e.g. reload() started a new one) must not\n // clobber the new stream's abortController or isLoading state.\n if (generation === this.streamGeneration) {\n this.currentStreamId = null\n this.devtoolsBridge.setCurrentStreamId(null)\n this.currentMessageId = null\n this.setCurrentRunId(null)\n this.activeClientTools = null\n this.activeContext = undefined\n this.abortController = null\n this.setIsLoading(false)\n this.pendingMessageBody = undefined // Ensure it's cleared even on error\n\n if (activeDevtoolsRunId && !runTerminalEventEmitted) {\n if (streamCompletedSuccessfully) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:completed',\n activeDevtoolsRunId,\n 'completed',\n )\n } else if (signal.aborted) {\n this.devtoolsBridge.emitRunLifecycle(\n 'run:cancelled',\n activeDevtoolsRunId,\n 'cancelled',\n )\n }\n }\n\n // Drain any actions that were queued while the stream was in progress\n await this.drainPostStreamActions()\n\n if (streamCompletedSuccessfully) {\n this.rememberProcessorMessageIds()\n if (this.status !== 'ready') {\n // Terminal run, but onStreamEnd never fired: the processor had\n // no assistant message to emit it for (e.g. a bare\n // RUN_FINISHED{stop}, #421). The normal path already set\n // 'ready', so this is a no-op.\n this.setStatus('ready')\n }\n // Auto-send queued messages once the run fully settles. Skip if a\n // drain loop is already walking the queue (avoids nested re-entry).\n if (!this.messageQueueDraining) {\n await this.drainQueue()\n }\n } else {\n // Error/abort settle for the active generation: don't strand or\n // later mis-order queued messages. A failed turn flushes the queue\n // (consistent with stop()); it must NOT auto-drain into a likely\n // broken endpoint.\n this.flushQueue()\n }\n }\n }\n\n return streamCompletedSuccessfully\n }\n\n /**\n * Start the client subscription loop.\n * This controls the connection lifecycle independently from request lifecycle.\n */\n subscribe(options?: { restart?: boolean }): void {\n const restart = options?.restart === true\n if (this.isSubscribed && !restart) {\n return\n }\n\n if (this.isSubscribed && restart) {\n this.abortSubscriptionLoop()\n }\n\n this.setIsSubscribed(true)\n this.setConnectionStatus('connecting')\n this.startSubscription()\n }\n\n /**\n * Unsubscribe and fully tear down live behavior.\n * This aborts an in-flight request and the subscription loop.\n */\n unsubscribe(): void {\n this.cancelInFlightStream({\n setReadyStatus: true,\n abortSubscription: true,\n })\n this.discardPendingSends()\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.setConnectionStatus('disconnected')\n }\n\n /**\n * Reload the last assistant message\n */\n async reload(): Promise<void> {\n const messages = this.processor.getMessages()\n if (messages.length === 0) return\n\n // Find the last user message\n const lastUserMessageIndex = messages.findLastIndex(\n (m) => m.role === 'user',\n )\n\n if (lastUserMessageIndex === -1) return\n\n // Cancel any active stream before reloading\n if (this.isLoading) {\n this.cancelInFlightStream()\n }\n // Discard pending follow-ups so \"regenerate last answer\" does not also\n // auto-send messages that were typed during the previous stream.\n this.discardPendingSends()\n\n this.events.reloaded(lastUserMessageIndex)\n\n // Remove all messages after the last user message\n this.processor.removeMessagesAfter(lastUserMessageIndex)\n this.devtoolsBridge.emitSnapshot()\n\n // Resend\n await this.streamResponse()\n }\n\n /**\n * Stop the current stream\n */\n stop(): void {\n // Invalidate deferred work from the stopped continuation.\n // This aborts the local request. A durable server run keeps going.\n this.continuationGeneration++\n const hadLocalStream = this.abortController !== null\n this.cancelInFlightStream({ setReadyStatus: true })\n this.discardPendingSends()\n this.lastResume = null\n this.activeInterruptSubmission = undefined\n this.interruptManager.reset()\n if (hadLocalStream) {\n this.resetSessionGenerating()\n }\n this.events.stopped()\n }\n\n /**\n * Clear all messages\n */\n clear(): void {\n const hadLocalStream = this.abortController !== null\n this.clearedStreamTracker.snapshotClear({\n messages: this.processor.getMessages(),\n activeRunIds: this.activeRunIds,\n currentRunId: this.currentRunId,\n })\n // Always cancel in-flight work so clear works without message persistence.\n if (this.isLoading || hadLocalStream) {\n this.cancelInFlightStream({ setReadyStatus: true })\n this.resetSessionGenerating({ preserveClearedStreamTracking: true })\n } else if (this.activeRunIds.size > 0) {\n this.resetSessionGenerating({ preserveClearedStreamTracking: true })\n }\n // Suppress persisting the empty snapshot that clearMessages emits, then\n // remove the stored conversation outright.\n this.persistor?.beginClear()\n this.processor.clearMessages()\n this.resetHistoryPaging()\n this.discardPendingSends()\n this.persistor?.remove()\n this.stoppedSubagentIds.clear()\n this.lastResume = null\n this.interruptManager.reset()\n this.pendingResumeThreadId = null\n this.pendingResumeParentRunId = null\n this.pendingResumeItems = null\n this.setError(undefined)\n this.events.messagesCleared()\n }\n\n /**\n * Add the result of a client-side tool execution\n */\n async addToolResult(result: ClientToolResult): Promise<void> {\n const clientTool = this.clientToolsRef.current.get(result.tool)\n await this.addToolResultForClientTool(\n result,\n clientTool,\n this.streamContinuationGeneration,\n )\n }\n\n private async addToolResultForClientTool(\n result: ClientToolResult,\n clientTool: AnyClientTool | undefined,\n continuationGeneration: number,\n context?: ChatClientRunEventContext,\n ): Promise<void> {\n if (result.state !== 'output-error') {\n try {\n let output =\n clientTool?.outputSchema && isStandardSchema(clientTool.outputSchema)\n ? await this.validateClientToolOutput(clientTool, result.output)\n : result.output\n // Only an interrupt resume needs JSON output. The legacy continuation\n // keeps the raw value.\n if (\n this.interruptManager\n .getDescriptors()\n .some((interrupt) => interrupt.toolCallId === result.toolCallId)\n ) {\n output = cloneAndDeepFreezeJson(output)\n }\n result = { ...result, output }\n } catch (error: any) {\n result = {\n ...result,\n output: null,\n state: 'output-error',\n errorText: error.message,\n }\n }\n }\n\n this.events.toolResultAdded(\n result.toolCallId,\n result.tool,\n result.output,\n result.state || 'output-available',\n context,\n )\n\n if (continuationGeneration !== this.continuationGeneration) return\n\n // Always update local message state so the tool-call part is terminal in\n // the UI even when the AG-UI interrupt path owns server continuation.\n this.processor.addToolResult(\n result.toolCallId,\n result.output,\n result.state === 'output-error'\n ? result.errorText || 'Tool execution failed'\n : undefined,\n )\n this.devtoolsBridge.emitSnapshot()\n\n const resolvedViaInterrupt =\n result.state === 'output-error'\n ? this.interruptManager.resolveClientToolError(\n result.toolCallId,\n result.errorText || 'Tool execution failed',\n )\n : this.interruptManager.resolveClientToolOutput(\n result.toolCallId,\n result.output,\n )\n if (resolvedViaInterrupt) {\n // Interrupt manager stages/submits the resume batch (deferred until the\n // parent stream settles when still loading). Skip legacy continuation.\n return\n }\n\n // If stream is in progress, queue continuation check for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(() =>\n continuationGeneration === this.continuationGeneration\n ? this.checkForContinuation()\n : Promise.resolve(),\n )\n return\n }\n\n await this.checkForContinuation()\n }\n\n private async validateClientToolOutput(\n clientTool: AnyClientTool,\n output: unknown,\n ): Promise<unknown> {\n const validation = await validateWithStandardSchema<unknown>(\n clientTool.outputSchema,\n output,\n )\n if (!validation.success) {\n throw new Error(\n validation.issues.map((issue) => issue.message).join(', '),\n )\n }\n return validation.data\n }\n\n /**\n * Respond to a tool approval request\n */\n async addToolApprovalResponse(response: {\n id: string // approval.id, not toolCallId\n approved: boolean\n }): Promise<void> {\n // Reflect the decision on the tool-call part so approval UIs that render\n // from `part.state` (the deprecated pre-interrupt pattern) clear the prompt\n // and show the response. The bound interrupt resolution below drives the\n // actual continuation; this keeps the legacy message-state surface in sync.\n this.processor.addToolApprovalResponse(response.id, response.approved)\n this.devtoolsBridge.emitSnapshot()\n\n if (\n this.interruptManager.resolveToolApprovalDecision(\n response.id,\n response.approved,\n )\n ) {\n return\n }\n // Find the tool call ID from the approval ID\n const messages = this.processor.getMessages()\n let foundToolCallId: string | undefined\n\n for (const msg of messages) {\n const toolCallPart = msg.parts.find(\n (p: MessagePart): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === response.id,\n )\n if (toolCallPart) {\n foundToolCallId = toolCallPart.id\n break\n }\n }\n\n if (foundToolCallId) {\n this.events.toolApprovalResponded(\n response.id,\n foundToolCallId,\n response.approved,\n )\n }\n\n // Add response via processor\n this.processor.addToolApprovalResponse(response.id, response.approved)\n this.devtoolsBridge.emitSnapshot()\n\n // If stream is in progress, queue continuation check for after it ends\n if (this.isLoading) {\n this.queuePostStreamAction(() => this.checkForContinuation())\n return\n }\n\n await this.checkForContinuation()\n }\n\n /**\n * Queue an action to be executed after the current stream ends\n */\n private queuePostStreamAction(action: () => Promise<void>): void {\n const continuationGeneration = this.continuationGeneration\n this.postStreamActions.push(async () => {\n if (continuationGeneration !== this.continuationGeneration) return\n await action()\n })\n }\n\n /**\n * Drain and execute all queued post-stream actions\n */\n private async drainPostStreamActions(): Promise<void> {\n if (this.draining) return\n this.draining = true\n try {\n let action: (() => Promise<void>) | undefined\n while ((action = this.postStreamActions.shift()) !== undefined) {\n await action()\n }\n } finally {\n this.draining = false\n }\n }\n\n /**\n * Check if we should continue the flow and do so if needed\n */\n private async checkForContinuation(): Promise<void> {\n // stop() bumps continuationGeneration without opening a new stream.\n if (this.streamContinuationGeneration !== this.continuationGeneration) {\n return\n }\n if (this.hasPendingInterrupts()) return\n\n // Prevent duplicate continuation attempts\n if (this.continuationPending || this.isLoading) {\n this.continuationSkipped = true\n return\n }\n\n if (this.shouldAutoSend()) {\n this.continuationPending = true\n this.continuationSkipped = false\n let succeeded = false\n try {\n succeeded = await this.streamResponse()\n } finally {\n this.continuationPending = false\n }\n // If a queued check was skipped while continuationPending was true\n // (e.g. a chained approval responded to during the stream), re-evaluate\n // now that the flag is cleared. Only replay after a successful stream —\n // aborted or errored streams should not trigger further continuation.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- mutated asynchronously during await\n if (this.continuationSkipped && succeeded) {\n this.continuationSkipped = false\n await this.checkForContinuation()\n }\n }\n }\n\n /**\n * Check if all tool calls are complete and we should auto-send.\n * Requires that there is at least one tool call in the last assistant message;\n * a text-only response has nothing to auto-send.\n */\n private shouldAutoSend(): boolean {\n // A pending interrupt owns the next send. Auto-continuing after a\n // completed server tool would start a sibling run and hide the card.\n if (this.lastResume) return false\n // Ownership follows the descriptors, not the submission handle. Generic\n // interrupts settle the resume stream through a post-stream action that\n // runs before `submitInterruptBatch`'s `finally` clears the handle, so\n // gating on the handle alone would strand a legacy client tool that the\n // native resume itself emitted (#1106).\n if (this.activeInterruptSubmission && this.hasPendingInterrupts()) {\n return false\n }\n if (this.interruptManager.getInterrupts().length > 0) return false\n const messages = this.processor.getMessages()\n const lastAssistant = messages.findLast(\n (m: UIMessage) => m.role === 'assistant',\n )\n if (!lastAssistant) return false\n const hasToolCalls = lastAssistant.parts.some(\n (p: MessagePart) => p.type === 'tool-call',\n )\n if (!hasToolCalls) return false\n return this.processor.areAllToolsComplete()\n }\n\n /**\n * Get current messages\n */\n getMessages(): Array<UIMessage<TTools>> {\n return this.processor.getMessages() as Array<UIMessage<TTools>>\n }\n\n getSubagents() {\n return [...this.subagentHandles.values()]\n }\n\n private devtoolsSnapshotQueued = false\n\n /** One devtools snapshot per microtask, however many chunks arrive. */\n private queueDevtoolsSnapshot(): void {\n if (this.devtoolsSnapshotQueued) return\n this.devtoolsSnapshotQueued = true\n queueMicrotask(() => {\n this.devtoolsSnapshotQueued = false\n this.devtoolsBridge.emitSnapshot()\n })\n }\n\n private syncSubagentHandles(): void {\n // Every card, nested ones included, gets a handle.\n const cards = collectSubagentParts(this.processor.getMessages())\n const present = new Set(cards.map((part) => part.subagent.id))\n // A card that left the messages (clear, reload) loses its handle.\n for (const id of this.subagentHandles.keys()) {\n if (!present.has(id)) this.subagentHandles.delete(id)\n }\n for (const part of cards) {\n const id = part.subagent.id\n const existing = this.subagentHandles.get(id)\n if (existing) {\n if (part.subagent === existing) continue\n // Keep the same live object, with exactly the card's fields. The\n // wire reads this object, so a field the card dropped goes too.\n const { stop } = existing\n for (const key of Object.keys(existing)) {\n Reflect.deleteProperty(existing, key)\n }\n Object.assign(existing, part.subagent, { stop })\n part.subagent = existing\n continue\n }\n const handle: SubagentHandle = {\n ...part.subagent,\n stop: () => {\n this.stopSubagent(id)\n },\n }\n this.subagentHandles.set(id, handle)\n part.subagent = handle\n }\n }\n\n private stopSubagent(id: string): void {\n const handle = this.subagentHandles.get(id)\n if (!handle || handle.status !== 'running') return\n this.stoppedSubagentIds.add(id)\n this.processor.processChunk({\n type: EventType.SUBAGENT_ERROR,\n subagentRunId: id,\n message: 'Stopped',\n timestamp: Date.now(),\n })\n this.syncSubagentHandles()\n handle.status = 'error'\n handle.error = { message: 'Stopped' }\n this.abortController?.abort()\n }\n\n /**\n * True when the last hydrate or older-page response said more messages exist.\n */\n getHasOlderMessages() {\n return this.hasOlderMessages\n }\n\n /**\n * Fetch the next older window and put it in front of the painted messages.\n *\n * No-op when there is no older page, no cursor, or a page load is already\n * running. A network failure rejects and leaves the painted messages as they\n * are; {@link getHasOlderMessages} stays true.\n */\n async loadOlderMessages() {\n const hydrate = this.connection.hydrate\n if (hydrate === undefined) return\n if (this.disposed) return\n if (!this.hasOlderMessages) return\n const cursor = this.olderMessagesCursor\n if (cursor === undefined) return\n if (this.loadOlderMessagesInFlight) return\n\n const hydrateOptions: ChatHydrateOptions = { before: cursor }\n if (this.historyPageSize !== undefined) {\n hydrateOptions.limit = this.historyPageSize\n }\n\n this.loadOlderMessagesInFlight = true\n const generation = this.historyGeneration\n try {\n const result = await hydrate(this.threadId, hydrateOptions)\n if (this.disposed) return\n if (generation !== this.historyGeneration) return\n const olderMessages = normalizeMessagesDates(result.messages)\n if (olderMessages.length === 0) {\n throw new Error('Older page was empty')\n }\n this.applyHydrationPage(result.page)\n this.processor.prependMessages(olderMessages)\n this.rememberServerMessageIds(olderMessages)\n } finally {\n this.loadOlderMessagesInFlight = false\n }\n }\n\n private resetHistoryPaging() {\n this.hasOlderMessages = false\n this.olderMessagesCursor = undefined\n this.knownServerMessageIds.clear()\n this.historyGeneration++\n }\n\n private applyHydrationPage(page: ChatHydrationResult['page']) {\n if (page?.truncated === true) {\n this.hasOlderMessages = true\n this.olderMessagesCursor = page.cursor\n return\n }\n this.hasOlderMessages = false\n this.olderMessagesCursor = undefined\n }\n\n private rememberServerMessageIds(messages: Array<UIMessage>) {\n const ids = messages.map((message) => message.id)\n for (const id of ids) {\n this.knownServerMessageIds.add(id)\n }\n }\n\n private rememberProcessorMessageIds() {\n if (this.historyPageSize === undefined) return\n this.rememberServerMessageIds(this.processor.getMessages())\n }\n\n private messagesForSend(messages: Array<UIMessage>) {\n if (this.historyPageSize === undefined) {\n return messages\n }\n const unknownMessages: Array<UIMessage> = []\n for (const message of messages) {\n const isKnown = this.knownServerMessageIds.has(message.id)\n if (isKnown) {\n continue\n }\n unknownMessages.push(message)\n }\n if (unknownMessages.length > 0) {\n return unknownMessages\n }\n // No new ids: reload has already dropped the old assistant, so this is\n // `[lastUser]`. Resume/continue still has the assistant, so the cutoff is\n // that assistant and the stored tool-call stays.\n const lastUserIndex = messages.findLastIndex(\n (message) => message.role === 'user',\n )\n if (lastUserIndex === -1) {\n return []\n }\n return messages.slice(lastUserIndex)\n }\n\n /**\n * True when an interrupt (or another direct send) claimed the client during\n * a drain. Read via a method so cross-await mutations are not constant-folded\n * by control-flow analysis.\n */\n private shouldAbortMessageQueueDrain(): boolean {\n return this.isLoading || this.stopMessageQueueDrain\n }\n\n /**\n * Deliver queued messages after a successful settle.\n * - `batch`: merge everything currently queued into one send, looping so\n * messages enqueued during that batch stream are not stranded.\n * - `fifo`: walk the queue in a loop, one stream at a time, until empty\n * (or until another send claims the client via interrupt).\n *\n * Uses `deliverMessage` directly so drains do not re-enter `sendMessage`'s\n * busy/queue policy (which would re-queue items and strand the rest).\n */\n private async drainQueue(): Promise<void> {\n // Note: do not gate on `sendInFlight`. Normal sends still hold\n // `sendInFlight` while `streamResponse`'s finally invokes drain; blocking\n // on it would permanently strand the queue.\n if (\n this.messageQueueDraining ||\n this.isLoading ||\n this.messageQueue.length === 0\n ) {\n return\n }\n\n this.messageQueueDraining = true\n this.stopMessageQueueDrain = false\n try {\n if (this.queueConfig.drain === 'batch') {\n while (this.messageQueue.length > 0) {\n if (this.shouldAbortMessageQueueDrain()) {\n return\n }\n const items = this.messageQueue.splice(0)\n this.emitQueueChange()\n const merged = mergeQueuedMessages(items)\n const completed = await this.deliverMessage(\n merged.content,\n merged.body,\n )\n // Failed/aborted deliver flushes the rest of the queue in streamResponse.\n if (!completed || this.shouldAbortMessageQueueDrain()) {\n return\n }\n }\n return\n }\n\n while (this.messageQueue.length > 0) {\n // Interrupt (or a new direct send) claimed the client — stop draining;\n // remaining items stay queued and will drain after that send settles.\n if (this.shouldAbortMessageQueueDrain()) {\n return\n }\n const next = this.messageQueue.shift()\n if (next === undefined) {\n return\n }\n this.emitQueueChange()\n const completed = await this.deliverMessage(next.content, next.body)\n // Failed/aborted deliver flushes the rest of the queue in streamResponse.\n if (!completed || this.shouldAbortMessageQueueDrain()) {\n return\n }\n }\n } finally {\n this.messageQueueDraining = false\n this.stopMessageQueueDrain = false\n }\n }\n\n /**\n * Drop any in-flight send claim and discard pending queued messages\n * (stop / error / clear / unsubscribe / reload).\n */\n private discardPendingSends(): void {\n this.sendInFlight = false\n this.flushQueue()\n }\n\n /**\n * Get the current send queue (messages held while a stream was in flight).\n */\n getQueue(): Array<QueuedMessage> {\n return this.messageQueue.map(({ id, content, createdAt }) => ({\n id,\n content,\n createdAt,\n }))\n }\n\n private emitQueueChange(): void {\n this.callbacksRef.current.onQueueChange(this.getQueue())\n this.devtoolsBridge.emitSnapshot()\n }\n\n /**\n * Remove a queued message by id before it drains.\n */\n cancelQueued(id: string): void {\n const index = this.messageQueue.findIndex((m) => m.id === id)\n if (index === -1) return\n this.messageQueue.splice(index, 1)\n this.emitQueueChange()\n }\n\n /**\n * Discard all pending queued messages (stop / error / clear / unsubscribe /\n * reload). Does not send them. Emits `onQueueChange([])` when anything was\n * removed.\n */\n private flushQueue(): void {\n if (this.messageQueue.length === 0) return\n this.messageQueue = []\n this.emitQueueChange()\n }\n\n /**\n * Get loading state\n */\n getIsLoading(): boolean {\n return this.isLoading\n }\n\n /**\n * Get current status\n */\n getStatus(): ChatClientState {\n return this.status\n }\n\n /**\n * Get whether the subscription loop is active\n */\n getIsSubscribed(): boolean {\n return this.isSubscribed\n }\n\n /**\n * Get current connection lifecycle status\n */\n getConnectionStatus(): ConnectionStatus {\n return this.connectionStatus\n }\n\n /**\n * Whether the shared session is actively generating.\n * Derived from stream run events (RUN_STARTED / RUN_FINISHED / RUN_ERROR).\n * Unlike `isLoading` (request-local), this reflects shared generation\n * activity visible to all subscribers (e.g. across tabs/devices).\n */\n getSessionGenerating(): boolean {\n return this.sessionGenerating\n }\n\n /**\n * Get current error\n */\n getError(): Error | undefined {\n return this.error\n }\n\n /**\n * Manually set messages\n */\n setMessagesManually(messages: Array<UIMessage<TTools>>): void {\n this.processor.setMessages(messages)\n this.devtoolsBridge.emitSnapshot()\n }\n\n /**\n * Update options refs (for use in React hooks to avoid recreating client)\n */\n updateOptions(options: ChatClientUpdateOptionsWithoutContext<TTools>): void\n updateOptions(\n options: ChatClientUpdateOptionsWithoutContext<TTools> &\n Pick<ChatClientOptions<TTools, TContext>, 'context'>,\n ): void\n updateOptions(\n options: ChatClientUpdateOptionsWithoutContext<TTools> & {\n context?: TContext | undefined\n },\n ): void {\n if (options.connection !== undefined || options.fetcher !== undefined) {\n const wasSubscribed = this.isSubscribed\n\n if (this.isLoading) {\n this.cancelInFlightStream({\n setReadyStatus: true,\n abortSubscription: true,\n })\n } else if (wasSubscribed) {\n this.abortSubscriptionLoop()\n }\n\n this.resetSessionGenerating()\n this.setIsSubscribed(false)\n this.setConnectionStatus('disconnected')\n const transport = resolveTransport({\n connection: options.connection,\n fetcher: options.fetcher,\n })\n this.connectionDrainsOnSend = connectionDrainsOnSend(transport)\n this.connection = normalizeConnectionAdapter(transport)\n\n if (wasSubscribed) {\n this.subscribe()\n }\n }\n // Replace each wire-payload slot independently so callers can update one\n // without wiping the other. Passing `undefined` for `body` or\n // `forwardedProps` leaves that slot unchanged; context is cleared when the\n // key is present with an `undefined` value.\n if (options.body !== undefined) {\n this.bodyOption = options.body\n }\n if (options.forwardedProps !== undefined) {\n this.forwardedPropsOption = options.forwardedProps\n }\n if (options.byok !== undefined) {\n this.byok = options.byok\n }\n if (options.byokProvider !== undefined) {\n this.byokProvider = options.byokProvider\n }\n if ('context' in options) {\n this.context = options.context\n }\n if (options.tools !== undefined) {\n this.interruptManager.updateTools(options.tools)\n this.clientToolsRef.current = new Map()\n for (const tool of options.tools) {\n this.clientToolsRef.current.set(tool.name, tool)\n }\n this.devtoolsBridge.notifyToolsChanged()\n }\n if (options.queue !== undefined) {\n this.queueConfig = normalizeQueueOption(options.queue)\n }\n if (options.onResponse !== undefined) {\n this.callbacksRef.current.onResponse = options.onResponse\n }\n if (options.onChunk !== undefined) {\n this.callbacksRef.current.onChunk = options.onChunk\n }\n if (options.onFinish !== undefined) {\n this.callbacksRef.current.onFinish = options.onFinish\n }\n if (options.onError !== undefined) {\n this.callbacksRef.current.onError = options.onError\n }\n if (options.onSubscriptionChange !== undefined) {\n this.callbacksRef.current.onSubscriptionChange =\n options.onSubscriptionChange\n }\n if (options.onConnectionStatusChange !== undefined) {\n this.callbacksRef.current.onConnectionStatusChange =\n options.onConnectionStatusChange\n }\n if (options.onSessionGeneratingChange !== undefined) {\n this.callbacksRef.current.onSessionGeneratingChange =\n options.onSessionGeneratingChange\n }\n if (options.onQueueChange !== undefined) {\n this.callbacksRef.current.onQueueChange = options.onQueueChange\n }\n if (options.onResumeStateChange !== undefined) {\n this.callbacksRef.current.onResumeStateChange =\n options.onResumeStateChange\n }\n if (options.onRunIdChange !== undefined) {\n this.callbacksRef.current.onRunIdChange = options.onRunIdChange\n }\n if (options.onInterruptStateChange !== undefined) {\n this.callbacksRef.current.onInterruptStateChange =\n options.onInterruptStateChange\n }\n if (options.onCustomEvent !== undefined) {\n this.callbacksRef.current.onCustomEvent = options.onCustomEvent\n }\n }\n\n dispose(): void {\n // FIRST, and latched: everything below is teardown, and an async callback that\n // lands mid-teardown must not start new work. In particular a hydration fetch\n // that resolves after this point must not open a tail — see `hydrateFromServer`.\n this.disposed = true\n // `unsubscribe()` below already aborts the in-flight stream (it calls\n // `cancelInFlightStream({ abortSubscription: true })`), so disposal does drop\n // an open tail. Verified by mutation: removing an extra abort here changes\n // nothing, because unsubscribe covers it.\n this.unsubscribe()\n this.devtoolsBridge.dispose()\n this.devtoolsMounted = false\n }\n}\n"],"mappings":"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"}