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

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 (155) 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 +15 -13
  4. package/.docs/organized/code-examples/with-ai-sdk-v7.md +14 -13
  5. package/.docs/organized/code-examples/with-artifacts.md +14 -13
  6. package/.docs/organized/code-examples/with-assistant-transport.md +20 -28
  7. package/.docs/organized/code-examples/with-browser-extension.md +18 -11
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +17 -15
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +10 -11
  10. package/.docs/organized/code-examples/with-cloud.md +19 -14
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +15 -14
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +14 -14
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +19 -17
  14. package/.docs/organized/code-examples/with-eve.md +66 -12
  15. package/.docs/organized/code-examples/with-expo.md +26 -31
  16. package/.docs/organized/code-examples/with-external-store.md +17 -12
  17. package/.docs/organized/code-examples/with-ffmpeg.md +21 -15
  18. package/.docs/organized/code-examples/with-generative-ui.md +271 -36
  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 +14 -13
  23. package/.docs/organized/code-examples/with-langchain.md +12 -13
  24. package/.docs/organized/code-examples/with-langgraph.md +19 -13
  25. package/.docs/organized/code-examples/with-livekit.md +13 -13
  26. package/.docs/organized/code-examples/with-mcp.md +14 -15
  27. package/.docs/organized/code-examples/with-nuxt.md +2428 -0
  28. package/.docs/organized/code-examples/with-opencode.md +23 -14
  29. package/.docs/organized/code-examples/with-openui.md +449 -0
  30. package/.docs/organized/code-examples/with-pi.md +59 -57
  31. package/.docs/organized/code-examples/with-react-hook-form.md +16 -15
  32. package/.docs/organized/code-examples/with-react-ink-web.md +7 -8
  33. package/.docs/organized/code-examples/with-react-ink.md +6 -6
  34. package/.docs/organized/code-examples/with-react-router.md +17 -11
  35. package/.docs/organized/code-examples/with-resumable-stream.md +12 -13
  36. package/.docs/organized/code-examples/with-store.md +27 -16
  37. package/.docs/organized/code-examples/with-svelte.md +415 -0
  38. package/.docs/organized/code-examples/with-sveltekit.md +1061 -0
  39. package/.docs/organized/code-examples/with-tanstack.md +19 -13
  40. package/.docs/organized/code-examples/with-tap-runtime.md +15 -15
  41. package/.docs/organized/code-examples/with-virtualized-thread.md +8 -9
  42. package/.docs/organized/code-examples/with-vue.md +408 -0
  43. package/.docs/raw/docs/(docs)/cli.mdx +7 -2
  44. package/.docs/raw/docs/(docs)/index.mdx +9 -76
  45. package/.docs/raw/docs/(docs)/installation.mdx +6 -20
  46. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +24 -4
  47. package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +5 -1
  48. package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +9 -2
  49. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +2 -2
  50. package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +47 -1
  51. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +29 -4
  52. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +1 -1
  53. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +52 -1
  54. package/.docs/raw/docs/(reference)/api-reference/voice/session.mdx +1 -1
  55. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +2 -2
  56. package/.docs/raw/docs/cloud/ai-sdk.mdx +4 -4
  57. package/.docs/raw/docs/cloud/index.mdx +1 -1
  58. package/.docs/raw/docs/copilots/model-context.mdx +4 -3
  59. package/.docs/raw/docs/copilots/motivation.mdx +4 -4
  60. package/.docs/raw/docs/guides/attachments.mdx +2 -2
  61. package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
  62. package/.docs/raw/docs/guides/context-api.mdx +15 -17
  63. package/.docs/raw/docs/guides/dictation.mdx +1 -1
  64. package/.docs/raw/docs/guides/electron.mdx +1 -1
  65. package/.docs/raw/docs/guides/mentions.mdx +2 -0
  66. package/.docs/raw/docs/guides/resumable-streams.mdx +74 -3
  67. package/.docs/raw/docs/guides/suggestions.mdx +15 -12
  68. package/.docs/raw/docs/ink/hooks.mdx +9 -4
  69. package/.docs/raw/docs/ink/primitives.mdx +5 -4
  70. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +16 -0
  71. package/.docs/raw/docs/integrations/auth/better-auth.mdx +1 -1
  72. package/.docs/raw/docs/integrations/auth/clerk.mdx +1 -1
  73. package/.docs/raw/docs/integrations/auth/next-auth.mdx +1 -1
  74. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +1 -1
  75. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +2 -2
  76. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
  77. package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
  78. package/.docs/raw/docs/integrations/observability/helicone.mdx +2 -2
  79. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  80. package/.docs/raw/docs/integrations/observability/langsmith.mdx +2 -2
  81. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +146 -128
  82. package/.docs/raw/docs/migrations/toolkit-tools.mdx +15 -13
  83. package/.docs/raw/docs/migrations/v0-15.mdx +118 -4
  84. package/.docs/raw/docs/primitives/attachment.mdx +2 -2
  85. package/.docs/raw/docs/primitives/composer.mdx +2 -2
  86. package/.docs/raw/docs/primitives/message.mdx +33 -1
  87. package/.docs/raw/docs/primitives/suggestion.mdx +4 -2
  88. package/.docs/raw/docs/primitives/thread.mdx +1 -1
  89. package/.docs/raw/docs/react-native/hooks.mdx +14 -4
  90. package/.docs/raw/docs/react-native/index.mdx +1 -1
  91. package/.docs/raw/docs/react-native/primitives.mdx +2 -2
  92. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +35 -5
  93. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +1 -1
  94. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +1 -1
  95. package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +9 -10
  96. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +9 -10
  97. package/.docs/raw/docs/runtimes/claude-managed-agents.mdx +118 -0
  98. package/.docs/raw/docs/runtimes/concepts/stability.mdx +2 -1
  99. package/.docs/raw/docs/runtimes/concepts/threads.mdx +106 -27
  100. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +19 -1
  101. package/.docs/raw/docs/runtimes/custom/external-store.mdx +34 -1
  102. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +36 -9
  103. package/.docs/raw/docs/runtimes/eve/overview.mdx +51 -0
  104. package/.docs/raw/docs/runtimes/eve/quickstart.mdx +51 -2
  105. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +9 -12
  106. package/.docs/raw/docs/runtimes/langchain.mdx +1 -1
  107. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +5 -1
  108. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +7 -7
  109. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +4 -4
  110. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +1 -0
  111. package/.docs/raw/docs/runtimes/opencode/overview.mdx +10 -0
  112. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +8 -1
  113. package/.docs/raw/docs/tools/backend.mdx +2 -2
  114. package/.docs/raw/docs/tools/defining-tools.mdx +28 -7
  115. package/.docs/raw/docs/tools/dynamic-tools.mdx +6 -4
  116. package/.docs/raw/docs/tools/generative-ui-primitive.mdx +180 -0
  117. package/.docs/raw/docs/tools/generative-ui-slack.mdx +167 -0
  118. package/.docs/raw/docs/tools/generative-ui-teams.mdx +160 -0
  119. package/.docs/raw/docs/tools/generative-ui.mdx +224 -211
  120. package/.docs/raw/docs/tools/index.mdx +2 -1
  121. package/.docs/raw/docs/tools/interactables.mdx +29 -17
  122. package/.docs/raw/docs/tools/mcp-apps.mdx +35 -6
  123. package/.docs/raw/docs/tools/mcp.mdx +9 -7
  124. package/.docs/raw/docs/tools/openui.mdx +175 -0
  125. package/.docs/raw/docs/tools/tool-ui.mdx +27 -24
  126. package/.docs/raw/docs/tools/user-managed-mcp.mdx +17 -8
  127. package/.docs/raw/docs/ui/attachment.mdx +27 -0
  128. package/.docs/raw/docs/ui/file.mdx +7 -2
  129. package/.docs/raw/docs/ui/image.mdx +1 -1
  130. package/.docs/raw/docs/ui/mcp-config.mdx +8 -3
  131. package/.docs/raw/docs/ui/model-selector.mdx +8 -8
  132. package/.docs/raw/docs/ui/part-grouping.mdx +1 -1
  133. package/.docs/raw/docs/ui/thread.mdx +24 -5
  134. package/.docs/raw/docs/utilities/react-o11y.mdx +7 -9
  135. package/dist/constants.js +2 -2
  136. package/dist/constants.js.map +1 -1
  137. package/dist/index.js.map +1 -1
  138. package/dist/prepare-docs/prepare.js.map +1 -1
  139. package/dist/tools/docs.js +4 -2
  140. package/dist/tools/docs.js.map +1 -1
  141. package/dist/tools/examples.js +2 -1
  142. package/dist/tools/examples.js.map +1 -1
  143. package/dist/tools/resources.js +2 -1
  144. package/dist/tools/resources.js.map +1 -1
  145. package/dist/tools/tests/test-setup.js +2 -1
  146. package/dist/tools/tests/test-setup.js.map +1 -1
  147. package/dist/tools/xulux-templates.js +4 -2
  148. package/dist/tools/xulux-templates.js.map +1 -1
  149. package/dist/utils/mdx.js +2 -1
  150. package/dist/utils/mdx.js.map +1 -1
  151. package/dist/xulux/catalog-client.js +1 -1
  152. package/dist/xulux/catalog-client.js.map +1 -1
  153. package/package.json +4 -4
  154. package/src/tools/tests/docs.test.ts +2 -2
  155. package/.docs/raw/docs/tools/interactables-legacy.mdx +0 -410
