@assistant-ui/mcp-docs-server 0.1.39 → 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 (185) hide show
  1. package/.docs/organized/code-examples/waterfall.md +17 -18
  2. package/.docs/organized/code-examples/with-a2a.md +20 -15
  3. package/.docs/organized/code-examples/with-ag-ui.md +19 -17
  4. package/.docs/organized/code-examples/with-ai-sdk-v7.md +17 -16
  5. package/.docs/organized/code-examples/with-artifacts.md +471 -145
  6. package/.docs/organized/code-examples/with-assistant-transport.md +23 -31
  7. package/.docs/organized/code-examples/with-browser-extension.md +21 -14
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +22 -20
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +13 -14
  10. package/.docs/organized/code-examples/with-cloud.md +22 -17
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +17 -16
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +17 -17
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +21 -19
  14. package/.docs/organized/code-examples/with-eve.md +70 -16
  15. package/.docs/organized/code-examples/with-expo.md +32 -51
  16. package/.docs/organized/code-examples/with-external-store.md +20 -15
  17. package/.docs/organized/code-examples/with-ffmpeg.md +24 -18
  18. package/.docs/organized/code-examples/with-generative-ui.md +20 -21
  19. package/.docs/organized/code-examples/with-google-adk.md +21 -16
  20. package/.docs/organized/code-examples/with-heat-graph.md +10 -11
  21. package/.docs/organized/code-examples/with-image-generation.md +13 -14
  22. package/.docs/organized/code-examples/with-interactables.md +17 -16
  23. package/.docs/organized/code-examples/with-langchain.md +13 -14
  24. package/.docs/organized/code-examples/with-langgraph.md +23 -17
  25. package/.docs/organized/code-examples/with-livekit.md +17 -17
  26. package/.docs/organized/code-examples/with-mcp.md +36 -32
  27. package/.docs/organized/code-examples/with-nuxt.md +2492 -0
  28. package/.docs/organized/code-examples/with-opencode.md +27 -21
  29. package/.docs/organized/code-examples/with-pi.md +69 -67
  30. package/.docs/organized/code-examples/with-react-hook-form.md +18 -17
  31. package/.docs/organized/code-examples/with-react-ink-web.md +10 -11
  32. package/.docs/organized/code-examples/with-react-ink.md +7 -7
  33. package/.docs/organized/code-examples/with-react-router.md +23 -17
  34. package/.docs/organized/code-examples/with-resumable-stream.md +15 -16
  35. package/.docs/organized/code-examples/with-store.md +31 -20
  36. package/.docs/organized/code-examples/with-tanstack.md +22 -16
  37. package/.docs/organized/code-examples/with-tap-runtime.md +19 -19
  38. package/.docs/organized/code-examples/with-virtualized-thread.md +12 -13
  39. package/.docs/organized/code-examples/with-vue.md +408 -0
  40. package/.docs/raw/docs/(docs)/cli.mdx +3 -1
  41. package/.docs/raw/docs/(docs)/devtools.mdx +7 -2
  42. package/.docs/raw/docs/(docs)/index.mdx +9 -76
  43. package/.docs/raw/docs/(docs)/installation.mdx +4 -18
  44. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +24 -4
  45. package/.docs/raw/docs/(reference)/api-reference/generative-ui/a2ui.mdx +40 -0
  46. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +3 -0
  47. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +19 -420
  48. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +4 -1
  49. package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +8 -0
  50. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +21 -0
  51. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -9
  52. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +1 -18
  53. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +24 -1
  54. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
  55. package/.docs/raw/docs/cloud/ai-sdk.mdx +2 -2
  56. package/.docs/raw/docs/cloud/langgraph.mdx +1 -1
  57. package/.docs/raw/docs/copilots/model-context.mdx +5 -4
  58. package/.docs/raw/docs/copilots/motivation.mdx +5 -5
  59. package/.docs/raw/docs/guides/attachments.mdx +3 -3
  60. package/.docs/raw/docs/guides/branching.mdx +2 -2
  61. package/.docs/raw/docs/guides/chain-of-thought.mdx +1 -1
  62. package/.docs/raw/docs/guides/context-api.mdx +89 -111
  63. package/.docs/raw/docs/guides/editing.mdx +5 -5
  64. package/.docs/raw/docs/guides/electron.mdx +369 -0
  65. package/.docs/raw/docs/guides/index.mdx +10 -0
  66. package/.docs/raw/docs/guides/quoting.mdx +3 -3
  67. package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +2 -2
  68. package/.docs/raw/docs/guides/resumable-streams.mdx +74 -3
  69. package/.docs/raw/docs/guides/suggestions.mdx +9 -9
  70. package/.docs/raw/docs/ink/hooks.mdx +12 -7
  71. package/.docs/raw/docs/ink/primitives.mdx +18 -10
  72. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +16 -0
  73. package/.docs/raw/docs/integrations/auth/better-auth.mdx +3 -3
  74. package/.docs/raw/docs/integrations/auth/clerk.mdx +3 -3
  75. package/.docs/raw/docs/integrations/auth/next-auth.mdx +4 -4
  76. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +1 -1
  77. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +2 -2
  78. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
  79. package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
  80. package/.docs/raw/docs/integrations/observability/helicone.mdx +2 -2
  81. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  82. package/.docs/raw/docs/integrations/observability/langsmith.mdx +3 -3
  83. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +36 -9
  84. package/.docs/raw/docs/migrations/toolkit-tools.mdx +15 -13
  85. package/.docs/raw/docs/migrations/v0-15.mdx +236 -0
  86. package/.docs/raw/docs/primitives/attachment.mdx +2 -2
  87. package/.docs/raw/docs/primitives/composer.mdx +3 -3
  88. package/.docs/raw/docs/primitives/message.mdx +33 -1
  89. package/.docs/raw/docs/primitives/suggestion.mdx +1 -1
  90. package/.docs/raw/docs/primitives/thread-list.mdx +2 -2
  91. package/.docs/raw/docs/react-native/hooks.mdx +17 -7
  92. package/.docs/raw/docs/react-native/index.mdx +3 -3
  93. package/.docs/raw/docs/react-native/primitives.mdx +41 -7
  94. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +3 -3
  95. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +5 -5
  96. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +5 -5
  97. package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +13 -14
  98. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +11 -12
  99. package/.docs/raw/docs/runtimes/concepts/threads.mdx +50 -11
  100. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +20 -2
  101. package/.docs/raw/docs/runtimes/custom/external-store.mdx +31 -2
  102. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +39 -12
  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 +2 -2
  107. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +1 -1
  108. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +1 -1
  109. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +7 -7
  110. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +4 -4
  111. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +4 -1
  112. package/.docs/raw/docs/runtimes/opencode/overview.mdx +10 -0
  113. package/.docs/raw/docs/tools/a2ui.mdx +107 -0
  114. package/.docs/raw/docs/tools/backend.mdx +2 -2
  115. package/.docs/raw/docs/tools/defining-tools.mdx +9 -7
  116. package/.docs/raw/docs/tools/dynamic-tools.mdx +6 -4
  117. package/.docs/raw/docs/tools/interactables-legacy.mdx +28 -15
  118. package/.docs/raw/docs/tools/interactables.mdx +27 -16
  119. package/.docs/raw/docs/tools/mcp-apps.mdx +96 -8
  120. package/.docs/raw/docs/tools/mcp.mdx +9 -7
  121. package/.docs/raw/docs/tools/tool-ui.mdx +26 -22
  122. package/.docs/raw/docs/tools/user-managed-mcp.mdx +64 -14
  123. package/.docs/raw/docs/ui/file.mdx +6 -1
  124. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +2 -0
  125. package/.docs/raw/docs/ui/mcp-config.mdx +8 -3
  126. package/.docs/raw/docs/ui/model-selector.mdx +9 -9
  127. package/.docs/raw/docs/ui/part-grouping.mdx +1 -5
  128. package/.docs/raw/docs/ui/reasoning.mdx +1 -1
  129. package/.docs/raw/docs/ui/thread.mdx +24 -5
  130. package/.docs/raw/docs/utilities/react-o11y.mdx +7 -9
  131. package/dist/constants.js +2 -2
  132. package/dist/constants.js.map +1 -1
  133. package/dist/index.d.ts +1 -1
  134. package/dist/index.js +2 -2
  135. package/dist/index.js.map +1 -1
  136. package/dist/prepare-docs/code-examples.js.map +1 -1
  137. package/dist/prepare-docs/prepare.d.ts +1 -1
  138. package/dist/prepare-docs/prepare.js.map +1 -1
  139. package/dist/stdio.d.ts +1 -1
  140. package/dist/tools/docs.d.ts +6 -10
  141. package/dist/tools/docs.d.ts.map +1 -1
  142. package/dist/tools/docs.js +6 -4
  143. package/dist/tools/docs.js.map +1 -1
  144. package/dist/tools/examples.d.ts +4 -8
  145. package/dist/tools/examples.d.ts.map +1 -1
  146. package/dist/tools/examples.js +4 -3
  147. package/dist/tools/examples.js.map +1 -1
  148. package/dist/tools/resources.d.ts +1 -1
  149. package/dist/tools/resources.d.ts.map +1 -1
  150. package/dist/tools/resources.js +3 -2
  151. package/dist/tools/resources.js.map +1 -1
  152. package/dist/tools/search.d.ts +4 -10
  153. package/dist/tools/search.d.ts.map +1 -1
  154. package/dist/tools/search.js +2 -2
  155. package/dist/tools/search.js.map +1 -1
  156. package/dist/tools/tests/mcp-test-client.d.ts +15 -0
  157. package/dist/tools/tests/mcp-test-client.d.ts.map +1 -0
  158. package/dist/tools/tests/mcp-test-client.js +68 -0
  159. package/dist/tools/tests/mcp-test-client.js.map +1 -0
  160. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  161. package/dist/tools/tests/test-setup.js +2 -1
  162. package/dist/tools/tests/test-setup.js.map +1 -1
  163. package/dist/tools/xulux-templates.d.ts +9 -23
  164. package/dist/tools/xulux-templates.d.ts.map +1 -1
  165. package/dist/tools/xulux-templates.js +8 -6
  166. package/dist/tools/xulux-templates.js.map +1 -1
  167. package/dist/utils/logger.d.ts.map +1 -1
  168. package/dist/utils/mdx.js +2 -1
  169. package/dist/utils/mdx.js.map +1 -1
  170. package/dist/utils/security.js.map +1 -1
  171. package/dist/xulux/catalog-client.js +1 -1
  172. package/dist/xulux/catalog-client.js.map +1 -1
  173. package/package.json +6 -5
  174. package/src/index.ts +2 -2
  175. package/src/tools/docs.ts +2 -2
  176. package/src/tools/examples.ts +2 -2
  177. package/src/tools/resources.ts +1 -4
  178. package/src/tools/search.ts +2 -2
  179. package/src/tools/tests/completions.test.ts +40 -26
  180. package/src/tools/tests/docs.test.ts +2 -2
  181. package/src/tools/tests/integration.test.ts +3 -4
  182. package/src/tools/tests/mcp-protocol.test.ts +160 -175
  183. package/src/tools/tests/mcp-test-client.ts +111 -0
  184. package/src/tools/tests/resources.test.ts +97 -66
  185. package/src/tools/xulux-templates.ts +4 -4
