tempest-react-sdk 0.33.2 → 0.34.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.
Files changed (36) hide show
  1. package/README.md +3 -3
  2. package/dist/components/AIChat/AIChat.cjs +2 -0
  3. package/dist/components/AIChat/AIChat.cjs.map +1 -0
  4. package/dist/components/AIChat/AIChat.js +141 -0
  5. package/dist/components/AIChat/AIChat.js.map +1 -0
  6. package/dist/components/AIChat/AIChat.module.cjs +2 -0
  7. package/dist/components/AIChat/AIChat.module.cjs.map +1 -0
  8. package/dist/components/AIChat/AIChat.module.js +58 -0
  9. package/dist/components/AIChat/AIChat.module.js.map +1 -0
  10. package/dist/components/AIChat/AIChatComposer.cjs +2 -0
  11. package/dist/components/AIChat/AIChatComposer.cjs.map +1 -0
  12. package/dist/components/AIChat/AIChatComposer.js +87 -0
  13. package/dist/components/AIChat/AIChatComposer.js.map +1 -0
  14. package/dist/components/AIChat/AIChatTurn.cjs +3 -0
  15. package/dist/components/AIChat/AIChatTurn.cjs.map +1 -0
  16. package/dist/components/AIChat/AIChatTurn.js +217 -0
  17. package/dist/components/AIChat/AIChatTurn.js.map +1 -0
  18. package/dist/components/AIChat/ai-chat-turns.cjs +2 -0
  19. package/dist/components/AIChat/ai-chat-turns.cjs.map +1 -0
  20. package/dist/components/AIChat/ai-chat-turns.js +90 -0
  21. package/dist/components/AIChat/ai-chat-turns.js.map +1 -0
  22. package/dist/styles.css +1 -1
  23. package/dist/tempest-react-sdk.cjs +1 -1
  24. package/dist/tempest-react-sdk.d.ts +415 -9
  25. package/dist/tempest-react-sdk.js +118 -114
  26. package/dist/ws/create-web-socket.cjs +1 -1
  27. package/dist/ws/create-web-socket.cjs.map +1 -1
  28. package/dist/ws/create-web-socket.js +54 -37
  29. package/dist/ws/create-web-socket.js.map +1 -1
  30. package/dist/ws/use-web-socket.cjs +1 -1
  31. package/dist/ws/use-web-socket.cjs.map +1 -1
  32. package/dist/ws/use-web-socket.js +49 -18
  33. package/dist/ws/use-web-socket.js.map +1 -1
  34. package/package.json +1 -1
  35. package/template/src/lib/api.ts +27 -5
  36. package/template/src/stores/auth.ts +14 -0
@@ -158,6 +158,307 @@ export declare interface AccordionProps {
158
158
  className?: string;
159
159
  }
160
160
 
