@timbal-ai/timbal-react 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,16 +1,180 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
- import React__default, { ReactNode, FC, ComponentType, ComponentPropsWithRef, ElementType } from 'react';
4
- import { ToolCallMessagePartComponent } from '@assistant-ui/react';
5
- export { ActionBarPrimitive, ComposerPrimitive, MessagePrimitive, ThreadPrimitive, useComposerRuntime, useMessageRuntime, useThread, useThreadRuntime } from '@assistant-ui/react';
3
+ import React__default, { ReactNode, FC, ComponentType, Dispatch, ComponentPropsWithRef, ElementType } from 'react';
4
+ import { AttachmentAdapter, ToolCallMessagePartComponent } from '@assistant-ui/react';
5
+ export { ActionBarPrimitive, AssistantRuntimeProvider, AttachmentAdapter, ComposerPrimitive, MessagePrimitive, ThreadPrimitive, useComposerRuntime, useMessageRuntime, useThread, useThreadRuntime } from '@assistant-ui/react';
6
+ import { WorkforceItem, Session } from '@timbal-ai/timbal-sdk';
7
+ export { parseSSELine } from '@timbal-ai/timbal-sdk';
6
8
  import * as class_variance_authority_types from 'class-variance-authority/types';
7
9
  import { VariantProps } from 'class-variance-authority';
8
10
  import { SyntaxHighlighterProps } from '@assistant-ui/react-markdown';
9
- import { Session } from '@timbal-ai/timbal-sdk';
10
11
  import { Tooltip as Tooltip$1, Avatar as Avatar$1, Dialog as Dialog$1 } from 'radix-ui';
11
12
  import { ClassValue } from 'clsx';
12
13
 
