@assistant-ui/mcp-docs-server 0.2.0 → 0.2.1

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.
Files changed (126) hide show
  1. package/.docs/organized/code-examples/waterfall.md +12 -13
  2. package/.docs/organized/code-examples/with-a2a.md +16 -11
  3. package/.docs/organized/code-examples/with-ag-ui.md +14 -12
  4. package/.docs/organized/code-examples/with-ai-sdk-v7.md +13 -12
  5. package/.docs/organized/code-examples/with-artifacts.md +13 -12
  6. package/.docs/organized/code-examples/with-assistant-transport.md +19 -27
  7. package/.docs/organized/code-examples/with-browser-extension.md +17 -10
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +16 -14
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +9 -10
  10. package/.docs/organized/code-examples/with-cloud.md +18 -13
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +13 -12
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +13 -13
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +17 -15
  14. package/.docs/organized/code-examples/with-eve.md +65 -11
  15. package/.docs/organized/code-examples/with-expo.md +20 -19
  16. package/.docs/organized/code-examples/with-external-store.md +16 -11
  17. package/.docs/organized/code-examples/with-ffmpeg.md +20 -14
  18. package/.docs/organized/code-examples/with-generative-ui.md +16 -17
  19. package/.docs/organized/code-examples/with-google-adk.md +17 -12
  20. package/.docs/organized/code-examples/with-heat-graph.md +6 -7
  21. package/.docs/organized/code-examples/with-image-generation.md +9 -10
  22. package/.docs/organized/code-examples/with-interactables.md +13 -12
  23. package/.docs/organized/code-examples/with-langchain.md +7 -8
  24. package/.docs/organized/code-examples/with-langgraph.md +17 -11
  25. package/.docs/organized/code-examples/with-livekit.md +12 -12
  26. package/.docs/organized/code-examples/with-mcp.md +13 -14
  27. package/.docs/organized/code-examples/with-nuxt.md +2492 -0
  28. package/.docs/organized/code-examples/with-opencode.md +23 -14
  29. package/.docs/organized/code-examples/with-pi.md +59 -57
  30. package/.docs/organized/code-examples/with-react-hook-form.md +14 -13
  31. package/.docs/organized/code-examples/with-react-ink-web.md +7 -8
  32. package/.docs/organized/code-examples/with-react-ink.md +5 -5
  33. package/.docs/organized/code-examples/with-react-router.md +15 -9
  34. package/.docs/organized/code-examples/with-resumable-stream.md +11 -12
  35. package/.docs/organized/code-examples/with-store.md +27 -16
  36. package/.docs/organized/code-examples/with-tanstack.md +18 -12
  37. package/.docs/organized/code-examples/with-tap-runtime.md +15 -15
  38. package/.docs/organized/code-examples/with-virtualized-thread.md +8 -9
  39. package/.docs/organized/code-examples/with-vue.md +408 -0
  40. package/.docs/raw/docs/(docs)/cli.mdx +1 -1
  41. package/.docs/raw/docs/(docs)/index.mdx +9 -76
  42. package/.docs/raw/docs/(docs)/installation.mdx +4 -18
  43. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +24 -4
  44. package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +8 -0
  45. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +24 -1
  46. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
  47. package/.docs/raw/docs/cloud/ai-sdk.mdx +2 -2
  48. package/.docs/raw/docs/copilots/model-context.mdx +4 -3
  49. package/.docs/raw/docs/copilots/motivation.mdx +4 -4
  50. package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
  51. package/.docs/raw/docs/guides/electron.mdx +1 -1
  52. package/.docs/raw/docs/guides/resumable-streams.mdx +74 -3
  53. package/.docs/raw/docs/guides/suggestions.mdx +9 -9
  54. package/.docs/raw/docs/ink/hooks.mdx +9 -4
  55. package/.docs/raw/docs/ink/primitives.mdx +4 -3
  56. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +16 -0
  57. package/.docs/raw/docs/integrations/auth/better-auth.mdx +1 -1
  58. package/.docs/raw/docs/integrations/auth/clerk.mdx +1 -1
  59. package/.docs/raw/docs/integrations/auth/next-auth.mdx +1 -1
  60. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +1 -1
  61. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +2 -2
  62. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
  63. package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
  64. package/.docs/raw/docs/integrations/observability/helicone.mdx +2 -2
  65. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  66. package/.docs/raw/docs/integrations/observability/langsmith.mdx +2 -2
  67. package/.docs/raw/docs/migrations/toolkit-tools.mdx +15 -13
  68. package/.docs/raw/docs/migrations/v0-15.mdx +84 -4
  69. package/.docs/raw/docs/primitives/attachment.mdx +2 -2
  70. package/.docs/raw/docs/primitives/composer.mdx +2 -2
  71. package/.docs/raw/docs/primitives/message.mdx +33 -1
  72. package/.docs/raw/docs/primitives/suggestion.mdx +1 -1
  73. package/.docs/raw/docs/react-native/hooks.mdx +14 -4
  74. package/.docs/raw/docs/react-native/index.mdx +1 -1
  75. package/.docs/raw/docs/react-native/primitives.mdx +2 -2
  76. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +1 -1
  77. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +1 -1
  78. package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +9 -10
  79. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +9 -10
  80. package/.docs/raw/docs/runtimes/concepts/threads.mdx +40 -1
  81. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +18 -0
  82. package/.docs/raw/docs/runtimes/custom/external-store.mdx +30 -1
  83. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +36 -9
  84. package/.docs/raw/docs/runtimes/eve/overview.mdx +51 -0
  85. package/.docs/raw/docs/runtimes/eve/quickstart.mdx +51 -2
  86. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -12
  87. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +1 -1
  88. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +7 -7
  89. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +4 -4
  90. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +1 -0
  91. package/.docs/raw/docs/runtimes/opencode/overview.mdx +10 -0
  92. package/.docs/raw/docs/tools/backend.mdx +2 -2
  93. package/.docs/raw/docs/tools/defining-tools.mdx +9 -7
  94. package/.docs/raw/docs/tools/dynamic-tools.mdx +6 -4
  95. package/.docs/raw/docs/tools/interactables-legacy.mdx +24 -11
  96. package/.docs/raw/docs/tools/interactables.mdx +24 -13
  97. package/.docs/raw/docs/tools/mcp-apps.mdx +35 -6
  98. package/.docs/raw/docs/tools/mcp.mdx +9 -7
  99. package/.docs/raw/docs/tools/tool-ui.mdx +26 -22
  100. package/.docs/raw/docs/tools/user-managed-mcp.mdx +12 -7
  101. package/.docs/raw/docs/ui/file.mdx +6 -1
  102. package/.docs/raw/docs/ui/mcp-config.mdx +8 -3
  103. package/.docs/raw/docs/ui/model-selector.mdx +8 -8
  104. package/.docs/raw/docs/ui/part-grouping.mdx +1 -1
  105. package/.docs/raw/docs/ui/thread.mdx +24 -5
  106. package/.docs/raw/docs/utilities/react-o11y.mdx +7 -9
  107. package/dist/constants.js +2 -2
  108. package/dist/constants.js.map +1 -1
  109. package/dist/index.js.map +1 -1
  110. package/dist/prepare-docs/prepare.js.map +1 -1
  111. package/dist/tools/docs.js +4 -2
  112. package/dist/tools/docs.js.map +1 -1
  113. package/dist/tools/examples.js +2 -1
  114. package/dist/tools/examples.js.map +1 -1
  115. package/dist/tools/resources.js +2 -1
  116. package/dist/tools/resources.js.map +1 -1
  117. package/dist/tools/tests/test-setup.js +2 -1
  118. package/dist/tools/tests/test-setup.js.map +1 -1
  119. package/dist/tools/xulux-templates.js +4 -2
  120. package/dist/tools/xulux-templates.js.map +1 -1
  121. package/dist/utils/mdx.js +2 -1
  122. package/dist/utils/mdx.js.map +1 -1
  123. package/dist/xulux/catalog-client.js +1 -1
  124. package/dist/xulux/catalog-client.js.map +1 -1
  125. package/package.json +4 -4
  126. package/src/tools/tests/docs.test.ts +2 -2
