@dudousxd/nestjs-agent-react 0.13.0 → 0.15.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.
@@ -0,0 +1,561 @@
1
+ import { ElicitationQuestion, ElicitationInputType, ToolPresentation, ToolCatalogEntry, ToolResultField, ToolResultView, ToolPresentationTone, ElicitationInput } from '@dudousxd/nestjs-agent-core';
2
+ import { ToolUIPart, DynamicToolUIPart, UIMessage } from 'ai';
3
+
4
+ /** What a form control can hand over: a field's value, a checkbox's state, a picked date, a list. */
5
+ type RawAnswer = string | number | boolean | Date | null | undefined | readonly RawAnswer[];
6
+ /** The question fields {@link coerceAnswer} reads. A transcript question and a core one both fit. */
7
+ type CoercibleQuestion = Pick<ElicitationQuestion, 'multiple'> & {
8
+ input?: {
9
+ type: ElicitationInputType;
10
+ } | null;
11
+ };
12
+ /**
13
+ * Turn whatever a form control produced into the `string[]` the answer route takes, in the
14
+ * question's canonical form: numbers as decimals, booleans as `"true"`/`"false"`, dates as
15
+ * `YYYY-MM-DD` (a `Date` is read as the local calendar day), trimmed where whitespace means
16
+ * nothing. An empty value becomes `[]` — no answer, not an empty string. A single-choice question
17
+ * keeps the first value.
18
+ *
19
+ * Coercion never judges: pair it with {@link validateAnswer} (the same rules the server applies) to
20
+ * say what is wrong before sending.
21
+ */
22
+ declare function coerceAnswer(question: CoercibleQuestion, raw: RawAnswer): string[];
23
+
24
+ /** Tool name → how the server said it is spoken about. */
25
+ type ToolCatalog = Record<string, ToolPresentation>;
26
+ /** Index `GET <base>/tools` by tool name, keeping only the tools that declared a presentation. */
27
+ declare function toolCatalogFrom(entries: readonly ToolCatalogEntry[]): ToolCatalog;
28
+ /** Dotted-path read. `undefined` rather than a throw, so a template can outlive a field. */
29
+ declare function readPath(value: unknown, path: string): unknown;
30
+ /**
31
+ * Fill `{dotted.path}` placeholders from `context`. A placeholder with nothing behind it collapses
32
+ * along with the whitespace in front of it, so "Reading {key}" degrades to "Reading" rather than
33
+ * printing braces at a person; a filled one keeps the whitespace the author wrote ("({n} files)").
34
+ */
35
+ declare function fillTemplate(template: string, context: unknown): string;
36
+ /**
37
+ * The sentence for one call: the presentation's `running` or `done` template over the call's input.
38
+ *
39
+ * Nothing here reads the tool's name. A tool the catalog does not describe — added since the catalog
40
+ * loaded, or declared without a presentation — is narrated generically (`fallback`), because the only
41
+ * other thing available to say is its identifier.
42
+ */
43
+ declare function phraseFor(presentation: ToolPresentation | undefined, input: unknown, isSettled: boolean, fallback?: {
44
+ running: string;
45
+ done: string;
46
+ }): string;
47
+
48
+ /** One labelled reading of a `metrics` view. */
49
+ interface ResolvedReading {
50
+ label: string;
51
+ /** The value as text (`—` for nothing). */
52
+ value: string;
53
+ unit: string | null;
54
+ }
55
+ /**
56
+ * A tool's output resolved through its view into plain data a renderer draws — never the payload
57
+ * itself. `null` from {@link resolveResultView} means "draw nothing".
58
+ */
59
+ type ResolvedResultView = {
60
+ kind: 'metrics';
61
+ readings: ResolvedReading[];
62
+ } | {
63
+ kind: 'table';
64
+ columns: ToolResultField[];
65
+ rows: string[][];
66
+ empty: string | null;
67
+ } | {
68
+ kind: 'log';
69
+ lines: string[];
70
+ } | {
71
+ kind: 'note';
72
+ text: string;
73
+ };
74
+ /**
75
+ * A view for an output whose tool declared none, from its shape: an array of flat records is a
76
+ * table, an array of strings a log, a flat record a set of readings. `undefined` when nothing fits —
77
+ * a serialized payload is what a person-facing surface exists to avoid, so failing to infer must not
78
+ * fall back to one.
79
+ */
80
+ declare function inferResultView(output: unknown): ToolResultView | undefined;
81
+ /**
82
+ * Read `output` through `view` (or one inferred from its shape when `view` is omitted and `infer` is
83
+ * on). `null` when there is nothing to draw: an `elsewhere` view, an empty reading set, a missing
84
+ * array. A table whose rows array is present but empty resolves with no rows and its `empty` line.
85
+ */
86
+ declare function resolveResultView(output: unknown, view: ToolResultView | undefined, options?: {
87
+ infer?: boolean;
88
+ }): ResolvedResultView | null;
89
+
90
+ /**
91
+ * Where one call stands, as a person reads it:
92
+ * - `running` — input streaming or the tool executing;
93
+ * - `awaiting-approval` — an `action` call parked on a person;
94
+ * - `done` / `failed` / `denied` — settled. A tool that RETURNS `{ error }` has failed just as much
95
+ * as one that threw; the two arrive differently and read the same to a person.
96
+ */
97
+ type ToolCallStatus = 'running' | 'awaiting-approval' | 'done' | 'failed' | 'denied';
98
+ interface ToolCallState {
99
+ status: ToolCallStatus;
100
+ isSettled: boolean;
101
+ isFailed: boolean;
102
+ isDenied: boolean;
103
+ output: unknown;
104
+ error: string | null;
105
+ }
106
+ /** Whether the stream classified this call as an `action` (it parks for approval before it runs). */
107
+ declare function isActionCall(part: AnyToolUIPart): boolean;
108
+ declare function toolCallState(part: AnyToolUIPart): ToolCallState;
109
+ /**
110
+ * Which failed calls the model went on to correct, by call id: a call that failed and was followed,
111
+ * in the same run, by a SUCCESS of the same tool. The operator wants to know a retry happened, not
112
+ * to read every attempt. A failed read is not corrected by a successful query.
113
+ */
114
+ declare function correctedCallIds(parts: readonly AnyToolUIPart[]): Set<string>;
115
+ /** Everything a surface needs to talk about one call without naming it. */
116
+ interface ToolCallDescription {
117
+ status: ToolCallStatus;
118
+ /** "Querying orders" / "Queried orders" — or the generic fallback when the tool declared nothing. */
119
+ phrase: string;
120
+ /** The server's noun phrase for the tool; `null` when it declared no presentation. */
121
+ label: string | null;
122
+ icon: string | null;
123
+ tone: ToolPresentationTone;
124
+ detail: string | null;
125
+ /** The approval prompt, templated over the call's input; `null` when the tool declared none. */
126
+ confirm: {
127
+ title: string;
128
+ verb: string;
129
+ detail: string | null;
130
+ } | null;
131
+ /** The output read through its view, once the call is `done`; `null` otherwise or when nothing to draw. */
132
+ result: ResolvedResultView | null;
133
+ error: string | null;
134
+ presentation: ToolPresentation | null;
135
+ }
136
+ interface DescribeToolCallOptions {
137
+ /** Words for a tool the catalog does not describe. Default `Working` / `Done`. */
138
+ fallback?: {
139
+ running: string;
140
+ done: string;
141
+ };
142
+ /** Infer a result view from the output's shape when the tool declared none. Default `false`. */
143
+ inferResult?: boolean;
144
+ }
145
+ declare function describeToolCall(part: AnyToolUIPart, catalog: ToolCatalog | undefined, options?: DescribeToolCallOptions): ToolCallDescription;
146
+ /**
147
+ * Calls folded under one key — "Database query ×3" — with the state that matters most across them.
148
+ */
149
+ interface ToolActivityGroup {
150
+ key: string;
151
+ /** The server's label for the group's tool; `null` when it declared no presentation. */
152
+ label: string | null;
153
+ icon: string | null;
154
+ /** Worst-first across the calls: running › awaiting-approval › failed › denied › done. */
155
+ status: ToolCallStatus;
156
+ count: number;
157
+ /** The phrase of the group's LATEST call — what is happening now. */
158
+ phrase: string;
159
+ calls: TranscriptToolCall[];
160
+ /** Calls nested under the group's calls (all depths), when nesting was not expanded. */
161
+ innerCount: number;
162
+ }
163
+ interface GroupToolActivityOptions {
164
+ catalog?: ToolCatalog;
165
+ /**
166
+ * What makes two calls "the same activity". Default: the tool's presentation `label`, else its
167
+ * name. Flippy-style source grouping passes its own (e.g. `github:search`).
168
+ */
169
+ keyOf?: (call: TranscriptToolCall, presentation: ToolPresentation | undefined) => string;
170
+ /**
171
+ * Replace a call that has nested calls with those calls (recursively), so the activity reads as
172
+ * what the code-mode run DID rather than "ran code". Default `false`: groups are built over the
173
+ * top-level calls, and `innerCount` says how much ran beneath them.
174
+ */
175
+ expandNested?: boolean;
176
+ /** Drop failed calls the model corrected with a later success of the same tool. Default `false`. */
177
+ hideCorrected?: boolean;
178
+ fallback?: {
179
+ running: string;
180
+ done: string;
181
+ };
182
+ }
183
+ /**
184
+ * Fold a tool run into activity groups, in order of first appearance. Pass `block.roots` (the
185
+ * transcript's call tree) so nesting is honoured; a flat `block.calls` works too and simply has
186
+ * nothing nested.
187
+ */
188
+ declare function groupToolActivity(calls: readonly TranscriptToolCall[], options?: GroupToolActivityOptions): ToolActivityGroup[];
189
+
190
+ /** A tool UI part on a `UIMessage` — a static `tool-*` part or the `dynamic-tool` part. */
191
+ type AnyToolUIPart = ToolUIPart | DynamicToolUIPart;
192
+ /**
193
+ * The AI SDK's chat status, plus `reconnecting`: a turn is in flight and its stream dropped, and the
194
+ * transport is re-attaching (`useAgentChat`). A busy status, like `streaming`.
195
+ */
196
+ type ChatStatus = 'ready' | 'submitted' | 'streaming' | 'reconnecting' | 'error';
197
+ /** Server-aggregated usage for an assistant turn. */
198
+ interface MessageUsageInfo {
199
+ inputTokens: number;
200
+ outputTokens: number;
201
+ /**
202
+ * `null` when no price is on record for the model that ran the turn. Distinct from `0`, which is
203
+ * a turn that genuinely cost nothing — printing `$0` for an unpriced turn states a number the
204
+ * store never had.
205
+ */
206
+ costUsd: number | null;
207
+ }
208
+ /** A run of contiguous prose. `isStreaming` is the PART's own state, not the message's. */
209
+ interface TranscriptTextBlock {
210
+ kind: 'text';
211
+ key: string;
212
+ text: string;
213
+ isStreaming: boolean;
214
+ }
215
+ /** One file on a message — an uploaded attachment, or one the model produced. */
216
+ interface TranscriptFile {
217
+ /** Presigned or otherwise directly fetchable. Display-only: the model reads its own copy. */
218
+ url: string;
219
+ mediaType: string;
220
+ filename: string | null;
221
+ /** `image/*`, which a renderer can show inline rather than as a link. */
222
+ isImage: boolean;
223
+ }
224
+ /** A run of contiguous files. Grouped so a renderer can lay several out as one strip. */
225
+ interface TranscriptFilesBlock {
226
+ kind: 'files';
227
+ key: string;
228
+ files: TranscriptFile[];
229
+ }
230
+ /**
231
+ * A run of contiguous reasoning. Carries its own disclosure state because a thread can hold many
232
+ * of them and each is toggled independently; `isOpen` defaults to `isStreaming` (thinking is worth
233
+ * watching live, worth folding away once answered) until `toggle` is called for that run.
234
+ */
235
+ interface TranscriptReasoningBlock {
236
+ kind: 'reasoning';
237
+ key: string;
238
+ text: string;
239
+ isStreaming: boolean;
240
+ /**
241
+ * How long the model thought, in ms — the backend's measurement once the run closed (live) or as
242
+ * persisted (reloaded). `null` while it still streams, or when nothing was recorded: pair it with
243
+ * `useElapsed(block.isStreaming)` for a ticking label (`block.durationMs ?? elapsed`).
244
+ */
245
+ durationMs: number | null;
246
+ isOpen: boolean;
247
+ toggle: (open?: boolean) => void;
248
+ }
249
+ /**
250
+ * A decision a run is waiting on a human for, and which one a parked call is currently sending.
251
+ *
252
+ * One call settles one way at a time, and WHICH one is what lets a surface report progress on the
253
+ * affordance the person pressed instead of on all of them.
254
+ */
255
+ type SettleAction = 'approve' | 'reject' | 'answer' | 'skip';
256
+ /** One such decision: whether it can be made, whether it is on its way, and how to send it. */
257
+ interface TranscriptSettleState {
258
+ available: boolean;
259
+ /** True from the click until the run resumes and settles the call. */
260
+ isSubmitting: boolean;
261
+ run: () => void;
262
+ }
263
+ /** How an approval stands. */
264
+ type TranscriptApprovalStatus = 'pending' | 'approved' | 'rejected' | 'expired';
265
+ /**
266
+ * Who has to settle a parked call, until when, and — once it settled — how. From the stream's
267
+ * `approval-requested` / `approval-settled` frames, or the same parts a reloaded thread carries.
268
+ * `null` on a call whose runner never said (the call can still be awaiting approval).
269
+ */
270
+ interface TranscriptApproval {
271
+ /** Open vocabulary the host defines: `'requester'`, `'admin'`, a role… */
272
+ approver: string;
273
+ /** ISO-8601; `null` when the request never lapses. Pair with `useApprovalCountdown`. */
274
+ expiresAt: string | null;
275
+ /** Why the call needs a person, when the runner said. */
276
+ reason: string | null;
277
+ /**
278
+ * `pending` until a settlement arrives; then what it said. Falls back to the call's own state
279
+ * (denied → `rejected`, an output → `approved`) for a runner that streams no settlement.
280
+ */
281
+ status: TranscriptApprovalStatus;
282
+ /** The approval also covers later calls of this tool in this thread. */
283
+ remember: boolean;
284
+ /** Opaque ref of who decided; `null` while pending, on an expiry, or when the runner did not say. */
285
+ decidedBy: string | null;
286
+ /** The surface the decision came through (`'web'`, `'slack'`, `'remembered'`, …). */
287
+ decidedVia: string | null;
288
+ /** What the person said when declining. */
289
+ decisionReason: string | null;
290
+ }
291
+ /** What an approval can carry beyond yes. */
292
+ interface ApproveOptions {
293
+ /** Approve later calls of the same tool in the same thread without asking again. */
294
+ remember?: boolean;
295
+ }
296
+ /** One call in a tool run, with whatever human decision it is parked on. */
297
+ interface TranscriptToolCall {
298
+ part: AnyToolUIPart;
299
+ toolCallId: string;
300
+ name: string;
301
+ /** `read` / `action` as the stream classified the call; `null` when the backend did not say. */
302
+ toolKind: string | null;
303
+ /** The call this one ran inside (a code-mode `execute`, a delegated agent); `null` for a top-level call. */
304
+ parentId: string | null;
305
+ /**
306
+ * Calls nested under this one within the same block, in stream order. Always empty for a call
307
+ * nothing names as its parent.
308
+ */
309
+ children: TranscriptToolCall[];
310
+ /** Who has to decide, when the runner said so. See {@link TranscriptApproval}. */
311
+ approval: TranscriptApproval | null;
312
+ /**
313
+ * How to talk about this call without naming it — status, phrase, label, icon, approval prompt,
314
+ * resolved result — from the server's presentation when `toolCatalog` was given, generic otherwise.
315
+ */
316
+ description: ToolCallDescription;
317
+ /**
318
+ * Parked on a person. An `action` tool's input lands and its output never follows on its own —
319
+ * the loop waits for an approval between the two — so a settled-looking card that never settles
320
+ * IS the pending approval.
321
+ */
322
+ isAwaitingApproval: boolean;
323
+ /** `run({ remember: true })` approves this tool for the rest of the thread. */
324
+ approve: TranscriptSettleState & {
325
+ run: (options?: ApproveOptions) => void;
326
+ };
327
+ reject: TranscriptSettleState;
328
+ /** A failed decision — the call is still parked, so the affordance stays live. */
329
+ error: string | null;
330
+ }
331
+ /**
332
+ * A run of CONSECUTIVE tool parts, grouped so a UI can collapse "5 tools ran" into one affordance.
333
+ * Any non-tool part between two tool parts ends the run — including the AI SDK's `step-start`
334
+ * markers, so tools from different steps never merge into one group.
335
+ */
336
+ interface TranscriptToolBlock {
337
+ kind: 'tools';
338
+ key: string;
339
+ parts: AnyToolUIPart[];
340
+ /** The same calls, each with its human-decision state. Same order as `parts`. */
341
+ calls: TranscriptToolCall[];
342
+ /**
343
+ * The same calls as a tree: every call whose parent is NOT in this block, each carrying its
344
+ * nested calls in `children`. Equal to `calls` when nothing is nested.
345
+ */
346
+ roots: TranscriptToolCall[];
347
+ /**
348
+ * The run folded into activity groups ("Database query ×3"), over `roots`, keyed by each tool's
349
+ * presentation label (else its name). For another grouping, call `groupToolActivity` yourself.
350
+ */
351
+ activity: ToolActivityGroup[];
352
+ }
353
+ /**
354
+ * A component the server pushed into the message (the stream's `ui` frame, or a persisted
355
+ * `data-ui` part). Rendered by looking `component` up in the host's own registry.
356
+ */
357
+ interface TranscriptUiBlock {
358
+ kind: 'ui';
359
+ key: string;
360
+ /** The component's identity within the message. */
361
+ id: string;
362
+ component: string;
363
+ props: Record<string, unknown>;
364
+ /** Schema version of `props`; `null` when the server did not stamp one. */
365
+ version: number | null;
366
+ /** The tool call that pushed it (`ctx.emitUi`); `null` for a component pushed outside a tool. */
367
+ toolCallId: string | null;
368
+ }
369
+ /** One choice a question offers, with its live selection state. */
370
+ interface TranscriptQuestionOption {
371
+ value: string;
372
+ label: string;
373
+ /** A single character the request suggested as a shortcut; `null` when it suggested none. */
374
+ hotkey: string | null;
375
+ isSelected: boolean;
376
+ /** Pre-picked by the agent — what an untouched question submits as. */
377
+ isDefault: boolean;
378
+ /** Single choice: replaces the selection. Multiple: adds or removes this value. */
379
+ select: () => void;
380
+ }
381
+ /** One question of a set, numbered against the whole. */
382
+ interface TranscriptQuestion {
383
+ id: string;
384
+ prompt: string;
385
+ /** A line of help under the prompt; `null` when the request gave none. */
386
+ description: string | null;
387
+ /**
388
+ * How the answer is typed — `{ type, placeholder?, required?, min?, max?, pattern? }` — or `null`
389
+ * for a plain pick from `options`. Render the control from `input.type`; `select` still picks
390
+ * from `options`.
391
+ */
392
+ input: ElicitationInput | null;
393
+ multiple: boolean;
394
+ /** 1-based. The request carries every question up front, so "Question 1 of N" is honest. */
395
+ position: number;
396
+ options: TranscriptQuestionOption[];
397
+ selected: string[];
398
+ /**
399
+ * The user has not touched this question, so a submission leaves it out and the server applies
400
+ * the same defaults it showed. Distinct from "selected happens to equal the defaults": only the
401
+ * first is recorded as `defaulted` rather than as a choice the user made.
402
+ */
403
+ isPristine: boolean;
404
+ /** The first selected value, or `''` — what a single text/number/date field shows. */
405
+ value: string;
406
+ /**
407
+ * Set a typed answer from whatever the control produced (a string, a number, a checkbox's
408
+ * boolean, a `Date`, a list); coerced to the question's canonical strings with `coerceAnswer`.
409
+ * `null`/`''` clears it. Does nothing once the set settled.
410
+ */
411
+ setValue: (raw: RawAnswer) => void;
412
+ /**
413
+ * Why the current selection would be refused (the server's own rules — `validateAnswer`), or
414
+ * `null`. A pristine required question with no default reads `requires an answer`; show it once
415
+ * the user tried to submit, or right away — the model does not decide that for you.
416
+ */
417
+ error: string | null;
418
+ }
419
+ /** How a question set settled, once it did. */
420
+ interface TranscriptElicitationOutcome {
421
+ answers: Record<string, string[]>;
422
+ /** The user declined to answer and let the agent proceed on its own picks. */
423
+ skipped: boolean;
424
+ /** Questions filled from their own defaults rather than by the human. */
425
+ defaulted: string[];
426
+ /** The questions against the chosen labels, as the model read them back. */
427
+ summary: string | null;
428
+ }
429
+ /**
430
+ * A question set the run put to the user, parked until someone settles it — the intake an agent
431
+ * declares and the model's own `ask` produce the same block, because the loop streams the same
432
+ * frame for both.
433
+ */
434
+ interface TranscriptElicitationBlock {
435
+ kind: 'elicitation';
436
+ key: string;
437
+ /** The parked tool call — what `answer`/`skip` route by. */
438
+ toolCallId: string;
439
+ preamble: string | null;
440
+ questions: TranscriptQuestion[];
441
+ questionCount: number;
442
+ /** Still waiting on a human. */
443
+ isPending: boolean;
444
+ /** Every question's current selection is acceptable (no question has an `error`). */
445
+ isValid: boolean;
446
+ outcome: TranscriptElicitationOutcome | null;
447
+ /** A failed submission — the run is still parked, so the form stays live. */
448
+ error: string | null;
449
+ answer: TranscriptSettleState;
450
+ skip: TranscriptSettleState;
451
+ }
452
+ /** One retrieved passage, as it rides a retrieval tool call's output. Mirrors core's `Passage`. */
453
+ interface RetrievedPassage {
454
+ id: string;
455
+ text: string;
456
+ score: number;
457
+ /** Citation-facing origin — a document title, URL or row id. */
458
+ source?: string;
459
+ metadata?: Record<string, unknown>;
460
+ }
461
+ /** The passages of one origin, folded together so a citation line reads once per source. */
462
+ interface TranscriptSource {
463
+ /** The `source` string the retriever stamped, falling back to the passage id when it stamped none. */
464
+ id: string;
465
+ label: string;
466
+ passageCount: number;
467
+ /** Highest relevance among this source's passages; the scale is the retriever's. */
468
+ topScore: number;
469
+ passages: RetrievedPassage[];
470
+ }
471
+ /**
472
+ * The provenance behind an answer: a run of CONSECUTIVE retrieval tool parts, aggregated by origin.
473
+ * Detection is structural — a tool output shaped `{ passages: [{ id, text, ... }] }` — because the
474
+ * tool's NAME is not fixed: inject-mode retrieval persists as `retrieve`, and `createRetrievalTool`
475
+ * lets the host rename `search_knowledge` to anything.
476
+ */
477
+ interface TranscriptSourcesBlock {
478
+ kind: 'sources';
479
+ key: string;
480
+ /** What was searched, when the tool call recorded a `{ query }` input. */
481
+ query: string | null;
482
+ sources: TranscriptSource[];
483
+ passageCount: number;
484
+ }
485
+ type TranscriptBlock = TranscriptTextBlock | TranscriptFilesBlock | TranscriptReasoningBlock | TranscriptToolBlock | TranscriptSourcesBlock | TranscriptElicitationBlock | TranscriptUiBlock;
486
+ /** Where a question set's selection state is held, and where its settlement is sent. */
487
+ interface ElicitationBlockOptions {
488
+ /** The values a question is showing, or `undefined` while the user has not touched it. */
489
+ picked: (toolCallId: string, questionId: string) => string[] | undefined;
490
+ pick: (toolCallId: string, questionId: string, values: string[]) => void;
491
+ canAnswer: boolean;
492
+ canSkip: boolean;
493
+ answer: (toolCallId: string) => void;
494
+ skip: (toolCallId: string) => void;
495
+ /** Which decision this call is sending, or `null` for none. */
496
+ submitting: (toolCallId: string) => SettleAction | null;
497
+ errorOf: (toolCallId: string) => string | null;
498
+ }
499
+ /** Where an approval decision is sent, and what the last one did. */
500
+ interface ApprovalBlockOptions {
501
+ canApprove: boolean;
502
+ canReject: boolean;
503
+ approve: (toolCallId: string, options?: ApproveOptions) => void;
504
+ reject: (toolCallId: string) => void;
505
+ /** Which decision this call is sending, or `null` for none. */
506
+ submitting: (toolCallId: string) => SettleAction | null;
507
+ errorOf: (toolCallId: string) => string | null;
508
+ }
509
+ interface BuildBlocksOptions {
510
+ /** Disclosure lookup for a reasoning run; `isStreaming` is the fallback when untouched. */
511
+ isReasoningOpen: (key: string, isStreaming: boolean) => boolean;
512
+ toggleReasoning: (key: string, open?: boolean) => void;
513
+ /**
514
+ * Lift retrieval tool parts out of the tool run into a `sources` block. Off by default, so a
515
+ * renderer wired to draw tool cards keeps receiving them as the tool calls they are.
516
+ */
517
+ sources?: boolean;
518
+ /**
519
+ * Lift a question set out of the tool run into an `elicitation` block. Omitted → it stays a tool
520
+ * card, which is all a host that has nowhere to send an answer could render anyway.
521
+ */
522
+ elicitation?: ElicitationBlockOptions;
523
+ /** Wire approve/reject onto the calls parked on a human. Omitted → they are reported, not actionable. */
524
+ approval?: ApprovalBlockOptions;
525
+ /** Server-declared tool presentations (`useToolCatalog`), for each call's `description`. */
526
+ toolCatalog?: ToolCatalog;
527
+ }
528
+ /**
529
+ * Walk a message's parts into renderable blocks, buffering consecutive tool parts into one run and
530
+ * consecutive files into one strip. A `data-ui` part becomes a `ui` block. A
531
+ * `data-approval-requested` part is metadata about a call — it is folded into that call's
532
+ * `approval` and takes no position of its own. Other `data-*` parts are dropped rather than guessed
533
+ * at, but they still terminate a tool run — their position in the transcript is meaningful even
534
+ * when their content is not modelled here.
535
+ */
536
+ declare function buildTranscriptBlocks(message: UIMessage, options: BuildBlocksOptions): TranscriptBlock[];
537
+ /** The message's prose, joined for the clipboard. Reasoning is excluded — it is not the answer. */
538
+ declare function extractMessageText(parts: UIMessage['parts'] | undefined): string;
539
+ interface UsageSummary extends MessageUsageInfo {
540
+ totalTokens: number;
541
+ /** `$0`, `$0.0123`, `$0.012`, `$1.23` — precision follows magnitude so sub-cent turns stay legible. */
542
+ costLabel: string;
543
+ tokensLabel: string;
544
+ }
545
+ declare function describeUsage(usage: MessageUsageInfo): UsageSummary;
546
+ interface TimestampInfo {
547
+ iso: string;
548
+ date: Date;
549
+ /** "just now" / "5 min. ago" / "Mar 3" — see {@link formatRelativeTime}. */
550
+ relative: string;
551
+ absolute: string;
552
+ }
553
+ /** `null` for an unparseable stamp, so a bad `created_at` renders nothing instead of "Invalid Date". */
554
+ declare function describeTimestamp(createdAt: string | null | undefined): TimestampInfo | null;
555
+ /**
556
+ * "just now" within ±10s (server `created_at` can land a few ms ahead of the client clock), a
557
+ * relative phrase up to a week, then a calendar date. Uses Intl.RelativeTimeFormat — no date-fns.
558
+ */
559
+ declare function formatRelativeTime(date: Date): string;
560
+
561
+ export { resolveResultView as $, type ApproveOptions as A, type BuildBlocksOptions as B, type ChatStatus as C, type DescribeToolCallOptions as D, type ElicitationBlockOptions as E, type TranscriptToolBlock as F, type GroupToolActivityOptions as G, type TranscriptToolCall as H, buildTranscriptBlocks as I, coerceAnswer as J, correctedCallIds as K, describeTimestamp as L, type MessageUsageInfo as M, describeToolCall as N, describeUsage as O, extractMessageText as P, fillTemplate as Q, type RawAnswer as R, type SettleAction as S, type TranscriptUiBlock as T, type UsageSummary as U, formatRelativeTime as V, groupToolActivity as W, inferResultView as X, isActionCall as Y, phraseFor as Z, readPath as _, type TranscriptBlock as a, toolCallState as a0, toolCatalogFrom as a1, type TimestampInfo as b, type ToolCatalog as c, type AnyToolUIPart as d, type TranscriptFile as e, type ApprovalBlockOptions as f, type CoercibleQuestion as g, type ResolvedReading as h, type ResolvedResultView as i, type RetrievedPassage as j, type ToolActivityGroup as k, type ToolCallDescription as l, type ToolCallState as m, type ToolCallStatus as n, type TranscriptApproval as o, type TranscriptApprovalStatus as p, type TranscriptElicitationBlock as q, type TranscriptElicitationOutcome as r, type TranscriptFilesBlock as s, type TranscriptQuestion as t, type TranscriptQuestionOption as u, type TranscriptReasoningBlock as v, type TranscriptSettleState as w, type TranscriptSource as x, type TranscriptSourcesBlock as y, type TranscriptTextBlock as z };
@@ -0,0 +1,86 @@
1
+ import { ComponentType, ReactNode } from 'react';
2
+
3
+ /** The `ui` frame component a composed tree is pushed under (`@dudousxd/nestjs-agent-genui`'s `GENUI_TREE_COMPONENT`). */
4
+ declare const GENUI_TREE_COMPONENT = "genui:tree";
5
+ /** One pushed component, normalized from whatever carried it (a transcript block, a `data-ui` part, a stored entry). */
6
+ interface GenerativeUIItem {
7
+ id: string;
8
+ component: string;
9
+ props: Record<string, unknown>;
10
+ version: number | null;
11
+ toolCallId: string | null;
12
+ }
13
+ /** A node of a composed tree (`genui:tree` frames): `{ type, props, children? }`. */
14
+ interface GenerativeUIElement {
15
+ type: string;
16
+ props: Record<string, unknown>;
17
+ children?: GenerativeUIElement[];
18
+ }
19
+ /**
20
+ * An app's renderer for one component: it receives the component's props spread, plus `children`
21
+ * when it is a layout node in a tree. Any React component — the library never styles anything.
22
+ */
23
+ type GenuiRenderer<P = any> = ComponentType<P & {
24
+ children?: ReactNode;
25
+ }>;
26
+ /** Component name → the app's renderer. */
27
+ type GenuiRegistry = Record<string, GenuiRenderer>;
28
+ /**
29
+ * Resolve a component the registry does not have — typically a tenant's own component, fetched for
30
+ * the exact `version` a message was rendered with. Return `null`/`undefined` for "no such
31
+ * component". May be async; results are cached per resolver, name and version.
32
+ */
33
+ type ResolveComponent = (name: string, version: number | null) => GenuiRenderer | null | undefined | Promise<GenuiRenderer | null | undefined>;
34
+ interface GenuiIssueLike {
35
+ path: (string | number)[];
36
+ message: string;
37
+ }
38
+ type ValidationLike = {
39
+ ok: true;
40
+ value: Record<string, unknown>;
41
+ } | {
42
+ ok: false;
43
+ issues: GenuiIssueLike[];
44
+ };
45
+ /**
46
+ * What the renderer needs from a catalog to validate props before drawing them. A
47
+ * `@dudousxd/nestjs-agent-genui` `Catalog` satisfies it; declared structurally so this subpath does
48
+ * not depend on that package.
49
+ */
50
+ interface GenuiCatalogLike {
51
+ has(name: string): boolean;
52
+ validate(name: string, props: unknown): Promise<ValidationLike>;
53
+ validateSync?(name: string, props: unknown): ValidationLike | undefined;
54
+ }
55
+ interface GenerativeUIOptions {
56
+ registry: GenuiRegistry;
57
+ /** Validate props against it before rendering. Components the catalog does not know render unvalidated. */
58
+ catalog?: GenuiCatalogLike;
59
+ resolveComponent?: ResolveComponent;
60
+ }
61
+ /** Why an item did not render. */
62
+ type GenerativeUIProblem = {
63
+ reason: 'unknown';
64
+ item: GenerativeUIItem;
65
+ } | {
66
+ reason: 'invalid';
67
+ item: GenerativeUIItem;
68
+ issues: GenuiIssueLike[];
69
+ } | {
70
+ reason: 'error';
71
+ item: GenerativeUIItem;
72
+ error: unknown;
73
+ };
74
+ type GenerativeUIState = {
75
+ status: 'ready';
76
+ item: GenerativeUIItem;
77
+ Component: GenuiRenderer;
78
+ props: Record<string, unknown>;
79
+ } | {
80
+ status: 'loading';
81
+ item: GenerativeUIItem;
82
+ } | ({
83
+ status: 'problem';
84
+ } & GenerativeUIProblem);
85
+
86
+ export { type GenerativeUIOptions as G, type ResolveComponent as R, type GenerativeUIItem as a, type GenerativeUIProblem as b, type GenerativeUIElement as c, type GenerativeUIState as d, GENUI_TREE_COMPONENT as e, type GenuiCatalogLike as f, type GenuiIssueLike as g, type GenuiRegistry as h, type GenuiRenderer as i };