@@ -283,7 +283,7 @@ This pattern records until the user stops dictation, then commits a single final
283
283
 
284
284
  ## Custom realtime providers
285
285
 
286
- You can implement `DictationAdapter` against any streaming STT service. [ElevenLabs Scribe](https://elevenlabs.io/docs/capabilities/speech-to-text) is one option for low-latency WebSocket transcription; scaffold a full example with:
286
+ You can implement `DictationAdapter` against any streaming STT service. [ElevenLabs Scribe](https://elevenlabs.io/docs/overview/capabilities/speech-to-text) is one option for low-latency WebSocket transcription; scaffold a full example with:
287
287
 
288
288
  ```bash
289
289
  npx assistant-ui create my-app -e with-elevenlabs-scribe
@@ -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,
@@ -82,6 +82,8 @@ const myAdapter: Unstable_TriggerAdapter = {
82
82
 
83
83
  The built-in `unstable_useLiveCompletionAdapter` wraps an async fetcher with debouncing, stale-request cancellation (results for an outdated query are dropped), and a single-entry cache. Its `search` returns the last results synchronously and schedules a debounced fetch when the query changes; when results arrive the returned `adapter` re-creates, which re-runs the popover lookup so the fresh items render. It also reports `isLoading`, which you pass to the popover to show a loading state.
84
84
 
85
+ When the fetcher reads from an account- or workspace-specific source, pass that source ID as `cacheKey`. Changing the key clears cached results and cancels any pending result from the previous source.
86
+
85
87
  ```tsx
86
88
  import {
87
89
  unstable_useLiveCompletionAdapter,
@@ -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",
@@ -138,7 +138,7 @@ The primitives available for rendering suggestions are `ThreadPrimitive.Suggesti
138
138
 
139
139
  ## Runtime driven suggestions
140
140
 
141
- The static `Suggestions(...)` API covers welcome screens. For follow up prompts that depend on the conversation, a tool result, or your backend, push suggestions through the runtime itself. They land on `thread.suggestions` rather than the static `suggestions` scope, so they render through a different component.
141
+ The static `Suggestions(...)` API covers welcome screens. For follow up prompts that depend on the conversation, a tool result, or your backend, push suggestions through the runtime itself. They land on `thread.suggestions`, and when no static `Suggestions(...)` configuration is set, the `suggestions` scope derives from them, so `ThreadPrimitive.Suggestions` renders them as well. Runtime suggestions use the `ThreadSuggestion` shape: a required `prompt` plus optional `title` and `label`. When `title` is omitted it falls back to the prompt, and an omitted `label` renders as empty, so a prompt-only runtime suggestion looks exactly like a plain string suggestion.
142
142
 
143
143
  ### Local runtime: `SuggestionAdapter`
144
144
 
@@ -263,7 +263,7 @@ The local runtime clears its suggestions when a new run starts. External store r
263
263
 
264
264
  ### Rendering runtime suggestions
265
265
 
266
- Static suggestions go through `ThreadPrimitive.Suggestions`; runtime suggestions go through `thread.suggestions`. The shadcn registry ships `ThreadFollowupSuggestions` for the common single line pill layout. For a custom layout, read the array yourself:
266
+ Runtime suggestions land on `thread.suggestions`, and `ThreadPrimitive.Suggestions` picks them up unless a static `Suggestions(...)` configuration overrides the scope. The shadcn registry ships `ThreadFollowupSuggestions` for the common single line pill layout. For a custom layout, read the array yourself:
267
267
 
268
268
  ```tsx
269
269
  import { useAuiState, ThreadPrimitive, AuiIf } from "@assistant-ui/react";
@@ -280,7 +280,10 @@ function FollowUps() {
280
280
  method="replace"
281
281
  autoSend
282
282
  >
283
- {s.prompt}
283
+ {s.title ?? s.prompt}
284
+ {s.label && (
285
+ <span className="text-muted-foreground ms-1">{s.label}</span>
286
+ )}
284
287
  </ThreadPrimitive.Suggestion>
285
288
  ))}
286
289
  </div>
@@ -315,13 +318,13 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
315
318
  ];
