@assistant-ui/mcp-docs-server 0.1.34 → 0.1.35
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/.docs/organized/code-examples/waterfall.md +4 -4
- package/.docs/organized/code-examples/with-a2a.md +5 -5
- package/.docs/organized/code-examples/with-ag-ui.md +6 -6
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +7 -7
- package/.docs/organized/code-examples/with-artifacts.md +7 -7
- package/.docs/organized/code-examples/with-assistant-transport.md +5 -5
- package/.docs/organized/code-examples/with-browser-extension.md +5 -5
- package/.docs/organized/code-examples/with-chain-of-thought.md +8 -8
- package/.docs/organized/code-examples/with-cloud-standalone.md +7 -7
- package/.docs/organized/code-examples/with-cloud.md +7 -7
- package/.docs/organized/code-examples/with-custom-thread-list.md +7 -7
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +9 -9
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +9 -9
- package/.docs/organized/code-examples/with-expo.md +43 -17
- package/.docs/organized/code-examples/with-external-store.md +5 -5
- package/.docs/organized/code-examples/with-ffmpeg.md +7 -7
- package/.docs/organized/code-examples/with-generative-ui.md +32 -308
- package/.docs/organized/code-examples/with-google-adk.md +5 -5
- package/.docs/organized/code-examples/with-heat-graph.md +4 -4
- package/.docs/organized/code-examples/with-image-generation.md +7 -7
- package/.docs/organized/code-examples/with-interactables.md +7 -7
- package/.docs/organized/code-examples/with-langchain.md +7 -7
- package/.docs/organized/code-examples/with-langgraph.md +6 -6
- package/.docs/organized/code-examples/with-livekit.md +9 -9
- package/.docs/organized/code-examples/with-mcp.md +7 -7
- package/.docs/organized/code-examples/with-opencode.md +106 -580
- package/.docs/organized/code-examples/with-pi.md +2044 -0
- package/.docs/organized/code-examples/with-react-hook-form.md +8 -8
- package/.docs/organized/code-examples/with-react-ink-web.md +691 -0
- package/.docs/organized/code-examples/with-react-ink.md +28 -16
- package/.docs/organized/code-examples/with-react-router.md +5 -5
- package/.docs/organized/code-examples/with-resumable-stream.md +8 -8
- package/.docs/organized/code-examples/with-store.md +14 -10
- package/.docs/organized/code-examples/with-tanstack.md +18 -4
- package/.docs/organized/code-examples/with-tap-runtime.md +5 -5
- package/.docs/organized/code-examples/with-virtualized-thread.md +656 -0
- package/.docs/raw/docs/(docs)/architecture.mdx +13 -12
- package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +44 -1
- package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +2 -23
- package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +22 -0
- package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +0 -7
- package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +20 -21
- package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
- package/.docs/raw/docs/guides/index.mdx +3 -0
- package/.docs/raw/docs/guides/input-history.mdx +55 -0
- package/.docs/raw/docs/guides/virtualization.mdx +63 -0
- package/.docs/raw/docs/ink/hooks.mdx +2 -2
- package/.docs/raw/docs/react-native/hooks.mdx +1 -1
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +1 -3
- package/.docs/raw/docs/runtimes/concepts/stability.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +1 -1
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +108 -4
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +6 -6
- package/.docs/raw/docs/tools/tool-ui.mdx +37 -1
- package/.docs/raw/docs/ui/dot-matrix.mdx +133 -0
- package/.docs/raw/docs/ui/model-selector.mdx +219 -52
- package/.docs/raw/docs/ui/number-roll.mdx +154 -0
- package/.docs/raw/docs/ui/reasoning.mdx +3 -3
- package/.docs/raw/docs/ui/streamdown.mdx +2 -0
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -1
- package/.docs/raw/docs/ui/thread.mdx +52 -0
- package/.docs/raw/docs/ui/tool-fallback.mdx +2 -2
- package/package.json +3 -3
|
@@ -3,7 +3,7 @@ title: Primitive Hooks
|
|
|
3
3
|
description: Primitive hooks for reading scoped assistant-ui runtime state, viewport behavior, timing, and message part data inside React components.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { useAssistantRuntime, useAttachmentRuntime, useCloudThreadListAdapter, useComposerRuntime, useEditComposerAttachmentRuntime, useMessageAttachmentRuntime, useMessagePartData, useMessagePartRuntime, useMessageRuntime, useScrollLock, useThreadComposerAttachmentRuntime, useThreadListItemRuntime, useThreadRuntime, useThreadViewportAutoScroll } from "@/generated/typeDocs";
|
|
6
|
+
import { unstable_useMessageStallDetection, useAssistantRuntime, useAttachmentRuntime, useCloudThreadListAdapter, useComposerRuntime, useEditComposerAttachmentRuntime, useMessageAttachmentRuntime, useMessagePartData, useMessagePartRuntime, useMessageRuntime, useScrollLock, useThreadComposerAttachmentRuntime, useThreadListItemRuntime, useThreadRuntime, useThreadViewportAutoScroll } from "@/generated/typeDocs";
|
|
7
7
|
|
|
8
8
|
{/* AUTO-GENERATED PAGE by scripts/generate-api-reference.mts */}
|
|
9
9
|
{/* Do not edit manually. */}
|
|
@@ -637,4 +637,47 @@ const useThreadViewport: { (): ThreadViewportState; <TSelected>(selector: (state
|
|
|
637
637
|
```ts
|
|
638
638
|
const useThreadViewportStore: { (): ReadonlyStore<ThreadViewportState>; (options: { optional: true; }): ReadonlyStore<ThreadViewportState> | null; };
|
|
639
639
|
```
|
|
640
|
+
|
|
641
|
+
### unstable_useComposerInputHistory
|
|
642
|
+
|
|
643
|
+
<Callout type="warn">
|
|
644
|
+
<strong>Deprecated.</strong> Under active development and might change without notice.
|
|
645
|
+
</Callout>
|
|
646
|
+
|
|
647
|
+
Terminal-style input history for the thread composer: ArrowUp on an
|
|
648
|
+
empty draft recalls previously sent user messages (newest first),
|
|
649
|
+
ArrowDown steps back toward the newest and finally restores the draft
|
|
650
|
+
that was being typed when browsing started.
|
|
651
|
+
|
|
652
|
+
Recall only triggers when the caret is on the first/last line with no
|
|
653
|
+
selection, so multi-line editing keeps native arrow behavior. The
|
|
654
|
+
handler yields to an open mention/slash popover, to IME composition,
|
|
655
|
+
to modifier keys, and to consumer handlers that already called
|
|
656
|
+
`preventDefault`. It is inert on edit composers.
|
|
657
|
+
|
|
658
|
+
```tsx
|
|
659
|
+
const history = unstable_useComposerInputHistory();
|
|
660
|
+
<ComposerPrimitive.Input {...history} />
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
```ts
|
|
664
|
+
function unstable_useComposerInputHistory(): Unstable_ComposerInputHistory;
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### unstable_useMessageStallDetection
|
|
668
|
+
|
|
669
|
+
<Callout type="warn">
|
|
670
|
+
<strong>Deprecated.</strong> Under active development and might change without notice.
|
|
671
|
+
</Callout>
|
|
672
|
+
|
|
673
|
+
Detects mid-run output stalls on the current message: while the message is
|
|
674
|
+
running, watches a fingerprint of its content (part count plus text,
|
|
675
|
+
argument, and result sizes) and reports a stall once the fingerprint stops
|
|
676
|
+
changing for `thresholdMs`. Useful for re-surfacing a "still working"
|
|
677
|
+
indicator during tool think-time or provider stalls, after the first
|
|
678
|
+
tokens have already streamed.
|
|
679
|
+
|
|
680
|
+
Must be used inside a message scope.
|
|
681
|
+
|
|
682
|
+
<ParametersTable {...unstable_useMessageStallDetection} />
|
|
640
683
|
{/* api-reference:end */}
|
|
@@ -29,7 +29,7 @@ const mergeModelContexts: (configSet: Set<ModelContextProvider>) => ModelContext
|
|
|
29
29
|
### ModelContextClient
|
|
30
30
|
|
|
31
31
|
```ts
|
|
32
|
-
const ModelContextClient:
|
|
32
|
+
const ModelContextClient: Resource<ClientOutput<"modelContext">, []>;
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
### ModelContextProvider
|
|
@@ -3,7 +3,7 @@ title: Tool Rendering
|
|
|
3
3
|
description: Register React renderers for assistant-ui tool calls, tool results, and model data parts.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { McpAppRenderer, McpAppsRemoteHost, getMcpAppFromToolPart, useAssistantDataUI, useAssistantToolUI } from "@/generated/typeDocs";
|
|
6
|
+
import { DataRenderers, McpAppRenderer, McpAppsRemoteHost, getMcpAppFromToolPart, useAssistantDataUI, useAssistantToolUI } from "@/generated/typeDocs";
|
|
7
7
|
|
|
8
8
|
{/* AUTO-GENERATED PAGE by scripts/generate-api-reference.mts */}
|
|
9
9
|
{/* Do not edit manually. */}
|
|
@@ -16,15 +16,7 @@ import { McpAppRenderer, McpAppsRemoteHost, getMcpAppFromToolPart, useAssistantD
|
|
|
16
16
|
|
|
17
17
|
### DataRenderers
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Data renderers are looked up by the part's `name` field. Use this resource
|
|
22
|
-
directly for a renderer scope, or prefer [useAssistantDataUI](/docs/api-reference/tools/rendering#useassistantdataui) /
|
|
23
|
-
[makeAssistantDataUI](/docs/api-reference/tools/rendering#makeassistantdataui) when registering from React components.
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
const DataRenderers: () => ResourceElement<ClientOutput<"dataRenderers">, undefined>;
|
|
27
|
-
```
|
|
19
|
+
<ParametersTable {...DataRenderers} />
|
|
28
20
|
|
|
29
21
|
### getMcpAppFromToolPart
|
|
30
22
|
|
|
@@ -84,23 +76,10 @@ const makeAssistantToolUI: <TArgs, TResult>(tool: AssistantToolUIProps<TArgs, TR
|
|
|
84
76
|
|
|
85
77
|
### McpAppRenderer
|
|
86
78
|
|
|
87
|
-
Creates a tool-call renderer for MCP Apps embedded in assistant messages.
|
|
88
|
-
|
|
89
|
-
Compose this into the `Tools` resource through its `mcpApp` option. When a
|
|
90
|
-
tool-call part carries `mcp.app` metadata for a `ui://` resource, the
|
|
91
|
-
renderer loads that resource from the configured host and displays it in a
|
|
92
|
-
sandboxed frame.
|
|
93
|
-
|
|
94
79
|
<ParametersTable {...McpAppRenderer} />
|
|
95
80
|
|
|
96
81
|
### McpAppsRemoteHost
|
|
97
82
|
|
|
98
|
-
Creates the default HTTP host for MCP App widgets.
|
|
99
|
-
|
|
100
|
-
The host POSTs widget requests to the configured route as `{ method,
|
|
101
|
-
params }`, using the method names expected by the assistant-ui MCP Apps
|
|
102
|
-
guide.
|
|
103
|
-
|
|
104
83
|
<ParametersTable {...McpAppsRemoteHost} />
|
|
105
84
|
|
|
106
85
|
### useAssistantDataUI
|
|
@@ -39,4 +39,26 @@ function WeatherToolUI({
|
|
|
39
39
|
```ts
|
|
40
40
|
const useToolArgsStatus: <TArgs extends Record<string, unknown> = Record<string, unknown>>() => ToolArgsStatus<TArgs>;
|
|
41
41
|
```
|
|
42
|
+
|
|
43
|
+
### useToolCallElapsed
|
|
44
|
+
|
|
45
|
+
Hook that returns the elapsed wall-clock time of the current tool call in
|
|
46
|
+
milliseconds, ticking once per second while the call runs.
|
|
47
|
+
|
|
48
|
+
Reads `part.timing`. Returns `undefined` when the part is not a tool call,
|
|
49
|
+
carries no timing, ended without a recorded completion (the duration is
|
|
50
|
+
unknown), or when no message part scope is available (so kit components
|
|
51
|
+
stay renderable standalone, e.g. in docs previews).
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
function ToolDuration() {
|
|
55
|
+
const elapsedMs = useToolCallElapsed();
|
|
56
|
+
if (elapsedMs === undefined) return null;
|
|
57
|
+
return <span>{(elapsedMs / 1000).toFixed(1)}s</span>;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const useToolCallElapsed: () => number;
|
|
63
|
+
```
|
|
42
64
|
{/* api-reference:end */}
|
|
@@ -80,13 +80,6 @@ type Toolkit = Record<string, ToolDefinition<any, any>>;
|
|
|
80
80
|
|
|
81
81
|
### Tools
|
|
82
82
|
|
|
83
|
-
Registers tools with model context and installs tool-call renderers.
|
|
84
|
-
|
|
85
|
-
Mount this resource near an assistant subtree when you want to expose a
|
|
86
|
-
group of tools declaratively. Tool definitions are registered with model
|
|
87
|
-
context, while each tool renderer is registered with the tools scope for
|
|
88
|
-
message rendering.
|
|
89
|
-
|
|
90
83
|
<ParametersTable {...Tools} />
|
|
91
84
|
|
|
92
85
|
### defineMcpToolkit
|
|
@@ -3,7 +3,7 @@ title: Utilities
|
|
|
3
3
|
description: Miscellaneous @assistant-ui/react utilities for custom rendering, composition, and advanced assistant UI behavior.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { AssistantCloud, ChainOfThoughtClient, DevToolsHooks, InMemoryThreadList, SingleThreadList } from "@/generated/typeDocs";
|
|
6
|
+
import { AssistantCloud, ChainOfThoughtClient, DevToolsHooks, InMemoryThreadList, Interactables, SingleThreadList, Suggestions, useSmooth } from "@/generated/typeDocs";
|
|
7
7
|
|
|
8
8
|
{/* AUTO-GENERATED PAGE by scripts/generate-api-reference.mts */}
|
|
9
9
|
{/* Do not edit manually. */}
|
|
@@ -38,35 +38,34 @@ const createMessageQueue: (driver: MessageQueueDriver) => MessageQueueController
|
|
|
38
38
|
|
|
39
39
|
### Interactables
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
const Interactables: () => ResourceElement<ClientOutput<"interactables">, undefined>;
|
|
43
|
-
```
|
|
41
|
+
<ParametersTable {...Interactables} />
|
|
44
42
|
|
|
45
43
|
### SingleThreadList
|
|
46
44
|
|
|
47
|
-
A minimal threads scope that wraps a single thread.
|
|
48
|
-
Automatically provided by ExternalThread when no threads scope exists.
|
|
49
|
-
Mounts the provided thread resource element.
|
|
50
|
-
|
|
51
45
|
<ParametersTable {...SingleThreadList} />
|
|
52
46
|
|
|
53
47
|
### Suggestions
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
49
|
+
<ParametersTable {...Suggestions} />
|
|
50
|
+
|
|
51
|
+
### useSmooth
|
|
52
|
+
|
|
53
|
+
Animates streamed message part text with a typewriter-style reveal.
|
|
54
|
+
|
|
55
|
+
Takes the current part state and a `smooth` argument: `false` disables,
|
|
56
|
+
`true` uses the default rate, and a SmoothOptions object tunes
|
|
57
|
+
the reveal. Returns the part state with `text` replaced by the revealed
|
|
58
|
+
prefix and `status` reporting `running` until the reveal catches up.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
const { text, status } = useSmooth(useMessagePartText(), {
|
|
62
|
+
drainMs: 500,
|
|
63
|
+
maxCharsPerFrame: 30,
|
|
64
|
+
});
|
|
68
65
|
```
|
|
69
66
|
|
|
67
|
+
<ParametersTable {...useSmooth} />
|
|
68
|
+
|
|
70
69
|
### unstable_defaultDirectiveFormatter
|
|
71
70
|
|
|
72
71
|
Default directive formatter using the `:type[label]{name=id}` syntax.
|
|
@@ -60,7 +60,7 @@ const AssistantMessage: FC = () => {
|
|
|
60
60
|
case "group-reasoning": {
|
|
61
61
|
const running = part.status.type === "running";
|
|
62
62
|
return (
|
|
63
|
-
<ReasoningRoot
|
|
63
|
+
<ReasoningRoot streaming={running}>
|
|
64
64
|
<ReasoningTrigger active={running} />
|
|
65
65
|
<ReasoningContent aria-busy={running}>
|
|
66
66
|
<ReasoningText>{children}</ReasoningText>
|
|
@@ -68,6 +68,9 @@ Render specialized message content.
|
|
|
68
68
|
<Card title="LaTeX" href="/docs/guides/latex">
|
|
69
69
|
Render math via React Markdown or Streamdown, with streaming-safe escape rules.
|
|
70
70
|
</Card>
|
|
71
|
+
<Card title="Thread Virtualization" href="/docs/guides/virtualization">
|
|
72
|
+
Render very long threads with @tanstack/react-virtual and per-index message rendering.
|
|
73
|
+
</Card>
|
|
71
74
|
</Cards>
|
|
72
75
|
|
|
73
76
|
## Audio
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Input History
|
|
3
|
+
description: Terminal-style ArrowUp/ArrowDown recall of previously sent messages in the assistant-ui React composer.
|
|
4
|
+
platforms: ["react"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Input history lets users press ArrowUp in an empty composer to recall previously sent messages, newest first, like a shell prompt. ArrowDown steps back toward the newest entry and finally restores the draft that was being typed when browsing started.
|
|
8
|
+
|
|
9
|
+
<Callout type="warn">
|
|
10
|
+
This API is marked unstable and may change without notice.
|
|
11
|
+
</Callout>
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Spread the hook's bundle onto `ComposerPrimitive.Input`:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import {
|
|
19
|
+
ComposerPrimitive,
|
|
20
|
+
unstable_useComposerInputHistory,
|
|
21
|
+
} from "@assistant-ui/react";
|
|
22
|
+
|
|
23
|
+
const Composer = () => {
|
|
24
|
+
const history = unstable_useComposerInputHistory();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<ComposerPrimitive.Root>
|
|
28
|
+
<ComposerPrimitive.Input {...history} />
|
|
29
|
+
<ComposerPrimitive.Send />
|
|
30
|
+
</ComposerPrimitive.Root>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The history ring is derived live from the current thread's user messages (trimmed, with adjacent duplicates collapsed), so it needs no persistence or configuration.
|
|
36
|
+
|
|
37
|
+
## Behavior
|
|
38
|
+
|
|
39
|
+
- ArrowUp starts recall only when the composer is empty (whitespace-only drafts count as empty and are restored on the way back down).
|
|
40
|
+
- While a recalled multi-line message is shown, arrows move the caret line by line; recall only steps when the caret is on the first line (ArrowUp) or last line (ArrowDown).
|
|
41
|
+
- An open mention or slash-command popover keeps owning the arrow keys; the hook yields whenever a trigger popover is active.
|
|
42
|
+
- IME composition, modifier keys, text selections, and events a preceding handler already `preventDefault`ed are left untouched.
|
|
43
|
+
- Switching threads or sending a message resets the browse position.
|
|
44
|
+
- The hook is inert on edit composers.
|
|
45
|
+
|
|
46
|
+
To interleave your own ArrowUp handling ahead of history (for example, stepping through queued messages), compose your handler before the hook's and call `preventDefault` when you consume the key:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<ComposerPrimitive.Input
|
|
50
|
+
onKeyDown={(e) => {
|
|
51
|
+
myHandler(e);
|
|
52
|
+
history.onKeyDown(e);
|
|
53
|
+
}}
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Thread Virtualization
|
|
3
|
+
description: Render very long threads with @tanstack/react-virtual and ThreadPrimitive.MessageByIndex.
|
|
4
|
+
platforms: ["react"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Virtualization mounts only the messages near the viewport and represents the rest as empty space, so a thread with thousands of messages scrolls like one with twenty. assistant-ui does not ship a virtualized thread component; this guide shows the supported composition, extracted from a production consumer and available as a runnable example: [`examples/with-virtualized-thread`](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-virtualized-thread).
|
|
8
|
+
|
|
9
|
+
## Do you need this?
|
|
10
|
+
|
|
11
|
+
Probably not. The default kit already renders message bodies with `content-visibility: auto` and `contain-intrinsic-size`, which skips paint work for off-screen messages, and `ThreadPrimitive.Viewport` handles auto-scroll. That covers typical threads. Reach for virtualization when React mount and update cost itself becomes the bottleneck: threads with hundreds to thousands of messages, or very heavy per-message content, where typing latency degrades because every message stays mounted.
|
|
12
|
+
|
|
13
|
+
## Rendering messages by index
|
|
14
|
+
|
|
15
|
+
`ThreadPrimitive.MessageByIndex` renders a single message at a given index and memoizes on the index plus per-field `components` identity. Pass a module-level components object so the memo holds and a streaming delta only re-renders the message it belongs to:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
const MESSAGE_COMPONENTS = { UserMessage, AssistantMessage };
|
|
19
|
+
|
|
20
|
+
<ThreadPrimitive.MessageByIndex index={index} components={MESSAGE_COMPONENTS} />;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Grouping into turns
|
|
24
|
+
|
|
25
|
+
Virtualizing per user turn (a user message plus the responses that follow it) gives the virtualizer stable, meaningfully sized items. Subscribe to a single signature string so the selector returns a primitive and the turn array only rebuilds when membership actually changes:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
const signature = useAuiState((s) =>
|
|
29
|
+
s.thread.messages.map((m, i) => `${i}:${m.role}:${m.id}`).join("\n"),
|
|
30
|
+
);
|
|
31
|
+
const turns = useMemo(() => buildTurns(signature), [signature]);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Padding spacers, not absolute positioning
|
|
35
|
+
|
|
36
|
+
Render the virtual items in normal document flow inside a spacer div whose `paddingTop`/`paddingBottom` represent the unmounted regions. Items remain regular flow children, so message CSS (including `position: sticky` patterns and the kit styling) keeps working, and `virtualizer.measureElement` records real heights as items mount:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
const items = virtualizer.getVirtualItems();
|
|
40
|
+
const paddingTop = items[0]?.start ?? 0;
|
|
41
|
+
const paddingBottom = Math.max(
|
|
42
|
+
0,
|
|
43
|
+
virtualizer.getTotalSize() - (items.at(-1)?.end ?? 0),
|
|
44
|
+
);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Owning the scroll element
|
|
48
|
+
|
|
49
|
+
The composition owns its scroll container instead of using `ThreadPrimitive.Viewport`: the built-in auto-scroll assumes every message is mounted, and its resize-driven re-pin can fight the virtualizer's measurement adjustments. Three pieces replace it:
|
|
50
|
+
|
|
51
|
+
1. **Auto-follow.** A ResizeObserver on the content wrapper re-pins the scroller to the bottom while a sticky flag is armed. The flag disarms when the user scrolls up (detected as `scrollTop` decreasing while `scrollHeight` and `clientHeight` are stable, the same heuristic the built-in viewport uses, plus wheel-up and touchmove) and re-arms when the user returns to the bottom.
|
|
52
|
+
2. **Measurement guard.** While pinned at the bottom, the virtualizer's own scroll adjustments on item re-measurement are suppressed via a custom `scrollToFn`; without this the two scroll writers fight and the view rubber-bands during streaming.
|
|
53
|
+
3. **Run-start jump.** A `useLayoutEffect` observes `s.thread.isRunning` flipping to true and jumps to the bottom before paint, so a just-sent message never flashes below the fold. The `thread.runStart` event is deprecated; deriving the transition from state is the supported path.
|
|
54
|
+
|
|
55
|
+
The production consumer this is extracted from disables streaming auto-follow entirely as a product choice; the example keeps following because it matches `ThreadPrimitive.Viewport`'s default behavior. Both are valid, and the disarm guard makes either safe.
|
|
56
|
+
|
|
57
|
+
## Try it
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
npx assistant-ui@latest create my-app
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then copy `app/VirtualizedThread.tsx`, `app/MyRuntimeProvider.tsx`, and `app/seed-messages.ts` from [the example](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-virtualized-thread), or clone the repo and run `pnpm --filter with-virtualized-thread dev`.
|
|
@@ -62,7 +62,7 @@ useAuiEvent("thread.runStart", (payload) => {
|
|
|
62
62
|
|
|
63
63
|
### useNotification
|
|
64
64
|
|
|
65
|
-
Ring the terminal bell and emit an OSC desktop notification when the assistant finishes a run, stops with an error, or pauses for
|
|
65
|
+
Ring the terminal bell and emit an OSC desktop notification when the assistant finishes a run, stops with an error, or pauses for user input.
|
|
66
66
|
|
|
67
67
|
```tsx
|
|
68
68
|
import { useNotification } from "@assistant-ui/react-ink";
|
|
@@ -130,7 +130,7 @@ const runtime = useLocalRuntime(chatModel, {
|
|
|
130
130
|
| `adapters.feedback` | `FeedbackAdapter` | Adapter for message feedback (thumbs up/down) |
|
|
131
131
|
| `adapters.suggestion` | `SuggestionAdapter` | Adapter for suggested prompts |
|
|
132
132
|
| `cloud` | `AssistantCloud` | Cloud instance for thread persistence via `@assistant-ui/cloud` |
|
|
133
|
-
| `unstable_humanToolNames` | `string[]` | Tool names that
|
|
133
|
+
| `unstable_humanToolNames` | `string[]` | Tool names that pause the run until a result is added via `addResult` |
|
|
134
134
|
|
|
135
135
|
### useRemoteThreadListRuntime
|
|
136
136
|
|
|
@@ -80,7 +80,7 @@ const runtime = useLocalRuntime(chatModel, {
|
|
|
80
80
|
| `maxSteps` | `number` | Maximum tool call steps per run |
|
|
81
81
|
| `cloud` | `AssistantCloud` | Optional cloud instance for persistence |
|
|
82
82
|
| `adapters` | `object` | Optional adapter overrides (see below) |
|
|
83
|
-
| `unstable_humanToolNames` | `string[]` | Tool names that pause the run
|
|
83
|
+
| `unstable_humanToolNames` | `string[]` | Tool names that pause the run until a result is added via `addResult` |
|
|
84
84
|
|
|
85
85
|
The `adapters` option accepts the following fields (all optional):
|
|
86
86
|
|
|
@@ -66,9 +66,7 @@ messages are gone on the next page load.
|
|
|
66
66
|
`showThinking: false`, so imported reasoning messages are dropped at conversion
|
|
67
67
|
time, the same way a live run never stores them.
|
|
68
68
|
|
|
69
|
-
`fromAgUiMessages` reconstructs the text, reasoning, and tool calls of each
|
|
70
|
-
message. Non-text message content such as images and files is not restored, so a
|
|
71
|
-
backend that persists multimodal messages loads only their text on reload.
|
|
69
|
+
`fromAgUiMessages` reconstructs the text, reasoning, and tool calls of each message. Multimodal user input (`image`, `audio`, `video`, and `document` parts, as well as legacy `binary` parts) is restored as attachments on the user message, so a backend that persists multimodal messages shows them again on reload and re-sends them on the next run. Legacy `binary` parts that only reference a file id are not restored.
|
|
72
70
|
|
|
73
71
|
## Thread list (experimental)
|
|
74
72
|
|
|
@@ -30,7 +30,7 @@ A non-exhaustive list of `unstable_` exports surfaced in the runtime docs.
|
|
|
30
30
|
| API | Package | Notes |
|
|
31
31
|
| --- | --- | --- |
|
|
32
32
|
| `unstable_createMessageConverter` | `@assistant-ui/react` | Message-format converter used by AssistantTransport and DataStream. |
|
|
33
|
-
| `unstable_humanToolNames` | `@assistant-ui/react` | Tool names that pause
|
|
33
|
+
| `unstable_humanToolNames` | `@assistant-ui/react` | Tool names that pause the run until a result is added via `addResult`. Only available on LocalRuntime; not supported in DataStream. |
|
|
34
34
|
| `unstable_threadListAdapter` | `@assistant-ui/react-langgraph` | LangGraph thread-list adapter slot on `useLangGraphRuntime`. |
|
|
35
35
|
| `unstable_createLangGraphStream` | `@assistant-ui/react-langgraph` | End-to-end cancellation primitive. |
|
|
36
36
|
| `unstable_Provider` | Various adapters | Thread-scoped provider on `RemoteThreadListAdapter`. Must render children synchronously. |
|
|
@@ -206,7 +206,7 @@ const runtime = useDataStreamRuntime({
|
|
|
206
206
|
## Tool integration
|
|
207
207
|
|
|
208
208
|
<Callout type="warn">
|
|
209
|
-
Human-in-the-loop tools (`unstable_humanToolNames`, `human()` interrupts) are not supported in the data stream runtime. Use [`LocalRuntime`](/docs/runtimes/custom/local-runtime) directly if you need
|
|
209
|
+
Human-in-the-loop tools (`unstable_humanToolNames`, `human()` interrupts) are not supported in the data stream runtime. Use [`LocalRuntime`](/docs/runtimes/custom/local-runtime#human-in-the-loop-tools) directly if you need them.
|
|
210
210
|
</Callout>
|
|
211
211
|
|
|
212
212
|
### Frontend tools
|
|
@@ -457,18 +457,122 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
|
|
|
457
457
|
|
|
458
458
|
See the [tools guide](/docs/tools/defining-tools) for advanced patterns.
|
|
459
459
|
|
|
460
|
-
### Human-in-the-loop
|
|
460
|
+
### Human-in-the-loop tools
|
|
461
461
|
|
|
462
|
-
|
|
462
|
+
Tools listed in `unstable_humanToolNames` are not executed by code. The run pauses on the tool call and the user supplies the result through the tool UI:
|
|
463
463
|
|
|
464
464
|
```ts
|
|
465
465
|
const runtime = useLocalRuntime(MyModelAdapter, {
|
|
466
|
-
unstable_humanToolNames: ["
|
|
466
|
+
unstable_humanToolNames: ["send_email"],
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
The pause is driven by the message status your adapter returns; `LocalRuntime` never sets it for you. When the model requests a human tool call, end the run with `status: { type: "requires-action", reason: "tool-calls" }`. Without that status, the runtime marks the message complete and nothing waits. Only return it while a listed tool call is missing its result: unresolved tool calls that are not listed do not hold the run, so the runtime would invoke your adapter again immediately.
|
|
471
|
+
|
|
472
|
+
```tsx
|
|
473
|
+
const MyModelAdapter: ChatModelAdapter = {
|
|
474
|
+
async run({ messages, abortSignal, unstable_getMessage }) {
|
|
475
|
+
const toolResults = unstable_getMessage().content.flatMap((part) =>
|
|
476
|
+
part.type === "tool-call" && part.result !== undefined
|
|
477
|
+
? [{ toolCallId: part.toolCallId, result: part.result }]
|
|
478
|
+
: [],
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
const result = await fetch("<YOUR_API_ENDPOINT>", {
|
|
482
|
+
method: "POST",
|
|
483
|
+
headers: { "Content-Type": "application/json" },
|
|
484
|
+
body: JSON.stringify({ messages, toolResults }),
|
|
485
|
+
signal: abortSignal,
|
|
486
|
+
});
|
|
487
|
+
const data = await result.json();
|
|
488
|
+
|
|
489
|
+
if (data.toolCall) {
|
|
490
|
+
return {
|
|
491
|
+
content: [
|
|
492
|
+
{
|
|
493
|
+
type: "tool-call",
|
|
494
|
+
toolCallId: data.toolCall.id,
|
|
495
|
+
toolName: data.toolCall.name,
|
|
496
|
+
args: data.toolCall.args,
|
|
497
|
+
argsText: JSON.stringify(data.toolCall.args),
|
|
498
|
+
},
|
|
499
|
+
],
|
|
500
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return { content: [{ type: "text", text: data.text }] };
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
The full loop:
|
|
510
|
+
|
|
511
|
+
1. **The run pauses.** While a listed tool call has no result, the runtime stops invoking your adapter. The unresolved tool call part reports `status.type === "requires-action"` to its renderer.
|
|
512
|
+
2. **The user responds.** The tool UI completes the call with `addResult(...)`. The stock [`ToolFallback`](/docs/ui/tool-fallback) component handles this out of the box: in the requires-action state it shows Allow and Deny buttons that record the decision as the tool result.
|
|
513
|
+
3. **The run resumes.** Once every listed tool call has a result, the runtime invokes your adapter again. The resumed call receives the same `messages` array as before (it ends at the user message; the in-progress assistant message is not part of it), so read the recorded results from `unstable_getMessage().content` as shown above. Content returned by the resumed call is appended to the same assistant message.
|
|
514
|
+
|
|
515
|
+
For a custom confirmation UI, register a human tool whose `render` completes the call with `addResult`. The shape of the result payload is yours to define; the adapter receives it verbatim and translates it for your backend:
|
|
516
|
+
|
|
517
|
+
```tsx
|
|
518
|
+
const toolkit = defineToolkit({
|
|
519
|
+
send_email: {
|
|
520
|
+
type: "human",
|
|
521
|
+
description: "Send an email after the user confirms",
|
|
522
|
+
parameters: z.object({ to: z.string(), subject: z.string() }),
|
|
523
|
+
render: ({ args, result, addResult }) => {
|
|
524
|
+
if (result) {
|
|
525
|
+
return <p>{result.approved ? "Sent" : `Cancelled: ${result.reason}`}</p>;
|
|
526
|
+
}
|
|
527
|
+
return (
|
|
528
|
+
<div>
|
|
529
|
+
<p>
|
|
530
|
+
Send "{args.subject}" to {args.to}?
|
|
531
|
+
</p>
|
|
532
|
+
<button onClick={() => addResult({ approved: true })}>Allow</button>
|
|
533
|
+
<button
|
|
534
|
+
onClick={() =>
|
|
535
|
+
addResult({ approved: false, reason: "User declined" })
|
|
536
|
+
}
|
|
537
|
+
>
|
|
538
|
+
Deny
|
|
539
|
+
</button>
|
|
540
|
+
</div>
|
|
541
|
+
);
|
|
542
|
+
},
|
|
543
|
+
},
|
|
467
544
|
});
|
|
468
545
|
```
|
|
469
546
|
|
|
470
547
|
`unstable_humanToolNames` is unstable; see [stability](/docs/runtimes/concepts/stability).
|
|
471
548
|
|
|
549
|
+
### Approval gates
|
|
550
|
+
|
|
551
|
+
The [server-side approval gate](/docs/tools/tool-ui#server-side-approval-gates) is also supported on `LocalRuntime`, for actions your backend executes after the user authorizes them. Where a human tool asks the user to supply the tool result, an approval gate asks the user to allow or block an action the adapter performs. Emit `approval: { id }` on the tool call part and end the run with the same `requires-action` status:
|
|
552
|
+
|
|
553
|
+
```tsx
|
|
554
|
+
return {
|
|
555
|
+
content: [
|
|
556
|
+
{
|
|
557
|
+
type: "tool-call",
|
|
558
|
+
toolCallId: data.toolCall.id,
|
|
559
|
+
toolName: data.toolCall.name,
|
|
560
|
+
args: data.toolCall.args,
|
|
561
|
+
argsText: JSON.stringify(data.toolCall.args),
|
|
562
|
+
approval: { id: data.toolCall.id },
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
status: { type: "requires-action", reason: "tool-calls" },
|
|
566
|
+
};
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
A tool call with a pending approval pauses the run, whether or not the tool is listed in `unstable_humanToolNames`. Always emit gates in the pending state (`approval: { id }` with no `approved` field); a part that arrives already decided is treated as resolved and the runtime invokes the adapter again immediately. The stock [`ToolFallback`](/docs/ui/tool-fallback) Allow and Deny buttons, or a custom renderer calling `respondToApproval({ approved, reason? })`, record the decision:
|
|
570
|
+
|
|
571
|
+
- **Deny** sets `approval.approved: false` and synthesizes an error result (`{ error: reason || "Tool approval denied" }` with `isError: true`), so the model sees the denial.
|
|
572
|
+
- **Allow** sets `approval.approved: true` and leaves the result empty; performing the action is your adapter's job.
|
|
573
|
+
|
|
574
|
+
Once every pending approval on the message is decided and every listed human tool has a result, the runtime invokes your adapter again. Read the decisions from `unstable_getMessage().content`, perform the approved actions, and return the follow-up response. A tool call that carries an approval is owned by the gate: it does not additionally require a result, even when its name is listed in `unstable_humanToolNames`.
|
|
575
|
+
|
|
472
576
|
## Resuming a run
|
|
473
577
|
|
|
474
578
|
`resumeRun` reconnects to an in-progress assistant run. Useful for page refresh, network reconnect, tab backgrounding, or thread switching when the backend is still generating.
|
|
@@ -743,7 +847,7 @@ const CustomAPIAdapter: ChatModelAdapter = {
|
|
|
743
847
|
name: "unstable_humanToolNames",
|
|
744
848
|
type: "string[]",
|
|
745
849
|
description:
|
|
746
|
-
"Tool names that
|
|
850
|
+
"Tool names that pause the run until the user supplies a result via addResult (unstable).",
|
|
747
851
|
},
|
|
748
852
|
]}
|
|
749
853
|
/>
|
|
@@ -28,13 +28,13 @@ assistant-ui ships React adapter packages for these. Pick the matching card and
|
|
|
28
28
|
icon={<VercelIcon width={20} height={20} />}
|
|
29
29
|
title="Vercel AI SDK"
|
|
30
30
|
description="useChat hook, streaming, tools, attachments, multi-step. v6 current; v5 / v4 legacy."
|
|
31
|
-
href="/docs/runtimes/ai-sdk"
|
|
31
|
+
href="/docs/runtimes/ai-sdk/overview"
|
|
32
32
|
/>
|
|
33
33
|
<Card
|
|
34
34
|
icon={<LangGraphIcon width={20} height={20} className="text-[#1C3C3C] dark:text-[#5b9595]" />}
|
|
35
35
|
title="LangGraph"
|
|
36
36
|
description="Direct integration with @langchain/langgraph-sdk. Subgraph events, UI messages, message metadata."
|
|
37
|
-
href="/docs/runtimes/langgraph"
|
|
37
|
+
href="/docs/runtimes/langgraph/overview"
|
|
38
38
|
/>
|
|
39
39
|
<Card
|
|
40
40
|
icon={<LangChainIcon width={20} height={20} className="text-[#7FC8FF]" />}
|
|
@@ -46,7 +46,7 @@ assistant-ui ships React adapter packages for these. Pick the matching card and
|
|
|
46
46
|
icon={<AdkIcon width={20} height={20} />}
|
|
47
47
|
title="Google ADK"
|
|
48
48
|
description="ADK JS or Python agents. Tool confirmations, auth flows, multi-agent, code execution."
|
|
49
|
-
href="/docs/runtimes/google-adk"
|
|
49
|
+
href="/docs/runtimes/google-adk/overview"
|
|
50
50
|
/>
|
|
51
51
|
<Card
|
|
52
52
|
icon={<A2AIcon width={20} height={20} />}
|
|
@@ -58,14 +58,14 @@ assistant-ui ships React adapter packages for these. Pick the matching card and
|
|
|
58
58
|
icon={<AguiIcon width={20} height={20} />}
|
|
59
59
|
title="AG-UI Protocol"
|
|
60
60
|
description="AG-UI agents (CopilotKit, custom servers). Streaming text, thinking, tool calls, state snapshots."
|
|
61
|
-
href="/docs/runtimes/ag-ui"
|
|
61
|
+
href="/docs/runtimes/ag-ui/overview"
|
|
62
62
|
/>
|
|
63
63
|
<PlatformOnly platforms={["react"]}>
|
|
64
64
|
<Card
|
|
65
65
|
icon={<OpenCodeIcon width={20} height={20} />}
|
|
66
66
|
title="OpenCode"
|
|
67
67
|
description="OpenCode coding-agent server. Permission flows, questions, fork / revert. Experimental."
|
|
68
|
-
href="/docs/runtimes/opencode"
|
|
68
|
+
href="/docs/runtimes/opencode/overview"
|
|
69
69
|
/>
|
|
70
70
|
</PlatformOnly>
|
|
71
71
|
</Cards>
|
|
@@ -104,7 +104,7 @@ If you do not know your framework yet, or your backend is custom, pick by what y
|
|
|
104
104
|
| Backend that already speaks the data stream protocol | [DataStream](/docs/runtimes/custom/data-stream) |
|
|
105
105
|
| Stream full agent state snapshots (not just messages) | [AssistantTransport](/docs/runtimes/custom/assistant-transport) |
|
|
106
106
|
|
|
107
|
-
If none of the framework adapters fits, start at [custom backend](/docs/runtimes/custom).
|
|
107
|
+
If none of the framework adapters fits, start at [custom backend](/docs/runtimes/custom/overview).
|
|
108
108
|
|
|
109
109
|
## Shared concepts
|
|
110
110
|
|