161
+ /**
162
+ * A conversation with a model: role-based turns, Markdown answers, a reasoning
163
+ * block, a streaming caret, per-turn actions and a composer that turns into a stop
164
+ * button while a turn is generating.
165
+ *
166
+ * This is the shape ChatGPT, Claude and DeepSeek converged on, and it is a different
167
+ * component from {@link Chat}, not a variant of it. A human thread is addressed by
168
+ * author and cares about delivery state; a model transcript is addressed by role,
169
+ * has no delivery state at all, and needs three things a human thread never does —
170
+ * partial output, reasoning separate from the answer, and re-asking.
171
+ *
172
+ * Presentational and controlled, like the rest of the SDK: it takes a list and emits
173
+ * intent (`onSend`, `onStop`, `onRegenerate`, `onEditSubmit`, `onFeedback`). The
174
+ * transport stays with the app, because "how do I stream from my backend" has a
175
+ * different answer per provider — the SDK's `createEventStream` covers SSE, `fetch`
176
+ * with a `ReadableStream` covers the rest, and either way the app owns the
177
+ * `AbortController` it hands to `onStop`.
178
+ *
179
+ * @example
180
+ * <AIChat
181
+ * messages={turns}
182
+ * pending={pending}
183
+ * onSend={(text) => ask(text)}
184
+ * onStop={() => controller.current?.abort()}
185
+ * onRegenerate={(turn) => reask(turn)}
186
+ * onFeedback={(turn, vote) => track("answer_rated", { id: turn.id, vote })}
187
+ * suggestions={["Resuma o último relatório", "Quais pedidos atrasaram?"]}
188
+ * />
189
+ */
190
+ export declare function AIChat({ messages, onSend, onStop, onRegenerate, onEditSubmit, onFeedback, onRetry, pending, suggestions, renderAvatar, renderContent, votes, header, emptyState, showSystem, defaultReasoningOpen, showLineNumbers, locale, placeholder, composerActions, composerFooter, composerDisabled, maxRows, onSendError, className, ...rest }: AIChatProps): JSX.Element;
191
+
192
+ /** A file carried by a turn — an upload on the way in, a document on the way out. */
193
+ export declare interface AIChatAttachment {
194
+ /** Stable identity. Used as the React key. */
195
+ id: string;
196
+ /** Name shown in the chip. */
197
+ name: string;
198
+ /** Size in bytes. Formatted for display when given. */
199
+ size?: number;
200
+ /** Image URL. When set the attachment renders as a thumbnail instead of a chip. */
201
+ url?: string;
202
+ /** MIME type. Used as the chip's secondary label when there is no size. */
203
+ mimeType?: string;
204
+ }
205
+
206
+ /**
207
+ * The prompt field of a conversation with a model: a textarea that grows with its
208
+ * content, sends on `Enter`, keeps `Shift+Enter` for a newline, and turns into a
209
+ * stop button while a turn is streaming.
210
+ *
211
+ * Uncontrolled on purpose. A draft changes on every keystroke, and lifting that into
212
+ * app state re-renders the whole transcript per character — with a streaming answer
213
+ * above, that is the one place where "controlled by default" costs something
214
+ * visible. Apps that need the draft (a persisted composer, a slash-command menu)
215
+ * read it from `onChange` or drive it through the ref.
216
+ *
217
+ * @example
218
+ * <AIChatComposer
219
+ * generating={generating}
220
+ * onSend={(text) => ask(text)}
221
+ * onStop={() => controller.abort()}
222
+ * footer={<small>Claude Opus 5 · pode errar</small>}
223
+ * />
224
+ */
225
+ export declare const AIChatComposer: ForwardRefExoticComponent<AIChatComposerProps & RefAttributes<AIChatComposerHandle>>;
226
+
227
+ /** Imperative handle, so a thread can focus or refill the field. */
228
+ export declare interface AIChatComposerHandle {
229
+ focus: () => void;
230
+ /** Replace the draft — used to put a prompt back in the field. */
231
+ setValue: (text: string) => void;
232
+ }
233
+
234
+ export declare interface AIChatComposerProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, OverriddenDomProps_5> {
235
+ /** Called with the trimmed prompt. The field clears only when this does not throw. */
236
+ onSend: (text: string) => void | Promise<void>;
237
+ /**
238
+ * Abort the turn in flight.
239
+ *
240
+ * When given together with `generating`, the send button becomes a stop button
241
+ * and `Escape` aborts too.
242
+ */
243
+ onStop?: () => void;
244
+ /** A turn is being generated. Replaces send with stop and refuses to send. */
245
+ generating?: boolean;
246
+ /** Locale for the placeholder and the button labels. Default `"pt-BR"`. */
247
+ locale?: "pt-BR" | "en";
248
+ /** Left of the send button — an attach control, a model picker, a tool toggle. */
249
+ actions?: ReactNode;
250
+ /** Under the field — a token count, the model name, a disclaimer. */
251
+ footer?: ReactNode;
252
+ /** Largest height the field grows to, in lines. Default 8. */
253
+ maxRows?: number;
254
+ /**
255
+ * Called when `onSend` rejects. The draft is kept either way.
256
+ *
257
+ * Without it the rejection is swallowed after the draft is preserved: re-throwing
258
+ * out of a DOM event handler surfaces as an unhandled promise rejection, which is
259
+ * console noise for the developer and nothing the user can act on. The visible
260
+ * signal is the prompt still sitting in the field; wire this to a toast to say why.
261
+ */
262
+ onError?: (error: unknown) => void;
263
+ }
264
+
265
+ /** One turn of a conversation with a model. */
266
+ export declare interface AIChatMessage {
267
+ /** Stable identity. Used as the React key and by every callback. */
268
+ id: string;
269
+ role: AIChatRole;
270
+ /**
271
+ * The text of the turn.
272
+ *
273
+ * An assistant turn is rendered as Markdown; a user turn is rendered as plain
274
+ * text with newlines preserved. That asymmetry is deliberate: a model emits
275
+ * Markdown by contract, while a person typing `2 * 3 * 4` did not mean to open
276
+ * an emphasis span.
277
+ */
278
+ content: string;
279
+ /**
280
+ * Reasoning the model exposed before answering — extended thinking, a
281
+ * chain-of-thought trace.
282
+ *
283
+ * Rendered in its own collapsible block above the answer, so a long trace never
284
+ * pushes the answer off screen.
285
+ */
286
+ reasoning?: string;
287
+ /**
288
+ * The turn is still arriving.
289
+ *
290
+ * Shows the caret, marks the block `aria-busy`, and hides the action row —
291
+ * copying or rating half an answer is never what somebody meant to do.
292
+ */
293
+ streaming?: boolean;
294
+ /** Generation failed. Shown under whatever streamed, with the retry control. */
295
+ error?: string;
296
+ /** Epoch milliseconds. */
297
+ createdAt?: number;
298
+ /** Model that produced the turn. Shown in the meta row of an assistant turn. */
299
+ model?: string;
300
+ attachments?: readonly AIChatAttachment[];
301
+ /** Anything the app wants to carry through to its own renderers. */
302
+ data?: Record<string, unknown>;
303
+ }
304
+
305
+ export declare interface AIChatProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_4> {
306
+ /** The transcript, **oldest first**. Never reordered by the component. */
307
+ messages: readonly AIChatMessage[];
308
+ /** Renders the composer when given. Receives the trimmed prompt. */
309
+ onSend?: (text: string) => void | Promise<void>;
310
+ /** Abort the turn in flight. Shows the stop button while generating. */
311
+ onStop?: () => void;
312
+ /** Ask again for the newest assistant turn. */
313
+ onRegenerate?: (message: AIChatMessage) => void;
314
+ /** Re-submit an edited user turn. The app decides what to drop after it. */
315
+ onEditSubmit?: (message: AIChatMessage, text: string) => void | Promise<void>;
316
+ /** Rating on an assistant turn. */
317
+ onFeedback?: (message: AIChatMessage, vote: AIChatVote) => void;
318
+ /** Retry a turn that carries an `error`. */
319
+ onRetry?: (message: AIChatMessage) => void;
320
+ /**
321
+ * The request is out and nothing has arrived yet.
322
+ *
323
+ * Distinct from a turn with `streaming: true`: apps that only push a message
324
+ * once the first token lands need somewhere to say "we asked", and without it the
325
+ * screen is frozen for however long the model takes to start.
326
+ */
327
+ pending?: boolean;
328
+ /** Prompts offered on an empty transcript. Clicking one sends it. */
329
+ suggestions?: readonly string[];
330
+ /** Avatar for a turn — an `<Avatar>`, an `<Icon>`, a logo. */
331
+ renderAvatar?: (message: AIChatMessage) => ReactNode;
332
+ /** Render a body yourself — a tool-call card, a chart, a citation list. */
333
+ renderContent?: (message: AIChatMessage) => ReactNode;
334
+ /** Ratings to show as pressed, by message id. Omit to keep them local. */
335
+ votes?: Readonly<Record<string, AIChatVote>>;
336
+ /** Rendered above the transcript, inside the panel. */
337
+ header?: ReactNode;
338
+ /** Shown when there are no turns and no suggestions. */
339
+ emptyState?: ReactNode;
340
+ /** Show `"system"` turns. Default `false`. */
341
+ showSystem?: boolean;
342
+ /** Reasoning blocks start expanded. Default `false`. */
343
+ defaultReasoningOpen?: boolean;
344
+ /** Show line numbers in fenced code. Default `false`. */
345
+ showLineNumbers?: boolean;
346
+ /** Locale for labels. Default `"pt-BR"`. */
347
+ locale?: "pt-BR" | "en";
348
+ /** Placeholder for the composer. */
349
+ placeholder?: string;
350
+ /** Extra controls inside the composer, before the send button. */
351
+ composerActions?: ReactNode;
352
+ /** Under the composer field — token count, model name, a disclaimer. */
353
+ composerFooter?: ReactNode;
354
+ /** Disable the composer — no credits, conversation archived, offline. */
355
+ composerDisabled?: boolean;
356
+ /** Largest height the composer grows to, in lines. Default 8. */
357
+ maxRows?: number;
358
+ /**
359
+ * Called when `onSend` **or** `onEditSubmit` rejects. The draft stays in the field
360
+ * either way.
361
+ */
362
+ onSendError?: (error: unknown) => void;
363
+ }
364
+
365
+ /** Who produced a turn. */
366
+ export declare type AIChatRole = "user" | "assistant" | "system";
367
+
368
+ /** Labels the conversation needs, per locale. */
369
+ export declare interface AIChatStrings {
370
+ thread: string;
371
+ empty: string;
372
+ emptyHint: string;
373
+ placeholder: string;
374
+ send: string;
375
+ stop: string;
376
+ regenerate: string;
377
+ copy: string;
378
+ copied: string;
379
+ edit: string;
380
+ save: string;
381
+ cancel: string;
382
+ editing: string;
383
+ good: string;
384
+ bad: string;
385
+ reasoning: string;
386
+ thinking: string;
387
+ generating: string;
388
+ done: string;
389
+ stopped: string;
390
+ you: string;
391
+ assistant: string;
392
+ system: string;
393
+ retry: string;
394
+ jumpToLatest: string;
395
+ attachment: string;
396
+ turnActions: string;
397
+ }
398
+
399
+ /** Locale strings for the conversation. */
400
+ export declare function aiChatStrings(locale: "pt-BR" | "en"): AIChatStrings;
401
+
402
+ /**
403
+ * One turn of a conversation with a model.
404
+ *
405
+ * Exported for apps that build their own transcript layout (a split view, a diff of
406
+ * two answers) but still want the SDK's turn: Markdown body, reasoning block,
407
+ * attachments, streaming caret, error state and the action row.
408
+ *
409
+ * @example
410
+ * <AIChatTurn message={turn} canRegenerate onRegenerate={(m) => reask(m)} />
411
+ */
412
+ export declare function AIChatTurn({ message, locale, canRegenerate, onRegenerate, onFeedback, onEditSubmit, onEditError, onRetry, renderAvatar, renderContent, vote, defaultReasoningOpen, showLineNumbers, }: AIChatTurnProps): JSX.Element;
413
+
414
+ export declare interface AIChatTurnProps {
415
+ /** The turn to render. */
416
+ message: AIChatMessage;
417
+ /** Locale for the labels. Default `"pt-BR"`. */
418
+ locale?: "pt-BR" | "en";
419
+ /**
420
+ * Offer the regenerate control.
421
+ *
422
+ * Only the newest assistant turn should get it — re-asking an older one throws
423
+ * away every turn after it, which is a different operation and needs its own
424
+ * confirmation.
425
+ */
426
+ canRegenerate?: boolean;
427
+ onRegenerate?: (message: AIChatMessage) => void;
428
+ onFeedback?: (message: AIChatMessage, vote: AIChatVote) => void;
429
+ /** Enables the edit control on a user turn. Receives the edited prompt. */
430
+ onEditSubmit?: (message: AIChatMessage, text: string) => void | Promise<void>;
431
+ /**
432
+ * Called when `onEditSubmit` rejects. The editor stays open with the draft either
433
+ * way.
434
+ *
435
+ * Without it the rejection is swallowed after the draft is preserved: re-throwing
436
+ * out of a click handler surfaces as an unhandled promise rejection, which is
437
+ * console noise for the developer and nothing the user can act on.
438
+ */
439
+ onEditError?: (error: unknown) => void;
440
+ /** Enables the retry control on a turn that carries an `error`. */
441
+ onRetry?: (message: AIChatMessage) => void;
442
+ renderAvatar?: (message: AIChatMessage) => ReactNode;
443
+ /** Render the body yourself — a tool-call card, a chart, a citation list. */
444
+ renderContent?: (message: AIChatMessage) => ReactNode;
445
+ /**
446
+ * Rating to show as pressed.
447
+ *
448
+ * Pass it to keep votes in app state (persisted across a reload); leave it out
449
+ * and the pressed state is kept locally, which is enough for a fire-and-forget
450
+ * `onFeedback`.
451
+ */
452
+ vote?: AIChatVote;
453
+ /** Reasoning blocks start expanded. Default `false`. */
454
+ defaultReasoningOpen?: boolean;
455
+ /** Show line numbers in fenced code. Default `false`. */
456
+ showLineNumbers?: boolean;
457
+ }
458
+
459
+ /** Rating an app can collect on an assistant turn. */
460
+ export declare type AIChatVote = "up" | "down";
461
+
161
462
  /**
162
463
  * Inline alert / notice with tone (info/success/warning/danger) and appearance
163
464
  * (soft/solid/outline). Accepts optional `icon`, `title`, `description` and
@@ -2456,10 +2757,36 @@ export declare interface CreateWebSocketOptions<T> {
2456
2757
  /**
2457
2758
  * Ping interval (ms). When set, the client sends `pingPayload` periodically
2458
2759
  * to keep the socket alive. Default: 0 (disabled).
2760
+ *
2761
+ * Leave it off against a `tempest-fastapi-sdk` server: that server pings on
2762
+ * its own and answers a client-sent `{"type":"ping"}` with nothing, while a
2763
+ * strict handler rejects the unknown frame. What it needs from the client
2764
+ * is the `pong` reply, which `respondToPing` sends for you.
2459
2765
  */
