@dudousxd/nestjs-agent-react 0.7.5 → 0.9.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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as ai from 'ai';
2
- import { ChatTransport, UIMessage, UIMessageChunk, ToolUIPart, DynamicToolUIPart } from 'ai';
2
+ import { ChatTransport, UIMessage, UIMessageChunk, ToolUIPart, DynamicToolUIPart, DataUIPart, UIDataTypes } from 'ai';
3
3
  import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
4
4
  import { StoredMessage, ThreadSummary, SkillCatalogEntry, ThreadDetail, MessageAttachment, QuotaView } from '@dudousxd/nestjs-agent-core';
5
5
  import React from 'react';
@@ -69,6 +69,15 @@ interface AgentChatTransportOptions {
69
69
  * message, and are rendered as native model content parts. Optimistic display of
70
70
  * the user's own attachment thumbnails is the consumer's concern (it stages the
71
71
  * upload), the same way history rendering reads `StoredMessage.attachments`.
72
+ *
73
+ * Frames beyond text/reasoning/tools become AI SDK data parts (seen by `useChat`'s `onData`):
74
+ * - `ui` → `data-ui` part, `id` = the component id (a repeat id updates it in place)
75
+ * - `approval-requested` → `data-approval-requested` part keyed by the call id, plus the SDK's
76
+ * native `tool-approval-request` (the part moves to `approval-requested`)
77
+ * - `title`, `cancelled` → transient `data-title` / `data-cancelled` (never stored on the message)
78
+ * - any other kind → `data-<kind>` part carrying the frame minus `kind`, keyed by its `id`
79
+ * when it has one — forwarded, never dropped
80
+ * `parentId` on a tool frame rides the part's `toolMetadata` next to `toolKind`.
72
81
  */
73
82
  declare class AgentChatTransport implements ChatTransport<UIMessage> {
74
83
  private readonly options;
@@ -105,7 +114,8 @@ declare class AgentChatTransport implements ChatTransport<UIMessage> {
105
114
  * Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.
106
115
  * Recognized frames:
107
116
  * - `event: meta` `data: {"runId","threadId"}` → records identity
108
- * - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool)
117
+ * - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool /
118
+ * `data-*`; see {@link AgentChatTransport} docs)
109
119
  * - `event: done` `data: {}` → terminates
110
120
  * - `event: error` `data: {code,message}` → error chunk
111
121
  *
@@ -335,6 +345,12 @@ interface TranscriptReasoningBlock {
335
345
  key: string;
336
346
  text: string;
337
347
  isStreaming: boolean;
348
+ /**
349
+ * How long the model thought, in ms — the backend's measurement once the run closed (live) or as
350
+ * persisted (reloaded). `null` while it still streams, or when nothing was recorded: pair it with
351
+ * `useElapsed(block.isStreaming)` for a ticking label (`block.durationMs ?? elapsed`).
352
+ */
353
+ durationMs: number | null;
338
354
  isOpen: boolean;
339
355
  toggle: (open?: boolean) => void;
340
356
  }
@@ -352,11 +368,33 @@ interface TranscriptSettleState {
352
368
  isSubmitting: boolean;
353
369
  run: () => void;
354
370
  }
371
+ /**
372
+ * Who has to settle a parked call, and until when — from the stream's `approval-requested` frame.
373
+ * `null` on a call whose runner never said (the call can still be awaiting approval).
374
+ */
375
+ interface TranscriptApproval {
376
+ /** Open vocabulary the host defines: `'requester'`, `'admin'`, a role… */
377
+ approver: string;
378
+ /** ISO-8601; `null` when the request never lapses. */
379
+ expiresAt: string | null;
380
+ reason: string | null;
381
+ }
355
382
  /** One call in a tool run, with whatever human decision it is parked on. */
356
383
  interface TranscriptToolCall {
357
384
  part: AnyToolUIPart;
358
385
  toolCallId: string;
359
386
  name: string;
387
+ /** `read` / `action` as the stream classified the call; `null` when the backend did not say. */
388
+ toolKind: string | null;
389
+ /** The call this one ran inside (a code-mode `execute`, a delegated agent); `null` for a top-level call. */
390
+ parentId: string | null;
391
+ /**
392
+ * Calls nested under this one within the same block, in stream order. Always empty for a call
393
+ * nothing names as its parent.
394
+ */
395
+ children: TranscriptToolCall[];
396
+ /** Who has to decide, when the runner said so. See {@link TranscriptApproval}. */
397
+ approval: TranscriptApproval | null;
360
398
  /**
361
399
  * Parked on a person. An `action` tool's input lands and its output never follows on its own —
362
400
  * the loop waits for an approval between the two — so a settled-looking card that never settles
@@ -379,6 +417,25 @@ interface TranscriptToolBlock {
379
417
  parts: AnyToolUIPart[];
380
418
  /** The same calls, each with its human-decision state. Same order as `parts`. */
381
419
  calls: TranscriptToolCall[];
420
+ /**
421
+ * The same calls as a tree: every call whose parent is NOT in this block, each carrying its
422
+ * nested calls in `children`. Equal to `calls` when nothing is nested.
423
+ */
424
+ roots: TranscriptToolCall[];
425
+ }
426
+ /**
427
+ * A component the server pushed into the message (the stream's `ui` frame, or a persisted
428
+ * `data-ui` part). Rendered by looking `component` up in the host's own registry.
429
+ */
430
+ interface TranscriptUiBlock {
431
+ kind: 'ui';
432
+ key: string;
433
+ /** The component's identity within the message. */
434
+ id: string;
435
+ component: string;
436
+ props: Record<string, unknown>;
437
+ /** Schema version of `props`; `null` when the server did not stamp one. */
438
+ version: number | null;
382
439
  }
383
440
  /** One choice a question offers, with its live selection state. */
384
441
  interface TranscriptQuestionOption {
@@ -472,7 +529,7 @@ interface TranscriptSourcesBlock {
472
529
  sources: TranscriptSource[];
473
530
  passageCount: number;
474
531
  }
475
- type TranscriptBlock = TranscriptTextBlock | TranscriptFilesBlock | TranscriptReasoningBlock | TranscriptToolBlock | TranscriptSourcesBlock | TranscriptElicitationBlock;
532
+ type TranscriptBlock = TranscriptTextBlock | TranscriptFilesBlock | TranscriptReasoningBlock | TranscriptToolBlock | TranscriptSourcesBlock | TranscriptElicitationBlock | TranscriptUiBlock;
476
533
  /** Where a question set's selection state is held, and where its settlement is sent. */
477
534
  interface ElicitationBlockOptions {
478
535
  /** The values a question is showing, or `undefined` while the user has not touched it. */
@@ -515,9 +572,11 @@ interface BuildBlocksOptions {
515
572
  }
516
573
  /**
517
574
  * Walk a message's parts into renderable blocks, buffering consecutive tool parts into one run and
518
- * consecutive files into one strip. Parts this library has no model for (`data-*`) are dropped
519
- * rather than guessed at, but they still terminate a tool run — their position in the transcript is
520
- * meaningful even when their content is not modelled here.
575
+ * consecutive files into one strip. A `data-ui` part becomes a `ui` block. A
576
+ * `data-approval-requested` part is metadata about a call — it is folded into that call's
577
+ * `approval` and takes no position of its own. Other `data-*` parts are dropped rather than guessed
578
+ * at, but they still terminate a tool run — their position in the transcript is meaningful even
579
+ * when their content is not modelled here.
521
580
  */
522
581
  declare function buildTranscriptBlocks(message: UIMessage, options: BuildBlocksOptions): TranscriptBlock[];
523
582
  /** The message's prose, joined for the clipboard. Reasoning is excluded — it is not the answer. */
@@ -729,6 +788,12 @@ type RenderTextFn = (text: string, ctx: {
729
788
  }) => React.ReactNode;
730
789
  /** Render a run of files — attachments on a user turn, or files the model produced. */
731
790
  type RenderFilesFn = (files: TranscriptFile[]) => React.ReactNode;
791
+ /**
792
+ * Render a component the server pushed into the message — look `block.component` up in your own
793
+ * registry. Omitted → pushed components are not drawn (there is no sensible default for an app's
794
+ * own component).
795
+ */
796
+ type RenderUiFn = (block: TranscriptUiBlock) => React.ReactNode;
732
797
  /** Render the body of a reasoning run; the disclosure toggle around it stays this component's. */
733
798
  type RenderReasoningFn = (text: string, ctx: {
734
799
  isStreaming: boolean;
@@ -761,6 +826,8 @@ interface MessageRenderSlots {
761
826
  renderReasoning?: RenderReasoningFn;
762
827
  /** Draw files yourself — a gallery, a viewer. Omitted → images inline, everything else a link. */
763
828
  renderFiles?: RenderFilesFn;
829
+ /** Draw a server-pushed component (a `ui` stream frame). Omitted → not drawn. */
830
+ renderUi?: RenderUiFn;
764
831
  /**
765
832
  * Extra content for the action row, after the built-in affordances and before usage — a badge
766
833
  * naming which agent answered, a status pill. Rendered for every message that has an action row.
@@ -825,6 +892,8 @@ interface MessageListProps {
825
892
  renderReasoning?: RenderReasoningFn;
826
893
  /** Draw a message's files yourself. Omitted → images inline, everything else a link. */
827
894
  renderFiles?: RenderFilesFn;
895
+ /** Draw a server-pushed component (a `ui` stream frame). Omitted → not drawn. */
896
+ renderUi?: RenderUiFn;
828
897
  reasoningLabel?: React.ReactNode;
829
898
  /** Extra content for one message's action row — e.g. a badge naming which agent answered. */
830
899
  getMeta?: (message: UIMessage) => React.ReactNode;
@@ -860,7 +929,7 @@ interface MessageListProps {
860
929
  * Every decision here comes from `useChatTranscript`; the file below it is markup. A host that
861
930
  * wants a different layout drives that hook directly and never imports this component.
862
931
  */
863
- declare function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, reasoningLabel, getMeta, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, classNames, }: MessageListProps): React.JSX.Element;
932
+ declare function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, renderUi, reasoningLabel, getMeta, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, classNames, }: MessageListProps): React.JSX.Element;
864
933
 
865
934
  /**
866
935
  * Languages the dictation mic offers. The Web Speech API expects a
@@ -1088,6 +1157,32 @@ interface ComposerAutocomplete {
1088
1157
  */
1089
1158
  declare function useComposerAutocomplete(options: UseComposerAutocompleteOptions): ComposerAutocomplete;
1090
1159
 
1160
+ /** The thinking time stamped on a reasoning part, or `null` when it carries none. */
1161
+ declare function readReasoningMs(part: {
1162
+ providerMetadata?: unknown;
1163
+ }): number | null;
1164
+ interface UseElapsedOptions {
1165
+ /** How often the value re-renders while running. Default 1000 — a seconds label needs no more. */
1166
+ intervalMs?: number;
1167
+ /** Injectable clock for tests. */
1168
+ now?: () => number;
1169
+ }
1170
+ /**
1171
+ * Milliseconds since `running` last turned true — ticking while it stays true, frozen at the final
1172
+ * value once it turns false, `0` before it has ever run. Headless: pair it with
1173
+ * {@link formatElapsed} or any label of your own.
1174
+ *
1175
+ * For a reasoning block, prefer the duration the block already carries (`block.durationMs`, the
1176
+ * server's own measurement) and fall back to this only while the block streams:
1177
+ * `block.durationMs ?? elapsed`.
1178
+ */
1179
+ declare function useElapsed(running: boolean, options?: UseElapsedOptions): number;
1180
+ /**
1181
+ * `"<1s"`, `"4s"`, `"1m 5s"`, `"1h 2m"` — whole units, the shape a "Thought for …" label wants.
1182
+ * Negative or non-finite input reads as `"<1s"` rather than a nonsense figure.
1183
+ */
1184
+ declare function formatElapsed(ms: number): string;
1185
+
1091
1186
  declare function storedMessageToUiMessage(message: StoredMessage): UIMessage;
1092
1187
 
1093
1188
  /**
@@ -1219,6 +1314,18 @@ interface UseAgentChatOptions {
1219
1314
  runId: string;
1220
1315
  status: 'completed' | 'failed';
1221
1316
  }) => void;
1317
+ /**
1318
+ * Every data part the stream delivers, as it arrives — including transient ones that are never
1319
+ * stored on a message (`data-title`, `data-cancelled`). Pushed components arrive as `data-ui`,
1320
+ * approval metadata as `data-approval-requested`, and a frame kind this version does not map as
1321
+ * `data-<kind>`. See `AgentChatTransport` for the full mapping.
1322
+ */
1323
+ onData?: (part: DataUIPart<UIDataTypes>) => void;
1324
+ /**
1325
+ * The server set or changed the thread's title while a turn streamed. Lets a header/sidebar
1326
+ * update the moment the title exists instead of refetching after the run settles.
1327
+ */
1328
+ onTitle?: (title: string) => void;
1222
1329
  /**
1223
1330
  * Track DETACHED sub-agents this conversation started: which are still working, and their answers
1224
1331
  * as they land (see {@link ChatBackground}). Default `false`.
@@ -1260,7 +1367,7 @@ interface AddToolResultArgs {
1260
1367
  * `AgentClient`. Mirrors flip's `useAdminChat`, generalized.
1261
1368
  */
1262
1369
  declare function useAgentChat(options: UseAgentChatOptions): {
1263
- sendMessage: (message?: (Omit<UIMessage<unknown, ai.UIDataTypes, ai.UITools>, "id" | "role"> & {
1370
+ sendMessage: (message?: (Omit<UIMessage<unknown, UIDataTypes, ai.UITools>, "id" | "role"> & {
1264
1371
  id?: string;
1265
1372
  role?: "system" | "user" | "assistant";
1266
1373
  } & {
@@ -1313,15 +1420,15 @@ declare function useAgentChat(options: UseAgentChatOptions): {
1313
1420
  }) => Promise<void>;
1314
1421
  regenerate: () => void;
1315
1422
  id: string;
1316
- setMessages: (messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[] | ((messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[]) => UIMessage<unknown, ai.UIDataTypes, ai.UITools>[])) => void;
1423
+ setMessages: (messages: UIMessage<unknown, UIDataTypes, ai.UITools>[] | ((messages: UIMessage<unknown, UIDataTypes, ai.UITools>[]) => UIMessage<unknown, UIDataTypes, ai.UITools>[])) => void;
1317
1424
  error: Error | undefined;
1318
1425
  stop: () => Promise<void>;
1319
1426
  status: ai.ChatStatus;
1320
- messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[];
1427
+ messages: UIMessage<unknown, UIDataTypes, ai.UITools>[];
1321
1428
  resumeStream: (options?: ai.ChatRequestOptions) => Promise<void>;
1322
- addToolOutput: ai.ChatAddToolOutputFunction<UIMessage<unknown, ai.UIDataTypes, ai.UITools>>;
1429
+ addToolOutput: ai.ChatAddToolOutputFunction<UIMessage<unknown, UIDataTypes, ai.UITools>>;
1323
1430
  addToolApprovalResponse: ai.ChatAddToolApproveResponseFunction;
1324
1431
  clearError: () => void;
1325
1432
  };
1326
1433
 
1327
- export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AggregatedTurnUsage, type AnyToolUIPart, type ApprovalBlockOptions, type AutocompleteInputProps, type AutocompleteItem, type AutocompleteListboxProps, type AutocompleteOptionProps, type AutocompleteSource, type BackgroundRun, type BuildBlocksOptions, type CancelResult, type ChatBackground, ChatInput, type ChatInputProps, type ChatStatus, type ChatTranscript, type CompletionEdit, type ComposerAutocomplete, DEFAULT_SPEECH_LANG, type ElicitationBlockOptions, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageItemView, type MessageItemViewProps, MessageList, type MessageListClassNames, type MessageListProps, type MessageUsageInfo, type QuotaToday, type RenderFilesFn, type RenderReasoningFn, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, type RetrievedPassage, SPEECH_LANGUAGES, type SettleAction, type SkillSuggestionData, type SkillsSourceOptions, type SpeechLanguage, type SpeechRecognitionHook, type StickToBottom, type StickToBottomOptions, type StoredTurnMetadata, type ThreadPatch, type TimestampInfo, type TranscriptActionState, type TranscriptBlock, type TranscriptCopyState, type TranscriptEditState, type TranscriptElicitationBlock, type TranscriptElicitationOutcome, type TranscriptFile, type TranscriptFilesBlock, type TranscriptItem, type TranscriptItemOptions, type TranscriptQuestion, type TranscriptQuestionOption, type TranscriptReasoningBlock, type TranscriptSettleState, type TranscriptSource, type TranscriptSourcesBlock, type TranscriptStopState, type TranscriptTextBlock, type TranscriptToolBlock, type TranscriptToolCall, type TranscriptWindow, type TriggerMatch, type TriggerPosition, type UsageSummary, type UseAgentChatOptions, type UseChatTranscriptOptions, type UseComposerAutocompleteOptions, type UseSpeechRecognitionOptions, type UseTranscriptItemOptions, applyCompletion, backgroundRunsFromThread, buildTranscriptBlocks, createSkillsSource, describeTimestamp, describeUsage, extractMessageText, filterAutocompleteItems, findActiveTrigger, formatRelativeTime, loadStoredSpeechLang, persistSpeechLang, storedMessageToUiMessage, storedThreadToUiMessages, useAgentChat, useChatTranscript, useComposerAutocomplete, useSpeechRecognition, useStickToBottom, useTranscriptItem };
1434
+ export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AggregatedTurnUsage, type AnyToolUIPart, type ApprovalBlockOptions, type AutocompleteInputProps, type AutocompleteItem, type AutocompleteListboxProps, type AutocompleteOptionProps, type AutocompleteSource, type BackgroundRun, type BuildBlocksOptions, type CancelResult, type ChatBackground, ChatInput, type ChatInputProps, type ChatStatus, type ChatTranscript, type CompletionEdit, type ComposerAutocomplete, DEFAULT_SPEECH_LANG, type ElicitationBlockOptions, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageItemView, type MessageItemViewProps, MessageList, type MessageListClassNames, type MessageListProps, type MessageUsageInfo, type QuotaToday, type RenderFilesFn, type RenderReasoningFn, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, type RenderUiFn, type RetrievedPassage, SPEECH_LANGUAGES, type SettleAction, type SkillSuggestionData, type SkillsSourceOptions, type SpeechLanguage, type SpeechRecognitionHook, type StickToBottom, type StickToBottomOptions, type StoredTurnMetadata, type ThreadPatch, type TimestampInfo, type TranscriptActionState, type TranscriptApproval, type TranscriptBlock, type TranscriptCopyState, type TranscriptEditState, type TranscriptElicitationBlock, type TranscriptElicitationOutcome, type TranscriptFile, type TranscriptFilesBlock, type TranscriptItem, type TranscriptItemOptions, type TranscriptQuestion, type TranscriptQuestionOption, type TranscriptReasoningBlock, type TranscriptSettleState, type TranscriptSource, type TranscriptSourcesBlock, type TranscriptStopState, type TranscriptTextBlock, type TranscriptToolBlock, type TranscriptToolCall, type TranscriptUiBlock, type TranscriptWindow, type TriggerMatch, type TriggerPosition, type UsageSummary, type UseAgentChatOptions, type UseChatTranscriptOptions, type UseComposerAutocompleteOptions, type UseElapsedOptions, type UseSpeechRecognitionOptions, type UseTranscriptItemOptions, applyCompletion, backgroundRunsFromThread, buildTranscriptBlocks, createSkillsSource, describeTimestamp, describeUsage, extractMessageText, filterAutocompleteItems, findActiveTrigger, formatElapsed, formatRelativeTime, loadStoredSpeechLang, persistSpeechLang, readReasoningMs, storedMessageToUiMessage, storedThreadToUiMessages, useAgentChat, useChatTranscript, useComposerAutocomplete, useElapsed, useSpeechRecognition, useStickToBottom, useTranscriptItem };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as ai from 'ai';
2
- import { ChatTransport, UIMessage, UIMessageChunk, ToolUIPart, DynamicToolUIPart } from 'ai';
2
+ import { ChatTransport, UIMessage, UIMessageChunk, ToolUIPart, DynamicToolUIPart, DataUIPart, UIDataTypes } from 'ai';
3
3
  import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
4
4
  import { StoredMessage, ThreadSummary, SkillCatalogEntry, ThreadDetail, MessageAttachment, QuotaView } from '@dudousxd/nestjs-agent-core';
5
5
  import React from 'react';
@@ -69,6 +69,15 @@ interface AgentChatTransportOptions {
69
69
  * message, and are rendered as native model content parts. Optimistic display of
70
70
  * the user's own attachment thumbnails is the consumer's concern (it stages the
71
71
  * upload), the same way history rendering reads `StoredMessage.attachments`.
72
+ *
73
+ * Frames beyond text/reasoning/tools become AI SDK data parts (seen by `useChat`'s `onData`):
74
+ * - `ui` → `data-ui` part, `id` = the component id (a repeat id updates it in place)
75
+ * - `approval-requested` → `data-approval-requested` part keyed by the call id, plus the SDK's
76
+ * native `tool-approval-request` (the part moves to `approval-requested`)
77
+ * - `title`, `cancelled` → transient `data-title` / `data-cancelled` (never stored on the message)
78
+ * - any other kind → `data-<kind>` part carrying the frame minus `kind`, keyed by its `id`
79
+ * when it has one — forwarded, never dropped
80
+ * `parentId` on a tool frame rides the part's `toolMetadata` next to `toolKind`.
72
81
  */
73
82
  declare class AgentChatTransport implements ChatTransport<UIMessage> {
74
83
  private readonly options;
@@ -105,7 +114,8 @@ declare class AgentChatTransport implements ChatTransport<UIMessage> {
105
114
  * Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.
106
115
  * Recognized frames:
107
116
  * - `event: meta` `data: {"runId","threadId"}` → records identity
108
- * - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool)
117
+ * - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool /
118
+ * `data-*`; see {@link AgentChatTransport} docs)
109
119
  * - `event: done` `data: {}` → terminates
110
120
  * - `event: error` `data: {code,message}` → error chunk
111
121
  *
@@ -335,6 +345,12 @@ interface TranscriptReasoningBlock {
335
345
  key: string;
336
346
  text: string;
337
347
  isStreaming: boolean;
348
+ /**
349
+ * How long the model thought, in ms — the backend's measurement once the run closed (live) or as
350
+ * persisted (reloaded). `null` while it still streams, or when nothing was recorded: pair it with
351
+ * `useElapsed(block.isStreaming)` for a ticking label (`block.durationMs ?? elapsed`).
352
+ */
353
+ durationMs: number | null;
338
354
  isOpen: boolean;
339
355
  toggle: (open?: boolean) => void;
340
356
  }
@@ -352,11 +368,33 @@ interface TranscriptSettleState {
352
368
  isSubmitting: boolean;
353
369
  run: () => void;
354
370
  }
371
+ /**
372
+ * Who has to settle a parked call, and until when — from the stream's `approval-requested` frame.
373
+ * `null` on a call whose runner never said (the call can still be awaiting approval).
374
+ */
375
+ interface TranscriptApproval {
376
+ /** Open vocabulary the host defines: `'requester'`, `'admin'`, a role… */
377
+ approver: string;
378
+ /** ISO-8601; `null` when the request never lapses. */
379
+ expiresAt: string | null;
380
+ reason: string | null;
381
+ }
355
382
  /** One call in a tool run, with whatever human decision it is parked on. */
356
383
  interface TranscriptToolCall {
357
384
  part: AnyToolUIPart;
358
385
  toolCallId: string;
359
386
  name: string;
387
+ /** `read` / `action` as the stream classified the call; `null` when the backend did not say. */
388
+ toolKind: string | null;
389
+ /** The call this one ran inside (a code-mode `execute`, a delegated agent); `null` for a top-level call. */
390
+ parentId: string | null;
391
+ /**
392
+ * Calls nested under this one within the same block, in stream order. Always empty for a call
393
+ * nothing names as its parent.
394
+ */
395
+ children: TranscriptToolCall[];
396
+ /** Who has to decide, when the runner said so. See {@link TranscriptApproval}. */
397
+ approval: TranscriptApproval | null;
360
398
  /**
361
399
  * Parked on a person. An `action` tool's input lands and its output never follows on its own —
362
400
  * the loop waits for an approval between the two — so a settled-looking card that never settles
@@ -379,6 +417,25 @@ interface TranscriptToolBlock {
379
417
  parts: AnyToolUIPart[];
380
418
  /** The same calls, each with its human-decision state. Same order as `parts`. */
381
419
  calls: TranscriptToolCall[];
420
+ /**
421
+ * The same calls as a tree: every call whose parent is NOT in this block, each carrying its
422
+ * nested calls in `children`. Equal to `calls` when nothing is nested.
423
+ */
424
+ roots: TranscriptToolCall[];
425
+ }
426
+ /**
427
+ * A component the server pushed into the message (the stream's `ui` frame, or a persisted
428
+ * `data-ui` part). Rendered by looking `component` up in the host's own registry.
429
+ */
430
+ interface TranscriptUiBlock {
431
+ kind: 'ui';
432
+ key: string;
433
+ /** The component's identity within the message. */
434
+ id: string;
435
+ component: string;
436
+ props: Record<string, unknown>;
437
+ /** Schema version of `props`; `null` when the server did not stamp one. */
438
+ version: number | null;
382
439
  }
383
440
  /** One choice a question offers, with its live selection state. */
384
441
  interface TranscriptQuestionOption {
@@ -472,7 +529,7 @@ interface TranscriptSourcesBlock {
472
529
  sources: TranscriptSource[];
473
530
  passageCount: number;
474
531
  }
475
- type TranscriptBlock = TranscriptTextBlock | TranscriptFilesBlock | TranscriptReasoningBlock | TranscriptToolBlock | TranscriptSourcesBlock | TranscriptElicitationBlock;
532
+ type TranscriptBlock = TranscriptTextBlock | TranscriptFilesBlock | TranscriptReasoningBlock | TranscriptToolBlock | TranscriptSourcesBlock | TranscriptElicitationBlock | TranscriptUiBlock;
476
533
  /** Where a question set's selection state is held, and where its settlement is sent. */
477
534
  interface ElicitationBlockOptions {
478
535
  /** The values a question is showing, or `undefined` while the user has not touched it. */
@@ -515,9 +572,11 @@ interface BuildBlocksOptions {
515
572
  }
516
573
  /**
517
574
  * Walk a message's parts into renderable blocks, buffering consecutive tool parts into one run and
518
- * consecutive files into one strip. Parts this library has no model for (`data-*`) are dropped
519
- * rather than guessed at, but they still terminate a tool run — their position in the transcript is
520
- * meaningful even when their content is not modelled here.
575
+ * consecutive files into one strip. A `data-ui` part becomes a `ui` block. A
576
+ * `data-approval-requested` part is metadata about a call — it is folded into that call's
577
+ * `approval` and takes no position of its own. Other `data-*` parts are dropped rather than guessed
578
+ * at, but they still terminate a tool run — their position in the transcript is meaningful even
579
+ * when their content is not modelled here.
521
580
  */
522
581
  declare function buildTranscriptBlocks(message: UIMessage, options: BuildBlocksOptions): TranscriptBlock[];
523
582
  /** The message's prose, joined for the clipboard. Reasoning is excluded — it is not the answer. */
@@ -729,6 +788,12 @@ type RenderTextFn = (text: string, ctx: {
729
788
  }) => React.ReactNode;
730
789
  /** Render a run of files — attachments on a user turn, or files the model produced. */
731
790
  type RenderFilesFn = (files: TranscriptFile[]) => React.ReactNode;
791
+ /**
792
+ * Render a component the server pushed into the message — look `block.component` up in your own
793
+ * registry. Omitted → pushed components are not drawn (there is no sensible default for an app's
794
+ * own component).
795
+ */
796
+ type RenderUiFn = (block: TranscriptUiBlock) => React.ReactNode;
732
797
  /** Render the body of a reasoning run; the disclosure toggle around it stays this component's. */
733
798
  type RenderReasoningFn = (text: string, ctx: {
734
799
  isStreaming: boolean;
@@ -761,6 +826,8 @@ interface MessageRenderSlots {
761
826
  renderReasoning?: RenderReasoningFn;
762
827
  /** Draw files yourself — a gallery, a viewer. Omitted → images inline, everything else a link. */
763
828
  renderFiles?: RenderFilesFn;
829
+ /** Draw a server-pushed component (a `ui` stream frame). Omitted → not drawn. */
830
+ renderUi?: RenderUiFn;
764
831
  /**
765
832
  * Extra content for the action row, after the built-in affordances and before usage — a badge
766
833
  * naming which agent answered, a status pill. Rendered for every message that has an action row.
@@ -825,6 +892,8 @@ interface MessageListProps {
825
892
  renderReasoning?: RenderReasoningFn;
826
893
  /** Draw a message's files yourself. Omitted → images inline, everything else a link. */
827
894
  renderFiles?: RenderFilesFn;
895
+ /** Draw a server-pushed component (a `ui` stream frame). Omitted → not drawn. */
896
+ renderUi?: RenderUiFn;
828
897
  reasoningLabel?: React.ReactNode;
829
898
  /** Extra content for one message's action row — e.g. a badge naming which agent answered. */
830
899
  getMeta?: (message: UIMessage) => React.ReactNode;
@@ -860,7 +929,7 @@ interface MessageListProps {
860
929
  * Every decision here comes from `useChatTranscript`; the file below it is markup. A host that
861
930
  * wants a different layout drives that hook directly and never imports this component.
862
931
  */
863
- declare function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, reasoningLabel, getMeta, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, classNames, }: MessageListProps): React.JSX.Element;
932
+ declare function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, renderUi, reasoningLabel, getMeta, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, classNames, }: MessageListProps): React.JSX.Element;
864
933
 
865
934
  /**
866
935
  * Languages the dictation mic offers. The Web Speech API expects a
@@ -1088,6 +1157,32 @@ interface ComposerAutocomplete {
1088
1157
  */
1089
1158
  declare function useComposerAutocomplete(options: UseComposerAutocompleteOptions): ComposerAutocomplete;
1090
1159
 
1160
+ /** The thinking time stamped on a reasoning part, or `null` when it carries none. */
1161
+ declare function readReasoningMs(part: {
1162
+ providerMetadata?: unknown;
1163
+ }): number | null;
1164
+ interface UseElapsedOptions {
1165
+ /** How often the value re-renders while running. Default 1000 — a seconds label needs no more. */
1166
+ intervalMs?: number;
1167
+ /** Injectable clock for tests. */
1168
+ now?: () => number;
1169
+ }
1170
+ /**
1171
+ * Milliseconds since `running` last turned true — ticking while it stays true, frozen at the final
1172
+ * value once it turns false, `0` before it has ever run. Headless: pair it with
1173
+ * {@link formatElapsed} or any label of your own.
1174
+ *
1175
+ * For a reasoning block, prefer the duration the block already carries (`block.durationMs`, the
1176
+ * server's own measurement) and fall back to this only while the block streams:
1177
+ * `block.durationMs ?? elapsed`.
1178
+ */
1179
+ declare function useElapsed(running: boolean, options?: UseElapsedOptions): number;
1180
+ /**
1181
+ * `"<1s"`, `"4s"`, `"1m 5s"`, `"1h 2m"` — whole units, the shape a "Thought for …" label wants.
1182
+ * Negative or non-finite input reads as `"<1s"` rather than a nonsense figure.
1183
+ */
1184
+ declare function formatElapsed(ms: number): string;
1185
+
1091
1186
  declare function storedMessageToUiMessage(message: StoredMessage): UIMessage;
1092
1187
 
1093
1188
  /**
@@ -1219,6 +1314,18 @@ interface UseAgentChatOptions {
1219
1314
  runId: string;
1220
1315
  status: 'completed' | 'failed';
1221
1316
  }) => void;
1317
+ /**
1318
+ * Every data part the stream delivers, as it arrives — including transient ones that are never
1319
+ * stored on a message (`data-title`, `data-cancelled`). Pushed components arrive as `data-ui`,
1320
+ * approval metadata as `data-approval-requested`, and a frame kind this version does not map as
1321
+ * `data-<kind>`. See `AgentChatTransport` for the full mapping.
1322
+ */
1323
+ onData?: (part: DataUIPart<UIDataTypes>) => void;
1324
+ /**
1325
+ * The server set or changed the thread's title while a turn streamed. Lets a header/sidebar
1326
+ * update the moment the title exists instead of refetching after the run settles.
1327
+ */
1328
+ onTitle?: (title: string) => void;
1222
1329
  /**
1223
1330
  * Track DETACHED sub-agents this conversation started: which are still working, and their answers
1224
1331
  * as they land (see {@link ChatBackground}). Default `false`.
@@ -1260,7 +1367,7 @@ interface AddToolResultArgs {
1260
1367
  * `AgentClient`. Mirrors flip's `useAdminChat`, generalized.
1261
1368
  */
1262
1369
  declare function useAgentChat(options: UseAgentChatOptions): {
1263
- sendMessage: (message?: (Omit<UIMessage<unknown, ai.UIDataTypes, ai.UITools>, "id" | "role"> & {
1370
+ sendMessage: (message?: (Omit<UIMessage<unknown, UIDataTypes, ai.UITools>, "id" | "role"> & {
1264
1371
  id?: string;
1265
1372
  role?: "system" | "user" | "assistant";
1266
1373
  } & {
@@ -1313,15 +1420,15 @@ declare function useAgentChat(options: UseAgentChatOptions): {
1313
1420
  }) => Promise<void>;
1314
1421
  regenerate: () => void;
1315
1422
  id: string;
1316
- setMessages: (messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[] | ((messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[]) => UIMessage<unknown, ai.UIDataTypes, ai.UITools>[])) => void;
1423
+ setMessages: (messages: UIMessage<unknown, UIDataTypes, ai.UITools>[] | ((messages: UIMessage<unknown, UIDataTypes, ai.UITools>[]) => UIMessage<unknown, UIDataTypes, ai.UITools>[])) => void;
1317
1424
  error: Error | undefined;
1318
1425
  stop: () => Promise<void>;
1319
1426
  status: ai.ChatStatus;
1320
- messages: UIMessage<unknown, ai.UIDataTypes, ai.UITools>[];
1427
+ messages: UIMessage<unknown, UIDataTypes, ai.UITools>[];
1321
1428
  resumeStream: (options?: ai.ChatRequestOptions) => Promise<void>;
1322
- addToolOutput: ai.ChatAddToolOutputFunction<UIMessage<unknown, ai.UIDataTypes, ai.UITools>>;
1429
+ addToolOutput: ai.ChatAddToolOutputFunction<UIMessage<unknown, UIDataTypes, ai.UITools>>;
1323
1430
  addToolApprovalResponse: ai.ChatAddToolApproveResponseFunction;
1324
1431
  clearError: () => void;
1325
1432
  };
1326
1433
 
1327
- export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AggregatedTurnUsage, type AnyToolUIPart, type ApprovalBlockOptions, type AutocompleteInputProps, type AutocompleteItem, type AutocompleteListboxProps, type AutocompleteOptionProps, type AutocompleteSource, type BackgroundRun, type BuildBlocksOptions, type CancelResult, type ChatBackground, ChatInput, type ChatInputProps, type ChatStatus, type ChatTranscript, type CompletionEdit, type ComposerAutocomplete, DEFAULT_SPEECH_LANG, type ElicitationBlockOptions, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageItemView, type MessageItemViewProps, MessageList, type MessageListClassNames, type MessageListProps, type MessageUsageInfo, type QuotaToday, type RenderFilesFn, type RenderReasoningFn, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, type RetrievedPassage, SPEECH_LANGUAGES, type SettleAction, type SkillSuggestionData, type SkillsSourceOptions, type SpeechLanguage, type SpeechRecognitionHook, type StickToBottom, type StickToBottomOptions, type StoredTurnMetadata, type ThreadPatch, type TimestampInfo, type TranscriptActionState, type TranscriptBlock, type TranscriptCopyState, type TranscriptEditState, type TranscriptElicitationBlock, type TranscriptElicitationOutcome, type TranscriptFile, type TranscriptFilesBlock, type TranscriptItem, type TranscriptItemOptions, type TranscriptQuestion, type TranscriptQuestionOption, type TranscriptReasoningBlock, type TranscriptSettleState, type TranscriptSource, type TranscriptSourcesBlock, type TranscriptStopState, type TranscriptTextBlock, type TranscriptToolBlock, type TranscriptToolCall, type TranscriptWindow, type TriggerMatch, type TriggerPosition, type UsageSummary, type UseAgentChatOptions, type UseChatTranscriptOptions, type UseComposerAutocompleteOptions, type UseSpeechRecognitionOptions, type UseTranscriptItemOptions, applyCompletion, backgroundRunsFromThread, buildTranscriptBlocks, createSkillsSource, describeTimestamp, describeUsage, extractMessageText, filterAutocompleteItems, findActiveTrigger, formatRelativeTime, loadStoredSpeechLang, persistSpeechLang, storedMessageToUiMessage, storedThreadToUiMessages, useAgentChat, useChatTranscript, useComposerAutocomplete, useSpeechRecognition, useStickToBottom, useTranscriptItem };
1434
+ export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AggregatedTurnUsage, type AnyToolUIPart, type ApprovalBlockOptions, type AutocompleteInputProps, type AutocompleteItem, type AutocompleteListboxProps, type AutocompleteOptionProps, type AutocompleteSource, type BackgroundRun, type BuildBlocksOptions, type CancelResult, type ChatBackground, ChatInput, type ChatInputProps, type ChatStatus, type ChatTranscript, type CompletionEdit, type ComposerAutocomplete, DEFAULT_SPEECH_LANG, type ElicitationBlockOptions, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageItemView, type MessageItemViewProps, MessageList, type MessageListClassNames, type MessageListProps, type MessageUsageInfo, type QuotaToday, type RenderFilesFn, type RenderReasoningFn, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, type RenderUiFn, type RetrievedPassage, SPEECH_LANGUAGES, type SettleAction, type SkillSuggestionData, type SkillsSourceOptions, type SpeechLanguage, type SpeechRecognitionHook, type StickToBottom, type StickToBottomOptions, type StoredTurnMetadata, type ThreadPatch, type TimestampInfo, type TranscriptActionState, type TranscriptApproval, type TranscriptBlock, type TranscriptCopyState, type TranscriptEditState, type TranscriptElicitationBlock, type TranscriptElicitationOutcome, type TranscriptFile, type TranscriptFilesBlock, type TranscriptItem, type TranscriptItemOptions, type TranscriptQuestion, type TranscriptQuestionOption, type TranscriptReasoningBlock, type TranscriptSettleState, type TranscriptSource, type TranscriptSourcesBlock, type TranscriptStopState, type TranscriptTextBlock, type TranscriptToolBlock, type TranscriptToolCall, type TranscriptUiBlock, type TranscriptWindow, type TriggerMatch, type TriggerPosition, type UsageSummary, type UseAgentChatOptions, type UseChatTranscriptOptions, type UseComposerAutocompleteOptions, type UseElapsedOptions, type UseSpeechRecognitionOptions, type UseTranscriptItemOptions, applyCompletion, backgroundRunsFromThread, buildTranscriptBlocks, createSkillsSource, describeTimestamp, describeUsage, extractMessageText, filterAutocompleteItems, findActiveTrigger, formatElapsed, formatRelativeTime, loadStoredSpeechLang, persistSpeechLang, readReasoningMs, storedMessageToUiMessage, storedThreadToUiMessages, useAgentChat, useChatTranscript, useComposerAutocomplete, useElapsed, useSpeechRecognition, useStickToBottom, useTranscriptItem };