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

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 (136) hide show
  1. package/.docs/organized/code-examples/waterfall.md +7 -7
  2. package/.docs/organized/code-examples/with-a2a.md +7 -7
  3. package/.docs/organized/code-examples/with-ag-ui.md +8 -8
  4. package/.docs/organized/code-examples/with-ai-sdk-v7.md +9 -9
  5. package/.docs/organized/code-examples/with-artifacts.md +463 -138
  6. package/.docs/organized/code-examples/with-assistant-transport.md +7 -7
  7. package/.docs/organized/code-examples/with-browser-extension.md +6 -6
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +11 -11
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +9 -9
  10. package/.docs/organized/code-examples/with-cloud.md +9 -9
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +9 -9
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +11 -11
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +11 -11
  14. package/.docs/organized/code-examples/with-eve.md +8 -8
  15. package/.docs/organized/code-examples/with-expo.md +26 -46
  16. package/.docs/organized/code-examples/with-external-store.md +7 -7
  17. package/.docs/organized/code-examples/with-ffmpeg.md +9 -9
  18. package/.docs/organized/code-examples/with-generative-ui.md +10 -10
  19. package/.docs/organized/code-examples/with-google-adk.md +8 -8
  20. package/.docs/organized/code-examples/with-heat-graph.md +6 -6
  21. package/.docs/organized/code-examples/with-image-generation.md +9 -9
  22. package/.docs/organized/code-examples/with-interactables.md +9 -9
  23. package/.docs/organized/code-examples/with-langchain.md +9 -9
  24. package/.docs/organized/code-examples/with-langgraph.md +9 -9
  25. package/.docs/organized/code-examples/with-livekit.md +11 -11
  26. package/.docs/organized/code-examples/with-mcp.md +28 -23
  27. package/.docs/organized/code-examples/with-opencode.md +8 -11
  28. package/.docs/organized/code-examples/with-pi.md +14 -14
  29. package/.docs/organized/code-examples/with-react-hook-form.md +10 -10
  30. package/.docs/organized/code-examples/with-react-ink-web.md +5 -5
  31. package/.docs/organized/code-examples/with-react-ink.md +2 -2
  32. package/.docs/organized/code-examples/with-react-router.md +11 -11
  33. package/.docs/organized/code-examples/with-resumable-stream.md +10 -10
  34. package/.docs/organized/code-examples/with-store.md +6 -6
  35. package/.docs/organized/code-examples/with-tanstack.md +9 -9
  36. package/.docs/organized/code-examples/with-tap-runtime.md +7 -7
  37. package/.docs/organized/code-examples/with-virtualized-thread.md +8 -8
  38. package/.docs/raw/docs/(docs)/cli.mdx +2 -0
  39. package/.docs/raw/docs/(docs)/devtools.mdx +7 -2
  40. package/.docs/raw/docs/(reference)/api-reference/generative-ui/a2ui.mdx +40 -0
  41. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +3 -0
  42. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +19 -420
  43. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +4 -1
  44. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +21 -0
  45. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -9
  46. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +1 -18
  47. package/.docs/raw/docs/cloud/langgraph.mdx +1 -1
  48. package/.docs/raw/docs/copilots/model-context.mdx +1 -1
  49. package/.docs/raw/docs/copilots/motivation.mdx +1 -1
  50. package/.docs/raw/docs/guides/attachments.mdx +3 -3
  51. package/.docs/raw/docs/guides/branching.mdx +2 -2
  52. package/.docs/raw/docs/guides/context-api.mdx +89 -111
  53. package/.docs/raw/docs/guides/editing.mdx +5 -5
  54. package/.docs/raw/docs/guides/electron.mdx +369 -0
  55. package/.docs/raw/docs/guides/index.mdx +10 -0
  56. package/.docs/raw/docs/guides/quoting.mdx +3 -3
  57. package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +2 -2
  58. package/.docs/raw/docs/ink/hooks.mdx +3 -3
  59. package/.docs/raw/docs/ink/primitives.mdx +14 -7
  60. package/.docs/raw/docs/integrations/auth/better-auth.mdx +2 -2
  61. package/.docs/raw/docs/integrations/auth/clerk.mdx +2 -2
  62. package/.docs/raw/docs/integrations/auth/next-auth.mdx +3 -3
  63. package/.docs/raw/docs/integrations/observability/langsmith.mdx +1 -1
  64. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +36 -9
  65. package/.docs/raw/docs/migrations/v0-15.mdx +156 -0
  66. package/.docs/raw/docs/primitives/composer.mdx +1 -1
  67. package/.docs/raw/docs/primitives/thread-list.mdx +2 -2
  68. package/.docs/raw/docs/react-native/hooks.mdx +3 -3
  69. package/.docs/raw/docs/react-native/index.mdx +2 -2
  70. package/.docs/raw/docs/react-native/primitives.mdx +39 -5
  71. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +3 -3
  72. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +4 -4
  73. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +4 -4
  74. package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +4 -4
  75. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +2 -2
  76. package/.docs/raw/docs/runtimes/concepts/threads.mdx +10 -10
  77. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +2 -2
  78. package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
  79. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +3 -3
  80. package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
  81. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +1 -1
  82. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +3 -1
  83. package/.docs/raw/docs/tools/a2ui.mdx +107 -0
  84. package/.docs/raw/docs/tools/interactables-legacy.mdx +4 -4
  85. package/.docs/raw/docs/tools/interactables.mdx +3 -3
  86. package/.docs/raw/docs/tools/mcp-apps.mdx +61 -2
  87. package/.docs/raw/docs/tools/user-managed-mcp.mdx +52 -7
  88. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +2 -0
  89. package/.docs/raw/docs/ui/model-selector.mdx +1 -1
  90. package/.docs/raw/docs/ui/part-grouping.mdx +0 -4
  91. package/.docs/raw/docs/ui/reasoning.mdx +1 -1
  92. package/dist/index.d.ts +1 -1
  93. package/dist/index.js +2 -2
  94. package/dist/index.js.map +1 -1
  95. package/dist/prepare-docs/code-examples.js.map +1 -1
  96. package/dist/prepare-docs/prepare.d.ts +1 -1
  97. package/dist/stdio.d.ts +1 -1
  98. package/dist/tools/docs.d.ts +6 -10
  99. package/dist/tools/docs.d.ts.map +1 -1
  100. package/dist/tools/docs.js +2 -2
  101. package/dist/tools/docs.js.map +1 -1
  102. package/dist/tools/examples.d.ts +4 -8
  103. package/dist/tools/examples.d.ts.map +1 -1
  104. package/dist/tools/examples.js +2 -2
  105. package/dist/tools/examples.js.map +1 -1
  106. package/dist/tools/resources.d.ts +1 -1
  107. package/dist/tools/resources.d.ts.map +1 -1
  108. package/dist/tools/resources.js +1 -1
  109. package/dist/tools/resources.js.map +1 -1
  110. package/dist/tools/search.d.ts +4 -10
  111. package/dist/tools/search.d.ts.map +1 -1
  112. package/dist/tools/search.js +2 -2
  113. package/dist/tools/search.js.map +1 -1
  114. package/dist/tools/tests/mcp-test-client.d.ts +15 -0
  115. package/dist/tools/tests/mcp-test-client.d.ts.map +1 -0
  116. package/dist/tools/tests/mcp-test-client.js +68 -0
  117. package/dist/tools/tests/mcp-test-client.js.map +1 -0
  118. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  119. package/dist/tools/xulux-templates.d.ts +9 -23
  120. package/dist/tools/xulux-templates.d.ts.map +1 -1
  121. package/dist/tools/xulux-templates.js +4 -4
  122. package/dist/tools/xulux-templates.js.map +1 -1
  123. package/dist/utils/logger.d.ts.map +1 -1
  124. package/dist/utils/security.js.map +1 -1
  125. package/package.json +4 -3
  126. package/src/index.ts +2 -2
  127. package/src/tools/docs.ts +2 -2
  128. package/src/tools/examples.ts +2 -2
  129. package/src/tools/resources.ts +1 -4
  130. package/src/tools/search.ts +2 -2
  131. package/src/tools/tests/completions.test.ts +40 -26
  132. package/src/tools/tests/integration.test.ts +3 -4
  133. package/src/tools/tests/mcp-protocol.test.ts +160 -175
  134. package/src/tools/tests/mcp-test-client.ts +111 -0
  135. package/src/tools/tests/resources.test.ts +97 -66
  136. package/src/tools/xulux-templates.ts +4 -4