@@ -396,7 +396,7 @@ Renders the quoted text from `s.composer.quote?.text`. Pass `children` to overri
396
396
 
397
397
  ### QuoteDismiss
398
398
 
399
- Pressable that clears the active quote by calling `aui.composer().setQuote(undefined)`.
399
+ Pressable that clears the active quote by calling `aui.composer.setQuote(undefined)`.
400
400
 
401
401
  ```tsx
402
402
  <ComposerPrimitive.QuoteDismiss>
@@ -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
@@ -683,7 +684,7 @@ Container `Box` for an attachment. Forwards all `Box` props.
683
684
 
684
685
  ### Remove
685
686
 
686
- `Pressable` that calls `aui.attachment().remove()` when activated (Enter while focused). Disabled when not focused or when the `disabled` prop is set.
687
+ `Pressable` that calls `aui.attachment.remove()` when activated (Enter while focused). Disabled when not focused or when the `disabled` prop is set.
687
688
 
688
689
  | Prop | Type | Description |
689
690
  |------|------|-------------|
@@ -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,12 +721,12 @@ 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
727
728
 
728
- Pressable that removes the queue item by calling `aui.queueItem().remove()`.
729
+ Pressable that removes the queue item by calling `aui.queueItem.remove()`.
729
730
 
730
731
  ```tsx