@@ -133,16 +133,96 @@ const aui = useAui(scopes, { parent });
133
133
 
134
134
  // After
135
135
  const Scoped = ({ children }) => {
136
- const aui = useAui(scopes); // extends the AuiProvider parent
137
- return <AuiProvider value={aui}>{children}</AuiProvider>;
136
+ const aui = useAui();
137
+ const config = AuiConfig(scopes);
138
+ return (
139
+ <AuiProvider extends={aui} config={config}>
140
+ {children}
141
+ </AuiProvider>
142
+ );
138
143
  };
139
144
 
140
- <AuiProvider value={parent}>
145
+ const rootConfig = AuiConfig({});
146
+
147
+ <AuiProvider extends={parent} config={rootConfig}>
141
148
  <Scoped />
142
149
  </AuiProvider>;
143
150
  ```
144
151
 
145
- Where `{ parent: null }` was used to detach from context, `<AuiProvider value={null}>` now provides an isolated empty root (experimental).
152
+ Where `{ parent: null }` was used to detach from context, `<AuiProvider extends={null} config={config}>` where `const config = AuiConfig({})` now provides an isolated empty root.
153
+
154
+ ## `AuiProvider` Grammar
155
+
156
+ `AuiProvider` takes a `config` built with `AuiConfig(...)` — raw object literals are a type error. At the top level, `config` alone creates the subtree's client. Nested under a parent provider, `extends` is mandatory: `extends={aui}` extends the parent, `extends={null}` isolates (dev-enforced). `ref` receives the resulting client after mount.
157
+
158
+ ```tsx
159
+ const aui = useAui();
160
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
161
+
162
+ // Top-level root
163
+ <AuiProvider config={config}>
164
+
165
+ // Nested: extend the parent
166
+ <AuiProvider extends={aui} config={config}>
167
+
168
+ // Nested: isolate from the parent
169
+ <AuiProvider extends={null} config={config}>
170
+ ```
171
+
172
+ `AuiConfig` is exported from `@assistant-ui/store` and re-exported from `@assistant-ui/react`, `@assistant-ui/react-native`, and `@assistant-ui/react-ink`.
173
+
174
+ ### `value` Prop Deprecated
175
+
176
+ ```tsx
177
+ // Before
178
+ <AuiProvider value={client}>
179
+ <AuiProvider value={null}>
180
+
181
+ // After
182
+ const config = AuiConfig({});
183
+
184
+ <AuiProvider extends={client} config={config}>
185
+ <AuiProvider extends={null} config={config}>
186
+ ```
187
+
188
+ The replacement exposes a client extending the given one, not the same instance — `useAui()` beneath it returns the new client, with scope access delegating to `client`. The deprecated `value={client}` form behaves the same way: it also exposes a derived client rather than the exact instance, and the given client must implement `subscribe`.
189
+
190
+ ### `useAui({ ... })` Extension Overload Deprecated
191
+
192
+ ```tsx
193
+ // Before
194
+ const aui = useAui({ tools: Tools({ toolkit }) });
195
+ return <AuiProvider value={aui}>{children}</AuiProvider>;
196
+
197
+ // After
198
+ const aui = useAui();
199
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
200
+ return (
201
+ <AuiProvider extends={aui} config={config}>
202
+ {children}
203
+ </AuiProvider>
204
+ );
205
+ ```
206
+
207
+ Where the extended client was passed to `<AssistantRuntimeProvider aui={aui}>`, use the new `config` prop instead — the scopes are provided alongside the runtime's threads scope:
208
+
209
+ ```tsx
210
+ // Before
211
+ const aui = useAui({ tools: Tools({ toolkit }) });
212
+ return (
213
+ <AssistantRuntimeProvider aui={aui} runtime={runtime}>
214
+ {children}
215
+ </AssistantRuntimeProvider>
216
+ );
217
+
218
+ // After
219
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
220
+ return (
221
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
222
+ {children}
223
+ </AssistantRuntimeProvider>
224
+ );
225
+ ```
146
226
 
147
227
  ## Still Deprecated (not removed)
148
228
 
@@ -53,7 +53,7 @@ import { AttachmentPrimitive, ComposerPrimitive } from "@assistant-ui/react";
53
53
  </ComposerPrimitive.Root>
54
54
  ```
