@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
@@ -48,15 +48,35 @@ primitives built on it. Components rendered outside an `AuiProvider`
48
48
  receive a default client whose scope accessors throw on use, so
49
49
  missing-provider mistakes surface at the point of use.
50
50
 
51
+ `config` is required and must be built with [AuiConfig](/docs/api-reference/utilities/miscellaneous#auiconfig). At the top
52
+ level, `config` alone creates this subtree's own client. Under a parent
53
+ provider, `extends` is mandatory: pass `extends={aui}` to extend the parent
54
+ client or `extends={null}` to isolate from it (enforced with a dev error).
55
+ Configs are identity-insensitive — a fresh object per render is safe.
56
+ A config whose scopes are all Derived keeps its scope set fixed
57
+ at mount (dev-enforced); configs with a root scope, and empty configs,
58
+ may grow and shrink scopes across renders. `ref` receives the resulting
59
+ client after mount.
60
+
51
61
  When mounting a runtime built with one of the runtime hooks, use
52
62
  [AssistantRuntimeProvider](/docs/api-reference/context-providers/assistant-runtime-provider#assistantruntimeprovider) — it installs an `AuiProvider`
53
63
  internally — rather than wiring `AuiProvider` yourself.
54
64
 
55
65
  ```tsx
56
- function ScopedAssistant({ children, scopes }) {
57
- const aui = useAui(scopes);
58
-
59
- return <AuiProvider value={aui}>{children}</AuiProvider>;
66
+ function MessageScope({ index, children }) {
67
+ const aui = useAui();
68
+ const config = AuiConfig({
69
+ message: Derived({
70
+ source: "thread",
71
+ query: { index },
72
+ get: (aui) => aui.thread.message({ index }),
73
+ }),
74
+ });
75
+ return (
76
+ <AuiProvider extends={aui} config={config}>
77
+ {children}
78
+ </AuiProvider>
79
+ );
60
80
  }
61
81
  ```
62
82
 
@@ -39,6 +39,14 @@ Eve's `send` API.
39
39
  const getEveMessageContent: (message: AppendMessage) => NonNullable<SendTurnPayload["message"]>;
40
40
  ```
41
41
 
42
+ ### toEveInputResponse
43
+
44
+ Converts an assistant-ui tool approval response into an Eve input response.
45
+
46
+ ```ts
47
+ const toEveInputResponse: (response: RespondToToolApprovalOptions) => InputResponse;
48
+ ```
49
+
42
50
  ### useEveAgentRuntime
43
51
 
44
52
  Connects Eve's `useEveAgent` hook to assistant-ui's runtime contract.
@@ -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, Suggestions, useSmooth } from "@/generated/typeDocs";
6
+ import { AssistantCloud, AuiConfig, ChainOfThoughtClient, DevToolsHooks, InMemoryThreadList, 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. */}
@@ -18,6 +18,29 @@ import { AssistantCloud, ChainOfThoughtClient, DevToolsHooks, InMemoryThreadList
18
18
 
19
19
  <ParametersTable {...AssistantCloud} />
20
20
 
21
+ ### AuiConfig
22
+
23
+ Builds a config for [AuiProvider](/docs/api-reference/context-providers/assistant-runtime-provider#auiprovider); the `config` prop only accepts
24
+ configs built with this helper.
25
+
26
+ A config is plain data: it can be hoisted to module scope, created inline
27
+ per render, or memoized — the provider never relies on config identity.
28
+
29
+ ```tsx
30
+ const aui = useAui();
31
+ const config = AuiConfig({
32
+ message: Derived({
33
+ source: "thread",
34
+ query: { index: 0 },
35
+ get: (aui) => aui.thread.message({ index: 0 }),
36
+ }),
37
+ });
38
+
39
+ <AuiProvider extends={aui} config={config}>{children}</AuiProvider>;
40
+ ```
41
+
42
+ <ParametersTable {...AuiConfig} />
43
+
21
44
  ### ChainOfThoughtClient
22
45
 
23
46
  <ParametersTable {...ChainOfThoughtClient} />
@@ -393,7 +393,7 @@ export async function POST(req: Request) {
393
393
  const { messages } = await req.json();
394
394
 
395
395
  const result = streamText({
396
- model: openai("gpt-5.4-mini"),
396
+ model: openai("gpt-5.6-luna"),
397
397
  messages,
398
398
  });
399
399
 
@@ -391,7 +391,7 @@ export async function POST(req: Request) {
391
391
  const { messages } = await req.json();
392
392
 
393
393
  const result = streamText({
394
- model: openai("gpt-5.4-mini"),
394
+ model: openai("gpt-5.6-luna"),
395
395
  messages,
396
396
  });
397
397
 
@@ -459,7 +459,7 @@ export async function POST(req: Request) {
459
459
  const samplingCalls: Record<string, SamplingCallData[]> = {};
460
460
 
461
461
  const result = streamText({
462
- model: openai("gpt-5.4-mini"),
462
+ model: openai("gpt-5.6-luna"),
463
463
  messages,
464
464
  tools: {
465
465
  delegate_to_gemini: tool({
@@ -63,6 +63,7 @@ export default defineToolkit({
63
63
 
64
64
  ```tsx title="app/Form.tsx"
65
65
  import {
66
+ AuiConfig,
66
67
  AuiProvider,
67
68
  makeAssistantVisible,
68
69
  Tools,
@@ -77,10 +78,10 @@ const ClickableButton = makeAssistantVisible(Button, {
77
78
 
78
79
  // Use in your component
79
80
  function Form() {
80
- const aui = useAui({ tools: Tools({ toolkit }) });
81
-
81
+ const aui = useAui();
82
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
82
83
  return (
83
- <AuiProvider value={aui}>
84
+ <AuiProvider extends={aui} config={config}>
84
85
  <form>{/* form fields */}</form>
85
86
  </AuiProvider>
86
87
  );
@@ -131,15 +131,15 @@ export default defineToolkit({
131
131
  ```
132
132
 
133
133
  ```tsx title="app/SmartTransactionHistory.tsx"
134
- import { AuiProvider, Tools, useAui } from "@assistant-ui/react";
134
+ import { AuiConfig, AuiProvider, Tools, useAui } from "@assistant-ui/react";
135
135
  import toolkit from "./transaction-toolkit";
136
136
 
137
137
  function SmartTransactionHistory() {
138
- const aui = useAui({ tools: Tools({ toolkit }) });
139
-
140
138
  // Previous instructions...
139
+ const aui = useAui();
140
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
141
141
  return (
142
- <AuiProvider value={aui}>
142
+ <AuiProvider extends={aui} config={config}>
143
143
  <TransactionHistory transactions={transactions} />
144
144
  </AuiProvider>
145
145
  );
@@ -109,7 +109,7 @@ export async function POST(req: Request) {
109
109
  const { messages } = await req.json();
110
110
 
111
111
  const result = streamText({
112
- model: openai("gpt-5.4-mini"),
112
+ model: openai("gpt-5.6-luna"),
113
113
  messages: await convertToModelMessages(messages),
114
114
  });
115
115
 
@@ -214,7 +214,7 @@ export function registerAssistantIpc(mainWindow: BrowserWindow) {
214
214
  void (async () => {
215
215
  try {
216
216
  const result = streamText({
217
- model: openai("gpt-5.4-mini"),
217
+ model: openai("gpt-5.6-luna"),
218
218
  messages: request.messages,
219
219
  ...(request.system ? { system: request.system } : {}),
220
220
  abortSignal: abortController.signal,
@@ -84,7 +84,7 @@ The context exposes two more verbs: `ctx.status(streamId)` returns `"streaming"
84
84
 
85
85
  ## Client side: native integration
86
86
 
87
- `@assistant-ui/react-ai-sdk` ships a `resumable` option on `AssistantChatTransport`. It captures the stream id from the response header, redirects `chat.resumeStream()` reconnects to your resume route, and clears the stored id when the response finishes naturally. Pair it with `useChatRuntime`, which fires `chat.resumeStream()` on mount whenever a pending id is present in storage.
87
+ `@assistant-ui/react-ai-sdk` ships a `resumable` option on `AssistantChatTransport`. It captures the stream id from the response header, redirects `chat.resumeStream()` reconnects to your resume route, and clears the stored id when the response finishes naturally. Pair it with `useChatRuntime`, which fires `chat.resumeStream()` whenever its resumable storage reports a pending id, including ids discovered after mount.
88
88
 
89
89
  ```tsx title="/app/page.tsx"
90
90
  "use client";
@@ -127,12 +127,83 @@ export default function Page() {
127
127
  }
128
128
  ```
129
129
 
130
- `onResumeError` runs when the client finds a stored stream id but the reconnect attempt fails. Use it to show a toast, report telemetry, or mark the thread as needing retry; assistant-ui still clears the stale stream id after the callback runs.
130
+ `onResumeError` runs when the client finds a stored stream id but the reconnect attempt fails. Use it to show a toast, report telemetry, or mark the thread as needing retry. Assistant-ui clears the failed stream id after the callback unless a newer id has replaced it.
131
131
 
132
- `createResumableSessionStorage` returns a `ResumableClientStorage` backed by `window.sessionStorage`. Pass `{ key }` to namespace per route or per chat surface, or supply your own implementation of the three methods (`getStreamId`, `setStreamId`, `clear`). If you are running on a transport that already wraps `fetch` or `prepareReconnectToStreamRequest`, the `resumable` option composes with your existing handlers.
132
+ `createResumableSessionStorage` returns a `ResumableClientStorage` backed by `window.sessionStorage`. Pass `{ key }` to namespace per route or chat surface; `key` also accepts a getter that is read lazily on every access, so you can derive it from the active thread's identity (see [Multiple threads](#multiple-threads)). While the getter returns `undefined`, reads report no pending stream and writes are dropped. Reuse one storage instance per key, because separate instances do not synchronize their in-memory caches. The persisted entry survives reloads, while in-memory ownership prevents one mounted thread from attaching to another thread's pending stream. An idle follower that learns a stream id from its backend can call `storage.setStreamId(streamId, threadId)` after the corresponding message exists in local history; the runtime attaches when that thread is current, without requiring a remount. The built-in storage holds one pending stream id per key; the [Multiple threads](#multiple-threads) pattern gives each thread its own key, so several threads stay resumable at once. A custom `ResumableClientStorage` can instead key its records by the optional `threadId` arguments and implement `subscribe(listener, threadId)` for post-mount updates, or replace the storage methods entirely. If you are running on a transport that already wraps `fetch` or `prepareReconnectToStreamRequest`, the `resumable` option composes with your existing handlers.
133
133
 
134
134
  The default finish detector scans the SSE body for the AI SDK `"type":"finish"` marker. Override `isFinishEvent` on the `resumable` option when you ship a custom encoder.
135
135
 
136
+ ### Multiple threads
137
+
138
+ The snippet above stores the pending stream id under one `sessionStorage` key, which is correct for a single chat surface. Under a thread list runtime (`useRemoteThreadListRuntime`, which `useChatRuntime` wraps) with more than one alive thread, a single shared key is written and cleared by whichever thread acts last: switch threads while a response is in flight and the response header writes its stream id to the shared key, which the thread you switched to reads on mount and resumes — replaying the other conversation's stream into this one. An unscoped clear when a stream finishes can also drop another thread's pending id. And after a reload, a pending id under the shared key is claimed by whichever thread is main at mount, which with a thread list is a fresh empty thread rather than the one that started the stream.
139
+
140
+ Scope the key to the thread, and construct the transport and storage per thread inside the per-thread runtime hook rather than once at module level. Derive the key from the thread's identity with a getter so it is read lazily on every access:
141
+
142
+ ```tsx title="/app/page.tsx"
143
+ "use client";
144
+
145
+ import {
146
+ AssistantCloud,
147
+ AssistantRuntimeProvider,
148
+ useAui,
149
+ useCloudThreadListAdapter,
150
+ useRemoteThreadListRuntime,
151
+ } from "@assistant-ui/react";
152
+ import {
153
+ AssistantChatTransport,
154
+ createResumableSessionStorage,
155
+ useChatRuntime,
156
+ } from "@assistant-ui/react-ai-sdk";
157
+ import { useMemo } from "react";
158
+ import { Thread } from "@/components/assistant-ui/thread";
159
+ import { ThreadList } from "@/components/assistant-ui/thread-list";
160
+
161
+ const cloud = new AssistantCloud({
162
+ baseUrl: process.env.NEXT_PUBLIC_ASSISTANT_BASE_URL!,
163
+ anonymous: true,
164
+ });
165
+
166
+ function ResumableThreadRuntime() {
167
+ const aui = useAui();
168
+ const transport = useMemo(
169
+ () =>
170
+ new AssistantChatTransport({
171
+ api: "/api/chat",
172
+ resumable: {
173
+ storage: createResumableSessionStorage({
174
+ key: () => {
175
+ const item = aui.threadListItem.getState();
176
+ return `aui-resumable-stream-id:${item.remoteId ?? item.id}`;
177
+ },
178
+ }),
179
+ resumeApi: (streamId) => `/api/chat/resume/${streamId}`,
180
+ },
181
+ }),
182
+ [aui],
183
+ );
184
+ return useChatRuntime({ transport });
185
+ }
186
+
187
+ export default function Page() {
188
+ const adapter = useCloudThreadListAdapter({ cloud });
189
+ const runtime = useRemoteThreadListRuntime({
190
+ adapter,
191
+ runtimeHook: ResumableThreadRuntime,
192
+ });
193
+
194
+ return (
195
+ <AssistantRuntimeProvider runtime={runtime}>
196
+ <ThreadList />
197
+ <Thread />
198
+ </AssistantRuntimeProvider>
199
+ );
200
+ }
201
+ ```
202
+
203
+ `useCloudThreadListAdapter` is the same adapter `useChatRuntime({ cloud })` builds internally, so cloud thread history and attachments keep working; to bring your own backend, pass a custom `RemoteThreadListAdapter` instead. Each thread's runtime hook constructs its own transport and storage, keyed by that thread's identity, so one conversation's stream id can never be read or cleared by another. `useChatRuntime` is a no-op thread list when nested as a `runtimeHook`, so it only runs the per-thread chat runtime against the per-thread transport; the real thread list is the outer `useRemoteThreadListRuntime`.
204
+
205
+ Key by `remoteId ?? id` under a recognizable prefix, read lazily. A brand-new thread has no `remoteId` yet, but the transport initializes the thread before the request goes out, so by the time the response header delivers the stream id the getter resolves to the `remoteId`. After a reload the same conversation comes back with that `remoteId` as its id, so the stored entry is found again and the pending stream resumes. The local-id fallback covers a thread that has never sent, whose key nothing else reads or writes. Keying by the local id alone would break resume across reloads for threads created in the current session, because their local `__LOCALID_` id is replaced by the `remoteId` when the list reloads.
206
+
136
207
  ## Storage choices
137
208
 
138
209
  The core package ships `createInMemoryResumableStreamStore` for development and tests. State lives in a process-local `Map`, so it does not survive a server restart. Useful options include `defaultTtlMs`, `maxChunkBytes`, `maxEntriesPerStream`, `maxStreams`, and `gcIntervalMs` for periodic eviction.
@@ -15,13 +15,13 @@ The Suggestions API allows you to configure a list of suggested prompts that are
15
15
  Configure suggestions using the `Suggestions()` API in your runtime provider:
16
16
 
17
17
  ```tsx
18
- import { useAui, Tools, Suggestions } from "@assistant-ui/react";
18
+ import { AuiConfig, Tools, Suggestions } from "@assistant-ui/react";
19
19
  import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
20
20
 
21
21
  function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
22
22
  const runtime = useChatRuntime();
23
23
 
24
- const aui = useAui({
24
+ const config = AuiConfig({
25
25
  tools: Tools({ toolkit: myToolkit }),
26
26
  suggestions: Suggestions([
27
27
  "What can you help me with?",
@@ -31,7 +31,7 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
31
31
  });
32
32
 
33
33
  return (
34
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
34
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
35
35
  {children}
36
36
  </AssistantRuntimeProvider>
37
37
  );
@@ -45,7 +45,7 @@ Suggestions can be provided as either strings or objects with title, label, and
45
45
  ### Simple Strings
46
46
 
47
47
  ```tsx
48
- const aui = useAui({
48
+ AuiConfig({
49
49
  suggestions: Suggestions([
50
50
  "What's the weather today?",
51
51
  "Help me write an email",
@@ -59,7 +59,7 @@ const aui = useAui({
59
59
  For more detailed suggestions with separate display text and prompts:
60
60
 
61
61
  ```tsx
62
- const aui = useAui({
62
+ AuiConfig({
63
63
  suggestions: Suggestions([
64
64
  {
65
65
  title: "Weather",
@@ -315,13 +315,13 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
315
315
  ];
316
316
  }, [user.isPremium]);
317
317
 
318
- const aui = useAui({
318
+ const config = AuiConfig({
319
319
  tools: Tools({ toolkit: myToolkit }),
320
320
  suggestions: Suggestions(suggestions),
321
321
  });
322
322
 
323
323
  return (
324
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
324
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
325
325
  {children}
326
326
  </AssistantRuntimeProvider>
327
327
  );
@@ -352,10 +352,10 @@ If your codebase uses the inline `ThreadPrimitive.Suggestion` component (which r
352
352
 
353
353
  ### Runtime-driven form
354
354
 
355
- 1. Configure suggestions in your runtime provider:
355
+ 1. Configure suggestions in your runtime provider's `config`:
356
356
 
357
357
  ```tsx
358
- const aui = useAui({
358
+ AuiConfig({
359
359
  suggestions: Suggestions(["What's the weather?"]),
360
360
  });
361
361
  ```
@@ -187,12 +187,17 @@ export default defineToolkit({
187
187
  ```
188
188
 
189
189
  ```tsx title="ToolProvider.tsx"
190
- import { AuiProvider, Tools, useAui } from "@assistant-ui/react-ink";
190
+ import { AuiConfig, AuiProvider, Tools, useAui } from "@assistant-ui/react-ink";
191
191
  import toolkit from "./weather-toolkit";
192
192
 
193
193
  function ToolProvider({ children }: { children: React.ReactNode }) {
194
- const aui = useAui({ tools: Tools({ toolkit }) });
195
- return <AuiProvider value={aui}>{children}</AuiProvider>;
194
+ const aui = useAui();
195
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
196
+ return (
197
+ <AuiProvider extends={aui} config={config}>
198
+ {children}
199
+ </AuiProvider>
200
+ );
196
201
  }
197
202
  ```
198
203
 
@@ -271,7 +276,7 @@ export function useWeatherToolkit() {
271
276
  }
272
277
  ```
273
278
 
274
- 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.
279
+ Import the toolkit hook and mount it with `<AuiProvider extends={aui} config={config}>` where `const aui = useAui()` and `const config = AuiConfig({ tools: Tools({ toolkit }) })`, as shown in the [Tools](#tools) section above.
275
280
 
276
281
  ## Runtime Providers
277
282
 
@@ -666,10 +666,11 @@ Container `Box` for an attachment. Forwards all `Box` props.
666
666
 
667
667
  ### Thumb
668
668
 
669
- `Text` component displaying a short identifier for the attachment. Renders the file extension (`.pdf`, `.png`, …) when the name has one. Falls back to the attachment `type` (e.g. `image`, `document`) otherwise, and finally to `file`.
669
+ `Text` component displaying a short identifier for the attachment. Renders the file extension (`.pdf`, `.png`, …) when the name has one. Falls back to the attachment `type` (e.g. `image`, `document`) otherwise, and finally to `file`. Passing `children` overrides the automatic label.
670
670
 
671
671
  | Prop | Type | Description |
672
672
  |------|------|-------------|
673
+ | `children` | `ReactNode` | Overrides the automatic extension/type label |
673
674
  | `...rest` | `ComponentProps<typeof Text>` | Forwarded to the underlying Ink `Text` |
674
675
 
675
676
  ### Status
@@ -712,7 +713,7 @@ Primitives for rendering individual queued composer items. Use inside a `Compose
712
713
 
713
714
  ### Text
714
715
 
715
- Renders the queue item's prompt with Ink `<Text>`. Pass `children` to override the displayed value.
716
+ Renders the queue item's text with Ink `<Text>`. Pass `children` to override the displayed value.
716
717
 
717
718
  ```tsx
718
719
  <QueueItemPrimitive.Text color="gray" />
@@ -720,7 +721,7 @@ Renders the queue item's prompt with Ink `<Text>`. Pass `children` to override t
720
721
 
721
722
  | Prop | Type | Description |
722
723
  |------|------|-------------|
723
- | `children` | `ReactNode` | Override content; defaults to `s.queueItem.prompt` |
724
+ | `children` | `ReactNode` | Override content; defaults to the text parts of `s.queueItem.parts` |
724
725
  | `...rest` | `TextProps` | Standard Ink Text props |
725
726
 
726
727
  ### Remove
@@ -489,6 +489,22 @@ In the adapter's `add`, call Uploadthing's client upload and read `url` from the
489
489
  </Tab>
490
490
  </Tabs>
491
491
 
492
+ ### Opaque file references (LangGraph / LangChain)
493
+
494
+ When the backend resolves stored files itself and the browser never holds a usable URL, send the storage id instead: set `sourceType: "id"` on the file part in `send()`, and the LangChain-family runtimes emit a `source_type: "id"` block with the value in the `id` key. `sourceType: "url"` similarly forces a url reference for non-http data and is honored by the LangChain-family, A2A, AG-UI, and Google ADK runtimes. `sourceType: "id"` is LangChain-family only; runtimes without the concept ignore the field.
495
+
496
+ ```ts
497
+ const content = [
498
+ {
499
+ type: "file" as const,
500
+ filename: name,
501
+ mimeType: contentType ?? "application/octet-stream",
502
+ data: fileId,
503
+ sourceType: "id" as const,
504
+ },
505
+ ];
506
+ ```
507
+
492
508
  ## Notes
493
509
 
494
510
  - **Persistence.** A URL the model sees on Monday must still resolve next week if the thread is reloaded. Either use storage with no expiry on the public URL, or have your [history adapter](/docs/integrations/persistence/custom-adapter) regenerate signed URLs on `load`. Don't store presigned URLs in the message row.
@@ -66,7 +66,7 @@ export async function POST(req: Request) {
66
66
 
67
67
  const { messages }: { messages: UIMessage[] } = await req.json();
68
68
  const result = streamText({
69
- model: openai("gpt-5.4-nano"),
69
+ model: openai("gpt-5.6-luna"),
70
70
  messages: await convertToModelMessages(messages),
71
71
  });
72
72
  return result.toUIMessageStreamResponse();
@@ -45,7 +45,7 @@ export async function POST(req: Request) {
45
45
 
46
46
  const { messages }: { messages: UIMessage[] } = await req.json();
47
47
  const result = streamText({
48
- model: openai("gpt-5.4-nano"),
48
+ model: openai("gpt-5.6-luna"),
49
49
  messages: await convertToModelMessages(messages),
50
50
  });
51
51
  return result.toUIMessageStreamResponse();
@@ -72,7 +72,7 @@ export async function POST(req: Request) {
72
72
 
73
73
  const { messages }: { messages: UIMessage[] } = await req.json();
74
74
  const result = streamText({
75
- model: openai("gpt-5.4-nano"),
75
+ model: openai("gpt-5.6-luna"),
76
76
  messages: await convertToModelMessages(messages),
77
77
  });
78
78
  return result.toUIMessageStreamResponse();
@@ -68,7 +68,7 @@ export type Env = {
68
68
  export class Chat extends AIChatAgent<Env> {
69
69
  async onChatMessage(onFinish: Parameters<typeof streamText>[0]["onFinish"]) {
70
70
  return streamText({
71
- model: openai("gpt-5.4-nano"),
71
+ model: openai("gpt-5.6-luna"),
72
72
  messages: await convertToModelMessages(this.messages),
73
73
  onFinish,
74
74
  });
@@ -81,11 +81,11 @@ export const chefAgent = new Agent({
81
81
  instructions:
82
82
  "You are Michel, a practical and experienced home chef. " +
83
83
  "You help people cook with whatever ingredients they have available.",
84
- model: "openai/gpt-5.4-mini",
84
+ model: "openai/gpt-5.6-luna",
85
85
  });
86
86
  ```
87
87
 
88
- `model: "openai/gpt-5.4-mini"` uses Mastra's model router. Set the provider key in `.env.local` so Next.js auto-loads it:
88
+ `model: "openai/gpt-5.6-luna"` uses Mastra's model router. Set the provider key in `.env.local` so Next.js auto-loads it:
89
89
 
90
90
  ```sh title=".env.local"
91
91
  OPENAI_API_KEY=sk-...
@@ -50,7 +50,7 @@ export const chefAgent = new Agent({
50
50
  instructions:
51
51
  "You are Michel, a practical and experienced home chef. " +
52
52
  "You help people cook with whatever ingredients they have available.",
53
- model: "openai/gpt-5.4-nano",
53
+ model: "openai/gpt-5.6-luna",
54
54
  });
55
55
  ```
56
56
 
@@ -48,7 +48,7 @@ The rest of this page is what to put in `baseURL`, `headers`, and `model(...)` f
48
48
 
49
49
  ## OpenRouter
50
50
 
51
- [OpenRouter](https://openrouter.ai/) aggregates 100+ models behind one OpenAI-compatible endpoint. The model ID is `provider/model` (e.g., `anthropic/claude-sonnet-4.6`, `openai/gpt-5.4-mini`, `meta-llama/llama-3.3-70b-instruct`).
51
+ [OpenRouter](https://openrouter.ai/) aggregates 100+ models behind one OpenAI-compatible endpoint. The model ID is `provider/model` (e.g., `anthropic/claude-sonnet-4.6`, `openai/gpt-5.6-luna`, `meta-llama/llama-3.3-70b-instruct`).
52
52
 
53
53
  ```sh title=".env.local"
54
54
  OPENROUTER_API_KEY=sk-or-...
@@ -129,7 +129,7 @@ const litellm = createOpenAI({
129
129
  });
130
130
 
131
131
  const result = streamText({
132
- model: litellm("gpt-5.4-mini"),
132
+ model: litellm("gpt-5.6-luna"),
133
133
  messages: await convertToModelMessages(messages),
134
134
  });
135
135
  ```
@@ -57,7 +57,7 @@ const openai = createOpenAI({
57
57
  export async function POST(req: Request) {
58
58
  const { messages }: { messages: UIMessage[] } = await req.json();
59
59
  const result = streamText({
60
- model: openai("gpt-5.4-mini"),
60
+ model: openai("gpt-5.6-luna"),
61
61
  messages: await convertToModelMessages(messages),
62
62
  });
63
63
  return result.toUIMessageStreamResponse();
@@ -80,7 +80,7 @@ const openai = new OpenAI({
80
80
  export async function POST(req: Request) {
81
81
  const { messages } = await req.json();
82
82
  const stream = await openai.chat.completions.create({
83
- model: "gpt-5.4-mini",
83
+ model: "gpt-5.6-luna",
84
84
  messages,
85
85
  stream: true,
86
86
  });
@@ -101,7 +101,7 @@ export async function POST(req: Request) {
101
101
  { traceName: "chat-completion", userId, sessionId },
102
102
  async () =>
103
103
  streamText({
104
- model: openai("gpt-5.4-nano"),
104
+ model: openai("gpt-5.6-luna"),
105
105
  messages: await convertToModelMessages(messages),
106
106
  experimental_telemetry: { isEnabled: true },
107
107
  }),
@@ -63,7 +63,7 @@ export async function POST(req: Request) {
63
63
  const { messages }: { messages: UIMessage[] } = await req.json();
64
64
 
65
65
  const result = streamText({
66
- model: openai("gpt-5.4-nano"),
66
+ model: openai("gpt-5.6-luna"),
67
67
  messages: await ai.convertToModelMessages(messages),
68
68
  });
69
69
 
@@ -84,7 +84,7 @@ Pass a `langsmith` provider option to tag traces with user, session, or run iden
84
84
  import { createLangSmithProviderOptions } from "langsmith/experimental/vercel";
85
85
 
86
86
  const result = streamText({
87
- model: openai("gpt-5.4-nano"),
87
+ model: openai("gpt-5.6-luna"),
88
88
  messages: await ai.convertToModelMessages(messages),
89
89
  providerOptions: {
90
90
  langsmith: createLangSmithProviderOptions({
@@ -73,20 +73,18 @@ export default defineToolkit({
73
73
 
74
74
  import {
75
75
  AssistantRuntimeProvider,
76
+ AuiConfig,
76
77
  Tools,
77
- useAui,
78
78
  } from "@assistant-ui/react";
79
79
  import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
80
80
  import toolkit from "./toolkit";
81
81
 
82
82
  export function App() {
83
83
  const runtime = useChatRuntime();
84
- const aui = useAui({
85
- tools: Tools({ toolkit }),
86
- });
84
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
87
85
 
88
86
  return (
89
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
87
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
90
88
  <Thread />
91
89
  </AssistantRuntimeProvider>
92
90
  );
@@ -100,7 +98,7 @@ export function App() {
100
98
  1. Create a `Toolkit` object.
101
99
  2. Move each `toolName` into the toolkit key.
102
100
  3. Move `description`, `parameters`, `execute`, `providerOptions`, `render`, `renderText`, and `display` onto the toolkit entry.
103
- 4. Register the toolkit once with `useAui({ tools: Tools({ toolkit }) })`.
101
+ 4. Register the toolkit once with `config={config}` on your runtime provider, where `const config = AuiConfig({ tools: Tools({ toolkit }) })`.
104
102
  5. Remove `<Tool />`, `<ToolUI />`, `useAssistantTool(...)`, and `useAssistantToolUI(...)` registrations.
105
103
 
106
104
  ## UI-Only Tool Renderers
@@ -126,7 +124,7 @@ export default defineToolkit({
126
124
  });
127
125
  ```
128
126
 
129
- Register it like any toolkit: `useAui({ tools: Tools({ toolkit }) })`.
127
+ Register it like any toolkit: `config={config}` where `const config = AuiConfig({ tools: Tools({ toolkit }) })`.
130
128
  Render-only entries upload no schema and run no browser code — they only attach
131
129
  UI for matching tool-call message parts. For MCP server catalogs, spread
132
130
  `defineMcpToolkit({ ... })` in the same generative toolkit.
@@ -163,20 +161,24 @@ export default defineToolkit({
163
161
  ```
164
162
 
165
163
  ```tsx title="TaskBoard.tsx"
166
- import { AuiProvider, Tools, useAui, useAuiToolOverrides } from "@assistant-ui/react";
164
+ import {
165
+ AuiConfig,
166
+ AuiProvider,
167
+ Tools,
168
+ useAui,
169
+ useAuiToolOverrides,
170
+ } from "@assistant-ui/react";
167
171
  import { useState, type Dispatch, type SetStateAction } from "react";
168
172
  import type { Task } from "./task-board-toolkit";
169
173
  import toolkit from "./task-board-toolkit";
170
174
 
171
175
  function TaskBoard() {
172
176
  const [tasks, setTasks] = useState<Task[]>([]);
173
-
174
- const aui = useAui({
175
- tools: Tools({ toolkit }),
176
- });
177
+ const aui = useAui();
178
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
177
179
 
178
180
  return (
179
- <AuiProvider value={aui}>
181
+ <AuiProvider extends={aui} config={config}>
180
182
  <TaskBoardToolOverrides setTasks={setTasks} />
181
183
  <TaskList tasks={tasks} />
182
184
  </AuiProvider>