2460
2766
  pingInterval?: number;
2461
2767
  /** Payload sent on each ping. Default: `JSON.stringify({ type: "ping" })`. */
2462
2768
  pingPayload?: string | Blob | BufferSource;
2769
+ /**
2770
+ * Reply to a server `{"type":"ping"}` with `pongPayload`. Default: true.
2771
+ *
2772
+ * `tempest-fastapi-sdk` closes a socket with code `4408` when no `pong`
2773
+ * arrives within `WS_HEARTBEAT_TIMEOUT_SECONDS`, so a client that stays
2774
+ * silent is dropped once per timeout. The ping is still forwarded to
2775
+ * `onMessage` — the reply is sent before your handler runs.
2776
+ */
2777
+ respondToPing?: boolean;
2778
+ /** Payload sent in reply to a server ping. Default: `JSON.stringify({ type: "pong" })`. */
2779
+ pongPayload?: string | Blob | BufferSource;
2780
+ /**
2781
+ * Buffer payloads sent while the socket is not open and flush them on the
2782
+ * next `open`. Default: false — `send()` returns false and drops.
2783
+ *
2784
+ * Without it, an action fired during reconnect backoff vanishes and the UI
2785
+ * cannot tell "never sent" from "sent and ignored".
2786
+ */
2787
+ queueWhileClosed?: boolean;
2788
+ /** Cap on buffered payloads when `queueWhileClosed` is on. Default: 100. */
2789
+ maxQueuedMessages?: number;
2463
2790
  /** Parse incoming frames. Default: JSON with raw-string fallback. */