731
732
  <QueueItemPrimitive.Remove>
@@ -742,7 +743,7 @@ Remaining props are forwarded to the underlying `Pressable` (Ink `Box` props).
742
743
 
743
744
  ### Steer
744
745
 
745
- Pressable that promotes the queue item to run next by calling `aui.queueItem().steer()`.
746
+ Pressable that promotes the queue item to run next by calling `aui.queueItem.steer()`.
746
747
 
747
748
  ```tsx
748
749
  <QueueItemPrimitive.Steer>
@@ -765,10 +766,17 @@ import { ActionBarPrimitive } from "@assistant-ui/react-ink";
765
766
 
766
767
  ### Copy
767
768
 
768
- Pressable that copies the message content. Supports function-as-children for copy state feedback.
769
+ Pressable that copies the message content. Pass a platform clipboard writer through `copyToClipboard`. Supports function-as-children for copy state feedback.
770
+
771
+ <InstallCommand npm={["clipboardy"]} />
769
772
 
770
773
  ```tsx
771
- <ActionBarPrimitive.Copy copiedDuration={3000}>
774
+ import clipboard from "clipboardy";
775
+
776
+ <ActionBarPrimitive.Copy
777
+ copiedDuration={3000}
778
+ copyToClipboard={clipboard.write}
779
+ >
772
780
  {({ isCopied }) => <Text>{isCopied ? "[Copied!]" : "[Copy]"}</Text>}