55
55
 
56
- `Root` renders a `<div>`, `unstable_Thumb` renders a `<div>` showing the file extension with a leading dot (e.g., `.pdf`), `Name` renders plain text, and `Remove` renders a `<button>`.
56
+ `Root` renders a `<div>`, `unstable_Thumb` renders a `<div>` showing the file extension with a leading dot (e.g., `.pdf`), or the attachment type (e.g., `image`) when the filename has no extension (including leading-dot names like `.env`), `Name` renders plain text, and `Remove` renders a `<button>`. If you pass `children` to `unstable_Thumb`, they override that text.
57
57
 
58
58
  <Callout type="info">
59
59
  Runtime setup: primitives require runtime context. Wrap your UI in `AssistantRuntimeProvider` with a runtime (for example `useLocalRuntime(...)`). See [Pick a Runtime](/docs/runtimes/pick-a-runtime).
@@ -124,7 +124,7 @@ Container for a single attachment item. Renders a `<div>` element unless `asChil
124
124
 
125
125
  ### unstable_Thumb
126
126
 
127
- Thumbnail slot for attachment previews. Renders a `<div>` element unless `asChild` is set.
127
+ Thumbnail slot for attachment previews. Renders a `<div>` element unless `asChild` is set. By default it shows the file extension with a leading dot (e.g., `.pdf`); if you pass `children`, they override the automatic text.
128
128
 
129
129
  ```tsx
130
130
  <AttachmentPrimitive.unstable_Thumb className="flex size-10 items-center justify-center rounded bg-muted text-xs" />
@@ -258,7 +258,7 @@ Renders a single attachment at a specific index.
258
258
 
259
259
  ### AttachmentDropzone
260
260
 
261
- Drag-and-drop zone for file attachments. Sets `data-dragging` when a file is being dragged over it. Renders a `<div>` element unless `asChild` is set.
261
+ Drag-and-drop zone for file attachments. Sets `data-dragging` when a file is being dragged over it and the runtime supports attachments; without the attachments capability the dropzone accepts no files and shows no highlight. Non-file drags (text, links) are ignored. Renders a `<div>` element unless `asChild` is set.
262
262
 
263
263
  ```tsx
264
264
  <ComposerPrimitive.AttachmentDropzone className="rounded-xl border-2 border-dashed data-[dragging]:border-primary data-[dragging]:bg-primary/5">
@@ -507,7 +507,7 @@ Wrap your composer with `AttachmentDropzone` to support dragging files directly
507
507
  </ComposerPrimitive.AttachmentDropzone>
508
508
  ```
509
509
 
510
- The dropzone sets `data-dragging` when a file is being dragged over it, so you can style the active state with CSS.
510
+ The dropzone sets `data-dragging` when a file is being dragged over it, so you can style the active state with CSS. It requires the runtime's attachments capability (an attachment adapter); without it the dropzone stays inert.
511
511
 