@@ -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
@@ -0,0 +1,156 @@
1
+ ---
2
+ title: Migration to v0.15
3
+ description: Drops the v0.12-era legacy runtime hooks, the deprecated tools map, and the "mcp-app" group key. Scope accessors become properties.
4
+ ---
5
+
6
+ Version 0.15 finishes the deprecation cycle started in v0.12: the legacy context hooks are removed in favor of `useAui` / `useAuiState`, and `aui` scope accessors become properties.
7
+
8
+ ## Migrate with an AI Agent
9
+
10
+ Paste this into an AI coding agent to run the migration for you:
11
+
12
+ ```text
13
+ Migrate this codebase from assistant-ui v0.14 to v0.15.
14
+
15
+ 1. Read the migration guide: https://assistant-ui.com/docs/migrations/v0-15
16
+ 2. Run `npx assistant-ui@latest upgrade` to apply the codemods.
17
+ 3. Apply the remaining changes from the guide by hand:
18
+ - Replace removed legacy hooks with the useAui / useAuiState
19
+ equivalents from the guide's mapping table.
20
+ - Replace availability checks with `aui.<scope>.source != null`.
21
+ - Replace `s.tools.tools` reads with `s.tools.toolUIs`.
22
+ - Replace the "mcp-app" groupPartByType key with "standalone-tool-call".
23
+ 4. Typecheck, build, and run tests; fix any remaining fallout.
24
+ ```
25
+
26
+ ## Automatic Migration
27
+
28
+ ```bash
29
+ npx assistant-ui@latest upgrade
30
+ ```
31
+
32
+ This runs the `v0-15/aui-accessor-calls-to-properties` codemod, which rewrites nullary accessor calls (`aui.thread()`) to property access (`aui.thread`).
33
+
34
+ ## Scope Accessors Are Properties
35
+
36
+ Nullary scope accessors are now properties. Calling them still works but is deprecated:
37
+
38
+ ```ts
39
+ // Before
40
+ aui.thread().getState();
41
+ aui.threads().switchToNewThread();
42
+
43
+ // After
44
+ aui.thread.getState();
45
+ aui.threads.switchToNewThread();
46
+ ```
47
+
48
+ Selecting an unavailable scope no longer throws: `aui.thread` always succeeds and is always truthy. Its `source` is `null` when the scope is unavailable, and any other property read (or a call) throws. Check availability via:
49
+
50
+ ```ts
51
+ if (aui.thread.source != null) {
52
+ // scope is available
53
+ }
54
+ ```
55
+
56
+ Accessors expose `source`, `query`, and `name` selection metadata as properties — previously found on the accessor function, now on the same names on the proxy. These three names are reserved and never resolve to scope methods.
57
+
58
+ ## Legacy Context Hooks Removed
59
+
60
+ The v0.12-era runtime hooks are removed. Replace state reads with `useAuiState` and actions with `useAui`:
61
+
62
+ | Removed | Replacement |
63
+ | ------------------------------------------------------------- | ----------------------------------------------- |
64
+ | `useAssistantRuntime()` | `useAui()` |
65
+ | `useThreadList(selector)` | `useAuiState((s) => s.threads)` |
66
+ | `useThreadRuntime()` | `useAui().thread` |
67
+ | `useThread(selector)` | `useAuiState((s) => s.thread)` |
68
+ | `useThreadComposer(selector)` | `useAuiState((s) => s.thread.composer)` |
69
+ | `useThreadModelContext(selector)` | `useAuiState((s) => s.thread.modelContext)` |
70
+ | `useMessageRuntime()` | `useAui().message` |
71
+ | `useMessage(selector)` | `useAuiState((s) => s.message)` |
72
+ | `useEditComposer(selector)` | `useAuiState((s) => s.message.composer)` |
73
+ | `useComposerRuntime()` | `useAui().composer` |
74
+ | `useComposer(selector)` | `useAuiState((s) => s.composer)` |
75
+ | `useMessagePartRuntime()` | `useAui().part` |
76
+ | `useMessagePart(selector)` | `useAuiState((s) => s.part)` |
77
+ | `useAttachmentRuntime()` | `useAui().attachment` |
78
+ | `useAttachment(selector)` | `useAuiState((s) => s.attachment)` |
79
+ | `useThreadListItemRuntime()` | `useAui().threadListItem` |
80
+ | `useThreadListItem(selector)` | `useAuiState((s) => s.threadListItem)` |
81
+
82
+ The attachment variants (`useThreadComposerAttachment(Runtime)`, `useEditComposerAttachment(Runtime)`, `useMessageAttachment(Runtime)`) are removed with them; use `useAui().attachment` / `useAuiState((s) => s.attachment)`.
83
+
84
+ ```tsx
85
+ // Before
86
+ const runtime = useAssistantRuntime();
87
+ const isRunning = useThread((s) => s.isRunning);
88
+ runtime.threads.switchToNewThread();
89
+
90
+ // After
91
+ const aui = useAui();
92
+ const isRunning = useAuiState((s) => s.thread.isRunning);
93
+ aui.threads.switchToNewThread();
94
+ ```
95
+
96
+ ## `ToolsState.tools` Removed
97
+
98
+ The component-only tool-UI map is replaced by `toolUIs`, whose entries carry the renderer alongside its presentation options:
99
+
100
+ ```tsx
101
+ // Before
102
+ const Render = useAuiState((s) => s.tools.tools[toolName]?.[0]);
103
+
104
+ // After
105
+ const Render = useAuiState((s) => s.tools.toolUIs[toolName]?.[0]?.render);
106
+ ```
107
+
108
+ ## `"mcp-app"` Group Key Removed
109
+
110
+ `groupPartByType` no longer accepts the `"mcp-app"` key. Use `"standalone-tool-call"`, a superset that matches MCP-app tool calls plus any tool call whose registered UI opts into standalone display:
111
+
112
+ ```tsx
113
+ // Before
114
+ groupPartByType({
115
+ "tool-call": ["group-tool"],
116
+ "mcp-app": [],
117
+ });
118
+
119
+ // After
120
+ groupPartByType({
121
+ "tool-call": ["group-tool"],
122
+ "standalone-tool-call": [],
123
+ });
124
+ ```
125
+
126
+ ## `useAui` `{ parent }` Config Removed
127
+
128
+ The second argument (`useAui(clients, { parent })`) is removed. Provide the parent via context instead: wrap with `AuiProvider` and call the context form beneath it.
129
+
130
+ ```tsx
131
+ // Before
132
+ const aui = useAui(scopes, { parent });
133
+
134
+ // After
135
+ const Scoped = ({ children }) => {
136
+ const aui = useAui(scopes); // extends the AuiProvider parent
137
+ return <AuiProvider value={aui}>{children}</AuiProvider>;
138
+ };
139
+
140
+ <AuiProvider value={parent}>
141
+ <Scoped />
142
+ </AuiProvider>;
143
+ ```
144
+
145
+ Where `{ parent: null }` was used to detach from context, `<AuiProvider value={null}>` now provides an isolated empty root (experimental).
146
+
147
+ ## Still Deprecated (not removed)
148
+
149
+ - Primitive `If` components (`ThreadPrimitive.If`, `MessagePrimitive.If`, `ThreadPrimitive.Empty`) — replaced by `AuiIf`. The codemod migrates these.
150
+ - `useMessagePartText` / `useMessagePartReasoning` / `useMessagePartSource` / `useMessagePartImage` / `useMessagePartFile` / `useMessagePartData` — use `useAuiState` to select and narrow `s.part`.
151
+ - The `components` prop on primitives — replaced by the children render function pattern (see the [v0.14 guide](/docs/migrations/v0-14)).
152
+
153
+ ## Getting Help
154
+
155
+ - File issues at https://github.com/assistant-ui/assistant-ui/issues
156
+ - See the [deprecation policy](/docs/migrations/deprecation-policy) for the support window
@@ -570,7 +570,7 @@ When `isSendDisabled` is `true`:
570
570
  - `composer.canSend` becomes `false`