773
781
  </ActionBarPrimitive.Copy>
774
782
  ```
@@ -776,7 +784,7 @@ Pressable that copies the message content. Supports function-as-children for cop
776
784
  | Prop | Type | Description |
777
785
  |------|------|-------------|
778
786
  | `copiedDuration` | `number` | Duration in ms to show "copied" state (default: 3000) |
779
- | `copyToClipboard` | `(text: string) => void` | Custom clipboard function |
787
+ | `copyToClipboard` | `(text: string) => void \| Promise<void>` | Platform clipboard writer |
780
788
 
781
789
  ### Edit
782
790
 
@@ -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.
@@ -22,7 +22,7 @@ Three integration points:
22
22
 
23
23
  1. **Auth handlers** mounted at `/api/auth/[...all]/route.ts` via `toNextJsHandler(auth)`.
24
24
  2. **API routes** read the session server-side with `auth.api.getSession({ headers: await headers() })`.
25
- 3. **Reload-on-auth** uses better-auth's React client to detect sign-in and trigger `aui.threads().reload()`.
25
+ 3. **Reload-on-auth** uses better-auth's React client to detect sign-in and trigger `aui.threads.reload()`.
26
26
 
27
27
  `session.user.id` is exposed on the session object directly, so route handlers can scope queries against it.
28
28
 
@@ -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();
@@ -136,7 +136,7 @@ export function ReloadOnAuth() {
136
136
  const aui = useAui();
137
137
  const { data: session, isPending } = authClient.useSession();
138
138
  useEffect(() => {
139
- if (!isPending && session) aui.threads().reload();
139
+ if (!isPending && session) aui.threads.reload();
140
140
  }, [isPending, session?.user?.id]);
141
141
  return null;
142
142
  }
@@ -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();
@@ -102,7 +102,7 @@ const rows = await db
102
102
 
103
103
  ### Reload threads after async auth
104
104
 
105
- The first render of `<MyProvider>` may run before Clerk resolves the user on the client. Drop a small effect inside `<AssistantRuntimeProvider>` that calls `aui.threads().reload()` once the user is loaded:
105
+ The first render of `<MyProvider>` may run before Clerk resolves the user on the client. Drop a small effect inside `<AssistantRuntimeProvider>` that calls `aui.threads.reload()` once the user is loaded:
106
106
 
107
107
  ```tsx title="app/components/ReloadOnAuth.tsx"
108
108
  "use client";
@@ -115,7 +115,7 @@ export function ReloadOnAuth() {
115
115
  const aui = useAui();
116
116
  const { isLoaded, isSignedIn, user } = useUser();
117
117
  useEffect(() => {
118
- if (isLoaded && isSignedIn) aui.threads().reload();
118
+ if (isLoaded && isSignedIn) aui.threads.reload();
119
119
  }, [isLoaded, isSignedIn, user?.id]);
120
120
  return null;
121
121
  }
@@ -18,7 +18,7 @@ Three places auth touches the integration:
18
18
 
19
19
  1. **API routes** (`/api/chat`, `/api/threads/*`) call `auth()` server-side, return 401 if no session, then scope DB queries by `session.user.id`.
20
20
  2. **`RemoteThreadListAdapter`** does nothing auth-specific; the cookie travels with the `fetch` calls automatically (same origin).
21
- 3. **Reload-on-auth**: if the session resolves after the initial render, call `aui.threads().reload()` so the list re-fetches with the new identity.
21
+ 3. **Reload-on-auth**: if the session resolves after the initial render, call `aui.threads.reload()` so the list re-fetches with the new identity.
22
22
 
23
23
  ## Setup
24
24
 