2464
2791
  parser?: (raw: string) => T;
2465
2792
  onOpen?: (event: Event) => void;
@@ -3316,7 +3643,7 @@ export declare interface Filter {
3316
3643
  */
3317
3644
  export declare function FilterBar({ fields, value, onChange, locale, actions, className, ...rest }: FilterBarProps): JSX.Element;
3318
3645
 
3319
- export declare interface FilterBarProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_7> {
3646
+ export declare interface FilterBarProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_9> {
3320
3647
  /** Fields the user may filter by. */
3321
3648
  fields: readonly FilterField[];
3322
3649
  /** Applied filters. Controlled. */
@@ -4459,6 +4786,9 @@ export declare function isDefined<T>(value: T | null | undefined): value is T;
4459
4786
  */
4460
4787
  export declare function isEmpty(value: unknown): boolean;
4461
4788
 
4789
+ /** Whether any turn in the thread is still streaming. */
4790
+ export declare function isGenerating(messages: readonly AIChatMessage[]): boolean;
4791
+
4462
4792
  /**
4463
4793
  * Detects iOS / iPadOS Safari, including modern iPads that report `MacIntel`
4464
4794
  * plus multi-touch instead of an `iPad` user agent.
@@ -4695,6 +5025,15 @@ export declare interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement
4695
5025
  required?: boolean;
4696
5026
  }
4697
5027
 
5028
+ /**
5029
+ * Id of the newest assistant turn, or `null` when there is none.
5030
+ *
5031
+ * Only that turn gets the regenerate control: re-asking an older one would throw
5032
+ * away every turn after it, which is a different operation ("branch here") and
5033
+ * needs its own confirmation.
5034
+ */
5035
+ export declare function lastAssistantId(messages: readonly AIChatMessage[]): string | null;
5036
+
4698
5037
  /**
4699
5038
  * Conflict-resolution helpers for the `applyRemote` callback of
4700
5039
  * {@link createOfflineSync}. Each takes the current local record (or
@@ -5024,7 +5363,7 @@ export declare type MarkdownInline = {
5024
5363
  type: "break";
5025
5364
  };
5026
5365
 
5027
- export declare interface MarkdownProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_6> {
5366
+ export declare interface MarkdownProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_8> {
5028
5367
  /** The Markdown source. */
5029
5368
  source: string;
5030
5369
  /**
@@ -5062,7 +5401,7 @@ export declare interface MarkdownProps extends Omit<HTMLAttributes<HTMLDivElemen
5062
5401
  */
5063
5402
  export declare function Masonry<T>({ items, children, itemKey, columns, gap, className, style, ...rest }: MasonryProps<T>): JSX.Element;
5064
5403
 
5065
- export declare interface MasonryProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_5> {
5404
+ export declare interface MasonryProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_7> {
5066
5405
  /** What to lay out. */
5067
5406
  items: readonly T[];
5068
5407
  /** Render one card. */
@@ -6021,16 +6360,22 @@ declare type OverriddenDomProps_2 = "children" | "onSubmit";
6021
6360
  declare type OverriddenDomProps_3 = "onSubmit" | "value" | "defaultValue" | "rows";
6022
6361
 
6023
6362
  /** DOM attributes this component redefines. */
6024
- declare type OverriddenDomProps_4 = "children" | "onChange" | "defaultValue";
6363
+ declare type OverriddenDomProps_4 = "children" | "onSubmit";
6364
+
6365
+ /** DOM attributes the composer redefines. */
6366
+ declare type OverriddenDomProps_5 = "onSubmit" | "value" | "defaultValue" | "rows";
6367
+
6368
+ /** DOM attributes this component redefines. */
6369
+ declare type OverriddenDomProps_6 = "children" | "onChange" | "defaultValue";
6025
6370
 
6026
6371
  /** DOM attributes this component redefines. */
6027
- declare type OverriddenDomProps_5 = "children";
6372
+ declare type OverriddenDomProps_7 = "children";
6028
6373
 
6029
6374
  /** DOM attributes this component redefines. */
6030
- declare type OverriddenDomProps_6 = "children";
6375
+ declare type OverriddenDomProps_8 = "children";
6031
6376
 
6032
6377
  /** DOM attributes this component redefines. */
6033
- declare type OverriddenDomProps_7 = "children" | "onChange";
6378
+ declare type OverriddenDomProps_9 = "children" | "onChange";
6034
6379
 
6035
6380
  /**
6036
6381
  * Page wrapper with header + (optional) toolbar + content + footer. Pairs
@@ -7026,6 +7371,9 @@ export declare interface RoleAccessControlConfig {
7026
7371
  role?: string | string[];
7027
7372
  }
7028
7373
 
7374
+ /** Role label used in the turn header and by screen readers. */
7375
+ export declare function roleLabel(role: AIChatRole, strings: AIChatStrings): string;
7376
+
7029
7377
  export { Route }
7030
7378
 
7031
7379
  /**
@@ -8188,6 +8536,21 @@ export declare type TagSize = "sm" | "md" | "lg";
8188
8536
 
8189
8537
  export declare type TagVariant = "neutral" | "primary" | "success" | "warning" | "danger" | "info";
8190
8538
 
8539
+ /**
8540
+ * A value that changes whenever the tail of the thread grows.
8541
+ *
8542
+ * The scroll effect cannot depend on the `messages` array alone. Streaming appends
8543
+ * to the **last** turn, and an app that mutates that object in place — or that
8544
+ * re-renders from a store holding the same array identity — would keep the same
8545
+ * dependency while the text grows, so the view would stop following the answer.
8546
+ * Length of the array, identity of the tail and length of its text together cover
8547
+ * both shapes.
8548
+ *
8549
+ * @param messages - The thread, oldest first.
8550
+ * @returns An opaque signature; compare with `===`.
8551
+ */
8552
+ export declare function tailSignature(messages: readonly AIChatMessage[]): string;
8553
+
8191
8554
  export declare interface TelemetryAdapter {
8192
8555
  /** Optional. Called when the provider mounts. */
8193
8556
  init?: () => void | Promise<void>;
@@ -8872,7 +9235,7 @@ export declare interface TransferItem {
8872
9235
  data?: Record<string, unknown>;
8873
9236
  }
8874
9237
 
8875
- export declare interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_4> {
9238
+ export declare interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps_6> {
8876
9239
  /** The whole catalogue. Both panes are derived from it. */
8877
9240
  items: readonly TransferItem[];
8878
9241
  /** Ids on the target side. Controlled. */
@@ -9021,6 +9384,15 @@ export declare interface TruncateTextProps extends HTMLAttributes<HTMLDivElement
9021
9384
  children: ReactNode;
9022
9385
  }
9023
9386
 
9387
+ /**
9388
+ * Clock label for a turn — the time, not a relative phrase.
9389
+ *
9390
+ * A transcript is read top to bottom in one sitting, so "há 2 minutos" on every turn
9391
+ * is noise that also has to be re-rendered on a timer. The wall clock is stable and
9392
+ * enough to answer the only question anyone asks of it ("was this today?").
9393
+ */
9394
+ export declare function turnTime(timestamp: number, locale?: "pt-BR" | "en"): string;
9395
+
9024
9396
  /** Sentence for the typing indicator, or `null` when nobody is typing. */
9025
9397
  export declare function typingLabel(names: readonly string[], locale?: "pt-BR" | "en"): string | null;
9026
9398
 
@@ -10782,6 +11154,17 @@ export { useWatch }
10782
11154
  /**
10783
11155
  * React hook around {@link createWebSocket}. Manages the connection lifecycle
10784
11156
  * for the host component and tears it down on unmount.
11157
+ *
11158
+ * Every callback is read through a ref, so `onOpen` / `onMessage` / `onClose` /
11159
+ * `onError` always run the latest closure — an inline arrow function is fine
11160
+ * and never reopens the socket. Connection-shaping options (`protocols`,
11161
+ * `maxRetries`, `initialBackoff`, `maxBackoff`, `pingInterval`,
11162
+ * `queueWhileClosed`) are baked into the connection, so changing one reopens
11163
+ * it with the new value rather than being silently ignored.
11164
+ *
11165
+ * @param url - Full ws:// or wss:// URL.
11166
+ * @param options - Connection configuration and callbacks.
11167
+ * @returns Status, last frame, and the `send` / `reconnect` controls.
10785
11168
  */
10786
11169
  export declare function useWebSocket<T = unknown>(url: string, options?: UseWebSocketOptions<T>): UseWebSocketResult<T>;
10787
11170
 
@@ -10792,7 +11175,15 @@ export declare interface UseWebSocketOptions<T> extends Omit<CreateWebSocketOpti
10792
11175
 
10793
11176
  export declare interface UseWebSocketResult<T> {
10794
11177
  status: WebSocketStatus;
10795
- /** Last decoded frame received. */
11178
+ /**
11179
+ * Last decoded frame received.
11180
+ *
11181
+ * A snapshot, not a stream: two frames arriving in the same tick collapse
11182
+ * into a single render and only the later one is ever visible. One server
11183
+ * action often emits several frames in a row, so anything that must see
11184
+ * every message has to use `onMessage`, which fires once per frame. Read
11185
+ * `lastMessage` for "what is the current state" rendering only.
11186
+ */
10796
11187
  lastMessage: WebSocketMessage<T> | null;
10797
11188
  /** Send a payload through the active connection. Returns false when not open. */
10798
11189
  send: (payload: string | Blob | BufferSource) => boolean;
@@ -10995,6 +11386,21 @@ export declare interface VirtualTableSort<T> {
10995
11386
 
10996
11387
  export declare type VirtualTableSortDirection = "asc" | "desc";
10997
11388
 
11389
+ /**
11390
+ * Turns to render, in order.
11391
+ *
11392
+ * System turns are dropped unless asked for: a system prompt is configuration, and
11393
+ * an app that shows it by default leaks its own instructions into the transcript.
11394
+ *
11395
+ * @param params.messages - The thread, oldest first. Never reordered.
11396
+ * @param params.showSystem - Keep `"system"` turns. Default `false`.
11397
+ * @returns The visible turns, in the given order.
11398
+ */
11399
+ export declare function visibleTurns({ messages, showSystem, }: {
11400
+ messages: readonly AIChatMessage[];
11401
+ showSystem?: boolean;
11402
+ }): AIChatMessage[];
11403
+
10998
11404
  /**
10999
11405
  * Render content that is hidden visually but remains available to screen
11000
11406
  * readers — the standard "sr-only" pattern. Useful for accessible labels on