571
571
  - `<ComposerPrimitive.Send>` is disabled
572
572
  - Enter and the `Cmd/Ctrl+Shift+Enter` steer hotkey become no-ops
573
- - `aui.composer().send()` short-circuits at the runtime, so direct calls cannot escape the gate
573
+ - `aui.composer.send()` short-circuits at the runtime, so direct calls cannot escape the gate
574
574
 
575
575
  Read the same flag from React with `<AuiIf>` to render hints alongside the input:
576
576
 
@@ -253,7 +253,7 @@ Button that appends the next page of threads. Renders a `<button>` element unles
253
253
  </ThreadListPrimitive.LoadMore>
254
254
  ```
255
255
 
256
- For scroll-driven loading, wrap `aui.threads().loadMore()` in your own `IntersectionObserver` at the application layer; assistant-ui ships the explicit button to keep the primitive surface predictable.
256
+ For scroll-driven loading, wrap `aui.threads.loadMore()` in your own `IntersectionObserver` at the application layer; assistant-ui ships the explicit button to keep the primitive surface predictable.
257
257
 
258
258
  ```tsx
259
259
  import { useAui, useAuiState } from "@assistant-ui/react";
@@ -271,7 +271,7 @@ function LoadMoreSentinel() {
271
271
  const el = ref.current;
272
272
  if (!el || disabled) return;
273
273
  const observer = new IntersectionObserver(([entry]) => {
274
- if (entry?.isIntersecting) aui.threads().loadMore();
274
+ if (entry?.isIntersecting) aui.threads.loadMore();
275
275
  });
276
276
  observer.observe(el);
277
277
  return () => observer.disconnect();
@@ -41,11 +41,11 @@ import { useAui } from "@assistant-ui/react-native";
41
41
  const aui = useAui();
42
42
 
43
43
  // Composer actions
44
- aui.composer().setText("Hello");
45
- aui.composer().send();
44
+ aui.composer.setText("Hello");
45
+ aui.composer.send();
46
46
 
47
47
  // Thread actions
48
- aui.thread().cancelRun();
48
+ aui.thread.cancelRun();
49
49
  ```
50
50
 
51
51
  ### useAuiEvent
@@ -197,7 +197,7 @@ function Composer() {
197
197
  >
198
198
  <TextInput
199
199
  value={text}
200
- onChangeText={(t) => aui.composer().setText(t)}
200
+ onChangeText={(t) => aui.composer.setText(t)}
201
201
  placeholder="Message..."
202
202
  multiline
203
203
  style={{
@@ -211,7 +211,7 @@ function Composer() {
211
211
  }}
212
212
  />
213
213
  <Pressable
214
- onPress={() => aui.composer().send()}
214
+ onPress={() => aui.composer.send()}
215
215
  disabled={isEmpty}
216
216
  style={{
217
217
  marginLeft: 8,
@@ -31,7 +31,9 @@ Container `View` for the thread area.
31
31
 
32
32
  ### ThreadPrimitive.Messages
33
33
 
34
- `FlatList`-based message list with automatic runtime integration. Each message is wrapped in a scoped context so that `useAuiState((s) => s.message)` works inside the rendered component.
34
+ Deprecated `FlatList`-based message list kept for backwards compatibility. It uses `ThreadPrimitive.MessagesFlatList` internally, but keeps the previous no-auto-scroll default so existing apps with custom scroll handling do not change behavior on upgrade.
35
+
36
+ Use `ThreadPrimitive.MessagesFlatList` for new React Native threads.
35
37
 
36
38
  ```tsx
37
39
  <ThreadPrimitive.Messages>
@@ -53,7 +55,29 @@ You can also provide role-specific components:
53
55
  | Prop | Type | Description |
54
56
  |------|------|-------------|
55
57
  | `components` | `MessageComponents` | Component map — provide either a `Message` component (used for all roles) or role-specific `UserMessage`, `AssistantMessage`, and optionally `SystemMessage`. Edit composers can be set via `EditComposer` or role-specific variants (`UserEditComposer`, `AssistantEditComposer`, `SystemEditComposer`). |
56
- | `...rest` | `FlatListProps` | Standard FlatList props (except `data`, `renderItem`) |
58
+ | `...rest` | `ThreadPrimitive.MessagesFlatList` props | Same props as `ThreadPrimitive.MessagesFlatList`, with the auto-scroll options defaulting to `false` on this deprecated wrapper |
59
+
60
+ ### ThreadPrimitive.MessagesFlatList
61
+
62
+ Canonical React Native message viewport backed by `FlatList`. It scopes each row to its message, forwards refs to the underlying `FlatList`, and handles scroll-to-bottom behavior for common chat flows.
63
+
64
+ ```tsx
65
+ <ThreadPrimitive.MessagesFlatList autoScroll>
66
+ {() => <MyMessage />}
67
+ </ThreadPrimitive.MessagesFlatList>
68
+ ```
69
+
70
+ | Prop | Type | Description |
71
+ |------|------|-------------|
72
+ | `children` | `({ message }) => ReactNode` | Render function for each message |
73
+ | `components` | `MessageComponents` | Component map alternative to `children` |
74
+ | `autoScroll` | `boolean` | Automatically keeps the list at the bottom while it is already near the bottom. Defaults to `true` |
75
+ | `scrollToBottomOnRunStart` | `boolean` | Scrolls to the bottom when a run starts. Defaults to `true` |
76
+ | `scrollToBottomOnInitialize` | `boolean` | Scrolls to the bottom when messages first appear. Defaults to `true` |
77
+ | `scrollToBottomOnThreadSwitch` | `boolean` | Scrolls to the bottom when switching threads. Defaults to `true` |
78
+ | `...rest` | `FlatListProps` | Standard FlatList props, except `data`, `renderItem`, and `children` |
79
+
80
+ The built-in auto-scroll behavior assumes the default, non-inverted `FlatList` direction.
57
81
 
58
82
  ### ThreadPrimitive.MessageByIndex
59
83
 
@@ -541,10 +565,20 @@ import { ActionBarPrimitive } from "@assistant-ui/react-native";
541
565
 
542
566
  ### ActionBarPrimitive.Copy
543
567
 
544
- `Pressable` that copies the message content. Supports function-as-children for copy state feedback.
568
+ `Pressable` that copies the message content. Pass a platform clipboard writer through `copyToClipboard`. Supports function-as-children for copy state feedback.
545
569
 
546
570
  ```tsx
547
- <ActionBarPrimitive.Copy copiedDuration={3000}>
571
+ import * as Clipboard from "expo-clipboard";
572
+
573
+ const copyToClipboard = async (text: string) => {
574
+ const didCopy = await Clipboard.setStringAsync(text);
575
+ if (!didCopy) throw new Error("Clipboard write failed");
576
+ };
577
+
578
+ <ActionBarPrimitive.Copy
579
+ copiedDuration={3000}
580
+ copyToClipboard={copyToClipboard}
581
+ >
548
582
  {({ isCopied }) => <Text>{isCopied ? "Copied!" : "Copy"}</Text>}
549
583
  </ActionBarPrimitive.Copy>
550
584
  ```
@@ -552,7 +586,7 @@ import { ActionBarPrimitive } from "@assistant-ui/react-native";
552
586
  | Prop | Type | Description |
553
587
  |------|------|-------------|
554
588
  | `copiedDuration` | `number` | Duration in ms to show "copied" state (default: 3000) |
555
- | `copyToClipboard` | `(text: string) => void` | Custom clipboard function |
589
+ | `copyToClipboard` | `(text: string) => void \| Promise<void>` | Platform clipboard writer |
556
590
 
557
591
  ### ActionBarPrimitive.Edit
558
592
 
@@ -11,9 +11,9 @@ import { VercelIcon } from "@/components/icons/vercel";
11
11
 
12
12
  | AI SDK | Runtime package | Docs |
13
13
  | --- | --- | --- |
14
- | `ai@^7` + `@ai-sdk/react@^4` | `@assistant-ui/react-ai-sdk` (latest) | [v7 (current)](/docs/runtimes/ai-sdk/v7) |
15
- | `ai@^6` + `@ai-sdk/react@^3` | `@assistant-ui/react-ai-sdk@1.x` | [v6 (legacy)](/docs/runtimes/ai-sdk/v6-legacy) |
16
- | `ai@^5` + `@ai-sdk/react@^2` | `@assistant-ui/react-ai-sdk@0.x` | [v5 (legacy)](/docs/runtimes/ai-sdk/v5-legacy) |
14
+ | `ai@^7` + `@ai-sdk/react@^4` | `@assistant-ui/react-ai-sdk@^1.4` (latest) | [v7 (current)](/docs/runtimes/ai-sdk/v7) |
15
+ | `ai@^6` + `@ai-sdk/react@^3` | `@assistant-ui/react-ai-sdk@1.3.40` | [v6 (legacy)](/docs/runtimes/ai-sdk/v6-legacy) |
16
+ | `ai@^5` + `@ai-sdk/react@^2` | `@assistant-ui/react-ai-sdk@1.1.21` | [v5 (legacy)](/docs/runtimes/ai-sdk/v5-legacy) |
17
17
  | `ai@^4` | `@assistant-ui/react-data-stream` | [v4 (legacy)](/docs/runtimes/ai-sdk/v4-legacy) |
18
18
 
19
19
  New projects should target v7. v6, v5, and v4 are documented for projects that have not migrated yet; all have known compatibility gaps and receive no new features.
@@ -11,9 +11,9 @@ AI SDK v4 is a legacy version. New projects should use [AI SDK v7](/docs/runtime
11
11
 
12
12
  ## Why legacy
13
13
 
14
- Vercel ships AI SDK majors roughly yearly; v4 is two majors behind v6. The current `@assistant-ui/react-ai-sdk` package targets v6+ exclusively, so v4 users plug into [`@assistant-ui/react-data-stream`](/docs/runtimes/custom/data-stream) instead — the same data stream protocol that v4's `toDataStreamResponse()` emits.
14
+ Vercel ships AI SDK majors roughly yearly; v4 is three majors behind v7. The current `@assistant-ui/react-ai-sdk` package targets the current AI SDK major (v7) exclusively, so v4 users plug into [`@assistant-ui/react-data-stream`](/docs/runtimes/custom/data-stream) instead — the same data stream protocol that v4's `toDataStreamResponse()` emits.
15
15
 
16
- This page is reference for existing v4 projects. Plan to migrate to v6 when feasible.
16
+ This page is reference for existing v4 projects. Plan to migrate to v7 when feasible.
17
17
 
18
18
  ## Setup
19
19
 
@@ -95,9 +95,9 @@ When you are ready to upgrade:
95
95
 
96
96
  AI SDK provides codemods at [ai-sdk.dev/docs/migration-guides](https://ai-sdk.dev/docs/migration-guides) that handle the package-side rewrites.
97
97
 
98
- ## Alternative: react-ai-sdk@0.1.10
98
+ ## Alternative: react-ai-sdk@0.10.16
99
99
 
100
- An older release line of `@assistant-ui/react-ai-sdk` (0.1.10) supported AI SDK v4 directly. It is no longer maintained and has no upgrade path; if you are starting a new v4 project, use `@assistant-ui/react-data-stream` instead.
100
+ An older release line of `@assistant-ui/react-ai-sdk` supported AI SDK v4 directly; its last release was `0.10.16`. It is no longer maintained and has no upgrade path; if you are starting a new v4 project, use `@assistant-ui/react-data-stream` instead.
101
101
 
102
102
  ## Related
103
103
 
@@ -6,12 +6,12 @@ description: Reference for projects still on AI SDK v5. New projects should use
6
6
  import { VercelIcon } from "@/components/icons/vercel";
7
7
 
8
8
  <Callout type="warn">
9
- AI SDK v5 is a legacy version. New projects should use [AI SDK v7](/docs/runtimes/ai-sdk/v7). v5 users stay on `@assistant-ui/react-ai-sdk@0.x`; the latest line targets v7+.
9
+ AI SDK v5 is a legacy version. New projects should use [AI SDK v7](/docs/runtimes/ai-sdk/v7). v5 users pin `@assistant-ui/react-ai-sdk@1.1.21` (the last v5-compatible release); later releases target newer AI SDK majors.
10
10
  </Callout>
11
11
 
12
12
  ## Why legacy
13
13
 
14
- AI SDK v6 introduced major API changes: async `convertToModelMessages`, a new tool schema, and `toUIMessageStreamResponse()`. v5 still works but no longer receives new features in `@assistant-ui/react-ai-sdk`. Pinning to `@0.x` keeps you on the v5-compatible surface.
14
+ AI SDK v6 introduced major API changes: async `convertToModelMessages`, a new tool schema, and `toUIMessageStreamResponse()`. v5 still works but no longer receives new features in `@assistant-ui/react-ai-sdk`. Pinning to `1.1.21` keeps you on the v5-compatible surface.
15
15
 
16
16
  This page is reference for existing v5 projects. Plan to migrate to v7 when feasible.
17
17
 
@@ -22,7 +22,7 @@ This page is reference for existing v5 projects. Plan to migrate to v7 when feas
22
22
 
23
23
  ### Install v5-compatible versions
24
24
 
25
- <InstallCommand npm={["@assistant-ui/react", "@assistant-ui/react-ai-sdk@0.x", "ai@^5", "@ai-sdk/openai@^1", "zod"]} />
25
+ <InstallCommand npm={["@assistant-ui/react", "@assistant-ui/react-ai-sdk@1.1.21", "ai@^5", "@ai-sdk/openai@^1", "zod"]} />
26
26
 
27
27
  </Step>
28
28
  <Step>
@@ -98,7 +98,7 @@ const runtime = useVercelUseChatRuntime(chat);
98
98
  | Feature | v5 | v6 |
99
99
  | --- | --- | --- |
100
100
  | `ai` package | `ai@^5` | `ai@^6` |
101
- | `@assistant-ui/react-ai-sdk` | `@0.x` | `@latest` |
101
+ | `@assistant-ui/react-ai-sdk` | `@1.1.21` | `@1.3.40` |
102
102
  | `@ai-sdk/openai` | `^1` | `^3` |
103
103
  | Message type | `Message` | `UIMessage` |
104
104
  | `convertToModelMessages` | Sync | Async (`await`) |
@@ -4,7 +4,7 @@ description: Reference for projects still on AI SDK v6. New projects should use
4
4
  ---
5
5
 
6
6
  <Callout type="warn">
7
- AI SDK v6 is a legacy version. New projects should use [AI SDK v7](/docs/runtimes/ai-sdk/v7). v6 users stay on `@assistant-ui/react-ai-sdk@1.x`; the latest line targets v7+.
7
+ AI SDK v6 is a legacy version. New projects should use [AI SDK v7](/docs/runtimes/ai-sdk/v7). v6 users pin `@assistant-ui/react-ai-sdk@1.3.40` (the last v6-compatible release); later releases target v7.
8
8
  </Callout>
9
9
 
10
10
  Requires `ai@^6` and `@ai-sdk/react@^3`. For other versions see the [overview](/docs/runtimes/ai-sdk/overview).
@@ -52,17 +52,17 @@ npm init -y
52
52
  <PlatformTabs>
53
53
  <Tab value="React">
54
54
 
55
- <InstallCommand npm={["@assistant-ui/react", "@assistant-ui/react-ai-sdk", "ai@^6", "@ai-sdk/react@^3", "@ai-sdk/openai", "zod"]} />
55
+ <InstallCommand npm={["@assistant-ui/react", "@assistant-ui/react-ai-sdk@1.3.40", "ai@^6", "@ai-sdk/react@^3", "@ai-sdk/openai", "zod"]} />
56
56
 
57
57
  </Tab>
58
58
  <Tab value="React Native">
59
59
 
60
- <InstallCommand expo={["@assistant-ui/react-native", "@assistant-ui/react-ai-sdk", "ai@^6", "@ai-sdk/react@^3", "@ai-sdk/openai", "zod"]} />
60
+ <InstallCommand expo={["@assistant-ui/react-native", "@assistant-ui/react-ai-sdk@1.3.40", "ai@^6", "@ai-sdk/react@^3", "@ai-sdk/openai", "zod"]} />
61
61
 
62
62
  </Tab>
63
63
  <Tab value="React Ink">
64
64
 
65
- <InstallCommand npm={["@assistant-ui/react-ink", "@assistant-ui/react-ai-sdk", "ai@^6", "@ai-sdk/react@^3", "@ai-sdk/openai", "zod", "ink", "react"]} />
65
+ <InstallCommand npm={["@assistant-ui/react-ink", "@assistant-ui/react-ai-sdk@1.3.40", "ai@^6", "@ai-sdk/react@^3", "@ai-sdk/openai", "zod", "ink", "react"]} />
66
66
 
67
67
  </Tab>
68
68
  </PlatformTabs>
@@ -3,7 +3,7 @@ title: AI SDK v7
3
3
  description: Integrate Vercel AI SDK v7 with assistant-ui for streaming chat.
4
4
  ---
5
5
 
6
- Current-version integration. Requires `ai@^7` and `@ai-sdk/react@^4`. For older versions see the [overview](/docs/runtimes/ai-sdk).
6
+ Current-version integration. Requires `ai@^7` and `@ai-sdk/react@^4`. For older versions see the [overview](/docs/runtimes/ai-sdk/overview).
7
7
 
8
8
  ## Quickstart
9
9
 
@@ -601,7 +601,7 @@ Beyond transports and adapters, `useChatRuntime` accepts options that control th
601
601
 
602
602
  ### onThreadIdChange
603
603
 
604
- Called whenever the active thread's settled ID changes, e.g. to sync the active thread to a URL query param:
604
+ Called whenever the runtime changes the active thread's settled ID, e.g. to sync the active thread to a URL query param. Changes supplied through the controlled `threadId` option are not echoed back through this callback.
605
605
 
606
606
  ```tsx
607
607
  const runtime = useChatRuntime({
@@ -149,7 +149,7 @@ const adapterWithHistory: RemoteThreadListAdapter = {
149
149
  const history = useMemo<ThreadHistoryAdapter>(
150
150
  () => ({
151
151
  async load() {
152
- const { remoteId } = aui.threadListItem().getState();
152
+ const { remoteId } = aui.threadListItem.getState();
153
153
  if (!remoteId) return { messages: [] };
154
154
  const rows = await fetch(
155
155
  `/api/threads/${remoteId}/messages`,
@@ -157,7 +157,7 @@ const adapterWithHistory: RemoteThreadListAdapter = {
157
157
  return { messages: rows.map(toThreadMessage) };
158
158
  },
159
159
  async append({ message, parentId }) {
160
- const { remoteId } = await aui.threadListItem().initialize();
160
+ const { remoteId } = await aui.threadListItem.initialize();
161
161
  await fetch(`/api/threads/${remoteId}/messages`, {
162
162
  method: "POST",
163
163
  body: JSON.stringify({ message, parentId }),
@@ -181,11 +181,11 @@ const adapterWithHistory: RemoteThreadListAdapter = {
181
181
 
182
182
  ### Avoiding the first-message race
183
183
 
184
- `append` may be called before the thread record exists in your backend. Always await `aui.threadListItem().initialize()` before writing:
184
+ `append` may be called before the thread record exists in your backend. Always await `aui.threadListItem.initialize()` before writing:
185
185
 
186
186
  ```ts
187
187
  async append({ message, parentId }) {
188
- const { remoteId } = await aui.threadListItem().initialize();
188
+ const { remoteId } = await aui.threadListItem.initialize();
189
189
  await saveMessage(remoteId, parentId, message);
190
190
  }
191
191
  ```
@@ -194,14 +194,14 @@ async append({ message, parentId }) {
194
194
 
195
195
  ### Reloading after async authentication
196
196
 
197
- If your adapter depends on a user that resolves asynchronously (oidc, `next-auth`, `better-auth`), the initial `list()` may run before the user is available. Call `aui.threads().reload()` after auth completes:
197
+ If your adapter depends on a user that resolves asynchronously (oidc, `next-auth`, `better-auth`), the initial `list()` may run before the user is available. Call `aui.threads.reload()` after auth completes:
198
198
 
199
199
  ```tsx
200
200
  function ReloadOnAuth() {
201
201
  const aui = useAui();
202
202
  const { isLoading, user } = useAuth();
203
203
  useEffect(() => {
204
- if (!isLoading && user) aui.threads().reload();
204
+ if (!isLoading && user) aui.threads.reload();
205
205
  }, [isLoading, user?.id]);
206
206
  return null;
207
207
  }
@@ -211,7 +211,7 @@ function ReloadOnAuth() {
211
211
 
212
212
  ### Paginating the thread list
213
213
 
214
- If your backend returns thread pages, return a `nextCursor` from `list()` and consume `aui.threads().hasMore` plus `aui.threads().loadMore()` in the UI. The runtime threads `params.after` back through `list()` on every `loadMore()`; the initial call passes no `params`, so treat a missing `after` as "first page". `reload()` resets the cursor so the next load starts from page 1 again.
214
+ If your backend returns thread pages, return a `nextCursor` from `list()` and consume `aui.threads.hasMore` plus `aui.threads.loadMore()` in the UI. The runtime threads `params.after` back through `list()` on every `loadMore()`; the initial call passes no `params`, so treat a missing `after` as "first page". `reload()` resets the cursor so the next load starts from page 1 again.
215
215
 
216
216
  ```ts title="threadListAdapter.ts (excerpt)"
217
217
  async list({ after } = {}) {
@@ -271,7 +271,7 @@ A few invariants worth knowing when wiring a custom UI on top of `loadMore()`:
271
271
  name: "list",
272
272
  type: "(params?: { after?: string }) => Promise<{ threads: RemoteThreadMetadata[]; nextCursor?: string }>",
273
273
  description:
274
- "Hydrate threads on mount. Each thread must include status and remoteId; title, externalId, and custom are optional. Return a `nextCursor` to enable `aui.threads().loadMore()`; the runtime will pass it back as `params.after` on the next call.",
274
+ "Hydrate threads on mount. Each thread must include status and remoteId; title, externalId, and custom are optional. Return a `nextCursor` to enable `aui.threads.loadMore()`; the runtime will pass it back as `params.after` on the next call.",
275
275
  required: true,
276
276
  },
277
277
  {
@@ -291,7 +291,7 @@ A few invariants worth knowing when wiring a custom UI on top of `loadMore()`:
291
291
  name: "updateCustom",
292
292
  type: "(remoteId: string, custom: Record<string, unknown> | undefined) => Promise<void>",
293
293
  description:
294
- "Optional. Persist replacement custom metadata from `aui.threadListItem().updateCustom(custom)`.",
294
+ "Optional. Persist replacement custom metadata from `aui.threadListItem.updateCustom(custom)`.",
295
295
  },
296
296
  {
297
297
  name: "archive",
@@ -361,7 +361,7 @@ function ThreadListItemMeta() {
361
361
  }
362
362
  ```
363
363
 
364
- `custom` is preserved across `rename`, `archive`, `unarchive`, and `generateTitle`. To replace it from your UI, implement `RemoteThreadListAdapter.updateCustom` and call `aui.threadListItem().updateCustom(custom)`. The cloud adapter persists this through `cloud.threads.update(threadId, { metadata })`. If your adapter mutates thread metadata through a separate application path, return the updated values from `fetch()` or call `aui.threads().reload()` to re-run `list()`.
364
+ `custom` is preserved across `rename`, `archive`, `unarchive`, and `generateTitle`. To replace it from your UI, implement `RemoteThreadListAdapter.updateCustom` and call `aui.threadListItem.updateCustom(custom)`. The cloud adapter persists this through `cloud.threads.update(threadId, { metadata })`. If your adapter mutates thread metadata through a separate application path, return the updated values from `fetch()` or call `aui.threads.reload()` to re-run `list()`.
365
365
 
366
366
  ## ExternalStoreThreadListAdapter
367
367
 
@@ -536,8 +536,8 @@ function useResumeOnMount(threadId: string) {
536
536
  r.json(),
537
537
  );
538
538
  if (status.isRunning) {
539
- const parentId = aui.thread().getState().messages.at(-1)?.id ?? null;
540
- aui.thread().resumeRun({ parentId });
539
+ const parentId = aui.thread.getState().messages.at(-1)?.id ?? null;
540
+ aui.thread.resumeRun({ parentId });
541
541
  }
542
542
  })();
543
543
  }, [aui, threadId]);
@@ -741,7 +741,7 @@ useExternalStoreRuntime({
741
741
  name: "isSendDisabled",
742
742
  type: "boolean",
743
743
  description:
744
- "Blocks new-message sending while leaving the input usable. When true, the thread composer's canSend becomes false, the Send button is disabled, Enter and the steer hotkey are no-ops, and aui.composer().send() short-circuits. Edit composers (saving message edits) ignore this flag. Use this to gate sending on external React state (e.g. while tools or auth are still loading).",
744
+ "Blocks new-message sending while leaving the input usable. When true, the thread composer's canSend becomes false, the Send button is disabled, Enter and the steer hotkey are no-ops, and aui.composer.send() short-circuits. Edit composers (saving message edits) ignore this flag. Use this to gate sending on external React state (e.g. while tools or auth are still loading).",
745
745
  default: "false",
746
746
  },
747
747
  {