@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
@@ -353,7 +353,7 @@ const openai = new OpenAI();
353
353
  const MyModelAdapter: ChatModelAdapter = {
354
354
  async *run({ messages, abortSignal, context }) {
355
355
  const stream = await openai.chat.completions.create({
356
- model: "gpt-5.4-mini",
356
+ model: "gpt-5.6-luna",
357
357
  messages: convertToOpenAIMessages(messages),
358
358
  stream: true,
359
359
  signal: abortSignal,
@@ -372,6 +372,34 @@ const MyModelAdapter: ChatModelAdapter = {
372
372
 
373
373
  Each yield replaces the previous content. Yield the full state every time, not deltas.
374
374
 
375
+ ### Only the last part streams
376
+
377
+ A text or reasoning part reports `status.type === "running"` only while it is the last part of the message. Every earlier one reads as complete, and the components that react to streaming follow that status: the reasoning disclosure auto-opens and pins its live preview while its part is running, and part status indicators resolve the same way. Tool-call parts are exempt, since their status comes from whether they have a result.
378
+
379
+ That makes an empty trailing part destructive. Yielding `[reasoning, text("")]` to reserve the text slot marks the reasoning part complete the moment the empty text part appears, even though reasoning is still streaming, and nothing in the UI signals why. Add a part on its first token instead of reserving it:
380
+
381
+ ```tsx
382
+ let reasoning = "";
383
+ let text = "";
384
+
385
+ for await (const chunk of stream) {
386
+ const delta = chunk.choices[0]?.delta;
387
+ text += delta?.content ?? "";
388
+ // Reasoning deltas are provider-specific; read whichever field yours sends
389
+ reasoning += (delta as { reasoning?: string })?.reasoning ?? "";
390
+
391
+ // Include each part only once it has content
392
+ yield {
393
+ content: [
394
+ ...(reasoning ? [{ type: "reasoning" as const, text: reasoning }] : []),
395
+ ...(text ? [{ type: "text" as const, text }] : []),
396
+ ],
397
+ };
398
+ }
399
+ ```
400
+
401
+ Guard every text or reasoning part this way, not just the trailing one. An empty part that is never filled also renders as an empty block, and whichever part ends up last is the only one that can report `running`.
402
+
375
403
  ### Streaming with tool calls
376
404
 
377
405
  Accumulate tool calls in a `Map` outside the streaming loop so they persist across chunks:
@@ -379,7 +407,7 @@ Accumulate tool calls in a `Map` outside the streaming loop so they persist acro
379
407
  ```tsx
380
408
  async *run({ messages, abortSignal, context }) {
381
409
  const stream = await openai.chat.completions.create({
382
- model: "gpt-5.4-mini",
410
+ model: "gpt-5.6-luna",
383
411
  messages: convertToOpenAIMessages(messages),
384
412
  tools: context.tools,
385
413
  stream: true,
@@ -415,7 +443,7 @@ If you build the `content` array fresh from the current chunk each iteration, to
415
443
 
416
444
  ## Tool calling
417
445
 
418
- `LocalRuntime` supports OpenAI-compatible function calling. Register tools through `useAui` so the runtime exposes them to your adapter via `context.tools`:
446
+ `LocalRuntime` supports OpenAI-compatible function calling. Register tools through the provider's `config` so the runtime exposes them to your adapter via `context.tools`:
419
447
 
420
448
  ```tsx title="app/toolkit.tsx"
421
449
  "use generative";
@@ -440,15 +468,14 @@ export default defineToolkit({
440
468
  ```
441
469
 
442
470
  ```tsx title="app/MyRuntimeProvider.tsx"
443
- import { useAui, Tools } from "@assistant-ui/react";
471
+ import { AuiConfig, Tools } from "@assistant-ui/react";
444
472
  import toolkit from "./toolkit";
445
473
 
446
474
  function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
447
475
  const runtime = useLocalRuntime(MyModelAdapter);
448
- const aui = useAui({ tools: Tools({ toolkit }) });
449
-
476
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
450
477
  return (
451
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
478
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
452
479
  {children}
453
480
  </AssistantRuntimeProvider>
454
481
  );
@@ -627,7 +654,7 @@ function useStreamReconnect(threadId: string) {
627
654
 
628
655
  ## Queueing messages during a run
629
656
 
630
- Set `unstable_enableMessageQueue` to keep the composer usable while a run is in progress. A message sent during a run is held in `composer.queue` and sent once the run settles; steering a queued message runs it next.
657
+ Set `unstable_enableMessageQueue` to keep the composer usable while a run is in progress. A message sent during a queued run steers by default — it lands in the steer lane, ahead of previously queued items; `send({ steer: false })` queues a follow-up behind them instead. Pending items are held in `composer.queue` and drain once the run settles.
631
658
 
632
659
  ```tsx
633
660
  const runtime = useLocalRuntime(MyModelAdapter, {
@@ -674,7 +701,7 @@ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
674
701
  const OpenAIAdapter: ChatModelAdapter = {
675
702
  async *run({ messages, abortSignal, context }) {
676
703
  const stream = await openai.chat.completions.create({
677
- model: "gpt-5.4-mini",
704
+ model: "gpt-5.6-luna",
678
705
  messages: messages.map((m) => ({
679
706
  role: m.role,
680
707
  content: m.content
@@ -49,6 +49,57 @@ export default function Home() {
49
49
  }
50
50
  ```
51
51
 
52
+ The `custom` bag of a per-turn `runConfig` is forwarded to Eve as
53
+ `clientContext`, so `runConfig: { custom: { page: "/pricing" } }` arrives as
54
+ `clientContext: { page: "/pricing" }`. An empty or absent bag is omitted, and
55
+ every value must be JSON-serializable.
56
+
57
+ Eve turns that object into a model context message, which is why the
58
+ assistant-ui `custom` envelope is unwrapped: the envelope would put a literal
59
+ `custom` key in the prompt. It is page or client context rather than run
60
+ configuration, and it cannot select a model or change how the turn executes.
61
+ Keep secrets, credentials, and personal data out of it, since the model reads
62
+ it as message text.
63
+
64
+ ## Connector authorization
65
+
66
+ When Eve pauses for connector authorization, the runtime emits an assistant
67
+ data part named `authorization`. Its data includes `state`, `name`, and the
68
+ available display fields such as `displayName`, `description`, `url`,
69
+ `userCode`, `instructions`, and `expiresAt`. Completed parts may also include
70
+ `outcome` and `reason`. `url` is present only when the connector supplied an
71
+ `http(s)` address; the adapter drops other schemes so a renderer can link to it
72
+ directly.
73
+
74
+ The Eve template ships this renderer as `components/eve-authorization.tsx` and
75
+ mounts it next to `<Thread />`. To add it to an existing app, register a data UI
76
+ for the part and type it with the exported `EveAuthorizationData`:
77
+
78
+ ```tsx title="authorization-ui.tsx"
79
+ import type { EveAuthorizationData } from "@assistant-ui/eve";
80
+ import { makeAssistantDataUI } from "@assistant-ui/react";
81
+
82
+ export const AuthorizationUI = makeAssistantDataUI<EveAuthorizationData>({
83
+ name: "authorization",
84
+ render: ({ data }) => (
85
+ <div>
86
+ {data.state === "required" ? (
87
+ <>
88
+ <p>{data.instructions ?? `Sign in to ${data.displayName ?? data.name}`}</p>
89
+ {data.userCode && <code>{data.userCode}</code>}
90
+ {data.url && <a href={data.url}>Continue to sign in</a>}
91
+ </>
92
+ ) : (
93
+ <p>{data.outcome ?? "Authorization completed"}</p>
94
+ )}
95
+ </div>
96
+ ),
97
+ });
98
+ ```
99
+
100
+ Mount `<AuthorizationUI />` inside the `AssistantRuntimeProvider` tree. The
101
+ standard thread intentionally leaves unmatched data parts unrendered.
102
+
52
103
  ## Requirements
53
104
 
54
105
  - Node.js 24 or higher.
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: Quickstart
3
- description: From-template and manual setup paths to a working Eve agent chat in assistant-ui.
3
+ description: Template, Eve CLI, and manual setup paths to a working Eve agent chat in assistant-ui.
4
4
  ---
5
5
 
6
- Two paths to a running Eve-powered assistant-ui app. The template is fastest; the manual path is what you adapt when integrating into an existing Next.js project.
6
+ Three paths to a running Eve-powered assistant-ui app. The template is fastest, `eve add` drops assistant-ui into an Eve app you already have, and the manual path is what you adapt when integrating into an existing Next.js project.
7
7
 
8
8
  ## From the template
9
9
 
@@ -45,6 +45,55 @@ Use Eve's terminal UI directly for command-line agent sessions.
45
45
  </Tab>
46
46
  </PlatformTabs>
47
47
 
48
+ ## With the Eve CLI
49
+
50
+ For an Eve app that already exists, register the assistant-ui registry once and install the chat page from it:
51
+
52
+ ```sh
53
+ eve registry add @assistant-ui=https://r.assistant-ui.com/{name}.json
54
+ eve add @assistant-ui/eve-chat --overwrite
55
+ ```
56
+
57
+ `eve registry add` records the namespace in your `package.json`. `eve add` then installs `@assistant-ui/react` and `@assistant-ui/eve`, writes `app/page.tsx`, and writes the thread component together with everything it imports into `components/assistant-ui` and `components/ui`. It expects an Eve app that carries the Next.js Web Chat scaffold, because the installed files import `@/lib/utils` through the `@/*` alias that `eve init --channel-web-nextjs` sets up.
58
+
59
+ `--overwrite` is global rather than per file. It is what lets the item replace the scaffold's own `app/page.tsx`, and it also replaces any `components/assistant-ui` or `components/ui` file already in the project, including `avatar`, `button`, `collapsible`, `dialog`, and `tooltip`, so install on a clean tree and read the diff before keeping it. A plain `eve add` skips every pre-existing file instead, which leaves `app/page.tsx` untouched. Inspect an item before installing it with `eve registry view @assistant-ui/eve-chat`, and browse the rest of the registry with `eve registry list --registry @assistant-ui`.
60
+
61
+ That namespace serves the Radix flavor, which matches the shadcn components in Eve's Next.js scaffold. Point it at `https://r.assistant-ui.com/base/{name}.json` instead for a Base UI project.
62
+
63
+ The Eve CLI writes registry files without editing CSS, so add the styles the reasoning and tool components animate with to `app/globals.css` yourself:
64
+
65
+ ```css title="app/globals.css"
66
+ @import "tw-shimmer";
67
+
68
+ @custom-variant data-open (&:where([data-state="open"], [data-open]:not([data-open="false"])));
69
+ @custom-variant data-closed (&:where([data-state="closed"], [data-closed]:not([data-closed="false"])));
70
+
71
+ @theme inline {
72
+ @keyframes collapsible-down {
73
+ from {
74
+ height: 0;
75
+ }
76
+ to {
77
+ height: var(
78
+ --radix-collapsible-content-height,
79
+ var(--collapsible-panel-height, auto)
80
+ );
81
+ }
82
+ }
83
+ @keyframes collapsible-up {
84
+ from {
85
+ height: var(
86
+ --radix-collapsible-content-height,
87
+ var(--collapsible-panel-height, auto)
88
+ );
89
+ }
90
+ to {
91
+ height: 0;
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
48
97
  ## Manual setup in an existing app
49
98
 
50
99
  <Steps>
@@ -389,9 +389,9 @@ Register the renderer with a backend toolkit entry inside `AssistantRuntimeProvi
389
389
  ```tsx
390
390
  import {
391
391
  AssistantRuntimeProvider,
392
+ AuiConfig,
392
393
  defineToolkit,
393
394
  Tools,
394
- useAui,
395
395
  } from "@assistant-ui/react";
396
396
  import {
397
397
  createAdkStream,
@@ -410,10 +410,9 @@ function App() {
410
410
  const runtime = useAdkRuntime({
411
411
  stream: createAdkStream({ api: "/api/chat" }),
412
412
  });
413
- const aui = useAui({ tools: Tools({ toolkit }) });
414
-
413
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
415
414
  return (
416
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
415
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
417
416
  <Thread />
418
417
  </AssistantRuntimeProvider>
419
418
  );
@@ -426,9 +425,9 @@ function App() {
426
425
  ```tsx
427
426
  import {
428
427
  AssistantRuntimeProvider,
428
+ AuiConfig,
429
429
  defineToolkit,
430
430
  Tools,
431
- useAui,
432
431
  } from "@assistant-ui/react-native";
433
432
  import {
434
433
  createAdkStream,
@@ -450,10 +449,9 @@ function App() {
450
449
  const runtime = useAdkRuntime({
451
450
  stream: createAdkStream({ api: `${API_URL}/api/chat` }),
452
451
  });
453
- const aui = useAui({ tools: Tools({ toolkit }) });
454
-
452
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
455
453
  return (
456
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
454
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
457
455
  <View style={{ flex: 1 }}>
458
456
  <Thread />
459
457
  </View>
@@ -468,9 +466,9 @@ function App() {
468
466
  ```tsx
469
467
  import {
470
468
  AssistantRuntimeProvider,
469
+ AuiConfig,
471
470
  defineToolkit,
472
471
  Tools,
473
- useAui,
474
472
  } from "@assistant-ui/react-ink";
475
473
  import {
476
474
  createAdkStream,
@@ -490,10 +488,9 @@ function App() {
490
488
  const runtime = useAdkRuntime({
491
489
  stream: createAdkStream({ api: "http://localhost:3000/api/chat" }),
492
490
  });
493
- const aui = useAui({ tools: Tools({ toolkit }) });
494
-
491
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
495
492
  return (
496
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
493
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
497
494
  <Box flexDirection="column">
498
495
  <Thread />
499
496
  </Box>
@@ -108,7 +108,7 @@ See [Generative UI](/docs/runtimes/langgraph/generative-ui) for full setup: enab
108
108
 
109
109
  ## Queueing messages during a run
110
110
 
111
- Set `unstable_enableMessageQueue` to keep the composer usable while a run is streaming. A message sent during a run is held in `composer.queue` and sent once the run settles; steering a queued message runs it next.
111
+ Set `unstable_enableMessageQueue` to keep the composer usable while a run is streaming. A message sent during a queued run steers by default — it lands in the steer lane, ahead of previously queued items; `send({ steer: false })` queues a follow-up behind them instead. Pending items are held in `composer.queue` and drain once the run settles.
112
112
 
113
113
  ```tsx
114
114
  const runtime = useLangGraphRuntime({
@@ -94,7 +94,7 @@ This simply displays the tool name and arguments passed to it, but not the resul
94
94
 
95
95
  import { Thread } from "@/components/assistant-ui/thread";
96
96
  import { PriceSnapshotToolUI } from "@/components/tools/price-snapshot/PriceSnapshotTool";
97
- import { AuiProvider, defineToolkit, Tools, useAui } from "@assistant-ui/react";
97
+ import { AuiConfig, AuiProvider, defineToolkit, Tools, useAui } from "@assistant-ui/react";
98
98
 
99
99
  const toolkit = defineToolkit({
100
100
  price_snapshot: {
@@ -104,10 +104,10 @@ const toolkit = defineToolkit({
104
104
  });
105
105
 
106
106
  export default function Home() {
107
- const aui = useAui({ tools: Tools({ toolkit }) });
108
-
107
+ const aui = useAui();
108
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
109
109
  return (
110
- <AuiProvider value={aui}>
110
+ <AuiProvider extends={aui} config={config}>
111
111
  <Thread />
112
112
  </AuiProvider>
113
113
  );
@@ -334,10 +334,10 @@ const toolkit = defineToolkit({
334
334
  });
335
335
 
336
336
  export default function Home() {
337
- const aui = useAui({ tools: Tools({ toolkit }) });
338
-
337
+ const aui = useAui();
338
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
339
339
  return (
340
- <AuiProvider value={aui}>
340
+ <AuiProvider extends={aui} config={config}>
341
341
  <Thread />
342
342
  </AuiProvider>
343
343
  );
@@ -212,7 +212,7 @@ export function TransactionConfirmationPending(props: TransactionConfirmation) {
212
212
  import { Thread } from "@/components/assistant-ui/thread";
213
213
  import { PriceSnapshotToolUI } from "@/components/tools/price-snapshot/PriceSnapshotTool";
214
214
  import { PurchaseStockToolUI } from "@/components/tools/purchase-stock/PurchaseStockTool";
215
- import { AuiProvider, defineToolkit, Tools, useAui } from "@assistant-ui/react";
215
+ import { AuiConfig, AuiProvider, defineToolkit, Tools, useAui } from "@assistant-ui/react";
216
216
 
217
217
  const toolkit = defineToolkit({
218
218
  price_snapshot: {
@@ -226,10 +226,10 @@ const toolkit = defineToolkit({
226
226
  });
227
227
 
228
228
  export default function Home() {
229
- const aui = useAui({ tools: Tools({ toolkit }) });
230
-
229
+ const aui = useAui();
230
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
231
231
  return (
232
- <AuiProvider value={aui}>
232
+ <AuiProvider extends={aui} config={config}>
233
233
  <Thread />
234
234
  </AuiProvider>
235
235
  );
@@ -112,6 +112,7 @@ The returned `OpenCodeThreadState` includes:
112
112
 
113
113
  - `sessionId`, `session`, `sessionStatus`, `loadState`, `runState`
114
114
  - `messageOrder`, `messagesById`, `pendingUserMessages`
115
+ - `childSessionsById` (OpenCode `task` child-session projections keyed by session ID)
115
116
  - `interactions.permissions` (pending + resolved), `interactions.questions` (pending + answered + rejected)
116
117
  - `unhandledEvents`, `sync` timestamps
117
118
 
@@ -28,6 +28,16 @@ If your backend is not OpenCode-based, see [picking a runtime](/docs/runtimes/pi
28
28
  - A running [OpenCode](https://opencode.ai/) server (defaults to `http://localhost:4096`).
29
29
  - React 18 or 19.
30
30
 
31
+ ## Sub-agent conversations
32
+
33
+ OpenCode `task` tool calls expose their child session through tool metadata. The runtime loads those child sessions, keeps them synchronized from the same OpenCode event stream, and projects their transcripts into `ToolCallMessagePart.messages`. Parallel and recursively nested tasks are supported.
34
+
35
+ Loading is eager, not keyed to what a renderer displays. Once a thread is subscribed, every `task` part in its transcript fetches its child session, and each child does the same for its own nested tasks, so a long session with many task calls fetches every sub-agent transcript whether or not any of them is expanded. Each child session is fetched once and then kept current from the event stream, so re-subscribing a thread does not refetch it.
36
+
37
+ A `task` part carries `messages` only once its child session has loaded. While the child is still loading, the part projects as an ordinary tool call, so an empty `messages` array always means the sub-agent produced nothing rather than that its transcript is still in flight.
38
+
39
+ Use assistant-ui's [`MessagePartPrimitive.Messages`](/docs/tools/multi-agent) inside a `task` tool renderer to display the child conversation. Nested conversations are read-only and inherit the parent thread's tool renderers.
40
+
31
41
  ## Install
32
42
 
33
43
  <InstallCommand npm={["@assistant-ui/react", "@assistant-ui/react-opencode", "@opencode-ai/sdk"]} />
@@ -42,7 +42,7 @@ export async function POST(req: Request) {
42
42
  const { messages, system, tools } = await req.json();
43
43
 
44
44
  const result = streamText({
45
- model: openai("gpt-5.4-nano"),
45
+ model: openai("gpt-5.6-luna"),
46
46
  system,
47
47
  messages: await convertToModelMessages(messages),
48
48
  tools: await aiToolkit.tools({ frontend: tools }),
@@ -79,7 +79,7 @@ export async function POST(req: Request) {
79
79
  const { messages, tools } = await req.json();
80
80
 
81
81
  const result = streamText({
82
- model: openai("gpt-5.4-nano"),
82
+ model: openai("gpt-5.6-luna"),
83
83
  messages: await convertToModelMessages(messages),
84
84
  tools: {
85
85
  ...frontendTools(tools ?? {}),
@@ -115,21 +115,23 @@ The inner `"use client"` inside `execute` marks this as a **frontend** tool —
115
115
 
116
116
  ### Register the toolkit
117
117
 
118
- Import the toolkit in your runtime provider and pass it to `useAui` via `Tools`:
118
+ Import the toolkit in your runtime provider and pass it to `config` via `Tools`:
119
119
 
120
120
  ```tsx title="app/MyRuntimeProvider.tsx"
121
121
  "use client";
122
122
 
123
- import { AssistantRuntimeProvider, Tools, useAui } from "@assistant-ui/react";
123
+ import { AssistantRuntimeProvider, AuiConfig, Tools } from "@assistant-ui/react";
124
124
  import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
125
125
  import toolkit from "./toolkit";
126
126
 
127
127
  export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
128
128
  const runtime = useChatRuntime();
129
- const aui = useAui({ tools: Tools({ toolkit }) });
130
-
129
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
131
130
  return (
132
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
131
+ <AssistantRuntimeProvider
132
+ runtime={runtime}
133
+ config={config}
134
+ >
133
135
  {children}
134
136
  </AssistantRuntimeProvider>
135
137
  );
@@ -157,7 +159,7 @@ export async function POST(req: Request) {
157
159
  const { messages, tools } = await req.json();
158
160
 
159
161
  const result = streamText({
160
- model: openai("gpt-5.4-nano"),
162
+ model: openai("gpt-5.6-luna"),
161
163
  messages: await convertToModelMessages(messages),
162
164
  tools: await aiToolkit.tools({ frontend: tools }),
163
165
  });
@@ -368,7 +370,7 @@ export const toolkit = defineToolkit({
368
370
  });
369
371
  ```
370
372
 
371
- Register it exactly like a generative toolkit: `useAui({ tools: Tools({ toolkit }) })`. The key must match the tool name your backend or MCP server publishes. Render-only entries upload no schema and run no browser code — they only attach UI to matching tool-call message parts.
373
+ Register it exactly like a generative toolkit: hoist `const config = AuiConfig({ tools: Tools({ toolkit }) })` in the component body and pass `config={config}`. The key must match the tool name your backend or MCP server publishes. Render-only entries upload no schema and run no browser code — they only attach UI to matching tool-call message parts.
372
374
 
373
375
  <Callout type="warn">
374
376
  This `{ type: "backend", render }` shape is **plain-toolkit only**. Inside a
@@ -43,6 +43,7 @@ The component that owns the state registers the toolkit, then renders a small nu
43
43
 
44
44
  ```tsx title="app/TaskBoard.tsx"
45
45
  import {
46
+ AuiConfig,
46
47
  AuiProvider,
47
48
  Tools,
48
49
  useAui,
@@ -54,10 +55,10 @@ import toolkit from "./task-board-toolkit";
54
55
 
55
56
  function TaskBoard() {
56
57
  const [tasks, setTasks] = useState<Task[]>([]);
57
- const aui = useAui({ tools: Tools({ toolkit }) });
58
-
58
+ const aui = useAui();
59
+ const config = AuiConfig({ tools: Tools({ toolkit }) });
59
60
  return (
60
- <AuiProvider value={aui}>
61
+ <AuiProvider extends={aui} config={config}>
61
62
  <TaskBoardToolOverrides setTasks={setTasks} />
62
63
  <TaskList tasks={tasks} />
63
64
  </AuiProvider>
@@ -102,7 +103,8 @@ The override supplies **only** the `execute`; the description, parameters, and `
102
103
  <Callout type="info">
103
104
  Keep the override keys stable after mount, and let only one mounted provider
104
105
  define a given tool name at a time. The null-returning overrides component
105
- re-binds the executor whenever the setter changes, without re-running `useAui`.
106
+ re-binds the executor whenever the setter changes, without remounting the
107
+ provider.
106
108
  </Callout>
107
109
 
108
110
  ## When to reach for this vs. Interactables
@@ -33,18 +33,20 @@ Common use cases:
33
33
  ### 1. Register the Interactables scope
34
34
 
35
35
  ```tsx
36
- import { useAui, Interactables, AssistantRuntimeProvider } from "@assistant-ui/react";
36
+ import { AuiConfig, Interactables, AssistantRuntimeProvider } from "@assistant-ui/react";
37
37
  import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
38
38
 
39
39
  function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
40
40
  const runtime = useChatRuntime();
41
-
42
- const aui = useAui({
41
+ const config = AuiConfig({
43
42
  interactables: Interactables(), // [!code ++]
44
43
  });
45
44
 
46
45
  return (
47
- <AssistantRuntimeProvider aui={aui} runtime={runtime}>
46
+ <AssistantRuntimeProvider
47
+ runtime={runtime}
48
+ config={config}
49
+ >
48
50
  {children}
49
51
  </AssistantRuntimeProvider>
50
52
  );
@@ -54,7 +56,7 @@ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
54
56
  <Callout type="idea">
55
57
  The legacy `interactables: Interactables()` scope and the new
56
58
  `unstable_interactables: unstable_Interactables()` scope are mutually
57
- exclusive. Mount only one interactables API in a single `useAui` provider.
59
+ exclusive. Mount only one interactables API in a single provider config.
58
60
  </Callout>
59
61
 
60
62
  ### 2. Create an interactable
@@ -266,10 +268,10 @@ const [state, { setState, setSelected, isPending, error, flush }] = useInteracta
266
268
 
267
269
  ### `Interactables`
268
270
 
269
- The scope resource that manages all interactables. Register it via `useAui`:
271
+ The scope resource that manages all interactables. Register it via your provider's `config`:
270
272
 
271
273
  ```tsx
272
- const aui = useAui({
274
+ AuiConfig({
273
275
  interactables: Interactables(),
274
276
  });
275
277
  ```
@@ -291,10 +293,21 @@ By default, interactable state is in-memory and lost on page refresh. You can ad
291
293
 
292
294
  ```tsx
293
295
  import { useEffect } from "react";
294
- import { useAui, Interactables } from "@assistant-ui/react";
296
+ import { AuiConfig, AuiProvider, Interactables, useAui } from "@assistant-ui/react";
295
297
 
296
298
  function MyRuntimeProvider({ children }) {
297
- const aui = useAui({ interactables: Interactables() });
299
+ const aui = useAui();
300
+ const config = AuiConfig({ interactables: Interactables() });
301
+ return (
302
+ <AuiProvider extends={aui} config={config}>
303
+ <PersistenceSetup />
304
+ {children}
305
+ </AuiProvider>
306
+ );
307
+ }
308
+
309
+ function PersistenceSetup() {
310
+ const aui = useAui();
298
311
 
299
312
  useEffect(() => {
300
313
  // Set up persistence adapter
@@ -311,7 +324,7 @@ function MyRuntimeProvider({ children }) {
311
324
  }
312
325
  }, [aui]);
313
326
 
314
- return /* ... */;
327
+ return null;
315
328
  }
316
329
  ```
317
330
 
@@ -346,7 +359,7 @@ aui.interactables.importState(snapshot);
346
359
  You can use `Interactables` alongside `Tools`:
347
360
 
348
361
  ```tsx
349
- const aui = useAui({
362
+ AuiConfig({
350
363
  tools: Tools({ toolkit: myToolkit }),
351
364
  interactables: Interactables(),
352
365
  });