512
512
  ### With Voice Input
513
513
 
@@ -112,6 +112,37 @@ Runtime setup: primitives require runtime context. Wrap your UI in `AssistantRun
112
112
  For most new code, prefer `MessagePrimitive.Parts` with a `children` render function. When you need adjacent grouping, use `MessagePrimitive.GroupedParts`.
113
113
  </Callout>
114
114
 
115
+ ### Part Types
116
+
117
+ A message part is one of three kinds, and which kind a new capability belongs to is decided by the list below rather than case by case.
118
+
119
+ | Kind | Parts | Grows by |
120
+ |------|-------|----------|
121
+ | Modality | `text`, `image`, `file` | Never. `file` carries every non-image binary modality through its `mimeType`. |
122
+ | Provider channel | `reasoning`, `source`, `tool-call`, `generative-ui` | Never. These mirror channels a model already emits. |
123
+ | Extensibility | `data` | Freely, through `name`. This is the growth path for anything app-level. |
124
+
125
+ `file` is the media carrier. Audio, video, PDFs and everything else are `{ type: "file", mimeType: "audio/mpeg" }` rather than part types of their own, so one renderer and one converter branch handle them all. The payload can be inline base64 or a URL; `sourceType` additionally allows an opaque storage id, which the LangChain-family runtimes honor and other adapters ignore.
126
+
127
+ Anything that is not a modality the model consumes or a channel it emits is a `data` part, routed by `name`:
128
+
129
+ ```tsx
130
+ <MessagePrimitive.Parts
131
+ components={{
132
+ data: {
133
+ by_name: { citation: MyCitation },
134
+ Fallback: MyDataFallback,
135
+ },
136
+ }}
137
+ />
138
+ ```
139
+
140
+ <Callout type="warn">
141
+ `Unstable_AudioMessagePart` and the `Unstable_Audio` slot are deprecated. The audio part cannot carry a filename, has no way to declare how its payload goes on the wire (no `sourceType`, so no storage id), and exists only on user messages, so a model that returns audio has no way to express it. Send audio as a `file` part with an `audio/*` mime type instead, and give it a filename.
142
+
143
+ Adapter coverage for the file path is still uneven, so check your adapter's converter before relying on file parts. Known so far: `react-pi` and the assistant-transport runtime have no file part on their user-content surface at all, so one is dropped; `react-data-stream` carries the payload but not the filename. Existing audio parts keep working; they will not gain fields.
144
+ </Callout>
145
+
115
146
  ### Tool Resolution
116
147
 
117
148
  Tool call parts resolve in this order:
@@ -149,7 +180,8 @@ Returning `null` still allows registered tool UIs and data renderer UIs to rende
149
180
  - `components.ChainOfThought` takes over all reasoning and tool-call rendering (mutually exclusive with `ToolGroup`, `ReasoningGroup`, `tools`, and `Reasoning`). This legacy path is deprecated; use `MessagePrimitive.GroupedParts` for grouped Chain of Thought in new code.
150
181
  - `data.by_name` and `data.Fallback` let you route custom data part types
151
182
  - `Quote` renders quoted message references from metadata
152
- - `Empty` and `Unstable_Audio` are available for edge and experimental rendering paths
183
+ - `Empty` is available for edge rendering paths
184
+ - `Unstable_Audio` is deprecated; render `audio/*` from the `File` slot instead
153
185
 
154
186
  ```tsx
155
187
  <MessagePrimitive.Parts
@@ -106,7 +106,7 @@ Both render a `<span>` and accept `children` to override the value from state:
106
106
 
107
107
  `Trigger`'s `send` prop controls what happens on click:
108
108
 
109
- - **`send={true}`**: immediately sends the suggestion as a new message. When the thread is running, it falls back to populating the composer instead.
109
+ - **`send={true}`**: immediately sends the suggestion as a new message. While a run is in progress, the suggestion is queued on runtimes that support queueing (leaving the composer draft untouched); otherwise the trigger is disabled.
110
110
  - **`send={false}`** (default): populates the composer text so the user can edit before sending
111
111
 
112
112
  ```tsx
@@ -155,12 +155,22 @@ export default defineToolkit({
155
155
  ```
156
156
 
157
157
  ```tsx title="ToolProvider.tsx"
158
- import { AuiProvider, Tools, useAui } from "@assistant-ui/react-native";
158
+ import {
159
+ AuiConfig,
160
+ AuiProvider,
161
+ Tools,
162
+ useAui,
163
+ } from "@assistant-ui/react-native";
159
164
  import toolkit from "./weather-toolkit";
160
165
 
161
166
  function ToolProvider({ children }: { children: React.ReactNode }) {
162
- const aui = useAui({ tools: Tools({ toolkit }) });
163
- return <AuiProvider value={aui}>{children}</AuiProvider>;
167
+ const aui = useAui();
168
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
169
+ return (
170
+ <AuiProvider extends={aui} config={config}>
171
+ {children}
172
+ </AuiProvider>
173
+ );
164
174
  }
165
175
  ```
166
176
 
@@ -218,7 +228,7 @@ export function useMyToolToolkit(someOuterProp: string) {
218
228
  }
219
229
  ```
220
230
 
221
- Import the toolkit hook, pass its result to `useAui({ tools: Tools({ toolkit }) })`, and provide the returned `aui` with `AuiProvider`, as shown in the [Tools](#tools) section above.
231
+ Import the toolkit hook, hoist `const aui = useAui()` and `const config = AuiConfig({ tools: Tools({ toolkit }) })` in the component body, and mount it with `<AuiProvider extends={aui} config={config}>`, as shown in the [Tools](#tools) section above.
222
232
 
223
233
  ### makeAssistantDataUI
224
234
 
@@ -69,7 +69,7 @@ export const maxDuration = 30;
69
69
  export async function POST(req: Request) {
70
70
  const { messages } = await req.json();
71
71
  const result = streamText({
72
- model: openai("gpt-5.4-nano"),
72
+ model: openai("gpt-5.6-luna"),
73
73
  messages: await convertToModelMessages(messages),
74
74
  });
75
75
  return result.toUIMessageStreamResponse();
@@ -437,7 +437,7 @@ Renders message content parts via a `components` prop. Tool call and data parts
437
437
  | `Reasoning` | `ReasoningMessagePartComponent` | Reasoning part renderer |
438
438
  | `Source` | `SourceMessagePartComponent` | Source part renderer |
439
439
  | `File` | `FileMessagePartComponent` | File part renderer |
440
- | `Unstable_Audio` | `AudioMessagePartComponent` | Audio part renderer (experimental) |
440
+ | `Unstable_Audio` | `Unstable_AudioMessagePartComponent` | Audio part renderer (deprecated, render `audio/*` from `File`) |
441
441
  | `tools` | `{ by_name?, Fallback? }` or `{ Override }` | Tool call rendering config — use `by_name` to map tool names to components, `Fallback` for unregistered tools, or `Override` to handle all tool calls |
442
442
  | `data` | `{ by_name?, Fallback? }` | Data part rendering config — use `by_name` to map data event names, `Fallback` for unmatched events |
443
443
  | `Empty` | `EmptyMessagePartComponent` | Component shown for empty messages |
@@ -541,7 +541,7 @@ Container `View` for an attachment.
541
541
 
542
542
  ### AttachmentPrimitive.Thumb
543
543
 
544
- `Text` component displaying the file extension (e.g. `.pdf`).
544
+ `Text` component displaying the file extension (e.g. `.pdf`), or the attachment type (e.g. `image`) when the filename has no extension (including leading-dot names like `.env`). If you pass `children`, they override that text.
545
545
 
546
546
  ```tsx
547
547
  <AttachmentPrimitive.Thumb style={styles.extension} />
@@ -35,7 +35,7 @@ import { openai } from "@ai-sdk/openai";
35
35
 
36
36
  export async function POST(req: Request) {
37
37
  const { messages } = await req.json();
38
- const result = streamText({ model: openai("gpt-5.4-nano"), messages });
38
+ const result = streamText({ model: openai("gpt-5.6-luna"), messages });
39
39
  return result.toDataStreamResponse();
40
40
  }
41
41
  ```
@@ -40,7 +40,7 @@ export const maxDuration = 30;
40
40
  export async function POST(req: Request) {
41
41
  const { messages }: { messages: Message[] } = await req.json();
42
42
  const result = streamText({
43
- model: openai("gpt-5.4-nano"),
43
+ model: openai("gpt-5.6-luna"),
44
44
  messages,
45
45
  tools: {
46
46
  get_current_weather: tool({
@@ -92,7 +92,7 @@ export async function POST(req: Request) {
92
92
  const { messages }: { messages: UIMessage[] } = await req.json();
93
93
 
94
94
  const result = streamText({
95
- model: openai("gpt-5.4-mini"),
95
+ model: openai("gpt-5.6-luna"),
96
96
  messages: await convertToModelMessages(messages), // async in v6
97
97
  tools: {
98
98
  get_current_weather: tool({
@@ -245,7 +245,7 @@ export async function POST(req: Request) {
245
245
  } = await req.json();
246
246
 
247
247
  const result = streamText({
248
- model: openai("gpt-5.4-mini"),
248
+ model: openai("gpt-5.6-luna"),
249
249
  system,
250
250
  messages: await convertToModelMessages(messages),
251
251
  tools: {
@@ -258,7 +258,7 @@ export async function POST(req: Request) {
258
258
  }
259
259
  ```
260
260
 
261
- Frontend tools are registered through `useAui` (see the [tools guide](/docs/tools/defining-tools)) and serialized for the backend via `frontendTools`.
261
+ Frontend tools are registered through the provider's `config` (see the [tools guide](/docs/tools/defining-tools)) and serialized for the backend via `frontendTools`.
262
262
 
263
263
  ## Multi-step tool calls
264
264
 
@@ -276,7 +276,7 @@ export async function POST(req: Request) {
276
276
  const { messages } = await req.json();
277
277
 
278
278
  const result = streamText({
279
- model: openai("gpt-5.4-mini"),
279
+ model: openai("gpt-5.6-luna"),
280
280
  messages: await convertToModelMessages(messages),
281
281
  tools: {
282
282
  /* ... */