@@ -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();
@@ -113,7 +113,7 @@ Apply the same pattern to `POST`, `PATCH`, and `DELETE` handlers. Always verify
113
113
 
114
114
  ### Reload threads after async auth
115
115
 
116
- The first render of `<MyProvider>` may run before `auth()` resolves on the client. The thread list will be empty until the user refreshes. Drop a small effect into the layout to call `aui.threads().reload()` once the session resolves.
116
+ The first render of `<MyProvider>` may run before `auth()` resolves on the client. The thread list will be empty until the user refreshes. Drop a small effect into the layout to call `aui.threads.reload()` once the session resolves.
117
117
 
118
118
  `useSession` requires `<SessionProvider>` higher in the tree. Wrap your root layout's client subtree once:
119
119
 
@@ -140,7 +140,7 @@ export function ReloadOnAuth() {
140
140
  const aui = useAui();
141
141
  const { status, data } = useSession();
142
142
  useEffect(() => {
143
- if (status === "authenticated") aui.threads().reload();
143
+ if (status === "authenticated") aui.threads.reload();
144
144
  }, [status, data?.user?.id]);
145
145
  return null;
146
146
  }
@@ -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
  }),
@@ -35,7 +35,7 @@ LANGSMITH_API_KEY=lsv2_pt_...
35
35
  LANGSMITH_PROJECT=assistant-ui
