@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/README.md CHANGED
@@ -111,8 +111,8 @@ function Chat() {
111
111
  | On the instance | What it is |
112
112
  |---|---|
113
113
  | `items` | The mounted window, oldest first. Each item carries `blocks`, `text`, `usage`, `timestamp`, `isStreaming`, `isLastAssistant`, and its action machines. |
114
- | `items[i].blocks` | `{ kind: 'text' \| 'reasoning' \| 'tools' \| 'sources' \| 'elicitation' }`. A `tools` block is a run of CONSECUTIVE tool parts — any other part between two calls (including a `step-start` marker) ends the run. A `reasoning` block carries `isOpen`/`toggle`, open while it streams. A `sources` block appears only under `sources: true`, an `elicitation` block only under `onAnswer` — see below. |
115
- | `items[i].blocks[n]` (`tools`) | Also carries `calls`: the same parts, each with `{ toolCallId, name, isAwaitingApproval, approve, reject, error }`. |
114
+ | `items[i].blocks` | `{ kind: 'text' \| 'reasoning' \| 'tools' \| 'sources' \| 'elicitation' \| 'ui' }`. A `ui` block is a component the server pushed (`{ id, component, props, version }`) — look `component` up in your own registry. A `tools` block is a run of CONSECUTIVE tool parts — any other part between two calls (including a `step-start` marker) ends the run. A `reasoning` block carries `isOpen`/`toggle`, open while it streams. A `sources` block appears only under `sources: true`, an `elicitation` block only under `onAnswer` — see below. |
115
+ | `items[i].blocks[n]` (`tools`) | Also carries `calls`: the same parts, each with `{ toolCallId, name, toolKind, parentId, children, approval, isAwaitingApproval, approve, reject, error }`, and `roots`: the same calls as a tree (a call nested under another by the stream's `parentId` sits in its parent's `children`). `approval` is `{ approver, expiresAt, reason }` when the runner said who has to decide, else `null`. |
116
116
  | `items[i].copy` | `{ available, copied, copy() }` — `copied` flashes for `copyResetMs` (default 1500). |
117
117
  | `items[i].edit` | `{ available, isEditing, draft, canSave, start(), cancel(), setDraft(), save(), getTextareaProps() }`. The prop-getter focuses with the caret at the end, saves on Enter, cancels on Escape. |
118
118
  | `items[i].fork` / `.regenerate` | `{ available, run() }`. Regenerate is offered on the last assistant message only. |
@@ -324,6 +324,10 @@ function CustomChat({ threadId }: { threadId?: string }) {
324
324
  onRunSettled: ({ status }) => {
325
325
  if (status === 'completed') refetchThreadList();
326
326
  },
327
+ // The server named (or renamed) the thread mid-stream — update the header now.
328
+ onTitle: (title) => setHeaderTitle(title),
329
+ // Every data part as it arrives: pushed `data-ui` components, `data-approval-requested`, …
330
+ onData: (part) => analytics.track(part.type),
327
331
  });
328
332
 
329
333
  return (
@@ -381,6 +385,20 @@ const transport = new AgentChatTransport({
381
385
  const chat = useChat({ transport });
382
386
  ```
383
387
 
388
+ Beyond text, reasoning and tool calls, the transport maps the rest of the stream vocabulary to AI SDK
389
+ data parts, which `useChat`'s `onData` (and `useAgentChat({ onData })`) sees as they arrive:
390
+
391
+ | Stream frame | Becomes |
392
+ |---|---|
393
+ | `ui` | a `data-ui` part keyed by the component id (a repeat id updates it in place) |
394
+ | `approval-requested` | a `data-approval-requested` part keyed by the call id, plus the SDK's native approval request — the tool part moves to `state: 'approval-requested'` |
395
+ | `title` / `cancelled` | transient `data-title` / `data-cancelled` (never stored on the message); `useAgentChat({ onTitle })` |
396
+ | a kind this version does not know | a `data-<kind>` part — forwarded, never dropped |
397
+
398
+ `parentId` on a tool frame rides the part's `toolMetadata` next to `toolKind`. The full wire
399
+ contract — for a backend that serves these routes without this library's loop — is
400
+ [docs/stream-protocol.md](../../docs/stream-protocol.md).
401
+
384
402
  ### Loading persisted history
385
403
 
386
404
  A reloaded thread's `StoredMessage[]` (from `AgentClient.getThread`) needs converting to `UIMessage[]`
@@ -399,6 +417,8 @@ const initialMessages = storedThreadToUiMessages(detail.messages);
399
417
  calls persists as "thinking…" + tool calls, then a separate final-answer row) into ONE `UIMessage` per
400
418
  conversational turn, matching how the live stream renders — and stamps `metadata.usage` on any turn it
401
419
  merged. For a single already-atomic row, `storedMessageToUiMessage` maps it 1:1 with no merging.
420
+ Persisted reasoning comes back as a `reasoning` part before the text (with its duration), and
421
+ persisted pushed components as `data-ui` parts, so a reloaded thread shows what the live one did.
402
422
 
403
423
  ### Attachments and the raw client
404
424
 
@@ -434,6 +454,19 @@ Reasoning frames arrive as `reasoning` parts on the message (the transport maps
434
454
  each run behind a disclosure toggle — open while it streams, folded once the answer lands — with
435
455
  `renderReasoning` and `reasoningLabel` slots to override the body and the label.
436
456
 
457
+ Thinking is timed and persisted: the backend reports each step's `reasoningMs` on `step-finish`, the
458
+ transport stamps it on the reasoning part, and the stored message keeps it, so a reasoning block's
459
+ `durationMs` reads the same live and after a reload. `useElapsed(running)` is the headless ticker
460
+ for while it still streams, and `formatElapsed(ms)` the label:
461
+
462
+ ```tsx
463
+ function ThoughtFor({ block }: { block: TranscriptReasoningBlock }) {
464
+ const elapsed = useElapsed(block.isStreaming);
465
+ const ms = block.isStreaming ? elapsed : block.durationMs;
466
+ return ms === null ? null : <span>Thought for {formatElapsed(ms)}</span>;
467
+ }
468
+ ```
469
+
437
470
  `ChatInput` surfaces cancel next to send:
438
471
 
439
472
  ```tsx