@@ -305,7 +305,7 @@ export async function POST(req: Request) {
305
305
  const { messages } = await req.json();
306
306
 
307
307
  const result = streamText({
308
- model: openai("gpt-5.4-mini"),
308
+ model: openai("gpt-5.6-luna"),
309
309
  messages: await convertToModelMessages(messages),
310
310
  tools: {
311
311
  deploy: tool({
@@ -356,9 +356,9 @@ Render the gate with a toolkit entry. The `approval` field carries the gate stat
356
356
  import { useChat } from "@ai-sdk/react";
357
357
  import {
358
358
  AssistantRuntimeProvider,
359
+ AuiConfig,
359
360
  defineToolkit,
360
361
  Tools,
361
- useAui,
362
362
  } from "@assistant-ui/react";
363
363
  import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
364
364
  import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
@@ -397,10 +397,9 @@ export default function Page() {
397
397
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
398
398
  });
399
399
  const runtime = useAISDKRuntime(chat);
400
- const aui = useAui({ tools: Tools({ toolkit }) });
401
-
400
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
402
401
  return (
403
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
402
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
404
403
  <Thread />
405
404
  </AssistantRuntimeProvider>
406
405
  );
@@ -424,7 +423,7 @@ import { openai } from "@ai-sdk/openai";
424
423
  export async function POST(req: Request) {
425
424
  const { messages } = await req.json();
426
425
  const result = streamText({
427
- model: openai("gpt-5.4-mini"),
426
+ model: openai("gpt-5.6-luna"),
428
427
  messages: await convertToModelMessages(injectQuoteContext(messages)),
429
428
  });
430
429
  return result.toUIMessageStreamResponse();
@@ -90,7 +90,7 @@ export async function POST(req: Request) {
90
90
  const { messages }: { messages: UIMessage[] } = await req.json();
91
91
 
92
92
  const result = streamText({
93
- model: openai("gpt-5.4-mini"),
93
+ model: openai("gpt-5.6-luna"),
94
94
  messages: await convertToModelMessages(messages), // async in v7
95
95
  tools: {
96
96
  get_current_weather: tool({
@@ -245,7 +245,7 @@ export async function POST(req: Request) {
245
245
  } = await req.json();
246
246
 
247
247
  const result = streamText({
248
- model: openai("gpt-5.4-mini"),
248
+ model: openai("gpt-5.6-luna"),
249
249
  system,
250
250
  messages: await convertToModelMessages(messages),
251
251
  tools: {
@@ -260,7 +260,7 @@ export async function POST(req: Request) {
260
260
  }
261
261
  ```
262
262
 
263
- Frontend tools are registered through `useAui` (see the [tools guide](/docs/tools/defining-tools)) and serialized for the backend via `frontendTools`.
263
+ Frontend tools are registered through the provider's `config` (see the [tools guide](/docs/tools/defining-tools)) and serialized for the backend via `frontendTools`.
264
264
 
265
265
  ## Multi-step tool calls
266
266
 
@@ -280,7 +280,7 @@ export async function POST(req: Request) {
280
280
  const { messages } = await req.json();
281
281
 
282
282
  const result = streamText({
283
- model: openai("gpt-5.4-mini"),
283
+ model: openai("gpt-5.6-luna"),
284
284
  messages: await convertToModelMessages(messages),
285
285
  tools: {
286
286
  /* ... */
@@ -311,7 +311,7 @@ export async function POST(req: Request) {
311
311
  const { messages } = await req.json();
312
312
 
313
313
  const result = streamText({
314
- model: openai("gpt-5.4-mini"),
314
+ model: openai("gpt-5.6-luna"),
315
315
  messages: await convertToModelMessages(messages),
316
316
  tools: {
317
317
  deploy: tool({
@@ -368,9 +368,9 @@ Render the gate with a toolkit entry. The `approval` field carries the gate stat
368
368
  import { useChat } from "@ai-sdk/react";
369
369
  import {
370
370
  AssistantRuntimeProvider,
371
+ AuiConfig,
371
372
  defineToolkit,
372
373
  Tools,
373
- useAui,
374
374
  } from "@assistant-ui/react";
375
375
  import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
376
376
  import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
@@ -410,10 +410,9 @@ export default function Page() {
410
410
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
411
411
  });
412
412
  const runtime = useAISDKRuntime(chat);
413
- const aui = useAui({ tools: Tools({ toolkit }) });
414
-
413
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
415
414
  return (
416
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
415
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
417
416
  <Thread />
418
417
  </AssistantRuntimeProvider>
419
418
  );
@@ -439,7 +438,7 @@ import { openai } from "@ai-sdk/openai";
439
438
  export async function POST(req: Request) {
440
439
  const { messages } = await req.json();
441
440
  const result = streamText({
442
- model: openai("gpt-5.4-mini"),
441
+ model: openai("gpt-5.6-luna"),
443
442
  messages: await convertToModelMessages(injectQuoteContext(messages)),
444
443
  });
445
444
  return createUIMessageStreamResponse({
@@ -209,6 +209,45 @@ function ReloadOnAuth() {
209
209
 
210
210
  `reload()` discards in-flight responses from superseded calls, so it is safe to invoke on every auth transition.
211
211
 
212
+ ### Refetching the open thread
213
+
214
+ `reload()` re-runs `list()`, which refreshes thread list metadata only. It does not touch the messages of the thread the user is looking at. When the open thread's server state changes out of band, so that nothing arrives over the stream (a human-in-the-loop interrupt raised by another process, a stalled stream, a status change picked up by polling), call `aui.threads.reloadMainThread()`:
215
+
216
+ ```tsx
217
+ function RefetchOnInterrupt({ status }: { status: string }) {
218
+ const aui = useAui();
219
+ useEffect(() => {
220
+ if (status !== "interrupted") return;
221
+ aui.threads.reloadMainThread().catch(reportError);
222
+ }, [status]);
223
+ return null;
224
+ }
225
+ ```
226
+
227
+ What the promise means depends on the path. On the remount path, it resolves as soon as the replacement runtime attaches, which happens before that runtime's `load()` has started, so awaiting it does not mean the thread is fresh and a failed load cannot reach the caller. On the in-place path, it settles with the refetch itself and rejects when it fails, which is why the example above still handles the rejection.
228
+
229
+ A thread that has not been sent yet is left alone because it holds no remote state.
230
+
231
+ What happens to a run in progress depends on the path. The remount path drops the runtime that was rendering the run; whether the run itself stops is up to that hook's unmount cleanup, which core cannot enforce. On the in-place path the runtime that declared the capability decides, since core does not stop the run for it. Either way this belongs on an event rather than a short timer: drive it from a state change like the one above, or skip the call while `useAuiState((s) => s.thread.isRunning)` is true.
232
+
233
+ How the refetch happens depends on the runtime, in one of three ways. When it declares the capability, the thread runtime is reused: composer drafts survive, existing messages stay rendered while the fresh state loads, and the returned promise settles with the refetch, rejecting if it fails. A remote thread list without the capability remounts the runtime hook instead, which re-runs `load()` at the cost of discarding unsent composer input, and resolves once the new runtime attaches. The single and in-memory thread lists hold no remote state and have no hook to remount, so there it resolves without doing anything.
234
+
235
+ `useAuiState((s) => s.thread.capabilities.refetchThread)` reports which of those you would get, in place or not. It is not a signal for whether to offer a refresh at all: it is false on the remount path, where the call still does the work, and false again where the call does nothing.
236
+
237
+ Both the LangGraph and Google ADK adapters register the in-place refetch capability when their runtime hook receives a `load` function; without one they fall back to the remount path. Other remote adapters take the remount path unless they provide the capability themselves.
238
+
239
+ For an external store runtime, declare it with `onRefetchThread`, which is unrelated to `onReload` (that one re-generates an assistant message):
240
+
241
+ ```ts
242
+ useExternalStoreRuntime({
243
+ messages,
244
+ onNew,
245
+ onRefetchThread: async () => {
246
+ setMessages(await fetchMessages(threadId));
247
+ },
248
+ });
249
+ ```
250
+
212
251
  ### Paginating the thread list
213
252
 
214
253
  If your backend returns thread pages, return a `nextCursor` from `list()` and consume `aui.threads.hasMore` plus `aui.threads.loadMore()` in the UI. The runtime threads `params.after` back through `list()` on every `loadMore()`; the initial call passes no `params`, so treat a missing `after` as "first page". `reload()` resets the cursor so the next load starts from page 1 again.
@@ -326,7 +365,7 @@ A few invariants worth knowing when wiring a custom UI on top of `loadMore()`:
326
365
  },
327
366
  {
328
367
  name: "unstable_Provider",
329
- type: "ComponentType<PropsWithChildren>",
368
+ type: "RemoteThreadListProviderComponent",
330
369
  description:
331
370
  "Optional wrapper rendered around each active thread. Inject thread-scoped adapters (history, attachments) here.",
332
371
  },
@@ -300,6 +300,7 @@ aui-state:[{"type":"set","path":["status"],"value":"completed"}]
300
300
  initialState: T,
301
301
  api: string,
302
302
  resumeApi?: string,
303
+ resumeStateApi?: string,
303
304
  protocol?: "data-stream" | "assistant-transport",
304
305
  converter: (state: T, connectionMetadata: ConnectionMetadata) => AssistantTransportState,
305
306
  headers?: Record<string, string> | Headers | (() => Promise<Record<string, string> | Headers>),
@@ -519,6 +520,23 @@ const runtime = useAssistantTransportRuntime({
519
520
  });
520
521
  ```
521
522
 
523
+ Setting `resumeStateApi` additionally hydrates resumed runs from the server-retained starting state. It requires a sync server that serves the initial-state route; do not configure it against a server without one, since a failing preflight fails the resume closed:
524
+
525
+ ```tsx
526
+ const runtime = useAssistantTransportRuntime({
527
+ // ...
528
+ resumeStateApi: "http://localhost:8010/initial-state",
529
+ });
530
+ ```
531
+
532
+ Before resuming, the runtime posts `{ threadId }` to `resumeStateApi`. The endpoint must return the state that started the active run together with its identity:
533
+
534
+ ```json
535
+ { "runId": "8b3a...", "state": { "messages": [] } }
536
+ ```
537
+
538
+ The runtime replaces its local base with this snapshot and includes `runId` in the resume request; the request carries no `state`, since the server replays from the snapshot it retained. A sync server should reject the resume when that ID no longer identifies the same run, preventing replay operations from being applied to a drifted or replaced base state. When no run is active, the endpoint responds `204 No Content` and the runtime skips the resume without raising an error. On a resume, `runId` takes precedence over fields supplied through `body` and survives `prepareSendCommandsRequest`; any `state` either of them supplies is stripped.
539
+
522
540
  ```tsx
523
541
  import { useAui } from "@assistant-ui/react";
524
542
  import { useEffect, useRef } from "react";
@@ -207,6 +207,7 @@ Each handler enables a specific UI feature.
207
207
  | `onEdit` | Message edit button |
208
208
  | `onReload` | Regenerate button |
209
209
  | `onCancel` | Cancel button while generating |
210
+ | `onRefetchThread` | `threads.reloadMainThread()` refetching the open thread in place |
210
211
  | `onAddToolResult` | Client-side tool result handoff |
211
212
  | `queue` | Queueing messages sent while a run is in progress |
212
213
 
@@ -395,7 +396,9 @@ const runtime = useExternalStoreRuntime({
395
396
 
396
397
  By default, sending while the thread is running is disabled. Provide a `queue` adapter to buffer a message sent during a run and process it once the run settles. The pending message is exposed on `composer.queue` and renders through [`ComposerPrimitive.Queue`](/docs/api-reference/primitives/composer).
397
398
 
398
- The `createMessageQueue` helper owns the FIFO ordering and the in-flight guard. Supply a driver that runs a message, pass its `adapter` to the runtime, and tell the queue when a run starts (`notifyBusy()`, so concurrent sends buffer) and ends (`notifyIdle()`).
399
+ The `createMessageQueue` helper owns the two-lane ordering and the in-flight guard: `steerItems` drain before `items`, and within each lane items drain in order. Supply a driver that runs a message, pass its `adapter` to the runtime, and tell the queue when a run starts (`notifyBusy()`, so concurrent sends buffer) and ends (`notifyIdle()`).
400
+
401
+ A hand-rolled adapter must implement the same contract: `items` and `steerItems` expose the lanes (each item carries a required `parts` projection of its content), `enqueue`/`steer` add to a lane, and `move(queueItemId, placement)` repositions with fail-fast anchors — unknown ids or anchors throw rather than being coerced. Individual items are addressable through `composer.queueItem({ id })` (or by index).
399
402
 
400
403
  ```tsx
401
404
  import { useEffect, useRef, useState } from "react";
@@ -418,6 +421,26 @@ useEffect(() => {
418
421
  }, [isRunning, queue]);
419
422
  ```
420
423
 
424
+ Queue policy on cancel, edit, and reload is host-owned. Call `queue.notifyCancelled()` in your `onCancel` handler before aborting so the cancelled run's settle pauses draining instead of dispatching the next item (the next send or run start resumes it), or call `queue.clear()` to drop the pending items. Call `queue.clear()` in your `onEdit` and `onReload` handlers so stale items do not drain onto the new branch.
425
+
426
+ ```tsx
427
+ const runtime = useExternalStoreRuntime({
428
+ // ...
429
+ onCancel: async () => {
430
+ queue.notifyCancelled(); // or queue.clear() to drop pending items
431
+ await cancelRun();
432
+ },
433
+ onEdit: async (message) => {
434
+ queue.clear();
435
+ // ...
436
+ },
437
+ onReload: async (parentId) => {
438
+ queue.clear();
439
+ // ...
440
+ },
441
+ });
442
+ ```
443
+
421
444
  ## Multi-thread
422
445
 
423
446
  `ExternalStoreRuntime` uses `ExternalStoreThreadListAdapter` (synchronous, inline). See [threads](/docs/runtimes/concepts/threads#externalstorethreadlistadapter) for the contract and best practices on keeping `currentThreadId` in sync with your store.
@@ -787,6 +810,12 @@ useExternalStoreRuntime({
787
810
  type: "() => Promise<void>",
788
811
  description: "Handler for cancelling the current generation.",
789
812
  },
813
+ {
814
+ name: "onRefetchThread",
815
+ type: "() => Promise<void>",
816
+ description:
817
+ "Handler for re-fetching this thread's state in place, driving threads.reloadMainThread(). Unrelated to onReload, which re-generates an assistant message.",
818
+ },
790
819
  {
791
820
  name: "onAddToolResult",
792
821
  type: "(options: AddToolResultOptions) => Promise<void> | Void",