36
36
  ```
37
37
 
38
- `LANGSMITH_PROJECT` controls which project receives traces; the default project applies if you omit it. See LangSmith's [environment variable reference](https://docs.langchain.com/langsmith/how_to_environment_variables) for the full list.
38
+ `LANGSMITH_PROJECT` controls which project receives traces; the default project applies if you omit it. See LangSmith's [project configuration guide](https://docs.langchain.com/langsmith/log-traces-to-project) for details.
39
39
 
40
40
  </Step>
41
41
  <Step>
@@ -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({
@@ -305,7 +305,7 @@ export const threadListAdapter: RemoteThreadListAdapter = {
305
305
  async append() {},
306
306
  withFormat: (fmt) => ({
307
307
  async load() {
308
- const { remoteId } = aui.threadListItem().getState();
308
+ const { remoteId } = aui.threadListItem.getState();
309
309
  if (!remoteId) return { messages: [] };
310
310
  const rows = await fetch(
311
311
  `/api/threads/${remoteId}/messages`,
@@ -322,7 +322,7 @@ export const threadListAdapter: RemoteThreadListAdapter = {
322
322
  };
323
323
  },
324
324
  async append(item) {
325
- const { remoteId } = await aui.threadListItem().initialize();
325
+ const { remoteId } = await aui.threadListItem.initialize();
326
326
  await fetch(`/api/threads/${remoteId}/messages`, {
327
327
  method: "POST",
328
328
  body: JSON.stringify({
@@ -422,7 +422,7 @@ export const threadListAdapter: RemoteThreadListAdapter = {
422
422
  async append() {},
423
423
  withFormat: (fmt) => ({
424
424
  async load() {
425
- const { remoteId } = aui.threadListItem().getState();
425
+ const { remoteId } = aui.threadListItem.getState();
426
426
  if (!remoteId) return { messages: [] };
427
427
  const rows = await fetch(
428
428
  `${API_URL}/threads/${remoteId}/messages`,
@@ -439,7 +439,7 @@ export const threadListAdapter: RemoteThreadListAdapter = {
439
439
  };
440
440
  },
441
441
  async append(item) {
442
- const { remoteId } = await aui.threadListItem().initialize();
442
+ const { remoteId } = await aui.threadListItem.initialize();
443
443
  await fetch(`${API_URL}/threads/${remoteId}/messages`, {
444
444
  method: "POST",
445
445
  body: JSON.stringify({
@@ -541,7 +541,7 @@ export const threadListAdapter: RemoteThreadListAdapter = {
541
541
  async append() {},
542
542
  withFormat: (fmt) => ({
543
543
  async load() {
544
- const { remoteId } = aui.threadListItem().getState();
544
+ const { remoteId } = aui.threadListItem.getState();
545
545
  if (!remoteId) return { messages: [] };
546
546
  const rows = await fetch(
547
547
  `${API_URL}/threads/${remoteId}/messages`,
@@ -558,7 +558,7 @@ export const threadListAdapter: RemoteThreadListAdapter = {
558
558
  };
559
559
  },
560
560
  async append(item) {
561
- const { remoteId } = await aui.threadListItem().initialize();
561
+ const { remoteId } = await aui.threadListItem.initialize();
562
562
  await fetch(`${API_URL}/threads/${remoteId}/messages`, {
563
563
  method: "POST",
564
564
  body: JSON.stringify({
@@ -592,6 +592,32 @@ The top-level `load`/`append` on the history adapter are required by the type bu
592
592
  </Step>
593
593
  <Step>
594
594
 
595
+ ### Support in-place updates (optional)
596
+
597
+ The optional `update(item, localMessageId)` method on the object returned from `withFormat` lets the runtime rewrite an already-persisted message in place. It receives the same `{ parentId, message }` item shape as `append`, plus the id of the row to rewrite.
598
+
599
+ The runtime calls `update` after a run for persisted messages whose content changed, to finalize assistant messages persisted early while waiting for tool call approval, and to refresh run timing metadata.
600
+
601
+ `update` is opt-in. When it is absent, messages paused for tool approval are not persisted until the run reaches a terminal status, so a page refresh during the pause loses the pending approval state. Implementing `update` enables early persistence.
602
+
603
+ ```tsx title="runtime/thread-adapter.tsx"
604
+ async update(item, localMessageId) {
605
+ const { remoteId } = await aui.threadListItem.initialize();
606
+ await fetch(`${API_URL}/threads/${remoteId}/messages/${localMessageId}`, {
607
+ method: "PATCH",
608
+ body: JSON.stringify({
609
+ format: fmt.format,
610
+ content: fmt.encode(item),
611
+ }),
612
+ });
613
+ },
614
+ ```
615
+
616
+ The backend needs a matching update route keyed by message id, mirroring the append route from the earlier step.
617
+
618
+ </Step>
619
+ <Step>
620
+
595
621
  ### Mount the runtime
596
622
 
597
623
  Wrap the app in a `useRemoteThreadListRuntime` that delegates per-thread runtime to `useChatRuntime`:
@@ -694,7 +720,7 @@ Send a message in a fresh thread. Check the database:
694
720
 
695
721
  - The `threads` table has a new row with the current `userId`.
696
722
  - The `messages` table has at least two rows (user + assistant) for that thread.
697
- - `format` matches what `fmt.format` wrote (`"ai-sdk/v6"` for AI SDK v6) and `content` is the encoded `UIMessage` (a `role` plus `parts`), not a placeholder blob.
723
+ - `format` matches what `fmt.format` wrote (`"ai-sdk/v6"`) and `content` is the encoded `UIMessage` (a `role` plus `parts`), not a placeholder blob. The format string names the stored `UIMessage` shape, not the installed AI SDK major; it stays `"ai-sdk/v6"` on AI SDK v7 and must never be renamed, or previously stored history stops matching.
698
724
  - Reload the page; the thread list and the messages survive.
699
725
 
700
726
  </Step>
@@ -702,9 +728,10 @@ Send a message in a fresh thread. Check the database:
702
728
 
703
729
  ## Notes
704
730
 
705
- - **First-message race.** `append` may fire before the thread row exists. The `unstable_Provider` example above always awaits `aui.threadListItem().initialize()` before writing; do the same in any custom implementation.
706
- - **Reload after async auth.** If `auth()` resolves after the initial `list()` call, threads won't appear until the user refreshes. Call `aui.threads().reload()` from a `useEffect` watching the session. Pattern is documented in [threads](/docs/runtimes/concepts/threads#reloading-after-async-authentication).
731
+ - **First-message race.** `append` may fire before the thread row exists. The `unstable_Provider` example above always awaits `aui.threadListItem.initialize()` before writing; do the same in any custom implementation.
732
+ - **Reload after async auth.** If `auth()` resolves after the initial `list()` call, threads won't appear until the user refreshes. Call `aui.threads.reload()` from a `useEffect` watching the session. Pattern is documented in [threads](/docs/runtimes/concepts/threads#reloading-after-async-authentication).
707
733
  - **Format string.** The `format` column is *not* a free-text label; it identifies the on-disk shape so multiple runtimes can coexist. Don't strip it. Don't make assumptions about its value (`useChatRuntime` is responsible for setting and decoding it).
734
+ - **Pending approvals need `update`.** Tool call approvals persist across reloads only when the formatted adapter implements `update`; omitting it keeps the pre-approval snapshot out of storage by design.
708
735
  - **`unstable_Provider` synchronous-children rule.** The Provider must render `children` on first commit; do not gate them behind suspense, loading state, or `useEffect`. Load data inside an always-rendered child.
709
736
 
710
737
  ## Related
@@ -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>