13
- type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;
14
+ type UploadFetchFn = (url: string, options?: RequestInit) => Promise<Response>;
15
+ interface CreateDefaultAttachmentAdapterOptions {
16
+ /**
17
+ * API base path used to derive the upload URL when {@link uploadUrl} is
18
+ * omitted. Trailing slashes are stripped. Defaults to `""` (relative
19
+ * `/files/upload`).
20
+ */
21
+ baseUrl?: string;
22
+ /**
23
+ * Absolute or relative URL the adapter `POST`s the multipart upload to.
24
+ * Defaults to `${baseUrl}/files/upload`.
25
+ */
26
+ uploadUrl?: string;
27
+ /**
28
+ * Custom fetch used for the upload. Defaults to {@link authFetch}. Do not
29
+ * set `Content-Type` on multipart uploads — the boundary must be automatic.
30
+ */
31
+ fetch?: UploadFetchFn;
32
+ /**
33
+ * MIME / extension `accept` string for the file picker.
34
+ */
35
+ accept?: string;
36
+ }
37
+ /** @deprecated Use {@link CreateDefaultAttachmentAdapterOptions}. */
38
+ type CreateUploadAttachmentAdapterOptions = CreateDefaultAttachmentAdapterOptions;
39
+ declare const DEFAULT_UPLOAD_ACCEPT = "image/*,application/pdf,text/*,.md,.json,.csv,.tsv,.xlsx,.docx";
40
+ /**
41
+ * Build an `AttachmentAdapter` that uploads each file to a Timbal-style
42
+ * `/files/upload` endpoint and returns a `CompleteAttachment` whose
43
+ * `content[]` references the returned URL.
44
+ */
45
+ declare function createDefaultAttachmentAdapter({ baseUrl, uploadUrl, fetch: fetchFn, accept, }?: CreateDefaultAttachmentAdapterOptions): AttachmentAdapter;
46
+ /** @deprecated Alias of {@link createDefaultAttachmentAdapter}. */
47
+ declare const createUploadAttachmentAdapter: typeof createDefaultAttachmentAdapter;
48
+
49
+ /** Tweaks for the built-in upload adapter (see {@link createDefaultAttachmentAdapter}). */
50
+ type TimbalAttachmentsConfig = {
51
+ uploadUrl?: string;
52
+ accept?: string;
53
+ };
54
+ /**
55
+ * Enable or customise composer attachments.
56
+ *
57
+ * - `true` — built-in adapter posting to `${baseUrl}/files/upload`
58
+ * - `{ uploadUrl?, accept? }` — same adapter with overrides
59
+ * - `AttachmentAdapter` — fully custom (e.g. presigned S3)
60
+ * - `null` — disable attachments (no `+` button / dropzone wiring)
61
+ * - `undefined` — off unless legacy `attachmentsUploadUrl` / `attachmentsAccept` are set
62
+ */
63
+ type TimbalAttachmentsProp = boolean | TimbalAttachmentsConfig | AttachmentAdapter | null;
64
+ interface ResolveAttachmentAdapterOptions {
65
+ baseUrl?: string;
66
+ fetch?: CreateDefaultAttachmentAdapterOptions["fetch"];
67
+ /** @deprecated Prefer `attachments={{ uploadUrl }}` */
68
+ uploadUrl?: string;
69
+ /** @deprecated Prefer `attachments={{ accept }}` */
70
+ accept?: string;
71
+ }
72
+ /**
73
+ * Resolve the `AttachmentAdapter` (if any) for {@link TimbalRuntimeProvider}.
74
+ */
75
+ declare function resolveAttachmentAdapter(attachments: TimbalAttachmentsProp | undefined, options?: ResolveAttachmentAdapterOptions): AttachmentAdapter | undefined;
76
+
77
+ interface TextContentPart {
78
+ type: "text";
79
+ text: string;
80
+ }
81
+ interface ThinkingContentPart {
82
+ type: "thinking";
83
+ text: string;
84
+ }
85
+ /**
86
+ * A tool invocation. `argsText` accumulates from streaming `tool_use_delta`
87
+ * events; `result` is set once the matching `tool_result` arrives in the
88
+ * `OUTPUT` event.
89
+ *
90
+ * `result` is always a JSON-serializable value (string, number, object, array)
91
+ * — the runtime preserves whatever the agent returns. `resultText` is the
92
+ * text representation of the result blocks from `tool_result.content`, useful
93
+ * as a quick fallback when callers don't want to walk the structured result.
94
+ */
95
+ interface ToolCallContentPart {
96
+ type: "tool-call";
97
+ toolCallId: string;
98
+ toolName: string;
99
+ argsText: string;
100
+ result?: unknown;
101
+ resultText?: string;
102
+ status?: "running" | "complete" | "error";
103
+ }
104
+ type ContentPart = TextContentPart | ThinkingContentPart | ToolCallContentPart;
105
+ type MessageRole = "user" | "assistant";
106
+ /**
107
+ * A file attached to a user message. We carry a single `dataUrl` field
108
+ * (despite the name, it may be either a `data:<mime>;base64,<bytes>` URL
109
+ * or a real `https://...` URL returned by an upload adapter) and project
110
+ * it both onto the wire (`{type:"file", file: dataUrl}`) and onto the
111
+ * assistant-ui display layer (`ImageMessagePart` / `FileMessagePart`
112
+ * inside `attachments[].content`).
113
+ *
114
+ * Both forms are accepted by Timbal's `FileContent` factory, so the same
115
+ * field works whether the attachment was inlined as base64 or uploaded
116
+ * to object storage.
117
+ */
118
+ interface ChatAttachment {
119
+ id: string;
120
+ type: "image" | "document" | "file";
121
+ name?: string;
122
+ contentType?: string;
123
+ /**
124
+ * Either a `data:<mime>;base64,<bytes>` URL (inline) or a remote
125
+ * `https://...` URL produced by an upload-style {@link AttachmentAdapter}.
126
+ */
127
+ dataUrl: string;
128
+ }
129
+ interface ChatMessage {
130
+ id: string;
131
+ role: MessageRole;
132
+ content: ContentPart[];
133
+ /** Files attached to a user message. Empty/undefined for assistant messages. */
134
+ attachments?: ChatAttachment[];
135
+ /** Run id stamped from the top-level `START` SSE event. */
136
+ runId?: string;
137
+ }
138
+
139
+ type FetchFn$1 = (url: string, options?: RequestInit) => Promise<Response>;
140
+ interface UseTimbalStreamOptions {
141
+ workforceId: string;
142
+ baseUrl?: string;
143
+ fetch?: FetchFn$1;
144
+ /**
145
+ * When true, every parsed SSE event is `console.debug`-ed with a
146
+ * `[timbal]` prefix. Useful for diagnosing tool/artifact rendering issues
147
+ * without screen-sharing. Default: `false`.
148
+ */
149
+ debug?: boolean;
150
+ }
151
+ interface SendOptions {
152
+ attachments?: ChatAttachment[];
153
+ /** Override the parent run id resolution. Pass `null` to start a new thread. */
154
+ parentId?: string | null;
155
+ }
156
+ interface TimbalStreamApi {
157
+ messages: ChatMessage[];
158
+ isRunning: boolean;
159
+ send: (input: string, options?: SendOptions) => Promise<void>;
160
+ reload: (messageId?: string | null) => Promise<void>;
161
+ cancel: () => void;
162
+ clear: () => void;
163
+ }
164
+ /**
165
+ * Lower-level streaming hook for callers that don't want the full `<Thread>`
166
+ * UI. Exposes the internal message state plus `send`, `reload`, `cancel`, and
167
+ * `clear` actions. Use this to build custom chat surfaces while reusing the
168
+ * Timbal SSE wire format and auth-aware fetching.
169
+ */
170
+ declare function useTimbalStream({ workforceId, baseUrl, fetch: fetchFn, debug, }: UseTimbalStreamOptions): TimbalStreamApi;
171
+ /**
172
+ * Access the underlying `useTimbalStream` API from inside a component tree
173
+ * wrapped by `<TimbalRuntimeProvider>` (or `<TimbalChat>`). Useful for custom
174
+ * UIs that need direct access to messages, send, or cancel without going
175
+ * through the assistant-ui runtime.
176
+ */
177
+ declare function useTimbalRuntime(): TimbalStreamApi;
14
178
  interface TimbalRuntimeProviderProps {
15
179
  workforceId: string;
16
180
  children: ReactNode;
@@ -23,21 +187,413 @@ interface TimbalRuntimeProviderProps {
23
187
  * Custom fetch function for API calls. Defaults to `authFetch` which
24
188
  * attaches Bearer tokens from localStorage and auto-refreshes on 401.
25
189
  */
26
- fetch?: FetchFn;
190
+ fetch?: FetchFn$1;
191
+ /**
192
+ * Enable composer attachments. `true` or `{ uploadUrl?, accept? }` uses
193
+ * the built-in upload adapter (`POST` to `${baseUrl}/files/upload` by
194
+ * default). Pass a custom {@link AttachmentAdapter} for full control, or
195
+ * `null` to disable. Omitted = off (back-compat with pre-attachment chats).
196
+ */
197
+ attachments?: TimbalAttachmentsProp;
198
+ /**
199
+ * Shorthand to enable the default upload adapter with a custom endpoint.
200
+ * Equivalent to `attachments={{ uploadUrl }}` when `attachments` is omitted.
201
+ */
202
+ attachmentsUploadUrl?: string;
203
+ /**
204
+ * Shorthand MIME `accept` for the default upload adapter when `attachments`
205
+ * is omitted or `true`.
206
+ */
207
+ attachmentsAccept?: string;
208
+ /**
209
+ * Forwarded to {@link useTimbalStream}. When `true`, every parsed SSE
210
+ * event is logged via `console.debug` with a `[timbal]` prefix.
211
+ */
212
+ debug?: boolean;
213
+ }
214
+ declare function TimbalRuntimeProvider({ workforceId, children, baseUrl, fetch: fetchFn, attachments, attachmentsUploadUrl, attachmentsAccept, debug, }: TimbalRuntimeProviderProps): react_jsx_runtime.JSX.Element;
215
+
216
+ interface ComposerProps {
217
+ /** Placeholder shown in the textarea. Default: "Send a message..." */
218
+ placeholder?: string;
219
+ /**
220
+ * Show the file-attach button. Default: true. Disable when the agent has
221
+ * no use for attachments to keep the UI clean.
222
+ */
223
+ showAttachments?: boolean;
224
+ /**
225
+ * Extra content rendered inside the toolbar, to the left of the send
226
+ * button. Use for custom buttons (voice, model picker, etc).
227
+ */
228
+ toolbar?: ReactNode;
229
+ /**
230
+ * Tooltip on the send button. Default: "Send message".
231
+ */
232
+ sendTooltip?: string;
233
+ /** Disable autofocus on mount. Default: false (autofocused). */
234
+ noAutoFocus?: boolean;
235
+ /** Extra className applied to the outer composer wrapper. */
236
+ className?: string;
27
237
  }
28
- declare function TimbalRuntimeProvider({ workforceId, children, baseUrl, fetch: fetchFn, }: TimbalRuntimeProviderProps): react_jsx_runtime.JSX.Element;
238
+ /**
239
+ * Composer v2 — auto-resizing textarea, Enter-to-send / Shift+Enter newline,
240
+ * attach button on the left, send/stop on the right. Use as a top-level
241
+ * component inside `<TimbalRuntimeProvider>` (or via `<Thread components={{
242
+ * Composer }}>`).
243
+ */
244
+ declare const Composer: FC<ComposerProps>;
29
245
 
30
246
  interface ThreadSuggestion {
247
+ /** Title shown on the chip. Also sent verbatim as the user message. */
31
248
  title: string;
249
+ /** Optional secondary line. */
32
250
  description?: string;
251
+ /** Optional leading icon. */
252
+ icon?: ReactNode;
253
+ /**
254
+ * Override the prompt sent when the chip is clicked. Useful when the chip
255
+ * label is short ("Weekly recap") but the prompt should be longer.
256
+ */
257
+ prompt?: string;
258
+ }
259
+ /**
260
+ * Suggestions can be passed as a static array, a thunk that returns an
261
+ * array, or an async function for dynamic / per-user suggestions.
262
+ */
263
+ type SuggestionsSource = ThreadSuggestion[] | (() => ThreadSuggestion[] | Promise<ThreadSuggestion[]>);
264
+ interface ThreadSuggestionsProps {
265
+ suggestions?: SuggestionsSource;
266
+ /**
267
+ * Compact layout: single row, horizontally scrollable, smaller chips. Use
268
+ * inline (e.g. above the composer or after a message) where vertical
269
+ * space is tight.
270
+ */
271
+ layout?: "grid" | "row";
272
+ className?: string;
273
+ }
274
+ /**
275
+ * Render suggestion chips. Resolves both static arrays and async sources.
276
+ * On click, appends the suggestion's `prompt` (or `title` if no prompt) as
277
+ * a user message via the thread runtime.
278
+ */
279
+ declare const Suggestions: FC<ThreadSuggestionsProps>;
280
+ /**
281
+ * Resolve a `SuggestionsSource` to an array. Re-runs when the source
282
+ * identity changes. Sync arrays / sync functions resolve immediately;
283
+ * async functions stream in once the promise settles.
284
+ */
285
+ declare function useResolvedSuggestions(source?: SuggestionsSource): ThreadSuggestion[] | undefined;
286
+ /**
287
+ * Props passed to a custom `Suggestions` slot component. Replace the default
288
+ * via `<Thread components={{ Suggestions: MySuggestions }}>`.
289
+ */
290
+ interface SuggestionsSlotProps {
291
+ suggestions?: SuggestionsSource;
292
+ }
293
+ type SuggestionsComponent = ComponentType<SuggestionsSlotProps>;
294
+
295
+ /** A value that is either a literal or a binding into local state. */
296
+ type UiBindable<T> = T | {
297
+ $bind: string;
298
+ };
299
+ interface UiArtifact {
300
+ type: "ui";
301
+ title?: string;
302
+ /** Initial values for `$bind` references. */
303
+ initialState?: Record<string, unknown>;
304
+ /** Root of the node tree. */
305
+ root: UiNode;
306
+ }
307
+ interface UiNodeBase {
308
+ id?: string;
309
+ className?: string;
310
+ }
311
+ interface UiBoxNode extends UiNodeBase {
312
+ kind: "box";
313
+ /** Flex direction. Default: "col". */
314
+ direction?: "row" | "col";
315
+ /** Tailwind spacing units (gap = `gap * 0.25rem`). */
316
+ gap?: number;
317
+ align?: "start" | "center" | "end" | "stretch";
318
+ justify?: "start" | "center" | "end" | "between" | "around";
319
+ wrap?: boolean;
320
+ padding?: number;
321
+ children?: UiNode[];
322
+ }
323
+ interface UiTextNode extends UiNodeBase {
324
+ kind: "text";
325
+ value: UiBindable<string | number>;
326
+ muted?: boolean;
327
+ size?: "xs" | "sm" | "base" | "lg";
328
+ weight?: "normal" | "medium" | "semibold" | "bold";
329
+ }
330
+ interface UiHeadingNode extends UiNodeBase {
331
+ kind: "heading";
332
+ value: UiBindable<string>;
333
+ level?: 1 | 2 | 3 | 4;
334
+ }
335
+ interface UiBadgeNode extends UiNodeBase {
336
+ kind: "badge";
337
+ value: UiBindable<string>;
338
+ tone?: "default" | "primary" | "success" | "warn" | "danger";
339
+ }
340
+ interface UiButtonNode extends UiNodeBase {
341
+ kind: "button";
342
+ label: UiBindable<string>;
343
+ variant?: "default" | "outline" | "ghost" | "secondary" | "destructive" | "link";
344
+ size?: "sm" | "default" | "lg";
345
+ disabled?: UiBindable<boolean>;
346
+ onClick?: UiAction | UiAction[];
347
+ }
348
+ /**
349
+ * Two-state toggle. `binding` is a dotted state path; clicking flips the
350
+ * stored boolean. Optional `onChange` fires *after* the state flip.
351
+ */
352
+ interface UiToggleNode extends UiNodeBase {
353
+ kind: "toggle";
354
+ label?: UiBindable<string>;
355
+ binding: string;
356
+ onChange?: UiAction | UiAction[];
357
+ }
358
+ /**
359
+ * Numeric range input. `binding` is a dotted state path; the slider reads and
360
+ * writes the stored number. Optional `onChange` fires after each commit.
361
+ */
362
+ interface UiSliderNode extends UiNodeBase {
363
+ kind: "slider";
364
+ binding: string;
365
+ min?: number;
366
+ max?: number;
367
+ step?: number;
368
+ label?: UiBindable<string>;
369
+ /** Show the current value next to the slider. Default: true. */
370
+ showValue?: boolean;
371
+ onChange?: UiAction | UiAction[];
372
+ }
373
+ interface UiTooltipNode extends UiNodeBase {
374
+ kind: "tooltip";
375
+ content: UiBindable<string>;
376
+ side?: "top" | "bottom" | "left" | "right";
377
+ child: UiNode;
378
+ }
379
+ /**
380
+ * Wrap any node to make it draggable. Drag is purely visual — release fires
381
+ * `onDragEnd`. If `snapBack` (default) the child returns to its origin.
382
+ */
383
+ interface UiDraggableNode extends UiNodeBase {
384
+ kind: "draggable";
385
+ child: UiNode;
386
+ axis?: "x" | "y" | "both";
387
+ snapBack?: boolean;
388
+ onDragEnd?: UiAction | UiAction[];
389
+ }
390
+ /**
391
+ * Escape hatch: a host-app-registered component. The host registers a
392
+ * renderer by name via `UiCustomNodeRegistryProvider`; props are passed
393
+ * through after binding resolution and children render recursively.
394
+ */
395
+ interface UiCustomNode extends UiNodeBase {
396
+ kind: "custom";
397
+ name: string;
398
+ props?: Record<string, unknown>;
399
+ children?: UiNode[];
33
400
  }
401
+ type UiNode = UiBoxNode | UiTextNode | UiHeadingNode | UiBadgeNode | UiButtonNode | UiToggleNode | UiSliderNode | UiTooltipNode | UiDraggableNode | UiCustomNode;
402
+ type UiAction =
403
+ /** Append a user message to the active thread. */
404
+ {
405
+ kind: "message";
406
+ text: UiBindable<string>;
407
+ }
408
+ /** Set a path in local state to a (possibly bound) value. */
409
+ | {
410
+ kind: "set";
411
+ path: string;
412
+ value: UiBindable<unknown>;
413
+ }
414
+ /** Flip a boolean at the given local-state path. */
415
+ | {
416
+ kind: "toggle";
417
+ path: string;
418
+ }
419
+ /** Bubble a named event to the host app via `UiEventProvider`. */
420
+ | {
421
+ kind: "emit";
422
+ name: string;
423
+ payload?: unknown;
424
+ };
425
+ declare function isUiBinding(value: unknown): value is {
426
+ $bind: string;
427
+ };
428
+
429
+ interface ChartArtifact {
430
+ type: "chart";
431
+ /** Chart kind. Renderer maps these to the underlying lib (e.g. recharts). */
432
+ chartType?: "bar" | "line" | "area" | "pie";
433
+ /** Optional title rendered above the chart. */
434
+ title?: string;
435
+ /** Array of data points. Keys map to series via `dataKey` / `xKey`. */
436
+ data: Array<Record<string, unknown>>;
437
+ /** Field name on each data point used for the X axis / category. */
438
+ xKey?: string;
439
+ /** Field name(s) used for series. Defaults to all keys except `xKey`. */
440
+ dataKey?: string | string[];
441
+ /** Optional unit label appended to numeric axis ticks. */
442
+ unit?: string;
443
+ }
444
+ interface QuestionOption {
445
+ id: string;
446
+ label: string;
447
+ description?: string;
448
+ }
449
+ /**
450
+ * Question artifact — renders an in-thread choice widget. When the user picks
451
+ * an option, the renderer calls back into the runtime with the selected
452
+ * label as a new user message. Agents should treat the user response as the
453
+ * answer.
454
+ */
455
+ interface QuestionArtifact {
456
+ type: "question";
457
+ /** Optional prompt shown above the options. Falls back to the message text. */
458
+ prompt?: string;
459
+ options: QuestionOption[];
460
+ /** Allow selecting more than one option. Default: false. */
461
+ multi?: boolean;
462
+ }
463
+ /** HTML/CSS/JS rendered in an iframe. See {@link HtmlArtifactView}. */
464
+ interface HtmlArtifact {
465
+ type: "html";
466
+ content: string;
467
+ /**
468
+ * When true (default) the HTML renders inside a sandboxed iframe. The
469
+ * sandbox allows scripts, forms, and modals but still isolates the
470
+ * document from the host page. Set to `false` for fully unrestricted
471
+ * inline HTML (scripts, external CDN assets, etc.) — trusted content only.
472
+ */
473
+ sandboxed?: boolean;
474
+ /** Optional title rendered above the iframe. */
475
+ title?: string;
476
+ /** Iframe height in CSS pixels or any valid CSS length. Default: "320px". */
477
+ height?: string;
478
+ }
479
+ interface JsonArtifact {
480
+ type: "json";
481
+ title?: string;
482
+ data: unknown;
483
+ }
484
+ interface TableArtifact {
485
+ type: "table";
486
+ title?: string;
487
+ columns?: Array<{
488
+ key: string;
489
+ label?: string;
490
+ }>;
491
+ rows: Array<Record<string, unknown>>;
492
+ }
493
+
494
+ type TimbalArtifact = ChartArtifact | QuestionArtifact | HtmlArtifact | JsonArtifact | TableArtifact | UiArtifact;
495
+ type AnyArtifact = TimbalArtifact | {
496
+ type: string;
497
+ [key: string]: unknown;
498
+ };
499
+ /**
500
+ * Type guard for artifact-shaped objects. Anything with a string `type` field
501
+ * is considered a candidate; specific renderers narrow further.
502
+ */
503
+ declare function isArtifact(value: unknown): value is AnyArtifact;
504
+
505
+ interface ArtifactRendererProps<T extends AnyArtifact = AnyArtifact> {
506
+ artifact: T;
507
+ }
508
+ type ArtifactRenderer<T extends AnyArtifact = AnyArtifact> = ComponentType<ArtifactRendererProps<T>>;
509
+ type ArtifactRegistry = Record<string, ArtifactRenderer<AnyArtifact>>;
510
+ declare const defaultArtifactRenderers: ArtifactRegistry;
511
+ /**
512
+ * Provide a custom artifact registry to the subtree. Custom renderers are
513
+ * merged on top of the defaults — pass `override: true` to replace.
514
+ */
515
+ declare const ArtifactRegistryProvider: FC<{
516
+ renderers?: ArtifactRegistry;
517
+ override?: boolean;
518
+ children: ReactNode;
519
+ }>;
520
+ declare function useArtifactRegistry(): ArtifactRegistry;
521
+ /**
522
+ * Render an artifact using the closest registry. Falls back to the JSON
523
+ * renderer when no entry matches the artifact's `type`.
524
+ */
525
+ declare const ArtifactView: FC<{
526
+ artifact: AnyArtifact;
527
+ }>;
528
+
529
+ type UiState = Record<string, unknown>;
530
+ type UiStateAction = {
531
+ type: "set";
532
+ path: string;
533
+ value: unknown;
534
+ } | {
535
+ type: "toggle";
536
+ path: string;
537
+ } | {
538
+ type: "replace";
539
+ state: UiState;
540
+ };
541
+ /** Read a dotted path from a state object. Returns undefined when missing. */
542
+ declare function getPath(state: UiState, path: string): unknown;
543
+ /**
544
+ * Set a dotted path on a state object, returning a new object. Intermediate
545
+ * objects are cloned (or created when missing) so the result is safe to use
546
+ * directly with React state without further copying.
547
+ */
548
+ declare function setPath(state: UiState, path: string, value: unknown): UiState;
549
+ /** Resolve a UiBindable into its concrete value against the given state. */
550
+ declare function resolveBindable<T>(value: UiBindable<T>, state: UiState): T;
551
+
552
+ declare function useUiState(): UiState;
553
+ declare function useUiDispatch(): Dispatch<UiStateAction>;
554
+ interface UiEventEnvelope {
555
+ name: string;
556
+ payload?: unknown;
557
+ }
558
+ /**
559
+ * Subscribe the host app to `emit`-kind actions fired from any UiArtifact in
560
+ * the subtree. Wrap your runtime / chat root once.
561
+ */
562
+ declare const UiEventProvider: FC<{
563
+ onEvent: (event: UiEventEnvelope) => void;
564
+ children: ReactNode;
565
+ }>;
566
+ declare function useUiEventEmitter(): ((event: UiEventEnvelope) => void) | null;
567
+ interface UiCustomNodeProps {
568
+ /** Already binding-resolved props from the artifact. */
569
+ props: Record<string, unknown>;
570
+ /** Recursively rendered children. */
571
+ children?: ReactNode;
572
+ }
573
+ type UiCustomNodeRenderer = ComponentType<UiCustomNodeProps>;
574
+ /**
575
+ * Register named renderers for `{ kind: "custom", name: "..." }` nodes. Lets
576
+ * host apps extend the palette without forking the package.
577
+ */
578
+ declare const UiCustomNodeRegistryProvider: FC<{
579
+ renderers: Record<string, UiCustomNodeRenderer>;
580
+ children: ReactNode;
581
+ }>;
582
+ declare function useUiCustomNodeRegistry(): Record<string, UiCustomNodeRenderer>;
583
+
34
584
  interface ThreadWelcomeConfig {
35
585
  heading?: string;
36
586
  subheading?: string;
37
587
  }
38
588
  interface ThreadWelcomeProps {
39
589
  config?: ThreadWelcomeConfig;
40
- suggestions?: ThreadSuggestion[];
590
+ suggestions?: SuggestionsSource;
591
+ /**
592
+ * The resolved `Suggestions` component (default or user-overridden via
593
+ * `components.Suggestions`). Custom Welcome implementations should render
594
+ * this and pass their `suggestions` source through.
595
+ */
596
+ Suggestions?: SuggestionsComponent;
41
597
  }
42
598
  interface ThreadComponents {
43
599
  /** Replace the user message bubble. Access message content via `MessagePrimitive.Parts`. */
@@ -46,44 +602,214 @@ interface ThreadComponents {
46
602
  AssistantMessage?: ComponentType;
47
603
  /** Replace the inline edit composer. */
48
604
  EditComposer?: ComponentType;
49
- /** Replace the composer (input bar). Receives `placeholder` from `composerPlaceholder`. */
50
- Composer?: ComponentType<{
51
- placeholder?: string;
52
- }>;
605
+ /** Replace the composer (input bar). Receives all `ComposerProps` from the parent. */
606
+ Composer?: ComponentType<ComposerProps>;
53
607
  /** Replace the welcome / empty state. Receives `config` and `suggestions` props. Controls its own visibility — use `useThread(s => s.isEmpty)` to replicate the default behaviour. */
54
608
  Welcome?: ComponentType<ThreadWelcomeProps>;
609
+ /** Replace the suggestion chip block (rendered inside Welcome and inline). */
610
+ Suggestions?: SuggestionsComponent;
55
611
  /** Replace the scroll-to-bottom button. */
56
612
  ScrollToBottom?: ComponentType;
57
613
  }
614
+ interface ThreadArtifactsConfig {
615
+ /** Custom artifact renderers, merged on top of the built-in defaults. */
616
+ renderers?: ArtifactRegistry;
617
+ /** Replace the built-in renderers entirely instead of merging. */
618
+ override?: boolean;
619
+ }
620
+
58
621
  interface ThreadProps {
59
622
  className?: string;
60
623
  /** Max width of the message column. Default: "44rem" */
61
624
  maxWidth?: string;
62
625
  /** Welcome screen text */
63
626
  welcome?: ThreadWelcomeConfig;
64
- /** Suggestion chips shown on the welcome screen */
65
- suggestions?: ThreadSuggestion[];
627
+ /**
628
+ * Welcome-screen suggestion chips. Accepts a static array, a thunk, or an
629
+ * async function for per-user suggestions.
630
+ */
631
+ suggestions?: SuggestionsSource;
66
632
  /** Composer input placeholder. Default: "Send a message..." */
67
633
  composerPlaceholder?: string;
68
634
  /** Override individual UI slots while keeping the rest as defaults. */
69
635
  components?: ThreadComponents;
636
+ /**
637
+ * Configure how rich tool/artifact results render. Pass `renderers` to add
638
+ * support for custom artifact `type` values. Built-in types (`chart`,
639
+ * `question`, `html`, `json`, `table`, `ui`) are always available unless
640
+ * `override: true` is set.
641
+ */
642
+ artifacts?: ThreadArtifactsConfig;
643
+ /**
644
+ * Called when a `ui` artifact fires an `{ kind: "emit" }` action. Use this
645
+ * to react to slider commits, drag gestures, or other host-side logic beyond
646
+ * the built-in `message` action (which already appends a user message).
647
+ */
648
+ onArtifactEvent?: (event: UiEventEnvelope) => void;
70
649
  }
71
650
  declare const Thread: FC<ThreadProps>;
72
651
 
73
652
  interface TimbalChatProps extends Omit<TimbalRuntimeProviderProps, "children">, ThreadProps {
74
653
  }
75
- declare function TimbalChat({ workforceId, baseUrl, fetch, ...threadProps }: TimbalChatProps): react_jsx_runtime.JSX.Element;
654
+ declare function TimbalChat({ workforceId, baseUrl, fetch, attachments, attachmentsUploadUrl, attachmentsAccept, debug, ...threadProps }: TimbalChatProps): react_jsx_runtime.JSX.Element;
76
655
 
77
656
  declare const MarkdownText: React.MemoExoticComponent<() => react_jsx_runtime.JSX.Element>;
78
657
 
79
658
  declare const ToolFallback: ToolCallMessagePartComponent;
80
659
 
660
+ /**
661
+ * Copy-paste this into a workforce agent system prompt (or append it to your
662
+ * blueprint's tool-result instructions) so the model knows which artifact
663
+ * payloads the chat UI can render.
664
+ *
665
+ * @example
666
+ * ```ts
667
+ * import { ARTIFACT_AGENT_INSTRUCTIONS } from "@timbal-ai/timbal-react";
668
+ *
669
+ * const systemPrompt = `${basePrompt}\n\n${ARTIFACT_AGENT_INSTRUCTIONS}`;
670
+ * ```
671
+ */
672
+ declare const ARTIFACT_AGENT_INSTRUCTIONS: string;
673
+
674
+ /**
675
+ * Render a `ui` artifact. Each instance gets its own local state seeded from
676
+ * `artifact.initialState`. Toggles, sliders, and `set` actions mutate this
677
+ * state via the reducer; bindings (`{ $bind: "path" }`) read from it.
678
+ *
679
+ * Host apps subscribe to `emit` actions via `<UiEventProvider>` from the
680
+ * registry module and extend the node palette via
681
+ * `<UiCustomNodeRegistryProvider>`.
682
+ */
683
+ declare const UiArtifactView: FC<{
684
+ artifact: UiArtifact;
685
+ }>;
686
+
687
+ declare const UiNodeView: FC<{
688
+ node: UiNode;
689
+ }>;
690
+
691
+ /**
692
+ * Markdown fence languages we treat as artifact payloads. `timbal-artifact`
693
+ * is the canonical form; `timbal` is accepted as an alias because most
694
+ * frontier models drop the suffix when generating fenced blocks.
695
+ */
696
+ declare const ARTIFACT_FENCE_LANGUAGES: ReadonlySet<string>;
697
+ /**
698
+ * Returns true if the given fenced-code-block language should be rendered
699
+ * as an artifact instead of as a code block.
700
+ */
701
+ declare function isArtifactFenceLanguage(language: string | undefined | null): boolean;
702
+ /**
703
+ * Parse a tool result into an artifact, if possible. Strings are tried as
704
+ * JSON first. Timbal often wraps tool return values as
705
+ * `{ type: "text", text: "<json>" }` — we unwrap those before checking
706
+ * `type`. Returns `null` for results that don't look like artifacts.
707
+ */
708
+ declare function parseArtifactFromToolResult(result: unknown): AnyArtifact | null;
709
+ interface MarkdownArtifactMatch {
710
+ /** The artifact payload. */
711
+ artifact: AnyArtifact;
712
+ /** The raw fenced block, including its ``` fences. */
713
+ raw: string;
714
+ /** Start offset in the source string. */
715
+ start: number;
716
+ /** End offset (exclusive). */
717
+ end: number;
718
+ }
719
+ /**
720
+ * Find every `timbal-artifact` fenced block in a markdown string and return
721
+ * each parsed payload along with its source offset. Use the offsets to splice
722
+ * a renderer in place of the fence — see `splitMarkdownByArtifacts`.
723
+ */
724
+ declare function findMarkdownArtifacts(markdown: string): MarkdownArtifactMatch[];
725
+ type MarkdownSegment = {
726
+ kind: "text";
727
+ text: string;
728
+ } | {
729
+ kind: "artifact";
730
+ artifact: AnyArtifact;
731
+ };
732
+ /**
733
+ * Split a markdown string into alternating text/artifact segments, preserving
734
+ * order. Useful when rendering markdown with inline artifact placeholders —
735
+ * render each segment with the appropriate component.
736
+ */
737
+ declare function splitMarkdownByArtifacts(markdown: string): MarkdownSegment[];
738
+
739
+ /**
740
+ * Drop-in replacement for `ToolFallback` that first tries to render the tool
741
+ * result as an artifact via the registry. Falls back to the standard tool
742
+ * panel when the result isn't artifact-shaped (or while the tool is still
743
+ * running).
744
+ *
745
+ * Use this as the `tools.Fallback` in `MessagePrimitive.Parts` to enable
746
+ * artifact rendering for any tool the agent calls.
747
+ */
748
+ declare const ToolArtifactFallback: ToolCallMessagePartComponent;
749
+
750
+ /**
751
+ * Shared chrome for built-in artifact renderers. Custom renderers don't have
752
+ * to use this — it's just a small visual baseline so charts, tables, and
753
+ * question widgets feel like a coherent set.
754
+ */
755
+ declare const ArtifactCard: FC<{
756
+ title?: string;
757
+ kind?: string;
758
+ className?: string;
759
+ bodyClassName?: string;
760
+ toolbar?: ReactNode;
761
+ children: ReactNode;
762
+ }>;
763
+
764
+ /**
765
+ * Lightweight SVG chart for the four basic chart types. We deliberately don't
766
+ * pull in `recharts` or similar — most artifact charts are small and these
767
+ * render fine at the cost of a few hundred lines of SVG. Apps that need
768
+ * production-grade charting can register a custom renderer that wraps their
769
+ * preferred lib.
770
+ */
771
+ declare const ChartArtifactView: FC<{
772
+ artifact: ChartArtifact;
773
+ }>;
774
+
775
+ /**
776
+ * Inline choice widget. Single-select submits immediately on click;
777
+ * multi-select shows a confirm button. The answer is sent as a regular user
778
+ * message via the assistant-ui runtime, so the agent treats it like any
779
+ * other reply.
780
+ */
781
+ declare const QuestionArtifactView: FC<{
782
+ artifact: QuestionArtifact;
783
+ }>;
784
+
785
+ /**
786
+ * Renders HTML inside an iframe. When `sandboxed` is true (default), scripts
787
+ * and forms run in an isolated sandbox. Set `sandboxed: false` for fully
788
+ * unrestricted inline HTML — trusted content only.
789
+ */
790
+ declare const HtmlArtifactView: FC<{
791
+ artifact: HtmlArtifact;
792
+ }>;
793
+
794
+ /**
795
+ * Default renderer used both for `{ type: "json" }` artifacts and as the
796
+ * fallback when no renderer is registered for a given type. Pretty-prints
797
+ * any JSON-serializable value.
798
+ */
799
+ declare const JsonArtifactView: FC<{
800
+ artifact: JsonArtifact | AnyArtifact;
801
+ }>;
802
+
803
+ declare const TableArtifactView: FC<{
804
+ artifact: TableArtifact;
805
+ }>;
806
+
81
807
  declare const UserMessageAttachments: FC;
82
808
  declare const ComposerAttachments: FC;
83
809
  declare const ComposerAddAttachment: FC;
84
810
 
85
811
  declare const buttonVariants: (props?: ({
86
- variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
812
+ variant?: "default" | "outline" | "ghost" | "secondary" | "destructive" | "link" | null | undefined;
87
813
  size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
88
814
  } & class_variance_authority_types.ClassProp) | undefined) => string;
89
815
  declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
@@ -98,6 +824,83 @@ declare const TooltipIconButton: React.ForwardRefExoticComponent<Omit<TooltipIco
98
824
 
99
825
  declare const ShikiSyntaxHighlighter: FC<SyntaxHighlighterProps>;
100
826
 
827
+ type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;
828
+ interface UseWorkforcesOptions {
829
+ /** Base URL for API calls. Default: `/api`. */
830
+ baseUrl?: string;
831
+ /** Custom fetch (defaults to `authFetch` for token-auth flows). */
832
+ fetch?: FetchFn;
833
+ /**
834
+ * Pick the initial selected workforce. By default we prefer the first
835
+ * `type === "agent"` entry, falling back to the first item. Pass a custom
836
+ * resolver to override (e.g. remember the user's last choice).
837
+ */
838
+ pickInitial?: (items: WorkforceItem[]) => WorkforceItem | undefined;
839
+ }
840
+ interface UseWorkforcesResult {
841
+ workforces: WorkforceItem[];
842
+ /** Currently selected workforce identifier (id ?? uid ?? name). */
843
+ selectedId: string;
844
+ setSelectedId: (id: string) => void;
845
+ /** The full `WorkforceItem` for `selectedId`, if any. */
846
+ selected: WorkforceItem | undefined;
847
+ isLoading: boolean;
848
+ error: Error | null;
849
+ /** Re-fetch the list. Resets `error`. */
850
+ refresh: () => Promise<void>;
851
+ }
852
+ /**
853
+ * Fetch the list of workforces exposed by the blueprint API and track a
854
+ * selection. Used to power `<WorkforceSelector />` but exported separately
855
+ * so apps can drive their own UI (e.g. a sidebar tree).
856
+ */
857
+ declare function useWorkforces(options?: UseWorkforcesOptions): UseWorkforcesResult;
858
+
859
+ interface WorkforceSelectorProps {
860
+ /** List of workforces to choose from. */
861
+ workforces: WorkforceItem[];
862
+ /** Currently selected workforce id. */
863
+ value: string;
864
+ /** Called when the user picks a different workforce. */
865
+ onChange: (id: string) => void;
866
+ /** Hide the selector when there's only one option. Default: true. */
867
+ hideWhenSingle?: boolean;
868
+ className?: string;
869
+ placeholder?: string;
870
+ }
871
+ /**
872
+ * Minimal headless workforce picker — wraps a styled native `<select>` so
873
+ * `timbal-react` doesn't need a Radix dropdown dependency. Apps that want a
874
+ * richer UI (search, descriptions) can build their own using
875
+ * `useWorkforces()`.
876
+ */
877
+ declare const WorkforceSelector: FC<WorkforceSelectorProps>;
878
+
879
+ interface TimbalChatShellProps extends Omit<TimbalChatProps, "workforceId"> {
880
+ /**
881
+ * Pre-selected workforce id. When omitted, the shell fetches the workforce
882
+ * list from `{baseUrl}/workforce` and picks the first agent automatically.
883
+ */
884
+ workforceId?: string;
885
+ /** Branding rendered at the start of the header (logo, etc). */
886
+ brand?: ReactNode;
887
+ /** Extra content rendered at the end of the header (theme toggle, logout). */
888
+ headerActions?: ReactNode;
889
+ /** Hide the built-in workforce selector. Default: false. */
890
+ hideWorkforceSelector?: boolean;
891
+ /** Class for the outer flex container. */
892
+ className?: string;
893
+ /** Class for the header bar. */
894
+ headerClassName?: string;
895
+ }
896
+ /**
897
+ * Drop-in shell that combines the most common blueprint patterns: a header
898
+ * with brand + workforce selector + actions, plus the chat thread occupying
899
+ * the remaining vertical space. Falls back to `<TimbalChat>` directly when
900
+ * `workforceId` is provided.
901
+ */
902
+ declare const TimbalChatShell: FC<TimbalChatShellProps>;
903
+
101
904
  interface SessionContextType {
102
905
  user: Session | null;
103
906
  loading: boolean;
@@ -162,4 +965,4 @@ declare const Shimmer: React.MemoExoticComponent<({ children, as: Component, cla
162
965
 
163
966
  declare function cn(...inputs: ClassValue[]): string;
164
967
 
165
- export { AuthGuard, Avatar, AvatarFallback, AvatarImage, Button, ComposerAddAttachment, ComposerAttachments, Dialog, DialogClose, DialogContent, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, MarkdownText, SessionProvider, Shimmer, ShikiSyntaxHighlighter as SyntaxHighlighter, type TextShimmerProps, Thread, type ThreadComponents, type ThreadProps, type ThreadSuggestion, type ThreadWelcomeConfig, type ThreadWelcomeProps, TimbalChat, type TimbalChatProps, TimbalRuntimeProvider, type TimbalRuntimeProviderProps, ToolFallback, Tooltip, TooltipContent, TooltipIconButton, type TooltipIconButtonProps, TooltipProvider, TooltipTrigger, UserMessageAttachments, authFetch, buttonVariants, clearTokens, cn, fetchCurrentUser, getAccessToken, getRefreshToken, refreshAccessToken, setAccessToken, setRefreshToken, useSession };
968
+ export { ARTIFACT_AGENT_INSTRUCTIONS, ARTIFACT_FENCE_LANGUAGES, type AnyArtifact, ArtifactCard, type ArtifactRegistry, ArtifactRegistryProvider, type ArtifactRenderer, type ArtifactRendererProps, ArtifactView, AuthGuard, Avatar, AvatarFallback, AvatarImage, Button, type ChartArtifact, ChartArtifactView, type ChatAttachment, type ChatMessage, Composer, ComposerAddAttachment, ComposerAttachments, type ComposerProps, type ContentPart, type CreateDefaultAttachmentAdapterOptions, type CreateUploadAttachmentAdapterOptions, DEFAULT_UPLOAD_ACCEPT, Dialog, DialogClose, DialogContent, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, type HtmlArtifact, HtmlArtifactView, type JsonArtifact, JsonArtifactView, type MarkdownArtifactMatch, type MarkdownSegment, MarkdownText, type QuestionArtifact, QuestionArtifactView, type QuestionOption, type ResolveAttachmentAdapterOptions, type SendOptions, SessionProvider, Shimmer, Suggestions, type SuggestionsComponent, type SuggestionsSlotProps, type SuggestionsSource, ShikiSyntaxHighlighter as SyntaxHighlighter, type TableArtifact, TableArtifactView, type TextContentPart, type TextShimmerProps, type ThinkingContentPart, Thread, type ThreadArtifactsConfig, type ThreadComponents, type ThreadProps, type ThreadSuggestion, type ThreadSuggestionsProps, type ThreadWelcomeConfig, type ThreadWelcomeProps, type TimbalArtifact, type TimbalAttachmentsConfig, type TimbalAttachmentsProp, TimbalChat, type TimbalChatProps, TimbalChatShell, type TimbalChatShellProps, TimbalRuntimeProvider, type TimbalRuntimeProviderProps, type TimbalStreamApi, ToolArtifactFallback, type ToolCallContentPart, ToolFallback, Tooltip, TooltipContent, TooltipIconButton, type TooltipIconButtonProps, TooltipProvider, TooltipTrigger, type UiAction, type UiArtifact, UiArtifactView, UiCustomNodeRegistryProvider, type UiEventEnvelope, UiEventProvider, type UiNode, UiNodeView, type UploadFetchFn, type UseTimbalStreamOptions, type UseWorkforcesOptions, type UseWorkforcesResult, UserMessageAttachments, WorkforceSelector, type WorkforceSelectorProps, authFetch, buttonVariants, clearTokens, cn, createDefaultAttachmentAdapter, createUploadAttachmentAdapter, defaultArtifactRenderers, fetchCurrentUser, findMarkdownArtifacts, getAccessToken, getPath, getRefreshToken, isArtifact, isArtifactFenceLanguage, isUiBinding, parseArtifactFromToolResult, refreshAccessToken, resolveAttachmentAdapter, resolveBindable, setAccessToken, setPath, setRefreshToken, splitMarkdownByArtifacts, useArtifactRegistry, useResolvedSuggestions, useSession, useTimbalRuntime, useTimbalStream, useUiCustomNodeRegistry, useUiDispatch, useUiEventEmitter, useUiState, useWorkforces };