@dudousxd/nestjs-agent-react 0.4.1 → 0.5.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.
- package/dist/index.cjs +71 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -1
- package/dist/index.d.ts +90 -1
- package/dist/index.js +70 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/agent-chat-transport.ts","../src/client.ts","../src/stored-message-to-ui-message.ts","../src/use-agent-chat.ts","../src/components/chat-input.tsx","../src/components/use-speech-recognition.ts","../src/components/message-item.tsx","../src/components/message-list.tsx","../src/components/speech-languages.ts"],"sourcesContent":["export {\n AgentChatTransport,\n type AgentChatTransportOptions,\n type AgentStreamMeta,\n} from './agent-chat-transport.js';\nexport {\n AgentClient,\n type AgentClientOptions,\n AgentHttpError,\n type CancelResult,\n type QuotaToday,\n type ThreadPatch,\n} from './client.js';\nexport { storedMessageToUiMessage } from './stored-message-to-ui-message.js';\nexport { useAgentChat, type UseAgentChatOptions } from './use-agent-chat.js';\nexport * from './components/index.js';\n","import type { AgentStreamEvent } from '@dudousxd/nestjs-agent-core';\nimport type { ChatTransport, UIMessage, UIMessageChunk } from 'ai';\n\n/** Identity surfaced by the backend's `meta` SSE frame / response headers. */\nexport interface AgentStreamMeta {\n runId: string;\n threadId: string;\n}\n\nexport interface AgentChatTransportOptions {\n /**\n * Origin + base path the agent endpoints hang off, e.g.\n * `https://api.example.com`. Endpoints are appended as\n * `${baseUrl}/agent/chat` etc. Defaults to `''` (same origin).\n */\n baseUrl?: string;\n /** Static headers merged into every request (e.g. a tenant ref). */\n headers?: Record<string, string>;\n /**\n * Resolved at request time — use for short-lived bearer tokens that\n * must not be captured once at construction. Merged over `headers`.\n */\n getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n /** Forwarded to `fetch` so cookie-based auth/impersonation works. */\n credentials?: RequestCredentials;\n /** Named agent to run the turn (backend `agent` field). */\n agent?: string;\n /**\n * Extra body fields evaluated per send — the hook injects `threadId`\n * and `pageContext` through this. Returned object is spread into the\n * request body after the SDK's own `body`.\n */\n getBody?: () => Record<string, unknown>;\n /**\n * The run id to resume on mount, if any. Returning `undefined` makes\n * `reconnectToStream` resolve to `null` WITHOUT hitting the network —\n * this is the generalized fix for \"useChat fires a doomed GET on mount\n * and surfaces its 404\". Wire this to the thread's `activeStreamId`.\n */\n getResumeRunId?: () => string | undefined;\n /** Fires whenever a stream emits its `meta` frame. */\n onMeta?: (meta: AgentStreamMeta) => void;\n /** Injectable for tests / non-browser runtimes. Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nconst HEADER_RUN_ID = 'x-agent-run-id';\nconst HEADER_THREAD_ID = 'x-agent-thread-id';\n\n/**\n * AI SDK v7 `ChatTransport` for the nestjs-agent backend. POSTs\n * `/agent/chat`, parses the backend's `meta` + `{delta}` + `done` SSE\n * frames, and re-emits them as the v7 UI-message chunk stream\n * (`start` → `text-start` → `text-delta`* → `text-end` → `finish`).\n *\n * The backend hydrates prior history from its store, so only the latest\n * user message text is sent each turn — keeping payloads tiny and\n * preventing the client from corrupting replayed history.\n *\n * Attachments (image/PDF for a vision-capable model) ride the per-send body:\n * `sendMessage({ text }, { body: { attachments: MessageAttachment[] } })`. They\n * flow to the backend as the turn's `attachments`, get persisted on the user\n * message, and are rendered as native model content parts. Optimistic display of\n * the user's own attachment thumbnails is the consumer's concern (it stages the\n * upload), the same way history rendering reads `StoredMessage.attachments`.\n */\nexport class AgentChatTransport implements ChatTransport<UIMessage> {\n private currentRunId: string | undefined;\n private currentThreadId: string | undefined;\n\n constructor(private readonly options: AgentChatTransportOptions = {}) {}\n\n /** Run id of the most recent stream — HITL approve/reject target this. */\n get runId(): string | undefined {\n return this.currentRunId;\n }\n\n /** Thread id of the most recent stream. */\n get threadId(): string | undefined {\n return this.currentThreadId;\n }\n\n async sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n const lastMessage = options.messages.at(-1);\n const message = lastMessage ? extractText(lastMessage) : '';\n const body: Record<string, unknown> = {\n ...(this.options.agent !== undefined ? { agent: this.options.agent } : {}),\n ...(this.options.getBody?.() ?? {}),\n ...((options.body as Record<string, unknown> | undefined) ?? {}),\n message,\n };\n const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n ...(await this.resolveHeaders(options.headers)),\n },\n body: JSON.stringify(body),\n ...(this.options.credentials !== undefined ? { credentials: this.options.credentials } : {}),\n ...(options.abortSignal ? { signal: options.abortSignal } : {}),\n });\n if (!response.ok || !response.body) {\n throw new Error(`Agent chat request failed: ${response.status} ${response.statusText}`);\n }\n this.captureHeaderMeta(response.headers);\n return this.toChunkStream(response.body);\n }\n\n async reconnectToStream(\n options: Parameters<ChatTransport<UIMessage>['reconnectToStream']>[0],\n ): Promise<ReadableStream<UIMessageChunk> | null> {\n const runId = this.options.getResumeRunId?.();\n // No buffered run → resolve null without a network round-trip so we\n // never surface a 404 from a doomed resume GET.\n if (runId === undefined) return null;\n const response = await this.fetchImpl()(\n `${this.baseUrl()}/agent/chat/${encodeURIComponent(runId)}/stream`,\n {\n method: 'GET',\n headers: {\n accept: 'text/event-stream',\n ...(await this.resolveHeaders(options.headers)),\n },\n ...(this.options.credentials !== undefined\n ? { credentials: this.options.credentials }\n : {}),\n },\n );\n if (response.status === 404) return null;\n if (!response.ok || !response.body) {\n throw new Error(`Agent stream reconnect failed: ${response.status} ${response.statusText}`);\n }\n this.captureHeaderMeta(response.headers);\n return this.toChunkStream(response.body);\n }\n\n private fetchImpl(): typeof fetch {\n return this.options.fetch ?? globalThis.fetch;\n }\n\n private baseUrl(): string {\n return (this.options.baseUrl ?? '').replace(/\\/$/, '');\n }\n\n private async resolveHeaders(\n perRequest: Record<string, string> | Headers | undefined,\n ): Promise<Record<string, string>> {\n const dynamic = (await this.options.getHeaders?.()) ?? {};\n const extra =\n perRequest instanceof Headers ? Object.fromEntries(perRequest.entries()) : (perRequest ?? {});\n return { ...this.options.headers, ...dynamic, ...extra };\n }\n\n private captureHeaderMeta(headers: Headers): void {\n const runId = headers.get(HEADER_RUN_ID);\n const threadId = headers.get(HEADER_THREAD_ID);\n if (runId) this.currentRunId = runId;\n if (threadId) this.currentThreadId = threadId;\n }\n\n private recordMeta(meta: AgentStreamMeta): void {\n this.currentRunId = meta.runId;\n this.currentThreadId = meta.threadId;\n this.options.onMeta?.(meta);\n }\n\n /**\n * Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.\n * Recognized frames:\n * - `event: meta` `data: {\"runId\",\"threadId\"}` → records identity\n * - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool)\n * - `event: done` `data: {}` → terminates\n * - `event: error` `data: {code,message}` → error chunk\n *\n * A run is ONE UI message with N steps. Each `step-start`/`step-finish` pair brackets a model\n * call plus its tool execution; text and reasoning open lazily and close at the step boundary, so\n * tool-call cards (input streaming → output) render live between the prose.\n */\n private toChunkStream(source: ReadableStream<Uint8Array>): ReadableStream<UIMessageChunk> {\n const reader = source.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let started = false;\n let stepOpen = false;\n let stepIndex = 0;\n let textId: string | null = null;\n let reasoningId: string | null = null;\n const record = (meta: AgentStreamMeta) => this.recordMeta(meta);\n\n return new ReadableStream<UIMessageChunk>({\n async pull(controller) {\n function ensureStarted() {\n if (started) return;\n started = true;\n controller.enqueue({ type: 'start' });\n }\n function openStep() {\n ensureStarted();\n if (stepOpen) closeStep();\n stepOpen = true;\n stepIndex += 1;\n textId = null;\n reasoningId = null;\n controller.enqueue({ type: 'start-step' });\n }\n function ensureStep() {\n if (!stepOpen) openStep();\n }\n function closeStep() {\n if (!stepOpen) return;\n if (textId !== null) {\n controller.enqueue({ type: 'text-end', id: textId });\n textId = null;\n }\n if (reasoningId !== null) {\n controller.enqueue({ type: 'reasoning-end', id: reasoningId });\n reasoningId = null;\n }\n controller.enqueue({ type: 'finish-step' });\n stepOpen = false;\n }\n function emit(event: AgentStreamEvent) {\n switch (event.kind) {\n case 'step-start':\n openStep();\n break;\n case 'step-finish':\n closeStep();\n // `costUsd` rides the step boundary once the backend reports it (older backends omit\n // it entirely — `undefined`, never a crash). `null` means \"priced provider/estimate\n // unavailable\", distinct from a real $0 turn — surfaced verbatim as message metadata\n // (merged onto `message.metadata` by the AI SDK) so a UI can render running cost\n // without polling `GET /quota/today`.\n if (event.costUsd !== undefined) {\n controller.enqueue({\n type: 'message-metadata',\n messageMetadata: { costUsd: event.costUsd },\n });\n }\n break;\n case 'text':\n ensureStep();\n if (textId === null) {\n textId = `txt-${stepIndex}`;\n controller.enqueue({ type: 'text-start', id: textId });\n }\n controller.enqueue({ type: 'text-delta', id: textId, delta: event.text });\n break;\n case 'reasoning':\n ensureStep();\n if (reasoningId === null) {\n reasoningId = `rsn-${stepIndex}`;\n controller.enqueue({ type: 'reasoning-start', id: reasoningId });\n }\n controller.enqueue({ type: 'reasoning-delta', id: reasoningId, delta: event.text });\n break;\n case 'tool-input-start':\n ensureStep();\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId: event.id,\n toolName: event.name,\n // `toolKind` is absent on older backends — omitted (not `undefined`-valued) so\n // `toolMetadata` itself is only present when there's something to say, letting a\n // UI gate approval affordances on `kind === 'action'` without hardcoding tool names.\n ...(event.toolKind !== undefined\n ? { toolMetadata: { toolKind: event.toolKind } }\n : {}),\n });\n break;\n case 'tool-input-delta':\n ensureStep();\n controller.enqueue({\n type: 'tool-input-delta',\n toolCallId: event.id,\n inputTextDelta: event.delta,\n });\n break;\n case 'tool-input-available':\n ensureStep();\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId: event.id,\n toolName: event.name,\n input: event.input,\n ...(event.toolKind !== undefined\n ? { toolMetadata: { toolKind: event.toolKind } }\n : {}),\n });\n break;\n case 'tool-output':\n ensureStep();\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId: event.id,\n output: event.output,\n });\n break;\n case 'tool-output-error':\n ensureStep();\n controller.enqueue({\n type: 'tool-output-error',\n toolCallId: event.id,\n errorText: event.error,\n });\n break;\n default:\n break;\n }\n }\n function finish() {\n ensureStarted();\n closeStep();\n controller.enqueue({ type: 'finish' });\n controller.close();\n }\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n finish();\n return;\n }\n buffer += decoder.decode(value, { stream: true });\n let separator = buffer.indexOf('\\n\\n');\n while (separator !== -1) {\n const rawEvent = buffer.slice(0, separator);\n buffer = buffer.slice(separator + 2);\n const frame = parseSseFrame(rawEvent);\n if (frame.event === 'done') {\n finish();\n return;\n }\n if (frame.event === 'error') {\n ensureStarted();\n controller.enqueue({ type: 'error', errorText: parseErrorText(frame.data) });\n controller.close();\n return;\n }\n if (frame.event === 'meta') {\n const meta = parseMeta(frame.data);\n if (meta) record(meta);\n } else if (frame.data) {\n const event = parseEvent(frame.data);\n if (event) emit(event);\n }\n separator = buffer.indexOf('\\n\\n');\n }\n }\n } catch (error) {\n controller.enqueue({\n type: 'error',\n errorText: error instanceof Error ? error.message : 'Agent stream error',\n });\n controller.close();\n }\n },\n cancel() {\n void reader.cancel();\n },\n });\n }\n}\n\ninterface SseFrame {\n event: string | undefined;\n data: string | undefined;\n}\n\n/** Split one `event:`/`data:` SSE block into its fields. */\nfunction parseSseFrame(raw: string): SseFrame {\n let event: string | undefined;\n const dataLines: string[] = [];\n for (const line of raw.split('\\n')) {\n if (line.startsWith('event:')) {\n event = line.slice('event:'.length).trim();\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice('data:'.length).trimStart());\n }\n }\n return {\n event,\n data: dataLines.length > 0 ? dataLines.join('\\n') : undefined,\n };\n}\n\nfunction parseMeta(data: string | undefined): AgentStreamMeta | null {\n if (!data) return null;\n try {\n const parsed: unknown = JSON.parse(data);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'runId' in parsed &&\n 'threadId' in parsed &&\n typeof parsed.runId === 'string' &&\n typeof parsed.threadId === 'string'\n ) {\n return { runId: parsed.runId, threadId: parsed.threadId };\n }\n } catch {\n /* malformed meta frame — ignore */\n }\n return null;\n}\n\n/** Pull a human-facing message out of the backend's `event: error` frame (`{code,message}`). */\nfunction parseErrorText(data: string | undefined): string {\n if (!data) return 'Agent run failed';\n try {\n const parsed: unknown = JSON.parse(data);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'message' in parsed &&\n typeof parsed.message === 'string'\n ) {\n return parsed.message;\n }\n } catch {\n /* malformed error frame — fall through to the default */\n }\n return 'Agent run failed';\n}\n\n/** Parse a `data:` frame as an `AgentStreamEvent`. Returns null for anything without a `kind`. */\nfunction parseEvent(data: string): AgentStreamEvent | null {\n try {\n const parsed: unknown = JSON.parse(data);\n if (parsed !== null && typeof parsed === 'object' && 'kind' in parsed) {\n return parsed as AgentStreamEvent;\n }\n } catch {\n /* not an event frame — ignore */\n }\n return null;\n}\n\n/** Collect all text parts of a UI message into a single string. */\nfunction extractText(message: UIMessage): string {\n let text = '';\n for (const part of message.parts ?? []) {\n if (part.type === 'text') text += part.text;\n }\n return text;\n}\n","import type {\n MessageAttachment,\n QuotaView,\n ThreadDetail,\n ThreadSummary,\n} from '@dudousxd/nestjs-agent-core';\n\n/**\n * Thrown by {@link AgentClient} on a non-2xx response. Carries the HTTP `status` so callers can\n * branch (e.g. 403 → \"not your thread\", 429 → quota) instead of string-matching a generic Error.\n */\nexport class AgentHttpError extends Error {\n constructor(\n readonly status: number,\n readonly method: string,\n readonly path: string,\n statusText: string,\n ) {\n super(`Agent request failed: ${method} ${path} → ${status} ${statusText}`);\n this.name = 'AgentHttpError';\n }\n}\n\n/** The quota-today read-model: usage, the configured limit (null → unlimited), and USD spend. */\nexport type QuotaToday = QuotaView;\n\nexport interface CancelResult {\n aborted: boolean;\n}\n\nexport interface OkResult {\n ok: boolean;\n}\n\n/**\n * Partial update accepted by `PATCH /agent/threads/:threadId`. `defaultAgent: null` clears a\n * previously-set default back to the module's own default; omitting it leaves the thread's\n * current default untouched.\n */\nexport interface ThreadPatch {\n title?: string;\n defaultAgent?: string | null;\n}\n\nexport interface AgentClientOptions {\n /** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */\n baseUrl?: string;\n /** Static headers merged into every request. */\n headers?: Record<string, string>;\n /** Resolved per request — for short-lived bearer tokens. */\n getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n /** Forwarded to fetch so cookie auth works. */\n credentials?: RequestCredentials;\n /** Injectable for tests / non-browser runtimes. */\n fetch?: typeof fetch;\n}\n\n/**\n * Framework-agnostic REST client for the nestjs-agent endpoints. Used by\n * `useAgentChat`, but standalone-usable (vanilla fetch, no React).\n */\nexport class AgentClient {\n constructor(private readonly options: AgentClientOptions = {}) {}\n\n listThreads(): Promise<ThreadSummary[]> {\n return this.request<ThreadSummary[]>('GET', '/agent/threads');\n }\n\n getThread(id: string): Promise<ThreadDetail> {\n return this.request<ThreadDetail>('GET', `/agent/threads/${encodeURIComponent(id)}`);\n }\n\n deleteThread(id: string): Promise<void> {\n return this.request<void>('DELETE', `/agent/threads/${encodeURIComponent(id)}`);\n }\n\n forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary> {\n return this.request<ThreadSummary>(\n 'POST',\n `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`,\n );\n }\n\n renameThread(id: string, title: string): Promise<OkResult> {\n return this.updateThread(id, { title });\n }\n\n /** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */\n updateThread(id: string, patch: ThreadPatch): Promise<OkResult> {\n return this.request<OkResult>('PATCH', `/agent/threads/${encodeURIComponent(id)}`, patch);\n }\n\n /**\n * Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —\n * mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is\n * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.\n */\n async uploadAttachment(file: File): Promise<MessageAttachment> {\n const fetchImpl = this.options.fetch ?? globalThis.fetch;\n const baseUrl = (this.options.baseUrl ?? '').replace(/\\/$/, '');\n const dynamic = (await this.options.getHeaders?.()) ?? {};\n const formData = new FormData();\n formData.append('file', file);\n const response = await fetchImpl(`${baseUrl}/agent/attachments`, {\n method: 'POST',\n headers: {\n accept: 'application/json',\n ...this.options.headers,\n ...dynamic,\n },\n body: formData,\n ...(this.options.credentials !== undefined ? { credentials: this.options.credentials } : {}),\n });\n return this.handleResponse<MessageAttachment>(response, 'POST', '/agent/attachments');\n }\n\n promoteThread(id: string): Promise<OkResult> {\n return this.request<OkResult>('POST', `/agent/threads/${encodeURIComponent(id)}/promote`);\n }\n\n truncateFromMessage(threadId: string, messageId: string): Promise<OkResult> {\n return this.request<OkResult>(\n 'DELETE',\n `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`,\n );\n }\n\n getQuotaToday(): Promise<QuotaToday> {\n return this.request<QuotaToday>('GET', '/agent/quota/today');\n }\n\n cancelStream(runId: string): Promise<CancelResult> {\n return this.request<CancelResult>('POST', `/agent/chat/${encodeURIComponent(runId)}/cancel`);\n }\n\n approveToolCall(input: { toolCallId: string }): Promise<void> {\n return this.request<void>('POST', '/agent/tool-call/approve', input);\n }\n\n rejectToolCall(input: { toolCallId: string; reason?: string }): Promise<void> {\n return this.request<void>('POST', '/agent/tool-call/reject', input);\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const fetchImpl = this.options.fetch ?? globalThis.fetch;\n const baseUrl = (this.options.baseUrl ?? '').replace(/\\/$/, '');\n const dynamic = (await this.options.getHeaders?.()) ?? {};\n const response = await fetchImpl(`${baseUrl}${path}`, {\n method,\n headers: {\n accept: 'application/json',\n ...(body !== undefined ? { 'content-type': 'application/json' } : {}),\n ...this.options.headers,\n ...dynamic,\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n ...(this.options.credentials !== undefined ? { credentials: this.options.credentials } : {}),\n });\n return this.handleResponse<T>(response, method, path);\n }\n\n private async handleResponse<T>(response: Response, method: string, path: string): Promise<T> {\n if (!response.ok) {\n throw new AgentHttpError(response.status, method, path, response.statusText);\n }\n if (response.status === 204) return undefined as T;\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n}\n","import type { StoredMessage } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\n\n/**\n * Convert a persisted `StoredMessage` (the agent-lib's on-disk shape) into an AI SDK v7\n * `UIMessage` so a loaded thread can seed `useChat`'s `initialMessages` / `messages`.\n *\n * The lib stores a message as flat `content` text plus `toolCalls`/`toolResults` arrays; the SDK\n * renders from `parts`. This maps:\n * - `content` → a single `text` part (skipped when empty).\n * - each `attachment` → a `file` part (image/PDF) so the user bubble re-renders its\n * thumbnails on a reloaded thread.\n * - each `toolCall` → a `tool-<name>` part, pairing its `toolResult` (matched by id):\n * - a result found → `output-available` state, carrying `output`.\n * - no result found → `input-available` state (the call never finished, e.g. the run was\n * interrupted) — `output` is omitted, never a fabricated value.\n * When the store reports the tool's kind (`'read' | 'action'`), it rides along as\n * `toolMetadata.toolKind` so a UI can render an approval affordance for `action` tools\n * without hardcoding tool-name sets, matching how live-streamed tool parts carry it.\n *\n * Live streaming still arrives as fully-typed SDK tool parts (see `AgentChatTransport`); this\n * converter only feeds replayed history, so the tool cards render the same either way.\n */\nexport function storedMessageToUiMessage(message: StoredMessage): UIMessage {\n const parts: UIMessage['parts'] = [];\n\n if (message.content) {\n parts.push({ type: 'text', text: message.content });\n }\n\n for (const attachment of message.attachments ?? []) {\n parts.push({\n type: 'file',\n mediaType: attachment.contentType,\n filename: attachment.name,\n url: attachment.url,\n });\n }\n\n for (const call of message.toolCalls ?? []) {\n const result = message.toolResults?.find((candidate) => candidate.id === call.id);\n // `call.kind` — the store's `'read' | 'action'` classification, once it lands there\n // (parallel to `RecordToolCallInput.toolType` and the stream's `toolKind`).\n const toolKind = call.kind;\n parts.push({\n type: `tool-${call.name}`,\n toolCallId: call.id,\n ...(toolKind !== undefined ? { toolMetadata: { toolKind } } : {}),\n ...(result !== undefined\n ? { state: 'output-available', input: call.input, output: result.output }\n : { state: 'input-available', input: call.input }),\n });\n }\n\n return { id: message.id, role: message.role, parts };\n}\n","import { useChat } from '@ai-sdk/react';\nimport type { ThreadDetail, ThreadSummary } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { AgentChatTransport, type AgentStreamMeta } from './agent-chat-transport.js';\nimport { AgentClient, type QuotaToday } from './client.js';\n\nexport interface UseAgentChatOptions {\n /** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */\n baseUrl?: string;\n /** Static headers merged into every request. */\n headers?: Record<string, string>;\n /** Resolved per request — for short-lived bearer tokens. */\n getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n /** Forwarded to fetch so cookie auth/impersonation works. */\n credentials?: RequestCredentials;\n /** Injectable for tests / non-browser runtimes. */\n fetch?: typeof fetch;\n /** Reuse an existing client instead of constructing one. */\n client?: AgentClient;\n /** Named agent to run each turn. */\n agent?: string;\n /** Thread this chat is bound to. Omitted → backend creates one on send. */\n threadId?: string;\n /** Persisted history to seed `useChat` with (consumed on mount only). */\n initialMessages?: UIMessage[];\n /**\n * Run id of a buffered stream to reconnect to on mount — wire this to the thread's\n * `activeStreamId`. Its presence both gates the SDK's `resume` and names the run, so a normal\n * mount (no id) never fires a doomed resume GET. Omitted → no resume.\n *\n * For the common case (no id in hand yet), prefer {@link UseAgentChatOptions.resume} — it does\n * this fetch-and-wire for you.\n */\n resumeRunId?: string;\n /**\n * When `true` and the hook (re)mounts with a `threadId`, the hook fetches the thread and, if its\n * `activeRunId` is non-null (a turn was still streaming when the page loaded/reloaded), attaches\n * to that run's `GET /agent/chat/:runId/stream` automatically — the in-flight turn streams into\n * this session's message state exactly as if the send had happened here. A no-op when `threadId`\n * is omitted (nothing to resume) or the fetched thread has no active run. Default `false`.\n */\n resume?: boolean;\n /** Read at every send to capture a page snapshot for the page-assistant. */\n getPageContext?: () => Record<string, unknown> | null;\n /** Fired after each streamed turn finishes (e.g. to refetch the sidebar). */\n onFinish?: () => void;\n /**\n * Fired once when a threadless chat's first send makes the backend create a thread — carries the\n * new id so the consumer can sync its URL/router. Not fired when `threadId` was supplied. The hook\n * ALSO remembers the id internally, so every subsequent send reuses it (no new thread per message)\n * even before the consumer navigates.\n */\n onThreadCreated?: (threadId: string) => void;\n}\n\ninterface AddToolResultArgs {\n tool: string;\n toolCallId: string;\n output: unknown;\n}\n\n/**\n * Generalized `useChat` wiring for the nestjs-agent backend. Wraps the\n * AI SDK v7 hook with `AgentChatTransport`, plus thread list/load/delete/\n * fork, quota, cancel, and HITL approve/reject — all driven through\n * `AgentClient`. Mirrors flip's `useAdminChat`, generalized.\n */\nexport function useAgentChat(options: UseAgentChatOptions) {\n const latest = useRef(options);\n latest.current = options;\n\n const [runId, setRunId] = useState<string | undefined>(options.resumeRunId);\n const runIdRef = useRef<string | undefined>(runId);\n runIdRef.current = runId;\n\n // Auto-resume (`resume: true`): the run id discovered from the thread's `activeRunId`, read by\n // the transport's `getResumeRunId` alongside the explicit `resumeRunId` option. A ref, not state\n // — it must be current by the time the SDK's resume effect (below) fires synchronously off it.\n const autoResumeRunIdRef = useRef<string | undefined>(undefined);\n // Flips once the auto-resume fetch resolves a live run. `useChat`'s own `resume` is a reactive\n // effect dependency (it doesn't need to be `true` at mount), so toggling this after the async\n // thread fetch still attaches to the stream.\n const [autoResume, setAutoResume] = useState(false);\n // The loaded thread's active run, if any — exposed regardless of `resume` so a consumer can\n // render a \"still generating\" affordance even when it wires resumption itself via `resumeRunId`.\n const [activeRunId, setActiveRunId] = useState<string | null>(null);\n\n // One-shot flag read (and cleared) by the transport's getBody so the next send carries\n // `regenerate: true` — telling the backend to re-run the last exchange instead of appending.\n const regenerateNext = useRef(false);\n\n // The thread the backend created for a threadless chat, captured from the `meta` frame. Every send\n // after the first reuses it (via getBody) so the conversation stays on ONE thread instead of\n // spawning a new one per message. Reset per mount; an explicit `threadId` option always wins.\n const createdThreadId = useRef<string | undefined>(undefined);\n\n // Identity-stable: per-render config is read through `latest`.\n // biome-ignore lint/correctness/useExhaustiveDependencies: stable by design\n const client = useMemo(() => {\n if (options.client) return options.client;\n return new AgentClient({\n ...(options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {}),\n ...(options.credentials !== undefined ? { credentials: options.credentials } : {}),\n ...(options.fetch !== undefined ? { fetch: options.fetch } : {}),\n getHeaders: async () => mergeHeaders(latest.current),\n });\n }, []);\n\n // Auto-resume: on (re)mount with `resume: true` and a `threadId`, read the thread and, if it\n // carries a live `activeRunId`, attach to it — the same GET-stream reconnect `resumeRunId`\n // already wires, just discovered instead of supplied. Re-runs if `resume`/`threadId` change; a\n // stale response from a since-abandoned fetch (thread swapped mid-flight) is dropped via `cancelled`.\n useEffect(() => {\n if (!options.resume || options.threadId === undefined) return;\n let cancelled = false;\n const threadId = options.threadId;\n void client.getThread(threadId).then((thread) => {\n if (cancelled) return;\n setActiveRunId(thread.activeRunId ?? null);\n if (thread.activeRunId != null) {\n autoResumeRunIdRef.current = thread.activeRunId;\n setAutoResume(true);\n }\n });\n return () => {\n cancelled = true;\n };\n }, [client, options.resume, options.threadId]);\n\n // Identity-stable: per-render config is read through `latest`.\n // biome-ignore lint/correctness/useExhaustiveDependencies: stable by design\n const transport = useMemo(() => {\n function onMeta(meta: AgentStreamMeta): void {\n setRunId(meta.runId);\n // A threadless chat just had its thread created server-side — remember it so subsequent sends\n // reuse it, and tell the consumer once so it can sync its URL. An explicit threadId option owns\n // the binding, so we never override it here.\n if (\n latest.current.threadId === undefined &&\n meta.threadId &&\n createdThreadId.current !== meta.threadId\n ) {\n createdThreadId.current = meta.threadId;\n latest.current.onThreadCreated?.(meta.threadId);\n }\n }\n return new AgentChatTransport({\n ...(options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {}),\n ...(options.credentials !== undefined ? { credentials: options.credentials } : {}),\n ...(options.fetch !== undefined ? { fetch: options.fetch } : {}),\n ...(options.agent !== undefined ? { agent: options.agent } : {}),\n getHeaders: async () => mergeHeaders(latest.current),\n getBody: () => {\n const current = latest.current;\n const pageContext = current.getPageContext?.() ?? null;\n const regenerate = regenerateNext.current;\n regenerateNext.current = false;\n // Prefer the explicit threadId; else the one the backend created for this chat, so the second\n // and later sends land on the same thread instead of forking a new one each time.\n const threadId = current.threadId ?? createdThreadId.current;\n return {\n ...(threadId !== undefined ? { threadId } : {}),\n ...(pageContext ? { pageContext } : {}),\n ...(regenerate ? { regenerate: true } : {}),\n };\n },\n getResumeRunId: () => latest.current.resumeRunId ?? autoResumeRunIdRef.current,\n onMeta,\n });\n }, []);\n\n const chat = useChat({\n transport,\n resume: options.resumeRunId !== undefined || autoResume,\n ...(options.threadId !== undefined ? { id: options.threadId } : {}),\n ...(options.initialMessages !== undefined ? { messages: options.initialMessages } : {}),\n onFinish: () => {\n latest.current.onFinish?.();\n },\n });\n\n const chatRef = useRef(chat);\n chatRef.current = chat;\n\n // chat.addToolResult is generic over the backend's tool registry, which\n // we don't statically type here. Expose a string-keyed adapter and narrow\n // once at the SDK boundary. (Documented cast — the only one in this file.)\n type SdkAddToolResult = typeof chat.addToolResult;\n type SdkArgs = Parameters<SdkAddToolResult>[0];\n const addToolResult = useCallback(\n ({ tool, toolCallId, output }: AddToolResultArgs) =>\n chatRef.current.addToolResult({\n tool,\n toolCallId,\n output,\n } as unknown as SdkArgs),\n [],\n );\n\n const [threads, setThreads] = useState<ThreadSummary[]>([]);\n const [quota, setQuota] = useState<QuotaToday | null>(null);\n\n const loadThreads = useCallback(async (): Promise<ThreadSummary[]> => {\n const list = await client.listThreads();\n setThreads(list);\n return list;\n }, [client]);\n\n const loadThread = useCallback(\n (id: string): Promise<ThreadDetail> => client.getThread(id),\n [client],\n );\n\n const deleteThread = useCallback(\n async (id: string): Promise<void> => {\n await client.deleteThread(id);\n setThreads((current) => current.filter((thread) => thread.id !== id));\n },\n [client],\n );\n\n const forkThread = useCallback(\n (threadId: string, messageId: string): Promise<ThreadSummary> =>\n client.forkFromMessage(threadId, messageId),\n [client],\n );\n\n const renameThread = useCallback(\n async (id: string, title: string): Promise<void> => {\n await client.renameThread(id, title);\n setThreads((current) =>\n current.map((thread) => (thread.id === id ? { ...thread, title } : thread)),\n );\n },\n [client],\n );\n\n // A promoted thread was transient — invisible to `listThreads` — so refetch to bring it into view.\n const promoteThread = useCallback(\n async (id: string): Promise<void> => {\n await client.promoteThread(id);\n await loadThreads();\n },\n [client, loadThreads],\n );\n\n const truncateFromMessage = useCallback(\n (threadId: string, messageId: string): Promise<void> =>\n client.truncateFromMessage(threadId, messageId).then(() => undefined),\n [client],\n );\n\n const loadQuota = useCallback(async (): Promise<QuotaToday> => {\n const today = await client.getQuotaToday();\n setQuota(today);\n return today;\n }, [client]);\n\n const cancel = useCallback(async (): Promise<void> => {\n // Close the SSE on the client first so the UI flips out of streaming,\n // then hard-abort server-side (a late terminal step can outlive the\n // connection close alone).\n chatRef.current.stop();\n const activeRunId = runIdRef.current;\n if (activeRunId) {\n try {\n await client.cancelStream(activeRunId);\n } catch {\n /* best-effort — the SDK stop already flipped the UI */\n }\n }\n }, [client]);\n\n // Approve / reject route by tool-call id alone — the server derives the run awaiting it (which is\n // the sub-agent's own run when the pending call belongs to a delegated agent), so no runId is sent.\n const approve = useCallback(\n async ({ toolCallId }: { toolCallId: string }): Promise<void> => {\n await client.approveToolCall({ toolCallId });\n },\n [client],\n );\n\n const reject = useCallback(\n async ({\n toolCallId,\n reason,\n }: {\n toolCallId: string;\n reason?: string;\n }): Promise<void> => {\n await client.rejectToolCall({\n toolCallId,\n ...(reason !== undefined ? { reason } : {}),\n });\n },\n [client],\n );\n\n // Re-run the last exchange: flag the next request as a regenerate (so the backend truncates and\n // re-answers instead of appending) and let the SDK re-issue it, dropping the last assistant turn.\n const regenerate = useCallback((): void => {\n regenerateNext.current = true;\n void chatRef.current.regenerate();\n }, []);\n\n return {\n ...chat,\n addToolResult,\n runId,\n /** The `resume`-fetched thread's active run id, or `null` once resolved with none. */\n activeRunId,\n client,\n threads,\n loadThreads,\n loadThread,\n deleteThread,\n forkThread,\n renameThread,\n promoteThread,\n truncateFromMessage,\n quota,\n loadQuota,\n cancel,\n approve,\n reject,\n regenerate,\n };\n}\n\nasync function mergeHeaders(options: UseAgentChatOptions): Promise<Record<string, string>> {\n const dynamic = (await options.getHeaders?.()) ?? {};\n return { ...options.headers, ...dynamic };\n}\n","import React, { useEffect, useState } from 'react';\nimport { useSpeechRecognition } from './use-speech-recognition.js';\n\nexport interface ChatInputProps {\n onSubmit: (text: string) => void;\n disabled?: boolean;\n placeholder?: string;\n rows?: number;\n /** Enable the dictation mic button (Web Speech API). Default `true`. */\n enableSpeech?: boolean;\n /** BCP-47 locale for dictation. Default `en-US`. */\n speechLang?: string;\n /** Label/content for the send button. Default `\"Send\"`. */\n sendLabel?: React.ReactNode;\n /** Wrapper class. The component ships no styles of its own. */\n className?: string;\n textareaClassName?: string;\n sendButtonClassName?: string;\n micButtonClassName?: string;\n /**\n * Custom mic affordance. Receives `isListening`; return any node. When\n * omitted a plain text button (\"Speak\" / \"Stop\") is rendered.\n */\n renderMic?: (state: { isListening: boolean }) => React.ReactNode;\n}\n\n/**\n * Styling-agnostic chat composer. Enter submits, Shift+Enter inserts a\n * newline. Optional dictation streams interim transcripts into the draft\n * and commits finalized chunks. No design-system or icon-library deps —\n * style it entirely through the `*ClassName` props.\n */\nexport function ChatInput({\n onSubmit,\n disabled,\n placeholder,\n rows = 2,\n enableSpeech = true,\n speechLang = 'en-US',\n sendLabel = 'Send',\n className,\n textareaClassName,\n sendButtonClassName,\n micButtonClassName,\n renderMic,\n}: ChatInputProps) {\n const [value, setValue] = useState('');\n // Interim dictation transcript shown appended to the draft until the\n // engine finalizes it — kept out of `value` so editing the rest of the\n // text doesn't persist a partial guess.\n const [interim, setInterim] = useState('');\n\n const speech = useSpeechRecognition({\n lang: speechLang,\n onFinalTranscript: (text) => {\n const trimmed = text.trim();\n if (!trimmed) return;\n setValue((prev) => (prev.length === 0 ? trimmed : `${prev.trimEnd()} ${trimmed}`));\n setInterim('');\n },\n onInterimTranscript: (text) => setInterim(text),\n });\n\n useEffect(() => {\n if (!speech.isListening) setInterim('');\n }, [speech.isListening]);\n\n function submit() {\n const trimmed = value.trim();\n if (!trimmed) return;\n onSubmit(trimmed);\n setValue('');\n setInterim('');\n }\n\n function handleKeyDown(event: React.KeyboardEvent<HTMLTextAreaElement>) {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n submit();\n }\n }\n\n function toggleMic() {\n if (speech.isListening) speech.stop();\n else speech.start();\n }\n\n const displayValue =\n speech.isListening && interim\n ? `${value}${value && !value.endsWith(' ') ? ' ' : ''}${interim}`\n : value;\n\n const showMic = enableSpeech && speech.isSupported;\n\n return (\n <div className={className}>\n <textarea\n className={textareaClassName}\n rows={rows}\n value={displayValue}\n disabled={disabled}\n onChange={(event) => {\n // Ignore changes while interim text is injected — the engine\n // overwrites it on the next result. Editing works when idle.\n if (speech.isListening && interim) return;\n setValue(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n placeholder={placeholder ?? 'Ask something (Enter to send, Shift+Enter for newline)'}\n />\n <button\n type=\"button\"\n className={sendButtonClassName}\n onClick={submit}\n disabled={disabled || !value.trim()}\n >\n {sendLabel}\n </button>\n {showMic ? (\n <button\n type=\"button\"\n className={micButtonClassName}\n onClick={toggleMic}\n disabled={disabled}\n aria-label={speech.isListening ? 'Stop dictating' : 'Start dictating'}\n >\n {renderMic\n ? renderMic({ isListening: speech.isListening })\n : speech.isListening\n ? 'Stop'\n : 'Speak'}\n </button>\n ) : null}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Minimal subset of the Web Speech API we use. The real types live\n * behind `webkit*` prefixes in some browsers and aren't in TS lib.dom,\n * so we declare just what we need.\n */\ninterface SpeechResultAlternative {\n transcript: string;\n}\ninterface SpeechRecognitionResult {\n readonly length: number;\n readonly isFinal: boolean;\n item(index: number): SpeechResultAlternative;\n [index: number]: SpeechResultAlternative;\n}\ninterface SpeechRecognitionResultList {\n readonly length: number;\n item(index: number): SpeechRecognitionResult;\n [index: number]: SpeechRecognitionResult;\n}\ninterface SpeechRecognitionEvent {\n results: SpeechRecognitionResultList;\n resultIndex: number;\n}\ninterface SpeechRecognitionErrorEvent {\n error: string;\n}\ninterface SpeechRecognitionInstance {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n onresult: ((event: SpeechRecognitionEvent) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEvent) => void) | null;\n onend: (() => void) | null;\n start(): void;\n stop(): void;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionInstance;\n\nfunction getRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === 'undefined') return null;\n const scope = window as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n };\n return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;\n}\n\nexport interface UseSpeechRecognitionOptions {\n lang?: string;\n onFinalTranscript: (text: string) => void;\n onInterimTranscript?: (text: string) => void;\n}\n\nexport interface SpeechRecognitionHook {\n isSupported: boolean;\n isListening: boolean;\n start(): void;\n stop(): void;\n}\n\n/**\n * Tiny wrapper around the Web Speech API. `start()` opens the mic and\n * streams interim transcripts via `onInterimTranscript`; on a finalized\n * chunk it fires `onFinalTranscript`. `isSupported` is false where the\n * API is missing (e.g. Firefox) — callers should hide the mic button.\n */\nexport function useSpeechRecognition({\n lang = 'en-US',\n onFinalTranscript,\n onInterimTranscript,\n}: UseSpeechRecognitionOptions): SpeechRecognitionHook {\n const recognitionRef = useRef<SpeechRecognitionInstance | null>(null);\n const [isListening, setIsListening] = useState(false);\n\n const ctor = useMemo(() => getRecognitionCtor(), []);\n const isSupported = ctor !== null;\n\n // Keep latest callbacks in refs so we don't tear down the recognition\n // object when they change identity each render.\n const finalRef = useRef(onFinalTranscript);\n const interimRef = useRef(onInterimTranscript);\n finalRef.current = onFinalTranscript;\n interimRef.current = onInterimTranscript;\n\n useEffect(() => {\n if (!ctor) return;\n const recognition = new ctor();\n recognition.lang = lang;\n recognition.continuous = false;\n recognition.interimResults = true;\n\n recognition.onresult = (event) => {\n let interim = '';\n for (let i = event.resultIndex; i < event.results.length; i++) {\n const result = event.results[i];\n if (!result) continue;\n const text = result[0]?.transcript ?? '';\n if (result.isFinal) {\n finalRef.current(text);\n } else {\n interim += text;\n }\n }\n if (interim && interimRef.current) interimRef.current(interim);\n };\n recognition.onerror = (event) => {\n if (event.error !== 'no-speech' && event.error !== 'aborted') {\n console.warn('SpeechRecognition error', event.error);\n }\n setIsListening(false);\n };\n recognition.onend = () => setIsListening(false);\n\n recognitionRef.current = recognition;\n return () => {\n try {\n recognition.stop();\n } catch {\n /* engine already stopped */\n }\n recognitionRef.current = null;\n };\n }, [ctor, lang]);\n\n const start = useCallback(() => {\n const recognition = recognitionRef.current;\n if (!recognition) return;\n try {\n recognition.start();\n setIsListening(true);\n } catch (error) {\n console.warn('SpeechRecognition start failed', error);\n }\n }, []);\n\n const stop = useCallback(() => {\n const recognition = recognitionRef.current;\n if (!recognition) return;\n try {\n recognition.stop();\n } catch {\n /* already stopped */\n }\n }, []);\n\n return { isSupported, isListening, start, stop };\n}\n","import {\n type DynamicToolUIPart,\n type ToolUIPart,\n type UIMessage,\n isTextUIPart,\n isToolUIPart,\n} from 'ai';\nimport React, { useEffect, useRef, useState } from 'react';\n\n/** A tool UI part on a `UIMessage` — a static `tool-*` part or the `dynamic-tool` part. */\nexport type AnyToolUIPart = ToolUIPart | DynamicToolUIPart;\n\nexport type RenderToolPartFn = (part: AnyToolUIPart, key: string) => React.ReactNode;\n\n/** Render a run of consecutive tool parts as one block (e.g. a collapsible tool group). */\nexport type RenderToolGroupFn = (parts: AnyToolUIPart[], key: string) => React.ReactNode;\n\n/** Render a message's text — pass a markdown renderer (e.g. `AgentMarkdown`) here if desired. */\nexport type RenderTextFn = (text: string, ctx: { isStreaming: boolean }) => React.ReactNode;\n\n/** Server-aggregated usage for an assistant turn, shown as a small inline line. */\nexport interface MessageUsageInfo {\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n}\n\nexport interface MessageItemClassNames {\n root?: string;\n /** Resolved per role so user/assistant/system bubbles can differ. */\n byRole?: Partial<Record<UIMessage['role'], string>>;\n text?: string;\n /** The action row under the bubble (copy / fork / regenerate / usage / timestamp). */\n actions?: string;\n actionButton?: string;\n usage?: string;\n timestamp?: string;\n editTextarea?: string;\n editActions?: string;\n}\n\nexport interface MessageItemProps {\n message: UIMessage;\n renderToolPart?: RenderToolPartFn;\n /** Takes precedence over `renderToolPart` for a run of consecutive tool parts. */\n renderToolGroup?: RenderToolGroupFn;\n renderText?: RenderTextFn;\n classNames?: MessageItemClassNames;\n /** Marks the in-flight assistant bubble (e.g. for a caret animation / deferred markdown render). */\n isStreaming?: boolean;\n /** User bubble gets a pencil → inline textarea; submitting calls back with the new text. */\n editable?: boolean;\n onEditSubmit?: (newText: string) => void | Promise<void>;\n /** Assistant bubble gets a \"Regenerate\" affordance. */\n regeneratable?: boolean;\n onRegenerate?: () => void | Promise<void>;\n /** When provided, a \"Fork\" affordance calls back with this message's id. */\n onFork?: (uiMessageId: string) => void | Promise<void>;\n /** Server-aggregated usage for this assistant turn (cost + tokens line). */\n usage?: MessageUsageInfo | null;\n /** ISO timestamp of the persisted message; rendered as a relative-time line when set. */\n createdAt?: string | null;\n}\n\n/**\n * Styling-agnostic renderer for a single v7 `UIMessage`. Ports flip's full chat-item UX —\n * copy, fork, regenerate, inline edit-and-resubmit, usage line, relative timestamp, and tool\n * grouping — but with ALL styling left to `classNames` and every action surfaced as a callback,\n * so no design-system / icon / date library is pulled in (relative time uses Intl.RelativeTimeFormat).\n */\nexport function MessageItem({\n message,\n renderToolPart,\n renderToolGroup,\n renderText,\n classNames,\n isStreaming = false,\n editable,\n onEditSubmit,\n regeneratable,\n onRegenerate,\n onFork,\n usage,\n createdAt,\n}: MessageItemProps) {\n const isUser = message.role === 'user';\n const parts = message.parts ?? [];\n const copyableText = extractText(parts);\n\n const actionRow = (\n <div className={classNames?.actions} data-actions-for={message.role}>\n {copyableText ? (\n <CopyButton text={copyableText} className={classNames?.actionButton} />\n ) : null}\n {onFork ? (\n <button\n type=\"button\"\n onClick={() => void onFork(message.id)}\n className={classNames?.actionButton}\n title=\"Fork thread from this message\"\n >\n Fork\n </button>\n ) : null}\n {!isUser && regeneratable && onRegenerate ? (\n <button\n type=\"button\"\n onClick={() => void onRegenerate()}\n className={classNames?.actionButton}\n title=\"Regenerate response\"\n >\n Regenerate\n </button>\n ) : null}\n {!isUser && usage ? <UsageInline usage={usage} className={classNames?.usage} /> : null}\n {createdAt ? (\n <TimestampInline createdAt={createdAt} className={classNames?.timestamp} />\n ) : null}\n </div>\n );\n\n if (isUser && editable && onEditSubmit) {\n return (\n <EditableUserBubble\n message={message}\n onSubmit={onEditSubmit}\n renderText={renderText}\n classNames={classNames}\n actions={actionRow}\n />\n );\n }\n\n const className = [classNames?.root, classNames?.byRole?.[message.role]]\n .filter((entry) => entry)\n .join(' ');\n\n return (\n <div\n className={className === '' ? undefined : className}\n data-role={message.role}\n data-streaming={isStreaming ? 'true' : undefined}\n >\n <div>\n {renderParts(message, parts, {\n renderText,\n renderToolPart,\n renderToolGroup,\n classNames,\n isStreaming,\n })}\n </div>\n {actionRow}\n </div>\n );\n}\n\ninterface RenderPartsCtx {\n renderText?: RenderTextFn | undefined;\n renderToolPart?: RenderToolPartFn | undefined;\n renderToolGroup?: RenderToolGroupFn | undefined;\n classNames?: MessageItemClassNames | undefined;\n isStreaming: boolean;\n}\n\n/** Walk the parts, buffering consecutive tool parts so they can render as one group. */\nfunction renderParts(\n message: UIMessage,\n parts: NonNullable<UIMessage['parts']>,\n ctx: RenderPartsCtx,\n): React.ReactNode[] {\n const blocks: React.ReactNode[] = [];\n let toolBuffer: AnyToolUIPart[] = [];\n let toolGroupCounter = 0;\n let textCounter = 0;\n\n function flushTools() {\n if (toolBuffer.length === 0) {\n return;\n }\n const key = `${message.id}-tools-${toolGroupCounter++}`;\n if (ctx.renderToolGroup) {\n blocks.push(\n <React.Fragment key={key}>{ctx.renderToolGroup(toolBuffer, key)}</React.Fragment>,\n );\n } else if (ctx.renderToolPart) {\n for (const part of toolBuffer) {\n const partKey = `${message.id}-${part.toolCallId}`;\n blocks.push(\n <React.Fragment key={partKey}>{ctx.renderToolPart(part, partKey)}</React.Fragment>,\n );\n }\n }\n toolBuffer = [];\n }\n\n for (const part of parts) {\n if (isToolUIPart(part)) {\n toolBuffer.push(part);\n continue;\n }\n flushTools();\n if (isTextUIPart(part)) {\n const key = `${message.id}-text-${textCounter++}`;\n blocks.push(\n <div key={key} className={ctx.classNames?.text}>\n {ctx.renderText ? ctx.renderText(part.text, { isStreaming: ctx.isStreaming }) : part.text}\n </div>,\n );\n }\n }\n flushTools();\n return blocks;\n}\n\ninterface EditableUserBubbleProps {\n message: UIMessage;\n onSubmit: (newText: string) => void | Promise<void>;\n renderText?: RenderTextFn | undefined;\n classNames?: MessageItemClassNames | undefined;\n actions?: React.ReactNode;\n}\n\nfunction EditableUserBubble({\n message,\n onSubmit,\n renderText,\n classNames,\n actions,\n}: EditableUserBubbleProps) {\n const initialText = extractText(message.parts ?? []);\n const [isEditing, setIsEditing] = useState(false);\n const [draft, setDraft] = useState(initialText);\n const textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n useEffect(() => {\n if (isEditing && textareaRef.current) {\n const el = textareaRef.current;\n el.focus();\n el.setSelectionRange(el.value.length, el.value.length);\n }\n }, [isEditing]);\n\n async function handleSave() {\n const next = draft.trim();\n if (!next) {\n return;\n }\n setIsEditing(false);\n await onSubmit(next);\n }\n\n if (!isEditing) {\n const rootClass = [classNames?.root, classNames?.byRole?.user]\n .filter((entry) => entry)\n .join(' ');\n return (\n <div className={rootClass === '' ? undefined : rootClass} data-role=\"user\">\n <div className={classNames?.text}>\n {renderText ? renderText(initialText, { isStreaming: false }) : initialText}\n </div>\n <button\n type=\"button\"\n onClick={() => {\n setDraft(initialText);\n setIsEditing(true);\n }}\n className={classNames?.actionButton}\n title=\"Edit & resubmit\"\n >\n Edit\n </button>\n {actions}\n </div>\n );\n }\n\n return (\n <div data-editing=\"true\">\n <textarea\n ref={textareaRef}\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n void handleSave();\n }\n if (event.key === 'Escape') {\n event.preventDefault();\n setIsEditing(false);\n }\n }}\n rows={Math.min(8, draft.split('\\n').length + 1)}\n className={classNames?.editTextarea}\n />\n <div className={classNames?.editActions}>\n <button type=\"button\" onClick={() => setIsEditing(false)}>\n Cancel\n </button>\n <button type=\"button\" onClick={() => void handleSave()} disabled={!draft.trim()}>\n Resend\n </button>\n </div>\n </div>\n );\n}\n\ninterface CopyButtonProps {\n text: string;\n className?: string | undefined;\n}\n\nfunction CopyButton({ text, className }: CopyButtonProps) {\n const [copied, setCopied] = useState(false);\n\n async function handleCopy() {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n } catch {\n // Clipboard can be unavailable (insecure context / denied permission) — fail quietly.\n }\n }\n\n return (\n <button\n type=\"button\"\n onClick={() => void handleCopy()}\n className={className}\n title={copied ? 'Copied!' : 'Copy message text'}\n >\n {copied ? 'Copied' : 'Copy'}\n </button>\n );\n}\n\nfunction UsageInline({\n usage,\n className,\n}: { usage: MessageUsageInfo; className?: string | undefined }) {\n const total = usage.inputTokens + usage.outputTokens;\n return (\n <span\n className={className}\n title={`Input ${usage.inputTokens.toLocaleString()} · Output ${usage.outputTokens.toLocaleString()} tokens`}\n >\n {formatCostUsd(usage.costUsd)} · {formatTokensShort(total)}\n </span>\n );\n}\n\nfunction TimestampInline({\n createdAt,\n className,\n}: { createdAt: string; className?: string | undefined }) {\n const parsed = new Date(createdAt);\n if (Number.isNaN(parsed.getTime())) {\n return null;\n }\n const absolute = parsed.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' });\n return (\n <span className={className} title={absolute}>\n {formatRelativeTime(parsed)}\n </span>\n );\n}\n\nconst RELATIVE_TIME = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto', style: 'narrow' });\nconst ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * \"just now\" within ±10s (server `created_at` can land a few ms ahead of the client clock), a\n * relative phrase up to a week, then a calendar date. Uses Intl.RelativeTimeFormat — no date-fns.\n */\nexport function formatRelativeTime(date: Date): string {\n const diffMs = Date.now() - date.getTime();\n if (Math.abs(diffMs) < 10_000) {\n return 'just now';\n }\n if (diffMs > ONE_WEEK_MS) {\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n const minutes = Math.round(-diffMs / 60_000);\n if (Math.abs(minutes) < 60) {\n return RELATIVE_TIME.format(minutes, 'minute');\n }\n const hours = Math.round(-diffMs / 3_600_000);\n if (Math.abs(hours) < 24) {\n return RELATIVE_TIME.format(hours, 'hour');\n }\n return RELATIVE_TIME.format(Math.round(-diffMs / 86_400_000), 'day');\n}\n\nfunction formatTokensShort(n: number): string {\n if (n < 1_000) {\n return `${n} tokens`;\n }\n if (n < 10_000) {\n return `${(n / 1_000).toFixed(1)}k tokens`;\n }\n return `${Math.round(n / 1_000)}k tokens`;\n}\n\nfunction formatCostUsd(cost: number): string {\n if (cost === 0) {\n return '$0';\n }\n if (cost < 0.01) {\n return `$${cost.toFixed(4)}`;\n }\n if (cost < 1) {\n return `$${cost.toFixed(3)}`;\n }\n return `$${cost.toFixed(2)}`;\n}\n\nfunction extractText(parts: NonNullable<UIMessage['parts']>): string {\n const out: string[] = [];\n for (const part of parts) {\n if (isTextUIPart(part)) {\n out.push(part.text);\n }\n }\n return out.join('\\n\\n').trim();\n}\n","import type { UIMessage } from 'ai';\nimport React, { useState } from 'react';\nimport {\n MessageItem,\n type MessageItemClassNames,\n type MessageUsageInfo,\n type RenderTextFn,\n type RenderToolGroupFn,\n type RenderToolPartFn,\n} from './message-item.js';\n\nexport type ChatStatus = 'ready' | 'submitted' | 'streaming' | 'error';\n\n/** Default number of most-recent messages mounted; older load on demand. */\nconst DEFAULT_VISIBLE_COUNT = 50;\nconst LOAD_EARLIER_STEP = 50;\n\nexport interface MessageListClassNames {\n root?: string;\n empty?: string;\n loadEarlier?: string;\n typing?: string;\n error?: string;\n errorRetry?: string;\n followUps?: string;\n followUpChip?: string;\n message?: MessageItemClassNames;\n}\n\nexport interface MessageListProps {\n messages: UIMessage[];\n status: ChatStatus;\n renderToolPart?: RenderToolPartFn;\n renderToolGroup?: RenderToolGroupFn;\n renderText?: RenderTextFn;\n /** When set, every message gets a \"Fork\" affordance calling back with its id. */\n onFork?: (uiMessageId: string) => void | Promise<void>;\n /** User messages get an inline edit-and-resubmit affordance. */\n editable?: boolean;\n onEditSubmit?: (uiMessageId: string, newText: string) => void | Promise<void>;\n /** The last assistant message gets a \"Regenerate\" affordance. */\n regeneratable?: boolean;\n onRegenerate?: (uiMessageId: string) => void | Promise<void>;\n /** Per-message resolvers for the usage line and the persisted timestamp. */\n getUsage?: (message: UIMessage) => MessageUsageInfo | null;\n getCreatedAt?: (message: UIMessage) => string | null;\n /** Shown (with example/follow-up chips) when idle and empty. */\n emptyState?: React.ReactNode;\n /** Node rendered while waiting for the first assistant token. */\n typingIndicator?: React.ReactNode;\n /** AI-generated follow-up prompts under the last assistant message. */\n followUps?: string[] | null;\n onFollowUpClick?: (text: string) => void;\n /** Last turn's error — paired with `onRetry`, renders a retry banner. */\n error?: Error | null;\n onRetry?: () => void | Promise<void>;\n classNames?: MessageListClassNames;\n}\n\n/**\n * Styling-agnostic message list. Keeps the proven UX — windowed render\n * with \"load earlier\", a typing indicator, an inline error+retry banner,\n * and follow-up chips — but ships no styles: drive everything through\n * `classNames` and the `emptyState`/`typingIndicator` slots.\n */\nexport function MessageList({\n messages,\n status,\n renderToolPart,\n renderToolGroup,\n renderText,\n onFork,\n editable,\n onEditSubmit,\n regeneratable,\n onRegenerate,\n getUsage,\n getCreatedAt,\n emptyState,\n typingIndicator,\n followUps,\n onFollowUpClick,\n error,\n onRetry,\n classNames,\n}: MessageListProps) {\n const [visibleCount, setVisibleCount] = useState(DEFAULT_VISIBLE_COUNT);\n\n if (messages.length === 0 && status === 'ready') {\n return <div className={classNames?.empty}>{emptyState}</div>;\n }\n\n const lastMessage = messages.at(-1);\n const showTypingBubble =\n (status === 'submitted' || status === 'streaming') && lastMessage?.role !== 'assistant';\n\n const showFollowUps =\n status === 'ready' &&\n !showTypingBubble &&\n lastMessage?.role === 'assistant' &&\n onFollowUpClick !== undefined &&\n !!followUps &&\n followUps.length > 0;\n\n // Mount only the tail; \"Load earlier\" reveals older chunks — critical\n // for long threads where mounting every markdown-rendered message would\n // lock the main thread during streaming.\n const visibleStart = Math.max(0, messages.length - visibleCount);\n const visibleMessages = messages.slice(visibleStart);\n const hiddenCount = visibleStart;\n\n let lastAssistantIndex = -1;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]?.role === 'assistant') {\n lastAssistantIndex = i;\n break;\n }\n }\n\n return (\n <div className={classNames?.root}>\n {hiddenCount > 0 ? (\n <button\n type=\"button\"\n className={classNames?.loadEarlier}\n onClick={() => setVisibleCount((count) => count + LOAD_EARLIER_STEP)}\n >\n Load earlier ({hiddenCount} more)\n </button>\n ) : null}\n {visibleMessages.map((message, index) => {\n const absoluteIndex = visibleStart + index;\n const isLastAssistant = absoluteIndex === lastAssistantIndex;\n const usage = getUsage?.(message) ?? null;\n const createdAt = getCreatedAt?.(message) ?? null;\n return (\n <MessageItem\n key={message.id}\n message={message}\n {...(renderToolPart ? { renderToolPart } : {})}\n {...(renderToolGroup ? { renderToolGroup } : {})}\n {...(renderText ? { renderText } : {})}\n {...(classNames?.message ? { classNames: classNames.message } : {})}\n {...(onFork ? { onFork } : {})}\n {...(editable ? { editable } : {})}\n {...(onEditSubmit\n ? { onEditSubmit: (text: string) => onEditSubmit(message.id, text) }\n : {})}\n {...(regeneratable && isLastAssistant ? { regeneratable } : {})}\n {...(onRegenerate ? { onRegenerate: () => onRegenerate(message.id) } : {})}\n {...(usage !== null ? { usage } : {})}\n {...(createdAt !== null ? { createdAt } : {})}\n isStreaming={status === 'streaming' && isLastAssistant}\n />\n );\n })}\n {showTypingBubble ? (\n <div className={classNames?.typing}>{typingIndicator ?? 'Thinking…'}</div>\n ) : null}\n {error && onRetry ? (\n <div className={classNames?.error}>\n <span>{error.message || \"The assistant didn't respond.\"}</span>\n <button type=\"button\" className={classNames?.errorRetry} onClick={() => void onRetry()}>\n Retry\n </button>\n </div>\n ) : null}\n {showFollowUps ? (\n <div className={classNames?.followUps}>\n {followUps.map((text) => (\n <button\n key={text}\n type=\"button\"\n className={classNames?.followUpChip}\n onClick={() => onFollowUpClick(text)}\n >\n {text}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n","/**\n * Languages the dictation mic offers. The Web Speech API expects a\n * BCP-47 tag and does NOT auto-detect — the engine forces incoming\n * audio onto the phonemes of whatever `recognition.lang` is set to, so\n * the host app picks the locale explicitly.\n */\nexport interface SpeechLanguage {\n /** BCP-47 tag fed straight into `SpeechRecognition.lang`. */\n tag: string;\n /** Human label for a dropdown. */\n label: string;\n}\n\nexport const DEFAULT_SPEECH_LANG = 'en-US';\n\nexport const SPEECH_LANGUAGES: ReadonlyArray<SpeechLanguage> = [\n { tag: 'en-US', label: 'English (US)' },\n { tag: 'en-GB', label: 'English (UK)' },\n { tag: 'pt-BR', label: 'Portuguese (Brazil)' },\n { tag: 'es-ES', label: 'Spanish (Spain)' },\n];\n\nconst STORAGE_KEY = 'nestjs-agent-chat-speech-lang';\n\n/**\n * Recover the last picked language from localStorage (sync — safe at\n * `useState` init). Falls back to the default if nothing's stored or the\n * stored tag isn't in the offered list.\n */\nexport function loadStoredSpeechLang(): string {\n if (typeof window === 'undefined') return DEFAULT_SPEECH_LANG;\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (raw && SPEECH_LANGUAGES.some((language) => language.tag === raw)) {\n return raw;\n }\n } catch {\n /* localStorage disabled (private window / webview) — use the default */\n }\n return DEFAULT_SPEECH_LANG;\n}\n\nexport function persistSpeechLang(tag: string): void {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(STORAGE_KEY, tag);\n } catch {\n /* same fallback — silently skip persistence */\n }\n}\n"],"mappings":"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","names":["HEADER_RUN_ID","HEADER_THREAD_ID","AgentChatTransport","currentRunId","currentThreadId","options","runId","threadId","sendMessages","lastMessage","messages","at","message","extractText","body","agent","undefined","getBody","response","fetchImpl","baseUrl","method","headers","accept","resolveHeaders","JSON","stringify","credentials","abortSignal","signal","ok","Error","status","statusText","captureHeaderMeta","toChunkStream","reconnectToStream","getResumeRunId","encodeURIComponent","fetch","globalThis","replace","perRequest","dynamic","getHeaders","extra","Headers","Object","fromEntries","entries","get","recordMeta","meta","onMeta","source","reader","getReader","decoder","TextDecoder","buffer","started","stepOpen","stepIndex","textId","reasoningId","record","ReadableStream","pull","controller","ensureStarted","enqueue","type","openStep","closeStep","ensureStep","id","emit","event","kind","costUsd","messageMetadata","delta","text","toolCallId","toolName","name","toolKind","toolMetadata","inputTextDelta","input","output","errorText","error","finish","close","done","value","read","decode","stream","separator","indexOf","rawEvent","slice","frame","parseSseFrame","parseErrorText","data","parseMeta","parseEvent","cancel","raw","dataLines","line","split","startsWith","length","trim","push","trimStart","join","parsed","parse","part","parts","AgentHttpError","Error","status","method","path","statusText","name","AgentClient","options","listThreads","request","getThread","id","encodeURIComponent","deleteThread","forkFromMessage","threadId","messageId","renameThread","title","updateThread","patch","uploadAttachment","file","fetchImpl","fetch","globalThis","baseUrl","replace","dynamic","getHeaders","formData","FormData","append","response","headers","accept","body","credentials","undefined","handleResponse","promoteThread","truncateFromMessage","getQuotaToday","cancelStream","runId","approveToolCall","input","rejectToolCall","JSON","stringify","ok","text","parse","storedMessageToUiMessage","message","parts","content","push","type","text","attachment","attachments","mediaType","contentType","filename","name","url","call","toolCalls","result","toolResults","find","candidate","id","toolKind","kind","toolCallId","undefined","toolMetadata","state","input","output","role","import_react","useAgentChat","options","latest","useRef","current","runId","setRunId","useState","resumeRunId","runIdRef","autoResumeRunIdRef","undefined","autoResume","setAutoResume","activeRunId","setActiveRunId","regenerateNext","createdThreadId","client","useMemo","AgentClient","baseUrl","credentials","fetch","getHeaders","mergeHeaders","useEffect","resume","threadId","cancelled","getThread","then","thread","transport","onMeta","meta","onThreadCreated","AgentChatTransport","agent","getBody","pageContext","getPageContext","regenerate","getResumeRunId","chat","useChat","id","initialMessages","messages","onFinish","chatRef","addToolResult","useCallback","tool","toolCallId","output","threads","setThreads","quota","setQuota","loadThreads","list","listThreads","loadThread","deleteThread","filter","forkThread","messageId","forkFromMessage","renameThread","title","map","promoteThread","truncateFromMessage","loadQuota","today","getQuotaToday","cancel","stop","cancelStream","approve","approveToolCall","reject","reason","rejectToolCall","dynamic","headers","import_react","import_react","getRecognitionCtor","window","scope","SpeechRecognition","webkitSpeechRecognition","useSpeechRecognition","lang","onFinalTranscript","onInterimTranscript","recognitionRef","useRef","isListening","setIsListening","useState","ctor","useMemo","isSupported","finalRef","interimRef","current","useEffect","recognition","continuous","interimResults","onresult","event","interim","i","resultIndex","results","length","result","text","transcript","isFinal","onerror","error","console","warn","onend","stop","start","useCallback","ChatInput","onSubmit","disabled","placeholder","rows","enableSpeech","speechLang","sendLabel","className","textareaClassName","sendButtonClassName","micButtonClassName","renderMic","value","setValue","useState","interim","setInterim","speech","useSpeechRecognition","lang","onFinalTranscript","text","trimmed","trim","prev","length","trimEnd","onInterimTranscript","useEffect","isListening","submit","handleKeyDown","event","key","shiftKey","preventDefault","toggleMic","stop","start","displayValue","endsWith","showMic","isSupported","React","div","textarea","onChange","target","onKeyDown","button","type","onClick","aria-label","import_react","MessageItem","message","renderToolPart","renderToolGroup","renderText","classNames","isStreaming","editable","onEditSubmit","regeneratable","onRegenerate","onFork","usage","createdAt","isUser","role","parts","copyableText","extractText","actionRow","React","div","className","actions","data-actions-for","CopyButton","text","actionButton","button","type","onClick","id","title","UsageInline","TimestampInline","timestamp","EditableUserBubble","onSubmit","root","byRole","filter","entry","join","undefined","data-role","data-streaming","renderParts","ctx","blocks","toolBuffer","toolGroupCounter","textCounter","flushTools","length","key","push","Fragment","part","partKey","toolCallId","isToolUIPart","isTextUIPart","initialText","isEditing","setIsEditing","useState","draft","setDraft","textareaRef","useRef","useEffect","current","el","focus","setSelectionRange","value","handleSave","next","trim","rootClass","user","data-editing","textarea","ref","onChange","event","target","onKeyDown","shiftKey","preventDefault","rows","Math","min","split","editTextarea","editActions","disabled","copied","setCopied","handleCopy","navigator","clipboard","writeText","setTimeout","total","inputTokens","outputTokens","span","toLocaleString","formatCostUsd","costUsd","formatTokensShort","parsed","Date","Number","isNaN","getTime","absolute","dateStyle","timeStyle","formatRelativeTime","RELATIVE_TIME","Intl","RelativeTimeFormat","numeric","style","ONE_WEEK_MS","date","diffMs","now","abs","toLocaleDateString","month","day","minutes","round","format","hours","n","toFixed","cost","out","import_react","DEFAULT_VISIBLE_COUNT","LOAD_EARLIER_STEP","MessageList","messages","status","renderToolPart","renderToolGroup","renderText","onFork","editable","onEditSubmit","regeneratable","onRegenerate","getUsage","getCreatedAt","emptyState","typingIndicator","followUps","onFollowUpClick","error","onRetry","classNames","visibleCount","setVisibleCount","useState","length","React","div","className","empty","lastMessage","at","showTypingBubble","role","showFollowUps","undefined","visibleStart","Math","max","visibleMessages","slice","hiddenCount","lastAssistantIndex","i","root","button","type","loadEarlier","onClick","count","map","message","index","absoluteIndex","isLastAssistant","usage","createdAt","MessageItem","key","id","text","isStreaming","typing","span","errorRetry","followUpChip","DEFAULT_SPEECH_LANG","SPEECH_LANGUAGES","tag","label","STORAGE_KEY","loadStoredSpeechLang","window","raw","localStorage","getItem","some","language","persistSpeechLang","setItem"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/agent-chat-transport.ts","../src/client.ts","../src/stored-message-to-ui-message.ts","../src/stored-thread-to-ui-messages.ts","../src/use-agent-chat.ts","../src/components/chat-input.tsx","../src/components/use-speech-recognition.ts","../src/components/message-item.tsx","../src/components/message-list.tsx","../src/components/speech-languages.ts"],"sourcesContent":["export {\n AgentChatTransport,\n type AgentChatTransportOptions,\n type AgentStreamMeta,\n} from './agent-chat-transport.js';\nexport {\n AgentClient,\n type AgentClientOptions,\n AgentHttpError,\n type CancelResult,\n type QuotaToday,\n type ThreadPatch,\n} from './client.js';\nexport { storedMessageToUiMessage } from './stored-message-to-ui-message.js';\nexport {\n type AggregatedTurnUsage,\n type StoredTurnMetadata,\n storedThreadToUiMessages,\n} from './stored-thread-to-ui-messages.js';\nexport { useAgentChat, type UseAgentChatOptions } from './use-agent-chat.js';\nexport * from './components/index.js';\n","import type { AgentStreamEvent } from '@dudousxd/nestjs-agent-core';\nimport type { ChatTransport, UIMessage, UIMessageChunk } from 'ai';\n\n/** Identity surfaced by the backend's `meta` SSE frame / response headers. */\nexport interface AgentStreamMeta {\n runId: string;\n threadId: string;\n}\n\nexport interface AgentChatTransportOptions {\n /**\n * Origin + base path the agent endpoints hang off, e.g.\n * `https://api.example.com`. Endpoints are appended as\n * `${baseUrl}/agent/chat` etc. Defaults to `''` (same origin).\n */\n baseUrl?: string;\n /** Static headers merged into every request (e.g. a tenant ref). */\n headers?: Record<string, string>;\n /**\n * Resolved at request time — use for short-lived bearer tokens that\n * must not be captured once at construction. Merged over `headers`.\n */\n getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n /** Forwarded to `fetch` so cookie-based auth/impersonation works. */\n credentials?: RequestCredentials;\n /** Named agent to run the turn (backend `agent` field). */\n agent?: string;\n /**\n * Extra body fields evaluated per send — the hook injects `threadId`\n * and `pageContext` through this. Returned object is spread into the\n * request body after the SDK's own `body`.\n */\n getBody?: () => Record<string, unknown>;\n /**\n * The run id to resume on mount, if any. Returning `undefined` makes\n * `reconnectToStream` resolve to `null` WITHOUT hitting the network —\n * this is the generalized fix for \"useChat fires a doomed GET on mount\n * and surfaces its 404\". Wire this to the thread's `activeStreamId`.\n */\n getResumeRunId?: () => string | undefined;\n /** Fires whenever a stream emits its `meta` frame. */\n onMeta?: (meta: AgentStreamMeta) => void;\n /**\n * Fires synchronously at the very start of `sendMessages`/`reconnectToStream`, before any network\n * call — i.e. strictly before that attempt's own `onMeta` (if any) can fire. Lets a caller reset\n * \"which run is THIS attempt settling\" state without racing React's render/effect timing: a\n * consumer that also read `onMeta` to learn the current run id would otherwise risk misattributing\n * an early failure (no meta ever arrives) to a PRIOR attempt's id.\n */\n onAttemptStart?: () => void;\n /** Injectable for tests / non-browser runtimes. Defaults to global fetch. */\n fetch?: typeof fetch;\n}\n\nconst HEADER_RUN_ID = 'x-agent-run-id';\nconst HEADER_THREAD_ID = 'x-agent-thread-id';\n\n/**\n * AI SDK v7 `ChatTransport` for the nestjs-agent backend. POSTs\n * `/agent/chat`, parses the backend's `meta` + `{delta}` + `done` SSE\n * frames, and re-emits them as the v7 UI-message chunk stream\n * (`start` → `text-start` → `text-delta`* → `text-end` → `finish`).\n *\n * The backend hydrates prior history from its store, so only the latest\n * user message text is sent each turn — keeping payloads tiny and\n * preventing the client from corrupting replayed history.\n *\n * Attachments (image/PDF for a vision-capable model) ride the per-send body:\n * `sendMessage({ text }, { body: { attachments: MessageAttachment[] } })`. They\n * flow to the backend as the turn's `attachments`, get persisted on the user\n * message, and are rendered as native model content parts. Optimistic display of\n * the user's own attachment thumbnails is the consumer's concern (it stages the\n * upload), the same way history rendering reads `StoredMessage.attachments`.\n */\nexport class AgentChatTransport implements ChatTransport<UIMessage> {\n private currentRunId: string | undefined;\n private currentThreadId: string | undefined;\n\n constructor(private readonly options: AgentChatTransportOptions = {}) {}\n\n /** Run id of the most recent stream — HITL approve/reject target this. */\n get runId(): string | undefined {\n return this.currentRunId;\n }\n\n /** Thread id of the most recent stream. */\n get threadId(): string | undefined {\n return this.currentThreadId;\n }\n\n async sendMessages(\n options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0],\n ): Promise<ReadableStream<UIMessageChunk>> {\n this.options.onAttemptStart?.();\n const lastMessage = options.messages.at(-1);\n const message = lastMessage ? extractText(lastMessage) : '';\n const body: Record<string, unknown> = {\n ...(this.options.agent !== undefined ? { agent: this.options.agent } : {}),\n ...(this.options.getBody?.() ?? {}),\n ...((options.body as Record<string, unknown> | undefined) ?? {}),\n message,\n };\n const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'text/event-stream',\n ...(await this.resolveHeaders(options.headers)),\n },\n body: JSON.stringify(body),\n ...(this.options.credentials !== undefined ? { credentials: this.options.credentials } : {}),\n ...(options.abortSignal ? { signal: options.abortSignal } : {}),\n });\n if (!response.ok || !response.body) {\n throw new Error(`Agent chat request failed: ${response.status} ${response.statusText}`);\n }\n this.captureHeaderMeta(response.headers);\n return this.toChunkStream(response.body);\n }\n\n async reconnectToStream(\n options: Parameters<ChatTransport<UIMessage>['reconnectToStream']>[0],\n ): Promise<ReadableStream<UIMessageChunk> | null> {\n this.options.onAttemptStart?.();\n const runId = this.options.getResumeRunId?.();\n // No buffered run → resolve null without a network round-trip so we\n // never surface a 404 from a doomed resume GET.\n if (runId === undefined) return null;\n const response = await this.fetchImpl()(\n `${this.baseUrl()}/agent/chat/${encodeURIComponent(runId)}/stream`,\n {\n method: 'GET',\n headers: {\n accept: 'text/event-stream',\n ...(await this.resolveHeaders(options.headers)),\n },\n ...(this.options.credentials !== undefined\n ? { credentials: this.options.credentials }\n : {}),\n },\n );\n if (response.status === 404) return null;\n if (!response.ok || !response.body) {\n throw new Error(`Agent stream reconnect failed: ${response.status} ${response.statusText}`);\n }\n this.captureHeaderMeta(response.headers);\n return this.toChunkStream(response.body);\n }\n\n private fetchImpl(): typeof fetch {\n return this.options.fetch ?? globalThis.fetch;\n }\n\n private baseUrl(): string {\n return (this.options.baseUrl ?? '').replace(/\\/$/, '');\n }\n\n private async resolveHeaders(\n perRequest: Record<string, string> | Headers | undefined,\n ): Promise<Record<string, string>> {\n const dynamic = (await this.options.getHeaders?.()) ?? {};\n const extra =\n perRequest instanceof Headers ? Object.fromEntries(perRequest.entries()) : (perRequest ?? {});\n return { ...this.options.headers, ...dynamic, ...extra };\n }\n\n private captureHeaderMeta(headers: Headers): void {\n const runId = headers.get(HEADER_RUN_ID);\n const threadId = headers.get(HEADER_THREAD_ID);\n if (runId) this.currentRunId = runId;\n if (threadId) this.currentThreadId = threadId;\n }\n\n private recordMeta(meta: AgentStreamMeta): void {\n this.currentRunId = meta.runId;\n this.currentThreadId = meta.threadId;\n this.options.onMeta?.(meta);\n }\n\n /**\n * Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.\n * Recognized frames:\n * - `event: meta` `data: {\"runId\",\"threadId\"}` → records identity\n * - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool)\n * - `event: done` `data: {}` → terminates\n * - `event: error` `data: {code,message}` → error chunk\n *\n * A run is ONE UI message with N steps. Each `step-start`/`step-finish` pair brackets a model\n * call plus its tool execution; text and reasoning open lazily and close at the step boundary, so\n * tool-call cards (input streaming → output) render live between the prose.\n */\n private toChunkStream(source: ReadableStream<Uint8Array>): ReadableStream<UIMessageChunk> {\n const reader = source.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let started = false;\n let stepOpen = false;\n let stepIndex = 0;\n let textId: string | null = null;\n let reasoningId: string | null = null;\n const record = (meta: AgentStreamMeta) => this.recordMeta(meta);\n\n return new ReadableStream<UIMessageChunk>({\n async pull(controller) {\n function ensureStarted() {\n if (started) return;\n started = true;\n controller.enqueue({ type: 'start' });\n }\n function openStep() {\n ensureStarted();\n if (stepOpen) closeStep();\n stepOpen = true;\n stepIndex += 1;\n textId = null;\n reasoningId = null;\n controller.enqueue({ type: 'start-step' });\n }\n function ensureStep() {\n if (!stepOpen) openStep();\n }\n function closeStep() {\n if (!stepOpen) return;\n if (textId !== null) {\n controller.enqueue({ type: 'text-end', id: textId });\n textId = null;\n }\n if (reasoningId !== null) {\n controller.enqueue({ type: 'reasoning-end', id: reasoningId });\n reasoningId = null;\n }\n controller.enqueue({ type: 'finish-step' });\n stepOpen = false;\n }\n function emit(event: AgentStreamEvent) {\n switch (event.kind) {\n case 'step-start':\n openStep();\n break;\n case 'step-finish':\n closeStep();\n // `costUsd` rides the step boundary once the backend reports it (older backends omit\n // it entirely — `undefined`, never a crash). `null` means \"priced provider/estimate\n // unavailable\", distinct from a real $0 turn — surfaced verbatim as message metadata\n // (merged onto `message.metadata` by the AI SDK) so a UI can render running cost\n // without polling `GET /quota/today`.\n if (event.costUsd !== undefined) {\n controller.enqueue({\n type: 'message-metadata',\n messageMetadata: { costUsd: event.costUsd },\n });\n }\n break;\n case 'text':\n ensureStep();\n if (textId === null) {\n textId = `txt-${stepIndex}`;\n controller.enqueue({ type: 'text-start', id: textId });\n }\n controller.enqueue({ type: 'text-delta', id: textId, delta: event.text });\n break;\n case 'reasoning':\n ensureStep();\n if (reasoningId === null) {\n reasoningId = `rsn-${stepIndex}`;\n controller.enqueue({ type: 'reasoning-start', id: reasoningId });\n }\n controller.enqueue({ type: 'reasoning-delta', id: reasoningId, delta: event.text });\n break;\n case 'tool-input-start':\n ensureStep();\n controller.enqueue({\n type: 'tool-input-start',\n toolCallId: event.id,\n toolName: event.name,\n // `toolKind` is absent on older backends — omitted (not `undefined`-valued) so\n // `toolMetadata` itself is only present when there's something to say, letting a\n // UI gate approval affordances on `kind === 'action'` without hardcoding tool names.\n ...(event.toolKind !== undefined\n ? { toolMetadata: { toolKind: event.toolKind } }\n : {}),\n });\n break;\n case 'tool-input-delta':\n ensureStep();\n controller.enqueue({\n type: 'tool-input-delta',\n toolCallId: event.id,\n inputTextDelta: event.delta,\n });\n break;\n case 'tool-input-available':\n ensureStep();\n controller.enqueue({\n type: 'tool-input-available',\n toolCallId: event.id,\n toolName: event.name,\n input: event.input,\n ...(event.toolKind !== undefined\n ? { toolMetadata: { toolKind: event.toolKind } }\n : {}),\n });\n break;\n case 'tool-output':\n ensureStep();\n controller.enqueue({\n type: 'tool-output-available',\n toolCallId: event.id,\n output: event.output,\n });\n break;\n case 'tool-output-error':\n ensureStep();\n controller.enqueue({\n type: 'tool-output-error',\n toolCallId: event.id,\n errorText: event.error,\n });\n break;\n default:\n break;\n }\n }\n function finish() {\n ensureStarted();\n closeStep();\n controller.enqueue({ type: 'finish' });\n controller.close();\n }\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n finish();\n return;\n }\n buffer += decoder.decode(value, { stream: true });\n let separator = buffer.indexOf('\\n\\n');\n while (separator !== -1) {\n const rawEvent = buffer.slice(0, separator);\n buffer = buffer.slice(separator + 2);\n const frame = parseSseFrame(rawEvent);\n if (frame.event === 'done') {\n finish();\n return;\n }\n if (frame.event === 'error') {\n ensureStarted();\n controller.enqueue({ type: 'error', errorText: parseErrorText(frame.data) });\n controller.close();\n return;\n }\n if (frame.event === 'meta') {\n const meta = parseMeta(frame.data);\n if (meta) record(meta);\n } else if (frame.data) {\n const event = parseEvent(frame.data);\n if (event) emit(event);\n }\n separator = buffer.indexOf('\\n\\n');\n }\n }\n } catch (error) {\n controller.enqueue({\n type: 'error',\n errorText: error instanceof Error ? error.message : 'Agent stream error',\n });\n controller.close();\n }\n },\n cancel() {\n void reader.cancel();\n },\n });\n }\n}\n\ninterface SseFrame {\n event: string | undefined;\n data: string | undefined;\n}\n\n/** Split one `event:`/`data:` SSE block into its fields. */\nfunction parseSseFrame(raw: string): SseFrame {\n let event: string | undefined;\n const dataLines: string[] = [];\n for (const line of raw.split('\\n')) {\n if (line.startsWith('event:')) {\n event = line.slice('event:'.length).trim();\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice('data:'.length).trimStart());\n }\n }\n return {\n event,\n data: dataLines.length > 0 ? dataLines.join('\\n') : undefined,\n };\n}\n\nfunction parseMeta(data: string | undefined): AgentStreamMeta | null {\n if (!data) return null;\n try {\n const parsed: unknown = JSON.parse(data);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'runId' in parsed &&\n 'threadId' in parsed &&\n typeof parsed.runId === 'string' &&\n typeof parsed.threadId === 'string'\n ) {\n return { runId: parsed.runId, threadId: parsed.threadId };\n }\n } catch {\n /* malformed meta frame — ignore */\n }\n return null;\n}\n\n/** Pull a human-facing message out of the backend's `event: error` frame (`{code,message}`). */\nfunction parseErrorText(data: string | undefined): string {\n if (!data) return 'Agent run failed';\n try {\n const parsed: unknown = JSON.parse(data);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'message' in parsed &&\n typeof parsed.message === 'string'\n ) {\n return parsed.message;\n }\n } catch {\n /* malformed error frame — fall through to the default */\n }\n return 'Agent run failed';\n}\n\n/** Parse a `data:` frame as an `AgentStreamEvent`. Returns null for anything without a `kind`. */\nfunction parseEvent(data: string): AgentStreamEvent | null {\n try {\n const parsed: unknown = JSON.parse(data);\n if (parsed !== null && typeof parsed === 'object' && 'kind' in parsed) {\n return parsed as AgentStreamEvent;\n }\n } catch {\n /* not an event frame — ignore */\n }\n return null;\n}\n\n/** Collect all text parts of a UI message into a single string. */\nfunction extractText(message: UIMessage): string {\n let text = '';\n for (const part of message.parts ?? []) {\n if (part.type === 'text') text += part.text;\n }\n return text;\n}\n","import type {\n MessageAttachment,\n QuotaView,\n ThreadDetail,\n ThreadSummary,\n} from '@dudousxd/nestjs-agent-core';\n\n/**\n * Thrown by {@link AgentClient} on a non-2xx response. Carries the HTTP `status` so callers can\n * branch (e.g. 403 → \"not your thread\", 429 → quota) instead of string-matching a generic Error.\n */\nexport class AgentHttpError extends Error {\n constructor(\n readonly status: number,\n readonly method: string,\n readonly path: string,\n statusText: string,\n ) {\n super(`Agent request failed: ${method} ${path} → ${status} ${statusText}`);\n this.name = 'AgentHttpError';\n }\n}\n\n/** The quota-today read-model: usage, the configured limit (null → unlimited), and USD spend. */\nexport type QuotaToday = QuotaView;\n\nexport interface CancelResult {\n aborted: boolean;\n}\n\nexport interface OkResult {\n ok: boolean;\n}\n\n/**\n * Partial update accepted by `PATCH /agent/threads/:threadId`. `defaultAgent: null` clears a\n * previously-set default back to the module's own default; omitting it leaves the thread's\n * current default untouched.\n */\nexport interface ThreadPatch {\n title?: string;\n defaultAgent?: string | null;\n}\n\nexport interface AgentClientOptions {\n /** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */\n baseUrl?: string;\n /** Static headers merged into every request. */\n headers?: Record<string, string>;\n /** Resolved per request — for short-lived bearer tokens. */\n getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n /** Forwarded to fetch so cookie auth works. */\n credentials?: RequestCredentials;\n /** Injectable for tests / non-browser runtimes. */\n fetch?: typeof fetch;\n}\n\n/**\n * Framework-agnostic REST client for the nestjs-agent endpoints. Used by\n * `useAgentChat`, but standalone-usable (vanilla fetch, no React).\n */\nexport class AgentClient {\n constructor(private readonly options: AgentClientOptions = {}) {}\n\n listThreads(): Promise<ThreadSummary[]> {\n return this.request<ThreadSummary[]>('GET', '/agent/threads');\n }\n\n getThread(id: string): Promise<ThreadDetail> {\n return this.request<ThreadDetail>('GET', `/agent/threads/${encodeURIComponent(id)}`);\n }\n\n deleteThread(id: string): Promise<void> {\n return this.request<void>('DELETE', `/agent/threads/${encodeURIComponent(id)}`);\n }\n\n forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary> {\n return this.request<ThreadSummary>(\n 'POST',\n `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`,\n );\n }\n\n renameThread(id: string, title: string): Promise<OkResult> {\n return this.updateThread(id, { title });\n }\n\n /** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */\n updateThread(id: string, patch: ThreadPatch): Promise<OkResult> {\n return this.request<OkResult>('PATCH', `/agent/threads/${encodeURIComponent(id)}`, patch);\n }\n\n /**\n * Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —\n * mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is\n * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.\n */\n async uploadAttachment(file: File): Promise<MessageAttachment> {\n const fetchImpl = this.options.fetch ?? globalThis.fetch;\n const baseUrl = (this.options.baseUrl ?? '').replace(/\\/$/, '');\n const dynamic = (await this.options.getHeaders?.()) ?? {};\n const formData = new FormData();\n formData.append('file', file);\n const response = await fetchImpl(`${baseUrl}/agent/attachments`, {\n method: 'POST',\n headers: {\n accept: 'application/json',\n ...this.options.headers,\n ...dynamic,\n },\n body: formData,\n ...(this.options.credentials !== undefined ? { credentials: this.options.credentials } : {}),\n });\n return this.handleResponse<MessageAttachment>(response, 'POST', '/agent/attachments');\n }\n\n promoteThread(id: string): Promise<OkResult> {\n return this.request<OkResult>('POST', `/agent/threads/${encodeURIComponent(id)}/promote`);\n }\n\n truncateFromMessage(threadId: string, messageId: string): Promise<OkResult> {\n return this.request<OkResult>(\n 'DELETE',\n `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`,\n );\n }\n\n getQuotaToday(): Promise<QuotaToday> {\n return this.request<QuotaToday>('GET', '/agent/quota/today');\n }\n\n cancelStream(runId: string): Promise<CancelResult> {\n return this.request<CancelResult>('POST', `/agent/chat/${encodeURIComponent(runId)}/cancel`);\n }\n\n approveToolCall(input: { toolCallId: string }): Promise<void> {\n return this.request<void>('POST', '/agent/tool-call/approve', input);\n }\n\n rejectToolCall(input: { toolCallId: string; reason?: string }): Promise<void> {\n return this.request<void>('POST', '/agent/tool-call/reject', input);\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const fetchImpl = this.options.fetch ?? globalThis.fetch;\n const baseUrl = (this.options.baseUrl ?? '').replace(/\\/$/, '');\n const dynamic = (await this.options.getHeaders?.()) ?? {};\n const response = await fetchImpl(`${baseUrl}${path}`, {\n method,\n headers: {\n accept: 'application/json',\n ...(body !== undefined ? { 'content-type': 'application/json' } : {}),\n ...this.options.headers,\n ...dynamic,\n },\n ...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n ...(this.options.credentials !== undefined ? { credentials: this.options.credentials } : {}),\n });\n return this.handleResponse<T>(response, method, path);\n }\n\n private async handleResponse<T>(response: Response, method: string, path: string): Promise<T> {\n if (!response.ok) {\n throw new AgentHttpError(response.status, method, path, response.statusText);\n }\n if (response.status === 204) return undefined as T;\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n}\n","import type { StoredMessage } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\n\n/**\n * Convert a persisted `StoredMessage` (the agent-lib's on-disk shape) into an AI SDK v7\n * `UIMessage` so a loaded thread can seed `useChat`'s `initialMessages` / `messages`.\n *\n * The lib stores a message as flat `content` text plus `toolCalls`/`toolResults` arrays; the SDK\n * renders from `parts`. This maps:\n * - `content` → a single `text` part (skipped when empty).\n * - each `attachment` → a `file` part (image/PDF) so the user bubble re-renders its\n * thumbnails on a reloaded thread.\n * - each `toolCall` → a `tool-<name>` part, pairing its `toolResult` (matched by id):\n * - a result found → `output-available` state, carrying `output`.\n * - no result found → `input-available` state (the call never finished, e.g. the run was\n * interrupted) — `output` is omitted, never a fabricated value.\n * When the store reports the tool's kind (`'read' | 'action'`), it rides along as\n * `toolMetadata.toolKind` so a UI can render an approval affordance for `action` tools\n * without hardcoding tool-name sets, matching how live-streamed tool parts carry it.\n *\n * Live streaming still arrives as fully-typed SDK tool parts (see `AgentChatTransport`); this\n * converter only feeds replayed history, so the tool cards render the same either way.\n */\nexport function storedMessageToUiMessage(message: StoredMessage): UIMessage {\n const parts: UIMessage['parts'] = [];\n\n if (message.content) {\n parts.push({ type: 'text', text: message.content });\n }\n\n for (const attachment of message.attachments ?? []) {\n parts.push({\n type: 'file',\n mediaType: attachment.contentType,\n filename: attachment.name,\n url: attachment.url,\n });\n }\n\n for (const call of message.toolCalls ?? []) {\n const result = message.toolResults?.find((candidate) => candidate.id === call.id);\n // `call.kind` — the store's `'read' | 'action'` classification, once it lands there\n // (parallel to `RecordToolCallInput.toolType` and the stream's `toolKind`).\n const toolKind = call.kind;\n parts.push({\n type: `tool-${call.name}`,\n toolCallId: call.id,\n ...(toolKind !== undefined ? { toolMetadata: { toolKind } } : {}),\n ...(result !== undefined\n ? { state: 'output-available', input: call.input, output: result.output }\n : { state: 'input-available', input: call.input }),\n });\n }\n\n return { id: message.id, role: message.role, parts };\n}\n","import type { StoredMessage } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\nimport { storedMessageToUiMessage } from './stored-message-to-ui-message.js';\n\n/**\n * Summed usage/cost across the stored-message rows merged into one turn (see\n * {@link storedThreadToUiMessages}). Mirrors `MessageUsage`, but `costUsd` is always present\n * (never omitted) since it's a computed aggregate, not a single provider report.\n */\nexport interface AggregatedTurnUsage {\n inputTokens: number;\n outputTokens: number;\n /**\n * `null` only when EVERY merged row reports a null/absent cost (no pricing store bound, or an\n * unpriced model) — same \"unknown, not a real $0\" distinction `MessageUsage.costUsd` makes for a\n * single row. A null cost on SOME rows (mixed with priced ones) contributes `0` to the sum\n * instead of poisoning the whole turn's total to `null` — the tokens are still real and summed\n * either way, so only the cost figure would be understated, never wrong in a \"$0 turn\" sense.\n */\n costUsd: number | null;\n}\n\n/** Metadata the grouped `UIMessage` carries on `.metadata.usage` — see {@link AggregatedTurnUsage}. */\nexport interface StoredTurnMetadata {\n usage: AggregatedTurnUsage;\n}\n\n/**\n * Convert a thread's full `StoredMessage[]` history into `UIMessage[]`, merging consecutive\n * assistant rows (no user message between them) into ONE `UIMessage` per turn.\n *\n * Why: the agent loop persists one row per model iteration (see `agent-loop.ts`'s per-step\n * `persist:assistant:${i}`) — a turn with tool calls appends \"I'll check that...\" + tool calls,\n * then a separate final-answer row. `storedMessageToUiMessage` maps 1:1, so a reloaded thread\n * renders 2-3 assistant bubbles (each with its own cost/tokens footer) for what the LIVE stream\n * renders as one continuous response (one `UIMessage`, stepped internally — see\n * `AgentChatTransport.toChunkStream`'s `start-step`/`finish-step` bracketing). This closes that gap\n * for replayed history.\n *\n * Merge shape: each row is mapped via `storedMessageToUiMessage` (unchanged — same text-then-tool\n * part ordering per row) and the parts are concatenated in row order, so the grouped message's parts\n * read as `[row1 text?, row1 tools?, row2 text?, row2 tools?, ...]` — exactly the sequence the live\n * transport streams for the same turn. The group's `id` is the LAST row's id (the final-answer row —\n * it's the one carrying `followUps`, and what a host keys turn-level actions like \"fork\" or\n * \"regenerate\" on). Non-assistant rows (user/system) pass through `storedMessageToUiMessage`\n * unchanged, one row → one message, same as today.\n *\n * A lone assistant row between two user rows (the common case: no tool calls, or already a single\n * iteration) has nothing to merge with, so it comes back byte-for-byte identical to calling\n * `storedMessageToUiMessage` directly — no `metadata` is added for that case either, preserving\n * exact backward compatibility for hosts that read stored usage some other way.\n *\n * Only an ACTUAL merge (2+ rows) attaches `metadata.usage` — the {@link AggregatedTurnUsage} summed\n * across the merged rows' `usage` (rows without a `usage` at all, e.g. an older/test fixture, are\n * skipped rather than treated as zero-and-priced).\n *\n * Deliberately NOT reproduced: the live transport's `{type: 'step-start'}` UI parts (one per model\n * call, emitted by the AI SDK for every `start-step` chunk — see `repro-real-frames.spec.ts` for a\n * captured wire dump). Those are structural brackets `MessageItem`'s `renderParts` never renders\n * (it only special-cases text and tool parts), and `storedMessageToUiMessage` has never emitted them\n * for a single row either — inventing them here would make a merged turn diverge from a same-shaped\n * unmerged one instead of converging on it. \"Indistinguishable from the live stream\" is scoped to\n * what actually renders: the text/tool-call content in step order, not the step brackets around it.\n */\nexport function storedThreadToUiMessages(messages: StoredMessage[]): UIMessage[] {\n const turns: StoredMessage[][] = [];\n for (const message of messages) {\n const currentTurn = turns.at(-1);\n const previousRow = currentTurn?.at(-1);\n if (message.role === 'assistant' && previousRow?.role === 'assistant' && currentTurn) {\n currentTurn.push(message);\n } else {\n turns.push([message]);\n }\n }\n return turns.map(mergeTurn);\n}\n\nfunction mergeTurn(rows: StoredMessage[]): UIMessage {\n const lastRow = rows.at(-1);\n if (lastRow === undefined) {\n // Unreachable: `storedThreadToUiMessages` never pushes an empty turn.\n throw new Error('storedThreadToUiMessages: encountered an empty turn');\n }\n if (rows.length === 1) {\n return storedMessageToUiMessage(lastRow);\n }\n\n const parts: UIMessage['parts'] = rows.flatMap((row) => storedMessageToUiMessage(row).parts);\n const usage = sumUsage(rows);\n\n return {\n id: lastRow.id,\n role: lastRow.role,\n parts,\n ...(usage !== undefined ? { metadata: { usage } satisfies StoredTurnMetadata } : {}),\n };\n}\n\nfunction sumUsage(rows: StoredMessage[]): AggregatedTurnUsage | undefined {\n const usages = rows\n .map((row) => row.usage)\n .filter((usage): usage is NonNullable<typeof usage> => usage !== undefined);\n if (usages.length === 0) {\n return undefined;\n }\n const allCostsUnknown = usages.every(\n (usage) => usage.costUsd === null || usage.costUsd === undefined,\n );\n return {\n inputTokens: usages.reduce((sum, usage) => sum + usage.inputTokens, 0),\n outputTokens: usages.reduce((sum, usage) => sum + usage.outputTokens, 0),\n costUsd: allCostsUnknown ? null : usages.reduce((sum, usage) => sum + (usage.costUsd ?? 0), 0),\n };\n}\n","import { useChat } from '@ai-sdk/react';\nimport type { ThreadDetail, ThreadSummary } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { AgentChatTransport, type AgentStreamMeta } from './agent-chat-transport.js';\nimport { AgentClient, type QuotaToday } from './client.js';\n\nexport interface UseAgentChatOptions {\n /** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */\n baseUrl?: string;\n /** Static headers merged into every request. */\n headers?: Record<string, string>;\n /** Resolved per request — for short-lived bearer tokens. */\n getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;\n /** Forwarded to fetch so cookie auth/impersonation works. */\n credentials?: RequestCredentials;\n /** Injectable for tests / non-browser runtimes. */\n fetch?: typeof fetch;\n /** Reuse an existing client instead of constructing one. */\n client?: AgentClient;\n /** Named agent to run each turn. */\n agent?: string;\n /** Thread this chat is bound to. Omitted → backend creates one on send. */\n threadId?: string;\n /** Persisted history to seed `useChat` with (consumed on mount only). */\n initialMessages?: UIMessage[];\n /**\n * Run id of a buffered stream to reconnect to on mount — wire this to the thread's\n * `activeStreamId`. Its presence both gates the SDK's `resume` and names the run, so a normal\n * mount (no id) never fires a doomed resume GET. Omitted → no resume.\n *\n * For the common case (no id in hand yet), prefer {@link UseAgentChatOptions.resume} — it does\n * this fetch-and-wire for you.\n */\n resumeRunId?: string;\n /**\n * When `true` and the hook (re)mounts with a `threadId`, the hook fetches the thread and, if its\n * `activeRunId` is non-null (a turn was still streaming when the page loaded/reloaded), attaches\n * to that run's `GET /agent/chat/:runId/stream` automatically — the in-flight turn streams into\n * this session's message state exactly as if the send had happened here. A no-op when `threadId`\n * is omitted (nothing to resume) or the fetched thread has no active run. Default `false`.\n */\n resume?: boolean;\n /** Read at every send to capture a page snapshot for the page-assistant. */\n getPageContext?: () => Record<string, unknown> | null;\n /** Fired after each streamed turn finishes (e.g. to refetch the sidebar). */\n onFinish?: () => void;\n /**\n * Fired once when a threadless chat's first send makes the backend create a thread — carries the\n * new id so the consumer can sync its URL/router. Not fired when `threadId` was supplied. The hook\n * ALSO remembers the id internally, so every subsequent send reuses it (no new thread per message)\n * even before the consumer navigates.\n */\n onThreadCreated?: (threadId: string) => void;\n /**\n * Fired exactly once per run when its stream settles — normally (`'completed'`) or via a thrown\n * failure / the backend's `event: error` frame (`'failed'`) — including a RESUMED stream's own\n * completion. By the time this fires, the server has already derived and persisted the thread's\n * title (and the run's terminal state), so it's the signal `onFinish` doesn't give you: `onFinish`\n * only says \"a turn rendered,\" never \"the server is done writing\" — without this, a host has no\n * reason to refetch the thread/list queries, and the header/sidebar title is stuck on \"Untitled\".\n *\n * Skipped when the attempt never got far enough to learn a run id (e.g. the initial POST itself\n * failed before any response headers/meta frame arrived) — nothing was created server-side, so\n * there's nothing to refetch.\n *\n * A user-initiated `cancel()`/`stop()` reports `'completed'` here (the AI SDK's own abort signal\n * doesn't set its `isError` flag) — the server-side cancel already recorded its own terminal run\n * state; this callback only reflects whether the CLIENT's stream loop exited with or without an\n * error, not why it stopped.\n */\n onRunSettled?: (outcome: { runId: string; status: 'completed' | 'failed' }) => void;\n}\n\ninterface AddToolResultArgs {\n tool: string;\n toolCallId: string;\n output: unknown;\n}\n\n/**\n * Generalized `useChat` wiring for the nestjs-agent backend. Wraps the\n * AI SDK v7 hook with `AgentChatTransport`, plus thread list/load/delete/\n * fork, quota, cancel, and HITL approve/reject — all driven through\n * `AgentClient`. Mirrors flip's `useAdminChat`, generalized.\n */\nexport function useAgentChat(options: UseAgentChatOptions) {\n const latest = useRef(options);\n latest.current = options;\n\n const [runId, setRunId] = useState<string | undefined>(options.resumeRunId);\n const runIdRef = useRef<string | undefined>(runId);\n runIdRef.current = runId;\n\n // The run id learned (via the `meta` frame) during the CURRENT in-flight attempt only — reset to\n // `undefined` by the transport's `onAttemptStart` the instant a new attempt starts (send OR\n // resume), synchronously and before any network call, then set by `onMeta` if that attempt gets\n // far enough to learn one. `onRunSettled` reads THIS (not `runIdRef`, which can still hold a PRIOR\n // run's id) so an attempt that fails before ever learning a run id never misattributes its outcome\n // to the run that just finished.\n const settlingRunIdRef = useRef<string | undefined>(undefined);\n\n // Auto-resume (`resume: true`): the run id discovered from the thread's `activeRunId`, read by\n // the transport's `getResumeRunId` alongside the explicit `resumeRunId` option. A ref, not state\n // — it must be current by the time the SDK's resume effect (below) fires synchronously off it.\n const autoResumeRunIdRef = useRef<string | undefined>(undefined);\n // Flips once the auto-resume fetch resolves a live run. `useChat`'s own `resume` is a reactive\n // effect dependency (it doesn't need to be `true` at mount), so toggling this after the async\n // thread fetch still attaches to the stream.\n const [autoResume, setAutoResume] = useState(false);\n // The loaded thread's active run, if any — exposed regardless of `resume` so a consumer can\n // render a \"still generating\" affordance even when it wires resumption itself via `resumeRunId`.\n const [activeRunId, setActiveRunId] = useState<string | null>(null);\n\n // One-shot flag read (and cleared) by the transport's getBody so the next send carries\n // `regenerate: true` — telling the backend to re-run the last exchange instead of appending.\n const regenerateNext = useRef(false);\n\n // The thread the backend created for a threadless chat, captured from the `meta` frame. Every send\n // after the first reuses it (via getBody) so the conversation stays on ONE thread instead of\n // spawning a new one per message. Reset per mount; an explicit `threadId` option always wins.\n const createdThreadId = useRef<string | undefined>(undefined);\n\n // Identity-stable: per-render config is read through `latest`.\n // biome-ignore lint/correctness/useExhaustiveDependencies: stable by design\n const client = useMemo(() => {\n if (options.client) return options.client;\n return new AgentClient({\n ...(options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {}),\n ...(options.credentials !== undefined ? { credentials: options.credentials } : {}),\n ...(options.fetch !== undefined ? { fetch: options.fetch } : {}),\n getHeaders: async () => mergeHeaders(latest.current),\n });\n }, []);\n\n // Auto-resume: on (re)mount with `resume: true` and a `threadId`, read the thread and, if it\n // carries a live `activeRunId`, attach to it — the same GET-stream reconnect `resumeRunId`\n // already wires, just discovered instead of supplied. Re-runs if `resume`/`threadId` change; a\n // stale response from a since-abandoned fetch (thread swapped mid-flight) is dropped via `cancelled`.\n useEffect(() => {\n if (!options.resume || options.threadId === undefined) return;\n let cancelled = false;\n const threadId = options.threadId;\n void client.getThread(threadId).then((thread) => {\n if (cancelled) return;\n setActiveRunId(thread.activeRunId ?? null);\n if (thread.activeRunId != null) {\n autoResumeRunIdRef.current = thread.activeRunId;\n setAutoResume(true);\n }\n });\n return () => {\n cancelled = true;\n };\n }, [client, options.resume, options.threadId]);\n\n // Identity-stable: per-render config is read through `latest`.\n // biome-ignore lint/correctness/useExhaustiveDependencies: stable by design\n const transport = useMemo(() => {\n function onMeta(meta: AgentStreamMeta): void {\n settlingRunIdRef.current = meta.runId;\n setRunId(meta.runId);\n // A threadless chat just had its thread created server-side — remember it so subsequent sends\n // reuse it, and tell the consumer once so it can sync its URL. An explicit threadId option owns\n // the binding, so we never override it here.\n if (\n latest.current.threadId === undefined &&\n meta.threadId &&\n createdThreadId.current !== meta.threadId\n ) {\n createdThreadId.current = meta.threadId;\n latest.current.onThreadCreated?.(meta.threadId);\n }\n }\n return new AgentChatTransport({\n ...(options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {}),\n ...(options.credentials !== undefined ? { credentials: options.credentials } : {}),\n ...(options.fetch !== undefined ? { fetch: options.fetch } : {}),\n ...(options.agent !== undefined ? { agent: options.agent } : {}),\n getHeaders: async () => mergeHeaders(latest.current),\n getBody: () => {\n const current = latest.current;\n const pageContext = current.getPageContext?.() ?? null;\n const regenerate = regenerateNext.current;\n regenerateNext.current = false;\n // Prefer the explicit threadId; else the one the backend created for this chat, so the second\n // and later sends land on the same thread instead of forking a new one each time.\n const threadId = current.threadId ?? createdThreadId.current;\n return {\n ...(threadId !== undefined ? { threadId } : {}),\n ...(pageContext ? { pageContext } : {}),\n ...(regenerate ? { regenerate: true } : {}),\n };\n },\n getResumeRunId: () => latest.current.resumeRunId ?? autoResumeRunIdRef.current,\n onMeta,\n onAttemptStart: () => {\n settlingRunIdRef.current = undefined;\n },\n });\n }, []);\n\n const chat = useChat({\n transport,\n resume: options.resumeRunId !== undefined || autoResume,\n ...(options.threadId !== undefined ? { id: options.threadId } : {}),\n ...(options.initialMessages !== undefined ? { messages: options.initialMessages } : {}),\n onFinish: ({ isError }) => {\n latest.current.onFinish?.();\n // Only the run learned DURING this attempt — see `settlingRunIdRef`'s comment. `undefined`\n // means the attempt never got a run id (failed before any header/meta frame), so there's\n // nothing server-side to report settling.\n const settledRunId = settlingRunIdRef.current;\n if (settledRunId !== undefined) {\n latest.current.onRunSettled?.({\n runId: settledRunId,\n status: isError ? 'failed' : 'completed',\n });\n }\n },\n });\n\n const chatRef = useRef(chat);\n chatRef.current = chat;\n\n // chat.addToolResult is generic over the backend's tool registry, which\n // we don't statically type here. Expose a string-keyed adapter and narrow\n // once at the SDK boundary. (Documented cast — the only one in this file.)\n type SdkAddToolResult = typeof chat.addToolResult;\n type SdkArgs = Parameters<SdkAddToolResult>[0];\n const addToolResult = useCallback(\n ({ tool, toolCallId, output }: AddToolResultArgs) =>\n chatRef.current.addToolResult({\n tool,\n toolCallId,\n output,\n } as unknown as SdkArgs),\n [],\n );\n\n const [threads, setThreads] = useState<ThreadSummary[]>([]);\n const [quota, setQuota] = useState<QuotaToday | null>(null);\n\n const loadThreads = useCallback(async (): Promise<ThreadSummary[]> => {\n const list = await client.listThreads();\n setThreads(list);\n return list;\n }, [client]);\n\n const loadThread = useCallback(\n (id: string): Promise<ThreadDetail> => client.getThread(id),\n [client],\n );\n\n const deleteThread = useCallback(\n async (id: string): Promise<void> => {\n await client.deleteThread(id);\n setThreads((current) => current.filter((thread) => thread.id !== id));\n },\n [client],\n );\n\n const forkThread = useCallback(\n (threadId: string, messageId: string): Promise<ThreadSummary> =>\n client.forkFromMessage(threadId, messageId),\n [client],\n );\n\n const renameThread = useCallback(\n async (id: string, title: string): Promise<void> => {\n await client.renameThread(id, title);\n setThreads((current) =>\n current.map((thread) => (thread.id === id ? { ...thread, title } : thread)),\n );\n },\n [client],\n );\n\n // A promoted thread was transient — invisible to `listThreads` — so refetch to bring it into view.\n const promoteThread = useCallback(\n async (id: string): Promise<void> => {\n await client.promoteThread(id);\n await loadThreads();\n },\n [client, loadThreads],\n );\n\n const truncateFromMessage = useCallback(\n (threadId: string, messageId: string): Promise<void> =>\n client.truncateFromMessage(threadId, messageId).then(() => undefined),\n [client],\n );\n\n const loadQuota = useCallback(async (): Promise<QuotaToday> => {\n const today = await client.getQuotaToday();\n setQuota(today);\n return today;\n }, [client]);\n\n const cancel = useCallback(async (): Promise<void> => {\n // Close the SSE on the client first so the UI flips out of streaming,\n // then hard-abort server-side (a late terminal step can outlive the\n // connection close alone).\n chatRef.current.stop();\n const activeRunId = runIdRef.current;\n if (activeRunId) {\n try {\n await client.cancelStream(activeRunId);\n } catch {\n /* best-effort — the SDK stop already flipped the UI */\n }\n }\n }, [client]);\n\n // Approve / reject route by tool-call id alone — the server derives the run awaiting it (which is\n // the sub-agent's own run when the pending call belongs to a delegated agent), so no runId is sent.\n const approve = useCallback(\n async ({ toolCallId }: { toolCallId: string }): Promise<void> => {\n await client.approveToolCall({ toolCallId });\n },\n [client],\n );\n\n const reject = useCallback(\n async ({\n toolCallId,\n reason,\n }: {\n toolCallId: string;\n reason?: string;\n }): Promise<void> => {\n await client.rejectToolCall({\n toolCallId,\n ...(reason !== undefined ? { reason } : {}),\n });\n },\n [client],\n );\n\n // Re-run the last exchange: flag the next request as a regenerate (so the backend truncates and\n // re-answers instead of appending) and let the SDK re-issue it, dropping the last assistant turn.\n const regenerate = useCallback((): void => {\n regenerateNext.current = true;\n void chatRef.current.regenerate();\n }, []);\n\n return {\n ...chat,\n addToolResult,\n runId,\n /** The `resume`-fetched thread's active run id, or `null` once resolved with none. */\n activeRunId,\n client,\n threads,\n loadThreads,\n loadThread,\n deleteThread,\n forkThread,\n renameThread,\n promoteThread,\n truncateFromMessage,\n quota,\n loadQuota,\n cancel,\n approve,\n reject,\n regenerate,\n };\n}\n\nasync function mergeHeaders(options: UseAgentChatOptions): Promise<Record<string, string>> {\n const dynamic = (await options.getHeaders?.()) ?? {};\n return { ...options.headers, ...dynamic };\n}\n","import React, { useEffect, useState } from 'react';\nimport { useSpeechRecognition } from './use-speech-recognition.js';\n\nexport interface ChatInputProps {\n onSubmit: (text: string) => void;\n disabled?: boolean;\n placeholder?: string;\n rows?: number;\n /** Enable the dictation mic button (Web Speech API). Default `true`. */\n enableSpeech?: boolean;\n /** BCP-47 locale for dictation. Default `en-US`. */\n speechLang?: string;\n /** Label/content for the send button. Default `\"Send\"`. */\n sendLabel?: React.ReactNode;\n /** Wrapper class. The component ships no styles of its own. */\n className?: string;\n textareaClassName?: string;\n sendButtonClassName?: string;\n micButtonClassName?: string;\n /**\n * Custom mic affordance. Receives `isListening`; return any node. When\n * omitted a plain text button (\"Speak\" / \"Stop\") is rendered.\n */\n renderMic?: (state: { isListening: boolean }) => React.ReactNode;\n}\n\n/**\n * Styling-agnostic chat composer. Enter submits, Shift+Enter inserts a\n * newline. Optional dictation streams interim transcripts into the draft\n * and commits finalized chunks. No design-system or icon-library deps —\n * style it entirely through the `*ClassName` props.\n */\nexport function ChatInput({\n onSubmit,\n disabled,\n placeholder,\n rows = 2,\n enableSpeech = true,\n speechLang = 'en-US',\n sendLabel = 'Send',\n className,\n textareaClassName,\n sendButtonClassName,\n micButtonClassName,\n renderMic,\n}: ChatInputProps) {\n const [value, setValue] = useState('');\n // Interim dictation transcript shown appended to the draft until the\n // engine finalizes it — kept out of `value` so editing the rest of the\n // text doesn't persist a partial guess.\n const [interim, setInterim] = useState('');\n\n const speech = useSpeechRecognition({\n lang: speechLang,\n onFinalTranscript: (text) => {\n const trimmed = text.trim();\n if (!trimmed) return;\n setValue((prev) => (prev.length === 0 ? trimmed : `${prev.trimEnd()} ${trimmed}`));\n setInterim('');\n },\n onInterimTranscript: (text) => setInterim(text),\n });\n\n useEffect(() => {\n if (!speech.isListening) setInterim('');\n }, [speech.isListening]);\n\n function submit() {\n const trimmed = value.trim();\n if (!trimmed) return;\n onSubmit(trimmed);\n setValue('');\n setInterim('');\n }\n\n function handleKeyDown(event: React.KeyboardEvent<HTMLTextAreaElement>) {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n submit();\n }\n }\n\n function toggleMic() {\n if (speech.isListening) speech.stop();\n else speech.start();\n }\n\n const displayValue =\n speech.isListening && interim\n ? `${value}${value && !value.endsWith(' ') ? ' ' : ''}${interim}`\n : value;\n\n const showMic = enableSpeech && speech.isSupported;\n\n return (\n <div className={className}>\n <textarea\n className={textareaClassName}\n rows={rows}\n value={displayValue}\n disabled={disabled}\n onChange={(event) => {\n // Ignore changes while interim text is injected — the engine\n // overwrites it on the next result. Editing works when idle.\n if (speech.isListening && interim) return;\n setValue(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n placeholder={placeholder ?? 'Ask something (Enter to send, Shift+Enter for newline)'}\n />\n <button\n type=\"button\"\n className={sendButtonClassName}\n onClick={submit}\n disabled={disabled || !value.trim()}\n >\n {sendLabel}\n </button>\n {showMic ? (\n <button\n type=\"button\"\n className={micButtonClassName}\n onClick={toggleMic}\n disabled={disabled}\n aria-label={speech.isListening ? 'Stop dictating' : 'Start dictating'}\n >\n {renderMic\n ? renderMic({ isListening: speech.isListening })\n : speech.isListening\n ? 'Stop'\n : 'Speak'}\n </button>\n ) : null}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\n/**\n * Minimal subset of the Web Speech API we use. The real types live\n * behind `webkit*` prefixes in some browsers and aren't in TS lib.dom,\n * so we declare just what we need.\n */\ninterface SpeechResultAlternative {\n transcript: string;\n}\ninterface SpeechRecognitionResult {\n readonly length: number;\n readonly isFinal: boolean;\n item(index: number): SpeechResultAlternative;\n [index: number]: SpeechResultAlternative;\n}\ninterface SpeechRecognitionResultList {\n readonly length: number;\n item(index: number): SpeechRecognitionResult;\n [index: number]: SpeechRecognitionResult;\n}\ninterface SpeechRecognitionEvent {\n results: SpeechRecognitionResultList;\n resultIndex: number;\n}\ninterface SpeechRecognitionErrorEvent {\n error: string;\n}\ninterface SpeechRecognitionInstance {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n onresult: ((event: SpeechRecognitionEvent) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEvent) => void) | null;\n onend: (() => void) | null;\n start(): void;\n stop(): void;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionInstance;\n\nfunction getRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === 'undefined') return null;\n const scope = window as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n };\n return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;\n}\n\nexport interface UseSpeechRecognitionOptions {\n lang?: string;\n onFinalTranscript: (text: string) => void;\n onInterimTranscript?: (text: string) => void;\n}\n\nexport interface SpeechRecognitionHook {\n isSupported: boolean;\n isListening: boolean;\n start(): void;\n stop(): void;\n}\n\n/**\n * Tiny wrapper around the Web Speech API. `start()` opens the mic and\n * streams interim transcripts via `onInterimTranscript`; on a finalized\n * chunk it fires `onFinalTranscript`. `isSupported` is false where the\n * API is missing (e.g. Firefox) — callers should hide the mic button.\n */\nexport function useSpeechRecognition({\n lang = 'en-US',\n onFinalTranscript,\n onInterimTranscript,\n}: UseSpeechRecognitionOptions): SpeechRecognitionHook {\n const recognitionRef = useRef<SpeechRecognitionInstance | null>(null);\n const [isListening, setIsListening] = useState(false);\n\n const ctor = useMemo(() => getRecognitionCtor(), []);\n const isSupported = ctor !== null;\n\n // Keep latest callbacks in refs so we don't tear down the recognition\n // object when they change identity each render.\n const finalRef = useRef(onFinalTranscript);\n const interimRef = useRef(onInterimTranscript);\n finalRef.current = onFinalTranscript;\n interimRef.current = onInterimTranscript;\n\n useEffect(() => {\n if (!ctor) return;\n const recognition = new ctor();\n recognition.lang = lang;\n recognition.continuous = false;\n recognition.interimResults = true;\n\n recognition.onresult = (event) => {\n let interim = '';\n for (let i = event.resultIndex; i < event.results.length; i++) {\n const result = event.results[i];\n if (!result) continue;\n const text = result[0]?.transcript ?? '';\n if (result.isFinal) {\n finalRef.current(text);\n } else {\n interim += text;\n }\n }\n if (interim && interimRef.current) interimRef.current(interim);\n };\n recognition.onerror = (event) => {\n if (event.error !== 'no-speech' && event.error !== 'aborted') {\n console.warn('SpeechRecognition error', event.error);\n }\n setIsListening(false);\n };\n recognition.onend = () => setIsListening(false);\n\n recognitionRef.current = recognition;\n return () => {\n try {\n recognition.stop();\n } catch {\n /* engine already stopped */\n }\n recognitionRef.current = null;\n };\n }, [ctor, lang]);\n\n const start = useCallback(() => {\n const recognition = recognitionRef.current;\n if (!recognition) return;\n try {\n recognition.start();\n setIsListening(true);\n } catch (error) {\n console.warn('SpeechRecognition start failed', error);\n }\n }, []);\n\n const stop = useCallback(() => {\n const recognition = recognitionRef.current;\n if (!recognition) return;\n try {\n recognition.stop();\n } catch {\n /* already stopped */\n }\n }, []);\n\n return { isSupported, isListening, start, stop };\n}\n","import {\n type DynamicToolUIPart,\n type ToolUIPart,\n type UIMessage,\n isTextUIPart,\n isToolUIPart,\n} from 'ai';\nimport React, { useEffect, useRef, useState } from 'react';\n\n/** A tool UI part on a `UIMessage` — a static `tool-*` part or the `dynamic-tool` part. */\nexport type AnyToolUIPart = ToolUIPart | DynamicToolUIPart;\n\nexport type RenderToolPartFn = (part: AnyToolUIPart, key: string) => React.ReactNode;\n\n/** Render a run of consecutive tool parts as one block (e.g. a collapsible tool group). */\nexport type RenderToolGroupFn = (parts: AnyToolUIPart[], key: string) => React.ReactNode;\n\n/** Render a message's text — pass a markdown renderer (e.g. `AgentMarkdown`) here if desired. */\nexport type RenderTextFn = (text: string, ctx: { isStreaming: boolean }) => React.ReactNode;\n\n/** Server-aggregated usage for an assistant turn, shown as a small inline line. */\nexport interface MessageUsageInfo {\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n}\n\nexport interface MessageItemClassNames {\n root?: string;\n /** Resolved per role so user/assistant/system bubbles can differ. */\n byRole?: Partial<Record<UIMessage['role'], string>>;\n text?: string;\n /** The action row under the bubble (copy / fork / regenerate / usage / timestamp). */\n actions?: string;\n actionButton?: string;\n usage?: string;\n timestamp?: string;\n editTextarea?: string;\n editActions?: string;\n}\n\nexport interface MessageItemProps {\n message: UIMessage;\n renderToolPart?: RenderToolPartFn;\n /** Takes precedence over `renderToolPart` for a run of consecutive tool parts. */\n renderToolGroup?: RenderToolGroupFn;\n renderText?: RenderTextFn;\n classNames?: MessageItemClassNames;\n /** Marks the in-flight assistant bubble (e.g. for a caret animation / deferred markdown render). */\n isStreaming?: boolean;\n /** User bubble gets a pencil → inline textarea; submitting calls back with the new text. */\n editable?: boolean;\n onEditSubmit?: (newText: string) => void | Promise<void>;\n /** Assistant bubble gets a \"Regenerate\" affordance. */\n regeneratable?: boolean;\n onRegenerate?: () => void | Promise<void>;\n /** When provided, a \"Fork\" affordance calls back with this message's id. */\n onFork?: (uiMessageId: string) => void | Promise<void>;\n /** Server-aggregated usage for this assistant turn (cost + tokens line). */\n usage?: MessageUsageInfo | null;\n /** ISO timestamp of the persisted message; rendered as a relative-time line when set. */\n createdAt?: string | null;\n}\n\n/**\n * Styling-agnostic renderer for a single v7 `UIMessage`. Ports flip's full chat-item UX —\n * copy, fork, regenerate, inline edit-and-resubmit, usage line, relative timestamp, and tool\n * grouping — but with ALL styling left to `classNames` and every action surfaced as a callback,\n * so no design-system / icon / date library is pulled in (relative time uses Intl.RelativeTimeFormat).\n */\nexport function MessageItem({\n message,\n renderToolPart,\n renderToolGroup,\n renderText,\n classNames,\n isStreaming = false,\n editable,\n onEditSubmit,\n regeneratable,\n onRegenerate,\n onFork,\n usage,\n createdAt,\n}: MessageItemProps) {\n const isUser = message.role === 'user';\n const parts = message.parts ?? [];\n const copyableText = extractText(parts);\n\n const actionRow = (\n <div className={classNames?.actions} data-actions-for={message.role}>\n {copyableText ? (\n <CopyButton text={copyableText} className={classNames?.actionButton} />\n ) : null}\n {onFork ? (\n <button\n type=\"button\"\n onClick={() => void onFork(message.id)}\n className={classNames?.actionButton}\n title=\"Fork thread from this message\"\n >\n Fork\n </button>\n ) : null}\n {!isUser && regeneratable && onRegenerate ? (\n <button\n type=\"button\"\n onClick={() => void onRegenerate()}\n className={classNames?.actionButton}\n title=\"Regenerate response\"\n >\n Regenerate\n </button>\n ) : null}\n {!isUser && usage ? <UsageInline usage={usage} className={classNames?.usage} /> : null}\n {createdAt ? (\n <TimestampInline createdAt={createdAt} className={classNames?.timestamp} />\n ) : null}\n </div>\n );\n\n if (isUser && editable && onEditSubmit) {\n return (\n <EditableUserBubble\n message={message}\n onSubmit={onEditSubmit}\n renderText={renderText}\n classNames={classNames}\n actions={actionRow}\n />\n );\n }\n\n const className = [classNames?.root, classNames?.byRole?.[message.role]]\n .filter((entry) => entry)\n .join(' ');\n\n return (\n <div\n className={className === '' ? undefined : className}\n data-role={message.role}\n data-streaming={isStreaming ? 'true' : undefined}\n >\n <div>\n {renderParts(message, parts, {\n renderText,\n renderToolPart,\n renderToolGroup,\n classNames,\n isStreaming,\n })}\n </div>\n {actionRow}\n </div>\n );\n}\n\ninterface RenderPartsCtx {\n renderText?: RenderTextFn | undefined;\n renderToolPart?: RenderToolPartFn | undefined;\n renderToolGroup?: RenderToolGroupFn | undefined;\n classNames?: MessageItemClassNames | undefined;\n isStreaming: boolean;\n}\n\n/** Walk the parts, buffering consecutive tool parts so they can render as one group. */\nfunction renderParts(\n message: UIMessage,\n parts: NonNullable<UIMessage['parts']>,\n ctx: RenderPartsCtx,\n): React.ReactNode[] {\n const blocks: React.ReactNode[] = [];\n let toolBuffer: AnyToolUIPart[] = [];\n let toolGroupCounter = 0;\n let textCounter = 0;\n\n function flushTools() {\n if (toolBuffer.length === 0) {\n return;\n }\n const key = `${message.id}-tools-${toolGroupCounter++}`;\n if (ctx.renderToolGroup) {\n blocks.push(\n <React.Fragment key={key}>{ctx.renderToolGroup(toolBuffer, key)}</React.Fragment>,\n );\n } else if (ctx.renderToolPart) {\n for (const part of toolBuffer) {\n const partKey = `${message.id}-${part.toolCallId}`;\n blocks.push(\n <React.Fragment key={partKey}>{ctx.renderToolPart(part, partKey)}</React.Fragment>,\n );\n }\n }\n toolBuffer = [];\n }\n\n for (const part of parts) {\n if (isToolUIPart(part)) {\n toolBuffer.push(part);\n continue;\n }\n flushTools();\n if (isTextUIPart(part)) {\n const key = `${message.id}-text-${textCounter++}`;\n blocks.push(\n <div key={key} className={ctx.classNames?.text}>\n {ctx.renderText ? ctx.renderText(part.text, { isStreaming: ctx.isStreaming }) : part.text}\n </div>,\n );\n }\n }\n flushTools();\n return blocks;\n}\n\ninterface EditableUserBubbleProps {\n message: UIMessage;\n onSubmit: (newText: string) => void | Promise<void>;\n renderText?: RenderTextFn | undefined;\n classNames?: MessageItemClassNames | undefined;\n actions?: React.ReactNode;\n}\n\nfunction EditableUserBubble({\n message,\n onSubmit,\n renderText,\n classNames,\n actions,\n}: EditableUserBubbleProps) {\n const initialText = extractText(message.parts ?? []);\n const [isEditing, setIsEditing] = useState(false);\n const [draft, setDraft] = useState(initialText);\n const textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n useEffect(() => {\n if (isEditing && textareaRef.current) {\n const el = textareaRef.current;\n el.focus();\n el.setSelectionRange(el.value.length, el.value.length);\n }\n }, [isEditing]);\n\n async function handleSave() {\n const next = draft.trim();\n if (!next) {\n return;\n }\n setIsEditing(false);\n await onSubmit(next);\n }\n\n if (!isEditing) {\n const rootClass = [classNames?.root, classNames?.byRole?.user]\n .filter((entry) => entry)\n .join(' ');\n return (\n <div className={rootClass === '' ? undefined : rootClass} data-role=\"user\">\n <div className={classNames?.text}>\n {renderText ? renderText(initialText, { isStreaming: false }) : initialText}\n </div>\n <button\n type=\"button\"\n onClick={() => {\n setDraft(initialText);\n setIsEditing(true);\n }}\n className={classNames?.actionButton}\n title=\"Edit & resubmit\"\n >\n Edit\n </button>\n {actions}\n </div>\n );\n }\n\n return (\n <div data-editing=\"true\">\n <textarea\n ref={textareaRef}\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n void handleSave();\n }\n if (event.key === 'Escape') {\n event.preventDefault();\n setIsEditing(false);\n }\n }}\n rows={Math.min(8, draft.split('\\n').length + 1)}\n className={classNames?.editTextarea}\n />\n <div className={classNames?.editActions}>\n <button type=\"button\" onClick={() => setIsEditing(false)}>\n Cancel\n </button>\n <button type=\"button\" onClick={() => void handleSave()} disabled={!draft.trim()}>\n Resend\n </button>\n </div>\n </div>\n );\n}\n\ninterface CopyButtonProps {\n text: string;\n className?: string | undefined;\n}\n\nfunction CopyButton({ text, className }: CopyButtonProps) {\n const [copied, setCopied] = useState(false);\n\n async function handleCopy() {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n } catch {\n // Clipboard can be unavailable (insecure context / denied permission) — fail quietly.\n }\n }\n\n return (\n <button\n type=\"button\"\n onClick={() => void handleCopy()}\n className={className}\n title={copied ? 'Copied!' : 'Copy message text'}\n >\n {copied ? 'Copied' : 'Copy'}\n </button>\n );\n}\n\nfunction UsageInline({\n usage,\n className,\n}: { usage: MessageUsageInfo; className?: string | undefined }) {\n const total = usage.inputTokens + usage.outputTokens;\n return (\n <span\n className={className}\n title={`Input ${usage.inputTokens.toLocaleString()} · Output ${usage.outputTokens.toLocaleString()} tokens`}\n >\n {formatCostUsd(usage.costUsd)} · {formatTokensShort(total)}\n </span>\n );\n}\n\nfunction TimestampInline({\n createdAt,\n className,\n}: { createdAt: string; className?: string | undefined }) {\n const parsed = new Date(createdAt);\n if (Number.isNaN(parsed.getTime())) {\n return null;\n }\n const absolute = parsed.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' });\n return (\n <span className={className} title={absolute}>\n {formatRelativeTime(parsed)}\n </span>\n );\n}\n\nconst RELATIVE_TIME = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto', style: 'narrow' });\nconst ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * \"just now\" within ±10s (server `created_at` can land a few ms ahead of the client clock), a\n * relative phrase up to a week, then a calendar date. Uses Intl.RelativeTimeFormat — no date-fns.\n */\nexport function formatRelativeTime(date: Date): string {\n const diffMs = Date.now() - date.getTime();\n if (Math.abs(diffMs) < 10_000) {\n return 'just now';\n }\n if (diffMs > ONE_WEEK_MS) {\n return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n const minutes = Math.round(-diffMs / 60_000);\n if (Math.abs(minutes) < 60) {\n return RELATIVE_TIME.format(minutes, 'minute');\n }\n const hours = Math.round(-diffMs / 3_600_000);\n if (Math.abs(hours) < 24) {\n return RELATIVE_TIME.format(hours, 'hour');\n }\n return RELATIVE_TIME.format(Math.round(-diffMs / 86_400_000), 'day');\n}\n\nfunction formatTokensShort(n: number): string {\n if (n < 1_000) {\n return `${n} tokens`;\n }\n if (n < 10_000) {\n return `${(n / 1_000).toFixed(1)}k tokens`;\n }\n return `${Math.round(n / 1_000)}k tokens`;\n}\n\nfunction formatCostUsd(cost: number): string {\n if (cost === 0) {\n return '$0';\n }\n if (cost < 0.01) {\n return `$${cost.toFixed(4)}`;\n }\n if (cost < 1) {\n return `$${cost.toFixed(3)}`;\n }\n return `$${cost.toFixed(2)}`;\n}\n\nfunction extractText(parts: NonNullable<UIMessage['parts']>): string {\n const out: string[] = [];\n for (const part of parts) {\n if (isTextUIPart(part)) {\n out.push(part.text);\n }\n }\n return out.join('\\n\\n').trim();\n}\n","import type { UIMessage } from 'ai';\nimport React, { useState } from 'react';\nimport {\n MessageItem,\n type MessageItemClassNames,\n type MessageUsageInfo,\n type RenderTextFn,\n type RenderToolGroupFn,\n type RenderToolPartFn,\n} from './message-item.js';\n\nexport type ChatStatus = 'ready' | 'submitted' | 'streaming' | 'error';\n\n/** Default number of most-recent messages mounted; older load on demand. */\nconst DEFAULT_VISIBLE_COUNT = 50;\nconst LOAD_EARLIER_STEP = 50;\n\nexport interface MessageListClassNames {\n root?: string;\n empty?: string;\n loadEarlier?: string;\n typing?: string;\n error?: string;\n errorRetry?: string;\n followUps?: string;\n followUpChip?: string;\n message?: MessageItemClassNames;\n}\n\nexport interface MessageListProps {\n messages: UIMessage[];\n status: ChatStatus;\n renderToolPart?: RenderToolPartFn;\n renderToolGroup?: RenderToolGroupFn;\n renderText?: RenderTextFn;\n /** When set, every message gets a \"Fork\" affordance calling back with its id. */\n onFork?: (uiMessageId: string) => void | Promise<void>;\n /** User messages get an inline edit-and-resubmit affordance. */\n editable?: boolean;\n onEditSubmit?: (uiMessageId: string, newText: string) => void | Promise<void>;\n /** The last assistant message gets a \"Regenerate\" affordance. */\n regeneratable?: boolean;\n onRegenerate?: (uiMessageId: string) => void | Promise<void>;\n /** Per-message resolvers for the usage line and the persisted timestamp. */\n getUsage?: (message: UIMessage) => MessageUsageInfo | null;\n getCreatedAt?: (message: UIMessage) => string | null;\n /** Shown (with example/follow-up chips) when idle and empty. */\n emptyState?: React.ReactNode;\n /** Node rendered while waiting for the first assistant token. */\n typingIndicator?: React.ReactNode;\n /** AI-generated follow-up prompts under the last assistant message. */\n followUps?: string[] | null;\n onFollowUpClick?: (text: string) => void;\n /** Last turn's error — paired with `onRetry`, renders a retry banner. */\n error?: Error | null;\n onRetry?: () => void | Promise<void>;\n classNames?: MessageListClassNames;\n}\n\n/**\n * Styling-agnostic message list. Keeps the proven UX — windowed render\n * with \"load earlier\", a typing indicator, an inline error+retry banner,\n * and follow-up chips — but ships no styles: drive everything through\n * `classNames` and the `emptyState`/`typingIndicator` slots.\n */\nexport function MessageList({\n messages,\n status,\n renderToolPart,\n renderToolGroup,\n renderText,\n onFork,\n editable,\n onEditSubmit,\n regeneratable,\n onRegenerate,\n getUsage,\n getCreatedAt,\n emptyState,\n typingIndicator,\n followUps,\n onFollowUpClick,\n error,\n onRetry,\n classNames,\n}: MessageListProps) {\n const [visibleCount, setVisibleCount] = useState(DEFAULT_VISIBLE_COUNT);\n\n if (messages.length === 0 && status === 'ready') {\n return <div className={classNames?.empty}>{emptyState}</div>;\n }\n\n const lastMessage = messages.at(-1);\n const showTypingBubble =\n (status === 'submitted' || status === 'streaming') && lastMessage?.role !== 'assistant';\n\n const showFollowUps =\n status === 'ready' &&\n !showTypingBubble &&\n lastMessage?.role === 'assistant' &&\n onFollowUpClick !== undefined &&\n !!followUps &&\n followUps.length > 0;\n\n // Mount only the tail; \"Load earlier\" reveals older chunks — critical\n // for long threads where mounting every markdown-rendered message would\n // lock the main thread during streaming.\n const visibleStart = Math.max(0, messages.length - visibleCount);\n const visibleMessages = messages.slice(visibleStart);\n const hiddenCount = visibleStart;\n\n let lastAssistantIndex = -1;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]?.role === 'assistant') {\n lastAssistantIndex = i;\n break;\n }\n }\n\n return (\n <div className={classNames?.root}>\n {hiddenCount > 0 ? (\n <button\n type=\"button\"\n className={classNames?.loadEarlier}\n onClick={() => setVisibleCount((count) => count + LOAD_EARLIER_STEP)}\n >\n Load earlier ({hiddenCount} more)\n </button>\n ) : null}\n {visibleMessages.map((message, index) => {\n const absoluteIndex = visibleStart + index;\n const isLastAssistant = absoluteIndex === lastAssistantIndex;\n const usage = getUsage?.(message) ?? null;\n const createdAt = getCreatedAt?.(message) ?? null;\n return (\n <MessageItem\n key={message.id}\n message={message}\n {...(renderToolPart ? { renderToolPart } : {})}\n {...(renderToolGroup ? { renderToolGroup } : {})}\n {...(renderText ? { renderText } : {})}\n {...(classNames?.message ? { classNames: classNames.message } : {})}\n {...(onFork ? { onFork } : {})}\n {...(editable ? { editable } : {})}\n {...(onEditSubmit\n ? { onEditSubmit: (text: string) => onEditSubmit(message.id, text) }\n : {})}\n {...(regeneratable && isLastAssistant ? { regeneratable } : {})}\n {...(onRegenerate ? { onRegenerate: () => onRegenerate(message.id) } : {})}\n {...(usage !== null ? { usage } : {})}\n {...(createdAt !== null ? { createdAt } : {})}\n isStreaming={status === 'streaming' && isLastAssistant}\n />\n );\n })}\n {showTypingBubble ? (\n <div className={classNames?.typing}>{typingIndicator ?? 'Thinking…'}</div>\n ) : null}\n {error && onRetry ? (\n <div className={classNames?.error}>\n <span>{error.message || \"The assistant didn't respond.\"}</span>\n <button type=\"button\" className={classNames?.errorRetry} onClick={() => void onRetry()}>\n Retry\n </button>\n </div>\n ) : null}\n {showFollowUps ? (\n <div className={classNames?.followUps}>\n {followUps.map((text) => (\n <button\n key={text}\n type=\"button\"\n className={classNames?.followUpChip}\n onClick={() => onFollowUpClick(text)}\n >\n {text}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n );\n}\n","/**\n * Languages the dictation mic offers. The Web Speech API expects a\n * BCP-47 tag and does NOT auto-detect — the engine forces incoming\n * audio onto the phonemes of whatever `recognition.lang` is set to, so\n * the host app picks the locale explicitly.\n */\nexport interface SpeechLanguage {\n /** BCP-47 tag fed straight into `SpeechRecognition.lang`. */\n tag: string;\n /** Human label for a dropdown. */\n label: string;\n}\n\nexport const DEFAULT_SPEECH_LANG = 'en-US';\n\nexport const SPEECH_LANGUAGES: ReadonlyArray<SpeechLanguage> = [\n { tag: 'en-US', label: 'English (US)' },\n { tag: 'en-GB', label: 'English (UK)' },\n { tag: 'pt-BR', label: 'Portuguese (Brazil)' },\n { tag: 'es-ES', label: 'Spanish (Spain)' },\n];\n\nconst STORAGE_KEY = 'nestjs-agent-chat-speech-lang';\n\n/**\n * Recover the last picked language from localStorage (sync — safe at\n * `useState` init). Falls back to the default if nothing's stored or the\n * stored tag isn't in the offered list.\n */\nexport function loadStoredSpeechLang(): string {\n if (typeof window === 'undefined') return DEFAULT_SPEECH_LANG;\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (raw && SPEECH_LANGUAGES.some((language) => language.tag === raw)) {\n return raw;\n }\n } catch {\n /* localStorage disabled (private window / webview) — use the default */\n }\n return DEFAULT_SPEECH_LANG;\n}\n\nexport function persistSpeechLang(tag: string): void {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(STORAGE_KEY, tag);\n } catch {\n /* same fallback — silently skip persistence */\n }\n}\n"],"mappings":"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","names":["HEADER_RUN_ID","HEADER_THREAD_ID","AgentChatTransport","currentRunId","currentThreadId","options","runId","threadId","sendMessages","onAttemptStart","lastMessage","messages","at","message","extractText","body","agent","undefined","getBody","response","fetchImpl","baseUrl","method","headers","accept","resolveHeaders","JSON","stringify","credentials","abortSignal","signal","ok","Error","status","statusText","captureHeaderMeta","toChunkStream","reconnectToStream","getResumeRunId","encodeURIComponent","fetch","globalThis","replace","perRequest","dynamic","getHeaders","extra","Headers","Object","fromEntries","entries","get","recordMeta","meta","onMeta","source","reader","getReader","decoder","TextDecoder","buffer","started","stepOpen","stepIndex","textId","reasoningId","record","ReadableStream","pull","controller","ensureStarted","enqueue","type","openStep","closeStep","ensureStep","id","emit","event","kind","costUsd","messageMetadata","delta","text","toolCallId","toolName","name","toolKind","toolMetadata","inputTextDelta","input","output","errorText","error","finish","close","done","value","read","decode","stream","separator","indexOf","rawEvent","slice","frame","parseSseFrame","parseErrorText","data","parseMeta","parseEvent","cancel","raw","dataLines","line","split","startsWith","length","trim","push","trimStart","join","parsed","parse","part","parts","AgentHttpError","Error","status","method","path","statusText","name","AgentClient","options","listThreads","request","getThread","id","encodeURIComponent","deleteThread","forkFromMessage","threadId","messageId","renameThread","title","updateThread","patch","uploadAttachment","file","fetchImpl","fetch","globalThis","baseUrl","replace","dynamic","getHeaders","formData","FormData","append","response","headers","accept","body","credentials","undefined","handleResponse","promoteThread","truncateFromMessage","getQuotaToday","cancelStream","runId","approveToolCall","input","rejectToolCall","JSON","stringify","ok","text","parse","storedMessageToUiMessage","message","parts","content","push","type","text","attachment","attachments","mediaType","contentType","filename","name","url","call","toolCalls","result","toolResults","find","candidate","id","toolKind","kind","toolCallId","undefined","toolMetadata","state","input","output","role","storedThreadToUiMessages","messages","turns","message","currentTurn","at","previousRow","role","push","map","mergeTurn","rows","lastRow","undefined","Error","length","storedMessageToUiMessage","parts","flatMap","row","usage","sumUsage","id","metadata","usages","filter","allCostsUnknown","every","costUsd","inputTokens","reduce","sum","outputTokens","import_react","useAgentChat","options","latest","useRef","current","runId","setRunId","useState","resumeRunId","runIdRef","settlingRunIdRef","undefined","autoResumeRunIdRef","autoResume","setAutoResume","activeRunId","setActiveRunId","regenerateNext","createdThreadId","client","useMemo","AgentClient","baseUrl","credentials","fetch","getHeaders","mergeHeaders","useEffect","resume","threadId","cancelled","getThread","then","thread","transport","onMeta","meta","onThreadCreated","AgentChatTransport","agent","getBody","pageContext","getPageContext","regenerate","getResumeRunId","onAttemptStart","chat","useChat","id","initialMessages","messages","onFinish","isError","settledRunId","onRunSettled","status","chatRef","addToolResult","useCallback","tool","toolCallId","output","threads","setThreads","quota","setQuota","loadThreads","list","listThreads","loadThread","deleteThread","filter","forkThread","messageId","forkFromMessage","renameThread","title","map","promoteThread","truncateFromMessage","loadQuota","today","getQuotaToday","cancel","stop","cancelStream","approve","approveToolCall","reject","reason","rejectToolCall","dynamic","headers","import_react","import_react","getRecognitionCtor","window","scope","SpeechRecognition","webkitSpeechRecognition","useSpeechRecognition","lang","onFinalTranscript","onInterimTranscript","recognitionRef","useRef","isListening","setIsListening","useState","ctor","useMemo","isSupported","finalRef","interimRef","current","useEffect","recognition","continuous","interimResults","onresult","event","interim","i","resultIndex","results","length","result","text","transcript","isFinal","onerror","error","console","warn","onend","stop","start","useCallback","ChatInput","onSubmit","disabled","placeholder","rows","enableSpeech","speechLang","sendLabel","className","textareaClassName","sendButtonClassName","micButtonClassName","renderMic","value","setValue","useState","interim","setInterim","speech","useSpeechRecognition","lang","onFinalTranscript","text","trimmed","trim","prev","length","trimEnd","onInterimTranscript","useEffect","isListening","submit","handleKeyDown","event","key","shiftKey","preventDefault","toggleMic","stop","start","displayValue","endsWith","showMic","isSupported","React","div","textarea","onChange","target","onKeyDown","button","type","onClick","aria-label","import_react","MessageItem","message","renderToolPart","renderToolGroup","renderText","classNames","isStreaming","editable","onEditSubmit","regeneratable","onRegenerate","onFork","usage","createdAt","isUser","role","parts","copyableText","extractText","actionRow","React","div","className","actions","data-actions-for","CopyButton","text","actionButton","button","type","onClick","id","title","UsageInline","TimestampInline","timestamp","EditableUserBubble","onSubmit","root","byRole","filter","entry","join","undefined","data-role","data-streaming","renderParts","ctx","blocks","toolBuffer","toolGroupCounter","textCounter","flushTools","length","key","push","Fragment","part","partKey","toolCallId","isToolUIPart","isTextUIPart","initialText","isEditing","setIsEditing","useState","draft","setDraft","textareaRef","useRef","useEffect","current","el","focus","setSelectionRange","value","handleSave","next","trim","rootClass","user","data-editing","textarea","ref","onChange","event","target","onKeyDown","shiftKey","preventDefault","rows","Math","min","split","editTextarea","editActions","disabled","copied","setCopied","handleCopy","navigator","clipboard","writeText","setTimeout","total","inputTokens","outputTokens","span","toLocaleString","formatCostUsd","costUsd","formatTokensShort","parsed","Date","Number","isNaN","getTime","absolute","dateStyle","timeStyle","formatRelativeTime","RELATIVE_TIME","Intl","RelativeTimeFormat","numeric","style","ONE_WEEK_MS","date","diffMs","now","abs","toLocaleDateString","month","day","minutes","round","format","hours","n","toFixed","cost","out","import_react","DEFAULT_VISIBLE_COUNT","LOAD_EARLIER_STEP","MessageList","messages","status","renderToolPart","renderToolGroup","renderText","onFork","editable","onEditSubmit","regeneratable","onRegenerate","getUsage","getCreatedAt","emptyState","typingIndicator","followUps","onFollowUpClick","error","onRetry","classNames","visibleCount","setVisibleCount","useState","length","React","div","className","empty","lastMessage","at","showTypingBubble","role","showFollowUps","undefined","visibleStart","Math","max","visibleMessages","slice","hiddenCount","lastAssistantIndex","i","root","button","type","loadEarlier","onClick","count","map","message","index","absoluteIndex","isLastAssistant","usage","createdAt","MessageItem","key","id","text","isStreaming","typing","span","errorRetry","followUpChip","DEFAULT_SPEECH_LANG","SPEECH_LANGUAGES","tag","label","STORAGE_KEY","loadStoredSpeechLang","window","raw","localStorage","getItem","some","language","persistSpeechLang","setItem"]}
|