@dudousxd/nestjs-agent-react 0.2.0 → 0.3.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 CHANGED
@@ -347,6 +347,17 @@ var AgentClient = class {
347
347
  forkFromMessage(threadId, messageId) {
348
348
  return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
349
349
  }
350
+ renameThread(id, title) {
351
+ return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, {
352
+ title
353
+ });
354
+ }
355
+ promoteThread(id) {
356
+ return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
357
+ }
358
+ truncateFromMessage(threadId, messageId) {
359
+ return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
360
+ }
350
361
  getQuotaToday() {
351
362
  return this.request("GET", "/agent/quota/today");
352
363
  }
@@ -496,6 +507,25 @@ function useAgentChat(options) {
496
507
  const forkThread = (0, import_react2.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
497
508
  client
498
509
  ]);
510
+ const renameThread = (0, import_react2.useCallback)(async (id, title) => {
511
+ await client.renameThread(id, title);
512
+ setThreads((current) => current.map((thread) => thread.id === id ? {
513
+ ...thread,
514
+ title
515
+ } : thread));
516
+ }, [
517
+ client
518
+ ]);
519
+ const promoteThread = (0, import_react2.useCallback)(async (id) => {
520
+ await client.promoteThread(id);
521
+ await loadThreads();
522
+ }, [
523
+ client,
524
+ loadThreads
525
+ ]);
526
+ const truncateFromMessage = (0, import_react2.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
527
+ client
528
+ ]);
499
529
  const loadQuota = (0, import_react2.useCallback)(async () => {
500
530
  const today = await client.getQuotaToday();
501
531
  setQuota(today);
@@ -546,6 +576,9 @@ function useAgentChat(options) {
546
576
  loadThread,
547
577
  deleteThread,
548
578
  forkThread,
579
+ renameThread,
580
+ promoteThread,
581
+ truncateFromMessage,
549
582
  quota,
550
583
  loadQuota,
551
584
  cancel,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/agent-chat-transport.ts","../src/client.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} from './client.js';\nexport { useAgentChat, type UseAgentChatOptions } from './use-agent-chat.js';\nexport * from './components/index.js';\n","import 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 */\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 synthesize a valid v7\n * UI-message chunk stream. Recognized backend frames:\n * - `event: meta` `data: {\"runId\",\"threadId\"}` → records identity\n * - `data: {\"delta\":\"...\"}` → `text-delta`\n * - `event: done` `data: {}` → terminates\n */\n private toChunkStream(source: ReadableStream<Uint8Array>): ReadableStream<UIMessageChunk> {\n const reader = source.getReader();\n const decoder = new TextDecoder();\n const textId = `txt-${Math.random().toString(36).slice(2)}`;\n let buffer = '';\n let started = false;\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 controller.enqueue({ type: 'start-step' });\n controller.enqueue({ type: 'text-start', id: textId });\n }\n function finish() {\n ensureStarted();\n controller.enqueue({ type: 'text-end', id: textId });\n controller.enqueue({ type: 'finish-step' });\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 delta = parseDelta(frame.data);\n if (delta) {\n ensureStarted();\n controller.enqueue({\n type: 'text-delta',\n id: textId,\n delta,\n });\n }\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\nfunction parseDelta(data: string): string | null {\n try {\n const parsed: unknown = JSON.parse(data);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'delta' in parsed &&\n typeof parsed.delta === 'string'\n ) {\n return parsed.delta;\n }\n } catch {\n /* not a delta 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 { ThreadDetail, ThreadSummary } 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\nexport interface QuotaToday {\n usedTokens: number;\n}\n\nexport interface CancelResult {\n aborted: boolean;\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 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 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 { useChat } from '@ai-sdk/react';\nimport type { ThreadDetail, ThreadSummary } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\nimport { useCallback, 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 resumeRunId?: string;\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\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 // 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 // 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 // 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 }\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 return {\n ...(current.threadId !== undefined ? { threadId: current.threadId } : {}),\n ...(pageContext ? { pageContext } : {}),\n ...(regenerate ? { regenerate: true } : {}),\n };\n },\n getResumeRunId: () => latest.current.resumeRunId,\n onMeta,\n });\n }, []);\n\n const chat = useChat({\n transport,\n resume: options.resumeRunId !== undefined,\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 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 client,\n threads,\n loadThreads,\n loadThread,\n deleteThread,\n forkThread,\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","textId","Math","random","toString","slice","buffer","started","record","ReadableStream","pull","controller","ensureStarted","enqueue","type","id","finish","close","done","value","read","decode","stream","separator","indexOf","rawEvent","frame","parseSseFrame","event","errorText","parseErrorText","data","parseMeta","delta","parseDelta","error","cancel","raw","dataLines","line","split","startsWith","length","trim","push","trimStart","join","parsed","parse","text","part","parts","AgentHttpError","Error","status","method","path","statusText","name","AgentClient","options","listThreads","request","getThread","id","encodeURIComponent","deleteThread","forkFromMessage","threadId","messageId","getQuotaToday","cancelStream","runId","approveToolCall","input","rejectToolCall","body","fetchImpl","fetch","globalThis","baseUrl","replace","dynamic","getHeaders","response","headers","accept","undefined","JSON","stringify","credentials","ok","text","parse","import_react","useAgentChat","options","latest","useRef","current","runId","setRunId","useState","resumeRunId","runIdRef","regenerateNext","client","useMemo","AgentClient","baseUrl","undefined","credentials","fetch","getHeaders","mergeHeaders","transport","onMeta","meta","AgentChatTransport","agent","getBody","pageContext","getPageContext","regenerate","threadId","getResumeRunId","chat","useChat","resume","id","initialMessages","messages","onFinish","chatRef","addToolResult","useCallback","tool","toolCallId","output","threads","setThreads","quota","setQuota","loadThreads","list","listThreads","loadThread","getThread","deleteThread","filter","thread","forkThread","messageId","forkFromMessage","loadQuota","today","getQuotaToday","cancel","stop","activeRunId","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/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} from './client.js';\nexport { useAgentChat, type UseAgentChatOptions } from './use-agent-chat.js';\nexport * from './components/index.js';\n","import 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 */\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 synthesize a valid v7\n * UI-message chunk stream. Recognized backend frames:\n * - `event: meta` `data: {\"runId\",\"threadId\"}` → records identity\n * - `data: {\"delta\":\"...\"}` → `text-delta`\n * - `event: done` `data: {}` → terminates\n */\n private toChunkStream(source: ReadableStream<Uint8Array>): ReadableStream<UIMessageChunk> {\n const reader = source.getReader();\n const decoder = new TextDecoder();\n const textId = `txt-${Math.random().toString(36).slice(2)}`;\n let buffer = '';\n let started = false;\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 controller.enqueue({ type: 'start-step' });\n controller.enqueue({ type: 'text-start', id: textId });\n }\n function finish() {\n ensureStarted();\n controller.enqueue({ type: 'text-end', id: textId });\n controller.enqueue({ type: 'finish-step' });\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 delta = parseDelta(frame.data);\n if (delta) {\n ensureStarted();\n controller.enqueue({\n type: 'text-delta',\n id: textId,\n delta,\n });\n }\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\nfunction parseDelta(data: string): string | null {\n try {\n const parsed: unknown = JSON.parse(data);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'delta' in parsed &&\n typeof parsed.delta === 'string'\n ) {\n return parsed.delta;\n }\n } catch {\n /* not a delta 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 { QuotaView, ThreadDetail, ThreadSummary } 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\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.request<OkResult>('PATCH', `/agent/threads/${encodeURIComponent(id)}`, { title });\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 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 { useChat } from '@ai-sdk/react';\nimport type { ThreadDetail, ThreadSummary } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\nimport { useCallback, 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 resumeRunId?: string;\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\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 // 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 // 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 // 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 }\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 return {\n ...(current.threadId !== undefined ? { threadId: current.threadId } : {}),\n ...(pageContext ? { pageContext } : {}),\n ...(regenerate ? { regenerate: true } : {}),\n };\n },\n getResumeRunId: () => latest.current.resumeRunId,\n onMeta,\n });\n }, []);\n\n const chat = useChat({\n transport,\n resume: options.resumeRunId !== undefined,\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 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;AC6CA,IAAMA,gBAAgB;AACtB,IAAMC,mBAAmB;AAYlB,IAAMC,qBAAN,MAAMA;EAbb,OAaaA;;;;EACHC;EACAC;EAER,YAA6BC,UAAqC,CAAC,GAAG;SAAzCA,UAAAA;EAA0C;;EAGvE,IAAIC,QAA4B;AAC9B,WAAO,KAAKH;EACd;;EAGA,IAAII,WAA+B;AACjC,WAAO,KAAKH;EACd;EAEA,MAAMI,aACJH,SACyC;AACzC,UAAMI,cAAcJ,QAAQK,SAASC,GAAG,EAAC;AACzC,UAAMC,UAAUH,cAAcI,YAAYJ,WAAAA,IAAe;AACzD,UAAMK,OAAgC;MACpC,GAAI,KAAKT,QAAQU,UAAUC,SAAY;QAAED,OAAO,KAAKV,QAAQU;MAAM,IAAI,CAAC;MACxE,GAAI,KAAKV,QAAQY,UAAO,KAAQ,CAAC;MACjC,GAAKZ,QAAQS,QAAgD,CAAC;MAC9DF;IACF;AACA,UAAMM,WAAW,MAAM,KAAKC,UAAS,EAAG,GAAG,KAAKC,QAAO,CAAA,eAAiB;MACtEC,QAAQ;MACRC,SAAS;QACP,gBAAgB;QAChBC,QAAQ;QACR,GAAI,MAAM,KAAKC,eAAenB,QAAQiB,OAAO;MAC/C;MACAR,MAAMW,KAAKC,UAAUZ,IAAAA;MACrB,GAAI,KAAKT,QAAQsB,gBAAgBX,SAAY;QAAEW,aAAa,KAAKtB,QAAQsB;MAAY,IAAI,CAAC;MAC1F,GAAItB,QAAQuB,cAAc;QAAEC,QAAQxB,QAAQuB;MAAY,IAAI,CAAC;IAC/D,CAAA;AACA,QAAI,CAACV,SAASY,MAAM,CAACZ,SAASJ,MAAM;AAClC,YAAM,IAAIiB,MAAM,8BAA8Bb,SAASc,MAAM,IAAId,SAASe,UAAU,EAAE;IACxF;AACA,SAAKC,kBAAkBhB,SAASI,OAAO;AACvC,WAAO,KAAKa,cAAcjB,SAASJ,IAAI;EACzC;EAEA,MAAMsB,kBACJ/B,SACgD;AAChD,UAAMC,QAAQ,KAAKD,QAAQgC,iBAAc;AAGzC,QAAI/B,UAAUU,OAAW,QAAO;AAChC,UAAME,WAAW,MAAM,KAAKC,UAAS,EACnC,GAAG,KAAKC,QAAO,CAAA,eAAiBkB,mBAAmBhC,KAAAA,CAAAA,WACnD;MACEe,QAAQ;MACRC,SAAS;QACPC,QAAQ;QACR,GAAI,MAAM,KAAKC,eAAenB,QAAQiB,OAAO;MAC/C;MACA,GAAI,KAAKjB,QAAQsB,gBAAgBX,SAC7B;QAAEW,aAAa,KAAKtB,QAAQsB;MAAY,IACxC,CAAC;IACP,CAAA;AAEF,QAAIT,SAASc,WAAW,IAAK,QAAO;AACpC,QAAI,CAACd,SAASY,MAAM,CAACZ,SAASJ,MAAM;AAClC,YAAM,IAAIiB,MAAM,kCAAkCb,SAASc,MAAM,IAAId,SAASe,UAAU,EAAE;IAC5F;AACA,SAAKC,kBAAkBhB,SAASI,OAAO;AACvC,WAAO,KAAKa,cAAcjB,SAASJ,IAAI;EACzC;EAEQK,YAA0B;AAChC,WAAO,KAAKd,QAAQkC,SAASC,WAAWD;EAC1C;EAEQnB,UAAkB;AACxB,YAAQ,KAAKf,QAAQe,WAAW,IAAIqB,QAAQ,OAAO,EAAA;EACrD;EAEA,MAAcjB,eACZkB,YACiC;AACjC,UAAMC,UAAW,MAAM,KAAKtC,QAAQuC,aAAU,KAAS,CAAC;AACxD,UAAMC,QACJH,sBAAsBI,UAAUC,OAAOC,YAAYN,WAAWO,QAAO,CAAA,IAAOP,cAAc,CAAC;AAC7F,WAAO;MAAE,GAAG,KAAKrC,QAAQiB;MAAS,GAAGqB;MAAS,GAAGE;IAAM;EACzD;EAEQX,kBAAkBZ,SAAwB;AAChD,UAAMhB,QAAQgB,QAAQ4B,IAAIlD,aAAAA;AAC1B,UAAMO,WAAWe,QAAQ4B,IAAIjD,gBAAAA;AAC7B,QAAIK,MAAO,MAAKH,eAAeG;AAC/B,QAAIC,SAAU,MAAKH,kBAAkBG;EACvC;EAEQ4C,WAAWC,MAA6B;AAC9C,SAAKjD,eAAeiD,KAAK9C;AACzB,SAAKF,kBAAkBgD,KAAK7C;AAC5B,SAAKF,QAAQgD,SAASD,IAAAA;EACxB;;;;;;;;EASQjB,cAAcmB,QAAoE;AACxF,UAAMC,SAASD,OAAOE,UAAS;AAC/B,UAAMC,UAAU,IAAIC,YAAAA;AACpB,UAAMC,SAAS,OAAOC,KAAKC,OAAM,EAAGC,SAAS,EAAA,EAAIC,MAAM,CAAA,CAAA;AACvD,QAAIC,SAAS;AACb,QAAIC,UAAU;AACd,UAAMC,SAAS,wBAACd,SAA0B,KAAKD,WAAWC,IAAAA,GAA3C;AAEf,WAAO,IAAIe,eAA+B;MACxC,MAAMC,KAAKC,YAAU;AACnB,iBAASC,gBAAAA;AACP,cAAIL,QAAS;AACbA,oBAAU;AACVI,qBAAWE,QAAQ;YAAEC,MAAM;UAAQ,CAAA;AACnCH,qBAAWE,QAAQ;YAAEC,MAAM;UAAa,CAAA;AACxCH,qBAAWE,QAAQ;YAAEC,MAAM;YAAcC,IAAId;UAAO,CAAA;QACtD;AANSW;AAOT,iBAASI,SAAAA;AACPJ,wBAAAA;AACAD,qBAAWE,QAAQ;YAAEC,MAAM;YAAYC,IAAId;UAAO,CAAA;AAClDU,qBAAWE,QAAQ;YAAEC,MAAM;UAAc,CAAA;AACzCH,qBAAWE,QAAQ;YAAEC,MAAM;UAAS,CAAA;AACpCH,qBAAWM,MAAK;QAClB;AANSD;AAOT,YAAI;AACF,iBAAO,MAAM;AACX,kBAAM,EAAEE,MAAMC,MAAK,IAAK,MAAMtB,OAAOuB,KAAI;AACzC,gBAAIF,MAAM;AACRF,qBAAAA;AACA;YACF;AACAV,sBAAUP,QAAQsB,OAAOF,OAAO;cAAEG,QAAQ;YAAK,CAAA;AAC/C,gBAAIC,YAAYjB,OAAOkB,QAAQ,MAAA;AAC/B,mBAAOD,cAAc,IAAI;AACvB,oBAAME,WAAWnB,OAAOD,MAAM,GAAGkB,SAAAA;AACjCjB,uBAASA,OAAOD,MAAMkB,YAAY,CAAA;AAClC,oBAAMG,QAAQC,cAAcF,QAAAA;AAC5B,kBAAIC,MAAME,UAAU,QAAQ;AAC1BZ,uBAAAA;AACA;cACF;AACA,kBAAIU,MAAME,UAAU,SAAS;AAC3BhB,8BAAAA;AACAD,2BAAWE,QAAQ;kBAAEC,MAAM;kBAASe,WAAWC,eAAeJ,MAAMK,IAAI;gBAAE,CAAA;AAC1EpB,2BAAWM,MAAK;AAChB;cACF;AACA,kBAAIS,MAAME,UAAU,QAAQ;AAC1B,sBAAMlC,OAAOsC,UAAUN,MAAMK,IAAI;AACjC,oBAAIrC,KAAMc,QAAOd,IAAAA;cACnB,WAAWgC,MAAMK,MAAM;AACrB,sBAAME,QAAQC,WAAWR,MAAMK,IAAI;AACnC,oBAAIE,OAAO;AACTrB,gCAAAA;AACAD,6BAAWE,QAAQ;oBACjBC,MAAM;oBACNC,IAAId;oBACJgC;kBACF,CAAA;gBACF;cACF;AACAV,0BAAYjB,OAAOkB,QAAQ,MAAA;YAC7B;UACF;QACF,SAASW,OAAO;AACdxB,qBAAWE,QAAQ;YACjBC,MAAM;YACNe,WAAWM,iBAAiB9D,QAAQ8D,MAAMjF,UAAU;UACtD,CAAA;AACAyD,qBAAWM,MAAK;QAClB;MACF;MACAmB,SAAAA;AACE,aAAKvC,OAAOuC,OAAM;MACpB;IACF,CAAA;EACF;AACF;AAQA,SAAST,cAAcU,KAAW;AAChC,MAAIT;AACJ,QAAMU,YAAsB,CAAA;AAC5B,aAAWC,QAAQF,IAAIG,MAAM,IAAA,GAAO;AAClC,QAAID,KAAKE,WAAW,QAAA,GAAW;AAC7Bb,cAAQW,KAAKlC,MAAM,SAASqC,MAAM,EAAEC,KAAI;IAC1C,WAAWJ,KAAKE,WAAW,OAAA,GAAU;AACnCH,gBAAUM,KAAKL,KAAKlC,MAAM,QAAQqC,MAAM,EAAEG,UAAS,CAAA;IACrD;EACF;AACA,SAAO;IACLjB;IACAG,MAAMO,UAAUI,SAAS,IAAIJ,UAAUQ,KAAK,IAAA,IAAQxF;EACtD;AACF;AAdSqE;AAgBT,SAASK,UAAUD,MAAwB;AACzC,MAAI,CAACA,KAAM,QAAO;AAClB,MAAI;AACF,UAAMgB,SAAkBhF,KAAKiF,MAAMjB,IAAAA;AACnC,QACEgB,WAAW,QACX,OAAOA,WAAW,YAClB,WAAWA,UACX,cAAcA,UACd,OAAOA,OAAOnG,UAAU,YACxB,OAAOmG,OAAOlG,aAAa,UAC3B;AACA,aAAO;QAAED,OAAOmG,OAAOnG;QAAOC,UAAUkG,OAAOlG;MAAS;IAC1D;EACF,QAAQ;EAER;AACA,SAAO;AACT;AAlBSmF;AAqBT,SAASF,eAAeC,MAAwB;AAC9C,MAAI,CAACA,KAAM,QAAO;AAClB,MAAI;AACF,UAAMgB,SAAkBhF,KAAKiF,MAAMjB,IAAAA;AACnC,QACEgB,WAAW,QACX,OAAOA,WAAW,YAClB,aAAaA,UACb,OAAOA,OAAO7F,YAAY,UAC1B;AACA,aAAO6F,OAAO7F;IAChB;EACF,QAAQ;EAER;AACA,SAAO;AACT;AAhBS4E;AAkBT,SAASI,WAAWH,MAAY;AAC9B,MAAI;AACF,UAAMgB,SAAkBhF,KAAKiF,MAAMjB,IAAAA;AACnC,QACEgB,WAAW,QACX,OAAOA,WAAW,YAClB,WAAWA,UACX,OAAOA,OAAOd,UAAU,UACxB;AACA,aAAOc,OAAOd;IAChB;EACF,QAAQ;EAER;AACA,SAAO;AACT;AAfSC;AAkBT,SAAS/E,YAAYD,SAAkB;AACrC,MAAI+F,OAAO;AACX,aAAWC,QAAQhG,QAAQiG,SAAS,CAAA,GAAI;AACtC,QAAID,KAAKpC,SAAS,OAAQmC,SAAQC,KAAKD;EACzC;AACA,SAAOA;AACT;AANS9F;;;AChUF,IAAMiG,iBAAN,cAA6BC,MAAAA;EAJpC,OAIoCA;;;;;;EAClC,YACWC,QACAC,QACAC,MACTC,YACA;AACA,UAAM,yBAAyBF,MAAAA,IAAUC,IAAAA,WAAUF,MAAAA,IAAUG,UAAAA,EAAY,GAAA,KALhEH,SAAAA,QAAAA,KACAC,SAAAA,QAAAA,KACAC,OAAAA;AAIT,SAAKE,OAAO;EACd;AACF;AA8BO,IAAMC,cAAN,MAAMA;EA5Cb,OA4CaA;;;;EACX,YAA6BC,UAA8B,CAAC,GAAG;SAAlCA,UAAAA;EAAmC;EAEhEC,cAAwC;AACtC,WAAO,KAAKC,QAAyB,OAAO,gBAAA;EAC9C;EAEAC,UAAUC,IAAmC;AAC3C,WAAO,KAAKF,QAAsB,OAAO,kBAAkBG,mBAAmBD,EAAAA,CAAAA,EAAK;EACrF;EAEAE,aAAaF,IAA2B;AACtC,WAAO,KAAKF,QAAc,UAAU,kBAAkBG,mBAAmBD,EAAAA,CAAAA,EAAK;EAChF;EAEAG,gBAAgBC,UAAkBC,WAA2C;AAC3E,WAAO,KAAKP,QACV,QACA,kBAAkBG,mBAAmBG,QAAAA,CAAAA,cAAuBH,mBAAmBI,SAAAA,CAAAA,EAAY;EAE/F;EAEAC,aAAaN,IAAYO,OAAkC;AACzD,WAAO,KAAKT,QAAkB,SAAS,kBAAkBG,mBAAmBD,EAAAA,CAAAA,IAAO;MAAEO;IAAM,CAAA;EAC7F;EAEAC,cAAcR,IAA+B;AAC3C,WAAO,KAAKF,QAAkB,QAAQ,kBAAkBG,mBAAmBD,EAAAA,CAAAA,UAAa;EAC1F;EAEAS,oBAAoBL,UAAkBC,WAAsC;AAC1E,WAAO,KAAKP,QACV,UACA,kBAAkBG,mBAAmBG,QAAAA,CAAAA,SAAkBH,mBAAmBI,SAAAA,CAAAA,EAAY;EAE1F;EAEAK,gBAAqC;AACnC,WAAO,KAAKZ,QAAoB,OAAO,oBAAA;EACzC;EAEAa,aAAaC,OAAsC;AACjD,WAAO,KAAKd,QAAsB,QAAQ,eAAeG,mBAAmBW,KAAAA,CAAAA,SAAe;EAC7F;EAEAC,gBAAgBC,OAA8C;AAC5D,WAAO,KAAKhB,QAAc,QAAQ,4BAA4BgB,KAAAA;EAChE;EAEAC,eAAeD,OAA+D;AAC5E,WAAO,KAAKhB,QAAc,QAAQ,2BAA2BgB,KAAAA;EAC/D;EAEA,MAAchB,QAAWP,QAAgBC,MAAcwB,MAA4B;AACjF,UAAMC,YAAY,KAAKrB,QAAQsB,SAASC,WAAWD;AACnD,UAAME,WAAW,KAAKxB,QAAQwB,WAAW,IAAIC,QAAQ,OAAO,EAAA;AAC5D,UAAMC,UAAW,MAAM,KAAK1B,QAAQ2B,aAAU,KAAS,CAAC;AACxD,UAAMC,WAAW,MAAMP,UAAU,GAAGG,OAAAA,GAAU5B,IAAAA,IAAQ;MACpDD;MACAkC,SAAS;QACPC,QAAQ;QACR,GAAIV,SAASW,SAAY;UAAE,gBAAgB;QAAmB,IAAI,CAAC;QACnE,GAAG,KAAK/B,QAAQ6B;QAChB,GAAGH;MACL;MACA,GAAIN,SAASW,SAAY;QAAEX,MAAMY,KAAKC,UAAUb,IAAAA;MAAM,IAAI,CAAC;MAC3D,GAAI,KAAKpB,QAAQkC,gBAAgBH,SAAY;QAAEG,aAAa,KAAKlC,QAAQkC;MAAY,IAAI,CAAC;IAC5F,CAAA;AACA,QAAI,CAACN,SAASO,IAAI;AAChB,YAAM,IAAI3C,eAAeoC,SAASlC,QAAQC,QAAQC,MAAMgC,SAAS/B,UAAU;IAC7E;AACA,QAAI+B,SAASlC,WAAW,IAAK,QAAOqC;AACpC,UAAMK,OAAO,MAAMR,SAASQ,KAAI;AAChC,QAAI,CAACA,KAAM,QAAOL;AAClB,WAAOC,KAAKK,MAAMD,IAAAA;EACpB;AACF;;;AC1HA,mBAAwB;AAGxB,IAAAE,gBAAuD;AA+ChD,SAASC,aAAaC,SAA4B;AACvD,QAAMC,aAASC,sBAAOF,OAAAA;AACtBC,SAAOE,UAAUH;AAEjB,QAAM,CAACI,OAAOC,QAAAA,QAAYC,wBAA6BN,QAAQO,WAAW;AAC1E,QAAMC,eAAWN,sBAA2BE,KAAAA;AAC5CI,WAASL,UAAUC;AAInB,QAAMK,qBAAiBP,sBAAO,KAAA;AAI9B,QAAMQ,aAASC,uBAAQ,MAAA;AACrB,QAAIX,QAAQU,OAAQ,QAAOV,QAAQU;AACnC,WAAO,IAAIE,YAAY;MACrB,GAAIZ,QAAQa,YAAYC,SAAY;QAAED,SAASb,QAAQa;MAAQ,IAAI,CAAC;MACpE,GAAIb,QAAQe,gBAAgBD,SAAY;QAAEC,aAAaf,QAAQe;MAAY,IAAI,CAAC;MAChF,GAAIf,QAAQgB,UAAUF,SAAY;QAAEE,OAAOhB,QAAQgB;MAAM,IAAI,CAAC;MAC9DC,YAAY,mCAAYC,aAAajB,OAAOE,OAAO,GAAvC;IACd,CAAA;EACF,GAAG,CAAA,CAAE;AAIL,QAAMgB,gBAAYR,uBAAQ,MAAA;AACxB,aAASS,OAAOC,MAAqB;AACnChB,eAASgB,KAAKjB,KAAK;IACrB;AAFSgB;AAGT,WAAO,IAAIE,mBAAmB;MAC5B,GAAItB,QAAQa,YAAYC,SAAY;QAAED,SAASb,QAAQa;MAAQ,IAAI,CAAC;MACpE,GAAIb,QAAQe,gBAAgBD,SAAY;QAAEC,aAAaf,QAAQe;MAAY,IAAI,CAAC;MAChF,GAAIf,QAAQgB,UAAUF,SAAY;QAAEE,OAAOhB,QAAQgB;MAAM,IAAI,CAAC;MAC9D,GAAIhB,QAAQuB,UAAUT,SAAY;QAAES,OAAOvB,QAAQuB;MAAM,IAAI,CAAC;MAC9DN,YAAY,mCAAYC,aAAajB,OAAOE,OAAO,GAAvC;MACZqB,SAAS,6BAAA;AACP,cAAMrB,UAAUF,OAAOE;AACvB,cAAMsB,cAActB,QAAQuB,iBAAc,KAAQ;AAClD,cAAMC,cAAalB,eAAeN;AAClCM,uBAAeN,UAAU;AACzB,eAAO;UACL,GAAIA,QAAQyB,aAAad,SAAY;YAAEc,UAAUzB,QAAQyB;UAAS,IAAI,CAAC;UACvE,GAAIH,cAAc;YAAEA;UAAY,IAAI,CAAC;UACrC,GAAIE,cAAa;YAAEA,YAAY;UAAK,IAAI,CAAC;QAC3C;MACF,GAVS;MAWTE,gBAAgB,6BAAM5B,OAAOE,QAAQI,aAArB;MAChBa;IACF,CAAA;EACF,GAAG,CAAA,CAAE;AAEL,QAAMU,WAAOC,sBAAQ;IACnBZ;IACAa,QAAQhC,QAAQO,gBAAgBO;IAChC,GAAId,QAAQ4B,aAAad,SAAY;MAAEmB,IAAIjC,QAAQ4B;IAAS,IAAI,CAAC;IACjE,GAAI5B,QAAQkC,oBAAoBpB,SAAY;MAAEqB,UAAUnC,QAAQkC;IAAgB,IAAI,CAAC;IACrFE,UAAU,6BAAA;AACRnC,aAAOE,QAAQiC,WAAQ;IACzB,GAFU;EAGZ,CAAA;AAEA,QAAMC,cAAUnC,sBAAO4B,IAAAA;AACvBO,UAAQlC,UAAU2B;AAOlB,QAAMQ,oBAAgBC,2BACpB,CAAC,EAAEC,MAAMC,YAAYC,OAAM,MACzBL,QAAQlC,QAAQmC,cAAc;IAC5BE;IACAC;IACAC;EACF,CAAA,GACF,CAAA,CAAE;AAGJ,QAAM,CAACC,SAASC,UAAAA,QAActC,wBAA0B,CAAA,CAAE;AAC1D,QAAM,CAACuC,OAAOC,QAAAA,QAAYxC,wBAA4B,IAAA;AAEtD,QAAMyC,kBAAcR,2BAAY,YAAA;AAC9B,UAAMS,OAAO,MAAMtC,OAAOuC,YAAW;AACrCL,eAAWI,IAAAA;AACX,WAAOA;EACT,GAAG;IAACtC;GAAO;AAEX,QAAMwC,iBAAaX,2BACjB,CAACN,OAAsCvB,OAAOyC,UAAUlB,EAAAA,GACxD;IAACvB;GAAO;AAGV,QAAM0C,mBAAeb,2BACnB,OAAON,OAAAA;AACL,UAAMvB,OAAO0C,aAAanB,EAAAA;AAC1BW,eAAW,CAACzC,YAAYA,QAAQkD,OAAO,CAACC,WAAWA,OAAOrB,OAAOA,EAAAA,CAAAA;EACnE,GACA;IAACvB;GAAO;AAGV,QAAM6C,iBAAahB,2BACjB,CAACX,UAAkB4B,cACjB9C,OAAO+C,gBAAgB7B,UAAU4B,SAAAA,GACnC;IAAC9C;GAAO;AAGV,QAAMgD,mBAAenB,2BACnB,OAAON,IAAY0B,UAAAA;AACjB,UAAMjD,OAAOgD,aAAazB,IAAI0B,KAAAA;AAC9Bf,eAAW,CAACzC,YACVA,QAAQyD,IAAI,CAACN,WAAYA,OAAOrB,OAAOA,KAAK;MAAE,GAAGqB;MAAQK;IAAM,IAAIL,MAAAA,CAAAA;EAEvE,GACA;IAAC5C;GAAO;AAIV,QAAMmD,oBAAgBtB,2BACpB,OAAON,OAAAA;AACL,UAAMvB,OAAOmD,cAAc5B,EAAAA;AAC3B,UAAMc,YAAAA;EACR,GACA;IAACrC;IAAQqC;GAAY;AAGvB,QAAMe,0BAAsBvB,2BAC1B,CAACX,UAAkB4B,cACjB9C,OAAOoD,oBAAoBlC,UAAU4B,SAAAA,EAAWO,KAAK,MAAMjD,MAAAA,GAC7D;IAACJ;GAAO;AAGV,QAAMsD,gBAAYzB,2BAAY,YAAA;AAC5B,UAAM0B,QAAQ,MAAMvD,OAAOwD,cAAa;AACxCpB,aAASmB,KAAAA;AACT,WAAOA;EACT,GAAG;IAACvD;GAAO;AAEX,QAAMyD,aAAS5B,2BAAY,YAAA;AAIzBF,YAAQlC,QAAQiE,KAAI;AACpB,UAAMC,cAAc7D,SAASL;AAC7B,QAAIkE,aAAa;AACf,UAAI;AACF,cAAM3D,OAAO4D,aAAaD,WAAAA;MAC5B,QAAQ;MAER;IACF;EACF,GAAG;IAAC3D;GAAO;AAIX,QAAM6D,cAAUhC,2BACd,OAAO,EAAEE,WAAU,MAA0B;AAC3C,UAAM/B,OAAO8D,gBAAgB;MAAE/B;IAAW,CAAA;EAC5C,GACA;IAAC/B;GAAO;AAGV,QAAM+D,aAASlC,2BACb,OAAO,EACLE,YACAiC,OAAM,MAIP;AACC,UAAMhE,OAAOiE,eAAe;MAC1BlC;MACA,GAAIiC,WAAW5D,SAAY;QAAE4D;MAAO,IAAI,CAAC;IAC3C,CAAA;EACF,GACA;IAAChE;GAAO;AAKV,QAAMiB,iBAAaY,2BAAY,MAAA;AAC7B9B,mBAAeN,UAAU;AACzB,SAAKkC,QAAQlC,QAAQwB,WAAU;EACjC,GAAG,CAAA,CAAE;AAEL,SAAO;IACL,GAAGG;IACHQ;IACAlC;IACAM;IACAiC;IACAI;IACAG;IACAE;IACAG;IACAG;IACAG;IACAC;IACAjB;IACAmB;IACAG;IACAI;IACAE;IACA9C;EACF;AACF;AA9MgB5B;AAgNhB,eAAemB,aAAalB,SAA4B;AACtD,QAAM4E,UAAW,MAAM5E,QAAQiB,aAAU,KAAS,CAAC;AACnD,SAAO;IAAE,GAAGjB,QAAQ6E;IAAS,GAAGD;EAAQ;AAC1C;AAHe1D;;;AClQf,IAAA4D,gBAA2C;;;ACA3C,IAAAC,gBAAkE;AAwClE,SAASC,qBAAAA;AACP,MAAI,OAAOC,WAAW,YAAa,QAAO;AAC1C,QAAMC,QAAQD;AAId,SAAOC,MAAMC,qBAAqBD,MAAME,2BAA2B;AACrE;AAPSJ;AA4BF,SAASK,qBAAqB,EACnCC,OAAO,SACPC,mBACAC,oBAAmB,GACS;AAC5B,QAAMC,qBAAiBC,sBAAyC,IAAA;AAChE,QAAM,CAACC,aAAaC,cAAAA,QAAkBC,wBAAS,KAAA;AAE/C,QAAMC,WAAOC,uBAAQ,MAAMf,mBAAAA,GAAsB,CAAA,CAAE;AACnD,QAAMgB,cAAcF,SAAS;AAI7B,QAAMG,eAAWP,sBAAOH,iBAAAA;AACxB,QAAMW,iBAAaR,sBAAOF,mBAAAA;AAC1BS,WAASE,UAAUZ;AACnBW,aAAWC,UAAUX;AAErBY,+BAAU,MAAA;AACR,QAAI,CAACN,KAAM;AACX,UAAMO,cAAc,IAAIP,KAAAA;AACxBO,gBAAYf,OAAOA;AACnBe,gBAAYC,aAAa;AACzBD,gBAAYE,iBAAiB;AAE7BF,gBAAYG,WAAW,CAACC,UAAAA;AACtB,UAAIC,UAAU;AACd,eAASC,IAAIF,MAAMG,aAAaD,IAAIF,MAAMI,QAAQC,QAAQH,KAAK;AAC7D,cAAMI,SAASN,MAAMI,QAAQF,CAAAA;AAC7B,YAAI,CAACI,OAAQ;AACb,cAAMC,OAAOD,OAAO,CAAA,GAAIE,cAAc;AACtC,YAAIF,OAAOG,SAAS;AAClBjB,mBAASE,QAAQa,IAAAA;QACnB,OAAO;AACLN,qBAAWM;QACb;MACF;AACA,UAAIN,WAAWR,WAAWC,QAASD,YAAWC,QAAQO,OAAAA;IACxD;AACAL,gBAAYc,UAAU,CAACV,UAAAA;AACrB,UAAIA,MAAMW,UAAU,eAAeX,MAAMW,UAAU,WAAW;AAC5DC,gBAAQC,KAAK,2BAA2Bb,MAAMW,KAAK;MACrD;AACAxB,qBAAe,KAAA;IACjB;AACAS,gBAAYkB,QAAQ,MAAM3B,eAAe,KAAA;AAEzCH,mBAAeU,UAAUE;AACzB,WAAO,MAAA;AACL,UAAI;AACFA,oBAAYmB,KAAI;MAClB,QAAQ;MAER;AACA/B,qBAAeU,UAAU;IAC3B;EACF,GAAG;IAACL;IAAMR;GAAK;AAEf,QAAMmC,YAAQC,2BAAY,MAAA;AACxB,UAAMrB,cAAcZ,eAAeU;AACnC,QAAI,CAACE,YAAa;AAClB,QAAI;AACFA,kBAAYoB,MAAK;AACjB7B,qBAAe,IAAA;IACjB,SAASwB,OAAO;AACdC,cAAQC,KAAK,kCAAkCF,KAAAA;IACjD;EACF,GAAG,CAAA,CAAE;AAEL,QAAMI,WAAOE,2BAAY,MAAA;AACvB,UAAMrB,cAAcZ,eAAeU;AACnC,QAAI,CAACE,YAAa;AAClB,QAAI;AACFA,kBAAYmB,KAAI;IAClB,QAAQ;IAER;EACF,GAAG,CAAA,CAAE;AAEL,SAAO;IAAExB;IAAaL;IAAa8B;IAAOD;EAAK;AACjD;AAhFgBnC;;;ADpCT,SAASsC,UAAU,EACxBC,UACAC,UACAC,aACAC,OAAO,GACPC,eAAe,MACfC,aAAa,SACbC,YAAY,QACZC,WACAC,mBACAC,qBACAC,oBACAC,UAAS,GACM;AACf,QAAM,CAACC,OAAOC,QAAAA,QAAYC,wBAAS,EAAA;AAInC,QAAM,CAACC,SAASC,UAAAA,QAAcF,wBAAS,EAAA;AAEvC,QAAMG,SAASC,qBAAqB;IAClCC,MAAMd;IACNe,mBAAmB,wBAACC,SAAAA;AAClB,YAAMC,UAAUD,KAAKE,KAAI;AACzB,UAAI,CAACD,QAAS;AACdT,eAAS,CAACW,SAAUA,KAAKC,WAAW,IAAIH,UAAU,GAAGE,KAAKE,QAAO,CAAA,IAAMJ,OAAAA,EAAS;AAChFN,iBAAW,EAAA;IACb,GALmB;IAMnBW,qBAAqB,wBAACN,SAASL,WAAWK,IAAAA,GAArB;EACvB,CAAA;AAEAO,+BAAU,MAAA;AACR,QAAI,CAACX,OAAOY,YAAab,YAAW,EAAA;EACtC,GAAG;IAACC,OAAOY;GAAY;AAEvB,WAASC,SAAAA;AACP,UAAMR,UAAUV,MAAMW,KAAI;AAC1B,QAAI,CAACD,QAAS;AACdtB,aAASsB,OAAAA;AACTT,aAAS,EAAA;AACTG,eAAW,EAAA;EACb;AANSc;AAQT,WAASC,cAAcC,OAA+C;AACpE,QAAIA,MAAMC,QAAQ,WAAW,CAACD,MAAME,UAAU;AAC5CF,YAAMG,eAAc;AACpBL,aAAAA;IACF;EACF;AALSC;AAOT,WAASK,YAAAA;AACP,QAAInB,OAAOY,YAAaZ,QAAOoB,KAAI;QAC9BpB,QAAOqB,MAAK;EACnB;AAHSF;AAKT,QAAMG,eACJtB,OAAOY,eAAed,UAClB,GAAGH,KAAAA,GAAQA,SAAS,CAACA,MAAM4B,SAAS,GAAA,IAAO,MAAM,EAAA,GAAKzB,OAAAA,KACtDH;AAEN,QAAM6B,UAAUrC,gBAAgBa,OAAOyB;AAEvC,SACE,8BAAAC,QAAA,cAACC,OAAAA;IAAIrC;KACH,8BAAAoC,QAAA,cAACE,YAAAA;IACCtC,WAAWC;IACXL;IACAS,OAAO2B;IACPtC;IACA6C,UAAU,wBAACd,UAAAA;AAGT,UAAIf,OAAOY,eAAed,QAAS;AACnCF,eAASmB,MAAMe,OAAOnC,KAAK;IAC7B,GALU;IAMVoC,WAAWjB;IACX7B,aAAaA,eAAe;MAE9B,8BAAAyC,QAAA,cAACM,UAAAA;IACCC,MAAK;IACL3C,WAAWE;IACX0C,SAASrB;IACT7B,UAAUA,YAAY,CAACW,MAAMW,KAAI;KAEhCjB,SAAAA,GAEFmC,UACC,8BAAAE,QAAA,cAACM,UAAAA;IACCC,MAAK;IACL3C,WAAWG;IACXyC,SAASf;IACTnC;IACAmD,cAAYnC,OAAOY,cAAc,mBAAmB;KAEnDlB,YACGA,UAAU;IAAEkB,aAAaZ,OAAOY;EAAY,CAAA,IAC5CZ,OAAOY,cACL,SACA,OAAA,IAEN,IAAA;AAGV;AAvGgB9B;;;AEhChB,gBAMO;AACP,IAAAsD,gBAAmD;AA+D5C,SAASC,YAAY,EAC1BC,SACAC,gBACAC,iBACAC,YACAC,YACAC,cAAc,OACdC,UACAC,cACAC,eACAC,cACAC,QACAC,OACAC,UAAS,GACQ;AACjB,QAAMC,SAASb,QAAQc,SAAS;AAChC,QAAMC,QAAQf,QAAQe,SAAS,CAAA;AAC/B,QAAMC,eAAeC,aAAYF,KAAAA;AAEjC,QAAMG,YACJ,8BAAAC,QAAA,cAACC,OAAAA;IAAIC,WAAWjB,YAAYkB;IAASC,oBAAkBvB,QAAQc;KAC5DE,eACC,8BAAAG,QAAA,cAACK,YAAAA;IAAWC,MAAMT;IAAcK,WAAWjB,YAAYsB;OACrD,MACHhB,SACC,8BAAAS,QAAA,cAACQ,UAAAA;IACCC,MAAK;IACLC,SAAS,6BAAM,KAAKnB,OAAOV,QAAQ8B,EAAE,GAA5B;IACTT,WAAWjB,YAAYsB;IACvBK,OAAM;KACP,MAAA,IAGC,MACH,CAAClB,UAAUL,iBAAiBC,eAC3B,8BAAAU,QAAA,cAACQ,UAAAA;IACCC,MAAK;IACLC,SAAS,6BAAM,KAAKpB,aAAAA,GAAX;IACTY,WAAWjB,YAAYsB;IACvBK,OAAM;KACP,YAAA,IAGC,MACH,CAAClB,UAAUF,QAAQ,8BAAAQ,QAAA,cAACa,aAAAA;IAAYrB;IAAcU,WAAWjB,YAAYO;OAAY,MACjFC,YACC,8BAAAO,QAAA,cAACc,iBAAAA;IAAgBrB;IAAsBS,WAAWjB,YAAY8B;OAC5D,IAAA;AAIR,MAAIrB,UAAUP,YAAYC,cAAc;AACtC,WACE,8BAAAY,QAAA,cAACgB,oBAAAA;MACCnC;MACAoC,UAAU7B;MACVJ;MACAC;MACAkB,SAASJ;;EAGf;AAEA,QAAMG,YAAY;IAACjB,YAAYiC;IAAMjC,YAAYkC,SAAStC,QAAQc,IAAI;IACnEyB,OAAO,CAACC,UAAUA,KAAAA,EAClBC,KAAK,GAAA;AAER,SACE,8BAAAtB,QAAA,cAACC,OAAAA;IACCC,WAAWA,cAAc,KAAKqB,SAAYrB;IAC1CsB,aAAW3C,QAAQc;IACnB8B,kBAAgBvC,cAAc,SAASqC;KAEvC,8BAAAvB,QAAA,cAACC,OAAAA,MACEyB,YAAY7C,SAASe,OAAO;IAC3BZ;IACAF;IACAC;IACAE;IACAC;EACF,CAAA,CAAA,GAEDa,SAAAA;AAGP;AArFgBnB;AAgGhB,SAAS8C,YACP7C,SACAe,OACA+B,KAAmB;AAEnB,QAAMC,SAA4B,CAAA;AAClC,MAAIC,aAA8B,CAAA;AAClC,MAAIC,mBAAmB;AACvB,MAAIC,cAAc;AAElB,WAASC,aAAAA;AACP,QAAIH,WAAWI,WAAW,GAAG;AAC3B;IACF;AACA,UAAMC,MAAM,GAAGrD,QAAQ8B,EAAE,UAAUmB,kBAAAA;AACnC,QAAIH,IAAI5C,iBAAiB;AACvB6C,aAAOO,KACL,8BAAAnC,QAAA,cAACA,cAAAA,QAAMoC,UAAQ;QAACF;SAAWP,IAAI5C,gBAAgB8C,YAAYK,GAAAA,CAAAA,CAAAA;IAE/D,WAAWP,IAAI7C,gBAAgB;AAC7B,iBAAWuD,QAAQR,YAAY;AAC7B,cAAMS,UAAU,GAAGzD,QAAQ8B,EAAE,IAAI0B,KAAKE,UAAU;AAChDX,eAAOO,KACL,8BAAAnC,QAAA,cAACA,cAAAA,QAAMoC,UAAQ;UAACF,KAAKI;WAAUX,IAAI7C,eAAeuD,MAAMC,OAAAA,CAAAA,CAAAA;MAE5D;IACF;AACAT,iBAAa,CAAA;EACf;AAlBSG;AAoBT,aAAWK,QAAQzC,OAAO;AACxB,YAAI4C,wBAAaH,IAAAA,GAAO;AACtBR,iBAAWM,KAAKE,IAAAA;AAChB;IACF;AACAL,eAAAA;AACA,YAAIS,wBAAaJ,IAAAA,GAAO;AACtB,YAAMH,MAAM,GAAGrD,QAAQ8B,EAAE,SAASoB,aAAAA;AAClCH,aAAOO,KACL,8BAAAnC,QAAA,cAACC,OAAAA;QAAIiC;QAAUhC,WAAWyB,IAAI1C,YAAYqB;SACvCqB,IAAI3C,aAAa2C,IAAI3C,WAAWqD,KAAK/B,MAAM;QAAEpB,aAAayC,IAAIzC;MAAY,CAAA,IAAKmD,KAAK/B,IAAI,CAAA;IAG/F;EACF;AACA0B,aAAAA;AACA,SAAOJ;AACT;AA/CSF;AAyDT,SAASV,mBAAmB,EAC1BnC,SACAoC,UACAjC,YACAC,YACAkB,QAAO,GACiB;AACxB,QAAMuC,cAAc5C,aAAYjB,QAAQe,SAAS,CAAA,CAAE;AACnD,QAAM,CAAC+C,WAAWC,YAAAA,QAAgBC,wBAAS,KAAA;AAC3C,QAAM,CAACC,OAAOC,QAAAA,QAAYF,wBAASH,WAAAA;AACnC,QAAMM,kBAAcC,sBAAmC,IAAA;AAEvDC,+BAAU,MAAA;AACR,QAAIP,aAAaK,YAAYG,SAAS;AACpC,YAAMC,KAAKJ,YAAYG;AACvBC,SAAGC,MAAK;AACRD,SAAGE,kBAAkBF,GAAGG,MAAMtB,QAAQmB,GAAGG,MAAMtB,MAAM;IACvD;EACF,GAAG;IAACU;GAAU;AAEd,iBAAea,aAAAA;AACb,UAAMC,OAAOX,MAAMY,KAAI;AACvB,QAAI,CAACD,MAAM;AACT;IACF;AACAb,iBAAa,KAAA;AACb,UAAM3B,SAASwC,IAAAA;EACjB;AAPeD;AASf,MAAI,CAACb,WAAW;AACd,UAAMgB,YAAY;MAAC1E,YAAYiC;MAAMjC,YAAYkC,QAAQyC;MACtDxC,OAAO,CAACC,UAAUA,KAAAA,EAClBC,KAAK,GAAA;AACR,WACE,8BAAAtB,QAAA,cAACC,OAAAA;MAAIC,WAAWyD,cAAc,KAAKpC,SAAYoC;MAAWnC,aAAU;OAClE,8BAAAxB,QAAA,cAACC,OAAAA;MAAIC,WAAWjB,YAAYqB;OACzBtB,aAAaA,WAAW0D,aAAa;MAAExD,aAAa;IAAM,CAAA,IAAKwD,WAAAA,GAElE,8BAAA1C,QAAA,cAACQ,UAAAA;MACCC,MAAK;MACLC,SAAS,6BAAA;AACPqC,iBAASL,WAAAA;AACTE,qBAAa,IAAA;MACf,GAHS;MAIT1C,WAAWjB,YAAYsB;MACvBK,OAAM;OACP,MAAA,GAGAT,OAAAA;EAGP;AAEA,SACE,8BAAAH,QAAA,cAACC,OAAAA;IAAI4D,gBAAa;KAChB,8BAAA7D,QAAA,cAAC8D,YAAAA;IACCC,KAAKf;IACLO,OAAOT;IACPkB,UAAU,wBAACC,UAAUlB,SAASkB,MAAMC,OAAOX,KAAK,GAAtC;IACVY,WAAW,wBAACF,UAAAA;AACV,UAAIA,MAAM/B,QAAQ,WAAW,CAAC+B,MAAMG,UAAU;AAC5CH,cAAMI,eAAc;AACpB,aAAKb,WAAAA;MACP;AACA,UAAIS,MAAM/B,QAAQ,UAAU;AAC1B+B,cAAMI,eAAc;AACpBzB,qBAAa,KAAA;MACf;IACF,GATW;IAUX0B,MAAMC,KAAKC,IAAI,GAAG1B,MAAM2B,MAAM,IAAA,EAAMxC,SAAS,CAAA;IAC7C/B,WAAWjB,YAAYyF;MAEzB,8BAAA1E,QAAA,cAACC,OAAAA;IAAIC,WAAWjB,YAAY0F;KAC1B,8BAAA3E,QAAA,cAACQ,UAAAA;IAAOC,MAAK;IAASC,SAAS,6BAAMkC,aAAa,KAAA,GAAnB;KAA2B,QAAA,GAG1D,8BAAA5C,QAAA,cAACQ,UAAAA;IAAOC,MAAK;IAASC,SAAS,6BAAM,KAAK8C,WAAAA,GAAX;IAAyBoB,UAAU,CAAC9B,MAAMY,KAAI;KAAI,QAAA,CAAA,CAAA;AAMzF;AAnFS1C;AA0FT,SAASX,WAAW,EAAEC,MAAMJ,UAAS,GAAmB;AACtD,QAAM,CAAC2E,QAAQC,SAAAA,QAAajC,wBAAS,KAAA;AAErC,iBAAekC,aAAAA;AACb,QAAI;AACF,YAAMC,UAAUC,UAAUC,UAAU5E,IAAAA;AACpCwE,gBAAU,IAAA;AACVK,iBAAW,MAAML,UAAU,KAAA,GAAQ,IAAA;IACrC,QAAQ;IAER;EACF;AAReC;AAUf,SACE,8BAAA/E,QAAA,cAACQ,UAAAA;IACCC,MAAK;IACLC,SAAS,6BAAM,KAAKqE,WAAAA,GAAX;IACT7E;IACAU,OAAOiE,SAAS,YAAY;KAE3BA,SAAS,WAAW,MAAA;AAG3B;AAvBSxE;AAyBT,SAASQ,YAAY,EACnBrB,OACAU,UAAS,GACmD;AAC5D,QAAMkF,QAAQ5F,MAAM6F,cAAc7F,MAAM8F;AACxC,SACE,8BAAAtF,QAAA,cAACuF,QAAAA;IACCrF;IACAU,OAAO,SAASpB,MAAM6F,YAAYG,eAAc,CAAA,gBAAehG,MAAM8F,aAAaE,eAAc,CAAA;KAE/FC,cAAcjG,MAAMkG,OAAO,GAAE,UAAIC,kBAAkBP,KAAAA,CAAAA;AAG1D;AAbSvE;AAeT,SAASC,gBAAgB,EACvBrB,WACAS,UAAS,GAC6C;AACtD,QAAM0F,SAAS,IAAIC,KAAKpG,SAAAA;AACxB,MAAIqG,OAAOC,MAAMH,OAAOI,QAAO,CAAA,GAAK;AAClC,WAAO;EACT;AACA,QAAMC,WAAWL,OAAOJ,eAAejE,QAAW;IAAE2E,WAAW;IAAUC,WAAW;EAAQ,CAAA;AAC5F,SACE,8BAAAnG,QAAA,cAACuF,QAAAA;IAAKrF;IAAsBU,OAAOqF;KAChCG,mBAAmBR,MAAAA,CAAAA;AAG1B;AAdS9E;AAgBT,IAAMuF,gBAAgB,IAAIC,KAAKC,mBAAmBhF,QAAW;EAAEiF,SAAS;EAAQC,OAAO;AAAS,CAAA;AAChG,IAAMC,cAAc,IAAI,KAAK,KAAK,KAAK;AAMhC,SAASN,mBAAmBO,MAAU;AAC3C,QAAMC,SAASf,KAAKgB,IAAG,IAAKF,KAAKX,QAAO;AACxC,MAAIzB,KAAKuC,IAAIF,MAAAA,IAAU,KAAQ;AAC7B,WAAO;EACT;AACA,MAAIA,SAASF,aAAa;AACxB,WAAOC,KAAKI,mBAAmBxF,QAAW;MAAEyF,OAAO;MAASC,KAAK;IAAU,CAAA;EAC7E;AACA,QAAMC,UAAU3C,KAAK4C,MAAM,CAACP,SAAS,GAAA;AACrC,MAAIrC,KAAKuC,IAAII,OAAAA,IAAW,IAAI;AAC1B,WAAOb,cAAce,OAAOF,SAAS,QAAA;EACvC;AACA,QAAMG,QAAQ9C,KAAK4C,MAAM,CAACP,SAAS,IAAA;AACnC,MAAIrC,KAAKuC,IAAIO,KAAAA,IAAS,IAAI;AACxB,WAAOhB,cAAce,OAAOC,OAAO,MAAA;EACrC;AACA,SAAOhB,cAAce,OAAO7C,KAAK4C,MAAM,CAACP,SAAS,KAAA,GAAa,KAAA;AAChE;AAjBgBR;AAmBhB,SAAST,kBAAkB2B,GAAS;AAClC,MAAIA,IAAI,KAAO;AACb,WAAO,GAAGA,CAAAA;EACZ;AACA,MAAIA,IAAI,KAAQ;AACd,WAAO,IAAIA,IAAI,KAAOC,QAAQ,CAAA,CAAA;EAChC;AACA,SAAO,GAAGhD,KAAK4C,MAAMG,IAAI,GAAA,CAAA;AAC3B;AARS3B;AAUT,SAASF,cAAc+B,MAAY;AACjC,MAAIA,SAAS,GAAG;AACd,WAAO;EACT;AACA,MAAIA,OAAO,MAAM;AACf,WAAO,IAAIA,KAAKD,QAAQ,CAAA,CAAA;EAC1B;AACA,MAAIC,OAAO,GAAG;AACZ,WAAO,IAAIA,KAAKD,QAAQ,CAAA,CAAA;EAC1B;AACA,SAAO,IAAIC,KAAKD,QAAQ,CAAA,CAAA;AAC1B;AAXS9B;AAaT,SAAS3F,aAAYF,OAAsC;AACzD,QAAM6H,MAAgB,CAAA;AACtB,aAAWpF,QAAQzC,OAAO;AACxB,YAAI6C,wBAAaJ,IAAAA,GAAO;AACtBoF,UAAItF,KAAKE,KAAK/B,IAAI;IACpB;EACF;AACA,SAAOmH,IAAInG,KAAK,MAAA,EAAQoC,KAAI;AAC9B;AARS5D,OAAAA,cAAAA;;;ACjaT,IAAA4H,gBAAgC;AAahC,IAAMC,wBAAwB;AAC9B,IAAMC,oBAAoB;AAkDnB,SAASC,YAAY,EAC1BC,UACAC,QACAC,gBACAC,iBACAC,YACAC,QACAC,UACAC,cACAC,eACAC,cACAC,UACAC,cACAC,YACAC,iBACAC,WACAC,iBACAC,OACAC,SACAC,WAAU,GACO;AACjB,QAAM,CAACC,cAAcC,eAAAA,QAAmBC,wBAASxB,qBAAAA;AAEjD,MAAIG,SAASsB,WAAW,KAAKrB,WAAW,SAAS;AAC/C,WAAO,8BAAAsB,QAAA,cAACC,OAAAA;MAAIC,WAAWP,YAAYQ;OAAQd,UAAAA;EAC7C;AAEA,QAAMe,cAAc3B,SAAS4B,GAAG,EAAC;AACjC,QAAMC,oBACH5B,WAAW,eAAeA,WAAW,gBAAgB0B,aAAaG,SAAS;AAE9E,QAAMC,gBACJ9B,WAAW,WACX,CAAC4B,oBACDF,aAAaG,SAAS,eACtBf,oBAAoBiB,UACpB,CAAC,CAAClB,aACFA,UAAUQ,SAAS;AAKrB,QAAMW,eAAeC,KAAKC,IAAI,GAAGnC,SAASsB,SAASH,YAAAA;AACnD,QAAMiB,kBAAkBpC,SAASqC,MAAMJ,YAAAA;AACvC,QAAMK,cAAcL;AAEpB,MAAIM,qBAAqB;AACzB,WAASC,IAAIxC,SAASsB,SAAS,GAAGkB,KAAK,GAAGA,KAAK;AAC7C,QAAIxC,SAASwC,CAAAA,GAAIV,SAAS,aAAa;AACrCS,2BAAqBC;AACrB;IACF;EACF;AAEA,SACE,8BAAAjB,QAAA,cAACC,OAAAA;IAAIC,WAAWP,YAAYuB;KACzBH,cAAc,IACb,8BAAAf,QAAA,cAACmB,UAAAA;IACCC,MAAK;IACLlB,WAAWP,YAAY0B;IACvBC,SAAS,6BAAMzB,gBAAgB,CAAC0B,UAAUA,QAAQhD,iBAAAA,GAAzC;KACV,kBACgBwC,aAAY,QAAA,IAE3B,MACHF,gBAAgBW,IAAI,CAACC,SAASC,UAAAA;AAC7B,UAAMC,gBAAgBjB,eAAegB;AACrC,UAAME,kBAAkBD,kBAAkBX;AAC1C,UAAMa,QAAQ1C,WAAWsC,OAAAA,KAAY;AACrC,UAAMK,YAAY1C,eAAeqC,OAAAA,KAAY;AAC7C,WACE,8BAAAzB,QAAA,cAAC+B,aAAAA;MACCC,KAAKP,QAAQQ;MACbR;MACC,GAAI9C,iBAAiB;QAAEA;MAAe,IAAI,CAAC;MAC3C,GAAIC,kBAAkB;QAAEA;MAAgB,IAAI,CAAC;MAC7C,GAAIC,aAAa;QAAEA;MAAW,IAAI,CAAC;MACnC,GAAIc,YAAY8B,UAAU;QAAE9B,YAAYA,WAAW8B;MAAQ,IAAI,CAAC;MAChE,GAAI3C,SAAS;QAAEA;MAAO,IAAI,CAAC;MAC3B,GAAIC,WAAW;QAAEA;MAAS,IAAI,CAAC;MAC/B,GAAIC,eACD;QAAEA,cAAc,wBAACkD,SAAiBlD,aAAayC,QAAQQ,IAAIC,IAAAA,GAA3C;MAAiD,IACjE,CAAC;MACJ,GAAIjD,iBAAiB2C,kBAAkB;QAAE3C;MAAc,IAAI,CAAC;MAC5D,GAAIC,eAAe;QAAEA,cAAc,6BAAMA,aAAauC,QAAQQ,EAAE,GAA7B;MAA+B,IAAI,CAAC;MACvE,GAAIJ,UAAU,OAAO;QAAEA;MAAM,IAAI,CAAC;MAClC,GAAIC,cAAc,OAAO;QAAEA;MAAU,IAAI,CAAC;MAC3CK,aAAazD,WAAW,eAAekD;;EAG7C,CAAA,GACCtB,mBACC,8BAAAN,QAAA,cAACC,OAAAA;IAAIC,WAAWP,YAAYyC;KAAS9C,mBAAmB,gBAAA,IACtD,MACHG,SAASC,UACR,8BAAAM,QAAA,cAACC,OAAAA;IAAIC,WAAWP,YAAYF;KAC1B,8BAAAO,QAAA,cAACqC,QAAAA,MAAM5C,MAAMgC,WAAW,+BAAA,GACxB,8BAAAzB,QAAA,cAACmB,UAAAA;IAAOC,MAAK;IAASlB,WAAWP,YAAY2C;IAAYhB,SAAS,6BAAM,KAAK5B,QAAAA,GAAX;KAAsB,OAAA,CAAA,IAIxF,MACHc,gBACC,8BAAAR,QAAA,cAACC,OAAAA;IAAIC,WAAWP,YAAYJ;KACzBA,UAAUiC,IAAI,CAACU,SACd,8BAAAlC,QAAA,cAACmB,UAAAA;IACCa,KAAKE;IACLd,MAAK;IACLlB,WAAWP,YAAY4C;IACvBjB,SAAS,6BAAM9B,gBAAgB0C,IAAAA,GAAtB;KAERA,IAAAA,CAAAA,CAAAA,IAIL,IAAA;AAGV;AAtHgB1D;;;ACpDT,IAAMgE,sBAAsB;AAE5B,IAAMC,mBAAkD;EAC7D;IAAEC,KAAK;IAASC,OAAO;EAAe;EACtC;IAAED,KAAK;IAASC,OAAO;EAAe;EACtC;IAAED,KAAK;IAASC,OAAO;EAAsB;EAC7C;IAAED,KAAK;IAASC,OAAO;EAAkB;;AAG3C,IAAMC,cAAc;AAOb,SAASC,uBAAAA;AACd,MAAI,OAAOC,WAAW,YAAa,QAAON;AAC1C,MAAI;AACF,UAAMO,MAAMD,OAAOE,aAAaC,QAAQL,WAAAA;AACxC,QAAIG,OAAON,iBAAiBS,KAAK,CAACC,aAAaA,SAAST,QAAQK,GAAAA,GAAM;AACpE,aAAOA;IACT;EACF,QAAQ;EAER;AACA,SAAOP;AACT;AAXgBK;AAaT,SAASO,kBAAkBV,KAAW;AAC3C,MAAI,OAAOI,WAAW,YAAa;AACnC,MAAI;AACFA,WAAOE,aAAaK,QAAQT,aAAaF,GAAAA;EAC3C,QAAQ;EAER;AACF;AAPgBU;","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","textId","Math","random","toString","slice","buffer","started","record","ReadableStream","pull","controller","ensureStarted","enqueue","type","id","finish","close","done","value","read","decode","stream","separator","indexOf","rawEvent","frame","parseSseFrame","event","errorText","parseErrorText","data","parseMeta","delta","parseDelta","error","cancel","raw","dataLines","line","split","startsWith","length","trim","push","trimStart","join","parsed","parse","text","part","parts","AgentHttpError","Error","status","method","path","statusText","name","AgentClient","options","listThreads","request","getThread","id","encodeURIComponent","deleteThread","forkFromMessage","threadId","messageId","renameThread","title","promoteThread","truncateFromMessage","getQuotaToday","cancelStream","runId","approveToolCall","input","rejectToolCall","body","fetchImpl","fetch","globalThis","baseUrl","replace","dynamic","getHeaders","response","headers","accept","undefined","JSON","stringify","credentials","ok","text","parse","import_react","useAgentChat","options","latest","useRef","current","runId","setRunId","useState","resumeRunId","runIdRef","regenerateNext","client","useMemo","AgentClient","baseUrl","undefined","credentials","fetch","getHeaders","mergeHeaders","transport","onMeta","meta","AgentChatTransport","agent","getBody","pageContext","getPageContext","regenerate","threadId","getResumeRunId","chat","useChat","resume","id","initialMessages","messages","onFinish","chatRef","addToolResult","useCallback","tool","toolCallId","output","threads","setThreads","quota","setQuota","loadThreads","list","listThreads","loadThread","getThread","deleteThread","filter","thread","forkThread","messageId","forkFromMessage","renameThread","title","map","promoteThread","truncateFromMessage","then","loadQuota","today","getQuotaToday","cancel","stop","activeRunId","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"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as ai from 'ai';
2
2
  import { ChatTransport, UIMessage, UIMessageChunk, ToolUIPart, DynamicToolUIPart } from 'ai';
3
- import { ThreadSummary, ThreadDetail } from '@dudousxd/nestjs-agent-core';
3
+ import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
4
+ import { ThreadSummary, ThreadDetail, QuotaView } from '@dudousxd/nestjs-agent-core';
4
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
6
  import React from 'react';
6
7
 
@@ -91,12 +92,14 @@ declare class AgentHttpError extends Error {
91
92
  readonly path: string;
92
93
  constructor(status: number, method: string, path: string, statusText: string);
93
94
  }
94
- interface QuotaToday {
95
- usedTokens: number;
96
- }
95
+ /** The quota-today read-model: usage, the configured limit (null → unlimited), and USD spend. */
96
+ type QuotaToday = QuotaView;
97
97
  interface CancelResult {
98
98
  aborted: boolean;
99
99
  }
100
+ interface OkResult {
101
+ ok: boolean;
102
+ }
100
103
  interface AgentClientOptions {
101
104
  /** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */
102
105
  baseUrl?: string;
@@ -120,6 +123,9 @@ declare class AgentClient {
120
123
  getThread(id: string): Promise<ThreadDetail>;
121
124
  deleteThread(id: string): Promise<void>;
122
125
  forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary>;
126
+ renameThread(id: string, title: string): Promise<OkResult>;
127
+ promoteThread(id: string): Promise<OkResult>;
128
+ truncateFromMessage(threadId: string, messageId: string): Promise<OkResult>;
123
129
  getQuotaToday(): Promise<QuotaToday>;
124
130
  cancelStream(runId: string): Promise<CancelResult>;
125
131
  approveToolCall(input: {
@@ -182,7 +188,10 @@ declare function useAgentChat(options: UseAgentChatOptions): {
182
188
  loadThread: (id: string) => Promise<ThreadDetail>;
183
189
  deleteThread: (id: string) => Promise<void>;
184
190
  forkThread: (threadId: string, messageId: string) => Promise<ThreadSummary>;
185
- quota: QuotaToday | null;
191
+ renameThread: (id: string, title: string) => Promise<void>;
192
+ promoteThread: (id: string) => Promise<void>;
193
+ truncateFromMessage: (threadId: string, messageId: string) => Promise<void>;
194
+ quota: _dudousxd_nestjs_agent_core.QuotaView | null;
186
195
  loadQuota: () => Promise<QuotaToday>;
187
196
  cancel: () => Promise<void>;
188
197
  approve: ({ toolCallId }: {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as ai from 'ai';
2
2
  import { ChatTransport, UIMessage, UIMessageChunk, ToolUIPart, DynamicToolUIPart } from 'ai';
3
- import { ThreadSummary, ThreadDetail } from '@dudousxd/nestjs-agent-core';
3
+ import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
4
+ import { ThreadSummary, ThreadDetail, QuotaView } from '@dudousxd/nestjs-agent-core';
4
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
6
  import React from 'react';
6
7
 
@@ -91,12 +92,14 @@ declare class AgentHttpError extends Error {
91
92
  readonly path: string;
92
93
  constructor(status: number, method: string, path: string, statusText: string);
93
94
  }
94
- interface QuotaToday {
95
- usedTokens: number;
96
- }
95
+ /** The quota-today read-model: usage, the configured limit (null → unlimited), and USD spend. */
96
+ type QuotaToday = QuotaView;
97
97
  interface CancelResult {
98
98
  aborted: boolean;
99
99
  }
100
+ interface OkResult {
101
+ ok: boolean;
102
+ }
100
103
  interface AgentClientOptions {
101
104
  /** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */
102
105
  baseUrl?: string;
@@ -120,6 +123,9 @@ declare class AgentClient {
120
123
  getThread(id: string): Promise<ThreadDetail>;
121
124
  deleteThread(id: string): Promise<void>;
122
125
  forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary>;
126
+ renameThread(id: string, title: string): Promise<OkResult>;
127
+ promoteThread(id: string): Promise<OkResult>;
128
+ truncateFromMessage(threadId: string, messageId: string): Promise<OkResult>;
123
129
  getQuotaToday(): Promise<QuotaToday>;
124
130
  cancelStream(runId: string): Promise<CancelResult>;
125
131
  approveToolCall(input: {
@@ -182,7 +188,10 @@ declare function useAgentChat(options: UseAgentChatOptions): {
182
188
  loadThread: (id: string) => Promise<ThreadDetail>;
183
189
  deleteThread: (id: string) => Promise<void>;
184
190
  forkThread: (threadId: string, messageId: string) => Promise<ThreadSummary>;
185
- quota: QuotaToday | null;
191
+ renameThread: (id: string, title: string) => Promise<void>;
192
+ promoteThread: (id: string) => Promise<void>;
193
+ truncateFromMessage: (threadId: string, messageId: string) => Promise<void>;
194
+ quota: _dudousxd_nestjs_agent_core.QuotaView | null;
186
195
  loadQuota: () => Promise<QuotaToday>;
187
196
  cancel: () => Promise<void>;
188
197
  approve: ({ toolCallId }: {
package/dist/index.js CHANGED
@@ -301,6 +301,17 @@ var AgentClient = class {
301
301
  forkFromMessage(threadId, messageId) {
302
302
  return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
303
303
  }
304
+ renameThread(id, title) {
305
+ return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, {
306
+ title
307
+ });
308
+ }
309
+ promoteThread(id) {
310
+ return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
311
+ }
312
+ truncateFromMessage(threadId, messageId) {
313
+ return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
314
+ }
304
315
  getQuotaToday() {
305
316
  return this.request("GET", "/agent/quota/today");
306
317
  }
@@ -450,6 +461,25 @@ function useAgentChat(options) {
450
461
  const forkThread = useCallback((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
451
462
  client
452
463
  ]);
464
+ const renameThread = useCallback(async (id, title) => {
465
+ await client.renameThread(id, title);
466
+ setThreads((current) => current.map((thread) => thread.id === id ? {
467
+ ...thread,
468
+ title
469
+ } : thread));
470
+ }, [
471
+ client
472
+ ]);
473
+ const promoteThread = useCallback(async (id) => {
474
+ await client.promoteThread(id);
475
+ await loadThreads();
476
+ }, [
477
+ client,
478
+ loadThreads
479
+ ]);
480
+ const truncateFromMessage = useCallback((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
481
+ client
482
+ ]);
453
483
  const loadQuota = useCallback(async () => {
454
484
  const today = await client.getQuotaToday();
455
485
  setQuota(today);
@@ -500,6 +530,9 @@ function useAgentChat(options) {
500
530
  loadThread,
501
531
  deleteThread,
502
532
  forkThread,
533
+ renameThread,
534
+ promoteThread,
535
+ truncateFromMessage,
503
536
  quota,
504
537
  loadQuota,
505
538
  cancel,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent-chat-transport.ts","../src/client.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":["import 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 */\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 synthesize a valid v7\n * UI-message chunk stream. Recognized backend frames:\n * - `event: meta` `data: {\"runId\",\"threadId\"}` → records identity\n * - `data: {\"delta\":\"...\"}` → `text-delta`\n * - `event: done` `data: {}` → terminates\n */\n private toChunkStream(source: ReadableStream<Uint8Array>): ReadableStream<UIMessageChunk> {\n const reader = source.getReader();\n const decoder = new TextDecoder();\n const textId = `txt-${Math.random().toString(36).slice(2)}`;\n let buffer = '';\n let started = false;\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 controller.enqueue({ type: 'start-step' });\n controller.enqueue({ type: 'text-start', id: textId });\n }\n function finish() {\n ensureStarted();\n controller.enqueue({ type: 'text-end', id: textId });\n controller.enqueue({ type: 'finish-step' });\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 delta = parseDelta(frame.data);\n if (delta) {\n ensureStarted();\n controller.enqueue({\n type: 'text-delta',\n id: textId,\n delta,\n });\n }\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\nfunction parseDelta(data: string): string | null {\n try {\n const parsed: unknown = JSON.parse(data);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'delta' in parsed &&\n typeof parsed.delta === 'string'\n ) {\n return parsed.delta;\n }\n } catch {\n /* not a delta 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 { ThreadDetail, ThreadSummary } 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\nexport interface QuotaToday {\n usedTokens: number;\n}\n\nexport interface CancelResult {\n aborted: boolean;\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 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 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 { useChat } from '@ai-sdk/react';\nimport type { ThreadDetail, ThreadSummary } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\nimport { useCallback, 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 resumeRunId?: string;\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\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 // 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 // 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 // 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 }\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 return {\n ...(current.threadId !== undefined ? { threadId: current.threadId } : {}),\n ...(pageContext ? { pageContext } : {}),\n ...(regenerate ? { regenerate: true } : {}),\n };\n },\n getResumeRunId: () => latest.current.resumeRunId,\n onMeta,\n });\n }, []);\n\n const chat = useChat({\n transport,\n resume: options.resumeRunId !== undefined,\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 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 client,\n threads,\n loadThreads,\n loadThread,\n deleteThread,\n forkThread,\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","textId","Math","random","toString","slice","buffer","started","record","ReadableStream","pull","controller","ensureStarted","enqueue","type","id","finish","close","done","value","read","decode","stream","separator","indexOf","rawEvent","frame","parseSseFrame","event","errorText","parseErrorText","data","parseMeta","delta","parseDelta","error","cancel","raw","dataLines","line","split","startsWith","length","trim","push","trimStart","join","parsed","parse","text","part","parts","AgentHttpError","Error","status","method","path","statusText","name","AgentClient","options","listThreads","request","getThread","id","encodeURIComponent","deleteThread","forkFromMessage","threadId","messageId","getQuotaToday","cancelStream","runId","approveToolCall","input","rejectToolCall","body","fetchImpl","fetch","globalThis","baseUrl","replace","dynamic","getHeaders","response","headers","accept","undefined","JSON","stringify","credentials","ok","text","parse","useChat","useCallback","useMemo","useRef","useState","useAgentChat","options","latest","useRef","current","runId","setRunId","useState","resumeRunId","runIdRef","regenerateNext","client","useMemo","AgentClient","baseUrl","undefined","credentials","fetch","getHeaders","mergeHeaders","transport","onMeta","meta","AgentChatTransport","agent","getBody","pageContext","getPageContext","regenerate","threadId","getResumeRunId","chat","useChat","resume","id","initialMessages","messages","onFinish","chatRef","addToolResult","useCallback","tool","toolCallId","output","threads","setThreads","quota","setQuota","loadThreads","list","listThreads","loadThread","getThread","deleteThread","filter","thread","forkThread","messageId","forkFromMessage","loadQuota","today","getQuotaToday","cancel","stop","activeRunId","cancelStream","approve","approveToolCall","reject","reason","rejectToolCall","dynamic","headers","React","useEffect","useState","useCallback","useEffect","useMemo","useRef","useState","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","div","textarea","onChange","target","onKeyDown","button","type","onClick","aria-label","isTextUIPart","isToolUIPart","React","useEffect","useRef","useState","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","React","useState","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/agent-chat-transport.ts","../src/client.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":["import 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 */\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 synthesize a valid v7\n * UI-message chunk stream. Recognized backend frames:\n * - `event: meta` `data: {\"runId\",\"threadId\"}` → records identity\n * - `data: {\"delta\":\"...\"}` → `text-delta`\n * - `event: done` `data: {}` → terminates\n */\n private toChunkStream(source: ReadableStream<Uint8Array>): ReadableStream<UIMessageChunk> {\n const reader = source.getReader();\n const decoder = new TextDecoder();\n const textId = `txt-${Math.random().toString(36).slice(2)}`;\n let buffer = '';\n let started = false;\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 controller.enqueue({ type: 'start-step' });\n controller.enqueue({ type: 'text-start', id: textId });\n }\n function finish() {\n ensureStarted();\n controller.enqueue({ type: 'text-end', id: textId });\n controller.enqueue({ type: 'finish-step' });\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 delta = parseDelta(frame.data);\n if (delta) {\n ensureStarted();\n controller.enqueue({\n type: 'text-delta',\n id: textId,\n delta,\n });\n }\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\nfunction parseDelta(data: string): string | null {\n try {\n const parsed: unknown = JSON.parse(data);\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'delta' in parsed &&\n typeof parsed.delta === 'string'\n ) {\n return parsed.delta;\n }\n } catch {\n /* not a delta 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 { QuotaView, ThreadDetail, ThreadSummary } 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\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.request<OkResult>('PATCH', `/agent/threads/${encodeURIComponent(id)}`, { title });\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 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 { useChat } from '@ai-sdk/react';\nimport type { ThreadDetail, ThreadSummary } from '@dudousxd/nestjs-agent-core';\nimport type { UIMessage } from 'ai';\nimport { useCallback, 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 resumeRunId?: string;\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\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 // 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 // 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 // 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 }\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 return {\n ...(current.threadId !== undefined ? { threadId: current.threadId } : {}),\n ...(pageContext ? { pageContext } : {}),\n ...(regenerate ? { regenerate: true } : {}),\n };\n },\n getResumeRunId: () => latest.current.resumeRunId,\n onMeta,\n });\n }, []);\n\n const chat = useChat({\n transport,\n resume: options.resumeRunId !== undefined,\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 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","textId","Math","random","toString","slice","buffer","started","record","ReadableStream","pull","controller","ensureStarted","enqueue","type","id","finish","close","done","value","read","decode","stream","separator","indexOf","rawEvent","frame","parseSseFrame","event","errorText","parseErrorText","data","parseMeta","delta","parseDelta","error","cancel","raw","dataLines","line","split","startsWith","length","trim","push","trimStart","join","parsed","parse","text","part","parts","AgentHttpError","Error","status","method","path","statusText","name","AgentClient","options","listThreads","request","getThread","id","encodeURIComponent","deleteThread","forkFromMessage","threadId","messageId","renameThread","title","promoteThread","truncateFromMessage","getQuotaToday","cancelStream","runId","approveToolCall","input","rejectToolCall","body","fetchImpl","fetch","globalThis","baseUrl","replace","dynamic","getHeaders","response","headers","accept","undefined","JSON","stringify","credentials","ok","text","parse","useChat","useCallback","useMemo","useRef","useState","useAgentChat","options","latest","useRef","current","runId","setRunId","useState","resumeRunId","runIdRef","regenerateNext","client","useMemo","AgentClient","baseUrl","undefined","credentials","fetch","getHeaders","mergeHeaders","transport","onMeta","meta","AgentChatTransport","agent","getBody","pageContext","getPageContext","regenerate","threadId","getResumeRunId","chat","useChat","resume","id","initialMessages","messages","onFinish","chatRef","addToolResult","useCallback","tool","toolCallId","output","threads","setThreads","quota","setQuota","loadThreads","list","listThreads","loadThread","getThread","deleteThread","filter","thread","forkThread","messageId","forkFromMessage","renameThread","title","map","promoteThread","truncateFromMessage","then","loadQuota","today","getQuotaToday","cancel","stop","activeRunId","cancelStream","approve","approveToolCall","reject","reason","rejectToolCall","dynamic","headers","React","useEffect","useState","useCallback","useEffect","useMemo","useRef","useState","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","div","textarea","onChange","target","onKeyDown","button","type","onClick","aria-label","isTextUIPart","isToolUIPart","React","useEffect","useRef","useState","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","React","useState","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-agent-react",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "nestjs-agent React adapter — useAgentChat hook, AgentChatTransport (AI SDK v7), REST client, and styling-agnostic chat UI",
5
5
  "license": "MIT",
6
6
  "author": "Davide Carvalho",
@@ -39,7 +39,7 @@
39
39
  "dist"
40
40
  ],
41
41
  "dependencies": {
42
- "@dudousxd/nestjs-agent-core": "0.2.0"
42
+ "@dudousxd/nestjs-agent-core": "0.3.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@ai-sdk/react": "^4.0.0",