316
319
  }, [user.isPremium]);
317
320
 
318
- const aui = useAui({
321
+ const config = AuiConfig({
319
322
  tools: Tools({ toolkit: myToolkit }),
320
323
  suggestions: Suggestions(suggestions),
321
324
  });
322
325
 
323
326
  return (
324
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
327
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
325
328
  {children}
326
329
  </AssistantRuntimeProvider>
327
330
  );
@@ -352,10 +355,10 @@ If your codebase uses the inline `ThreadPrimitive.Suggestion` component (which r
352
355
 
353
356
  ### Runtime-driven form
354
357
 
355
- 1. Configure suggestions in your runtime provider:
358
+ 1. Configure suggestions in your runtime provider's `config`:
356
359
 
357
360
  ```tsx
358
- const aui = useAui({
361
+ AuiConfig({
359
362
  suggestions: Suggestions(["What's the weather?"]),
360
363
  });
361
364
  ```
@@ -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
@@ -1216,7 +1217,7 @@ Container `Box` for the status bar. Forwards all `Box` props; pass `gap` to spac
1216
1217
  ```tsx
1217
1218
  <StatusBarPrimitive.Root gap={1}>
1218
1219
  <StatusBarPrimitive.Status />
1219
- <StatusBarPrimitive.ModelName name="gpt-5" />
1220
+ <StatusBarPrimitive.ModelName name="gpt-5.6-luna" />
1220
1221
  <StatusBarPrimitive.MessageCount />
1221
1222
  <StatusBarPrimitive.TokenCount />
1222
1223
  <StatusBarPrimitive.Latency />
@@ -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({