@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
@@ -202,24 +202,7 @@ const isThreadEmpty: AuiIf.Condition = (s) => s.thread.isEmpty;
202
202
 
203
203
  ### AuiIf
204
204
 
205
- Conditionally renders children based on a slice of assistant state.
206
-
207
- A thin wrapper around [useAuiState](/docs/api-reference/hooks/state#useauistate) that renders its children
208
- when `condition` returns `true` and unmounts them when it returns
209
- `false`. Keeps render logic declarative without mounting unused
210
- subtrees.
211
-
212
- ```tsx
213
- <AuiIf condition={(s) => s.thread.isRunning}>
214
- <CancelButton />
215
- </AuiIf>
216
- ```
217
-
218
- ```tsx
219
- <AuiIf condition={(s) => s.thread.messages.length === 0}>
220
- <EmptyState />
221
- </AuiIf>
222
- ```
205
+ Renders `children` while `condition` selects `true` from the assistant state.
223
206
 
224
207
  <ParametersTable {...AuiIf} />
225
208
  {/* api-reference:end */}
@@ -6,7 +6,7 @@ description: Integrate cloud persistence and thread management with LangGraph Cl
6
6
 
7
7
  ## Overview
8
8
 
9
- This guide shows how to integrate Assistant Cloud with [LangGraph Cloud](https://langchain-ai.github.io/langgraph/cloud/) using assistant-ui's runtime system and pre-built UI components.
9
+ This guide shows how to integrate Assistant Cloud with [LangGraph Cloud](https://docs.langchain.com/langsmith/deploy-to-cloud-overview) using assistant-ui's runtime system and pre-built UI components.
10
10
 
11
11
  ## Prerequisites
12
12
 
@@ -112,7 +112,7 @@ function MyComponent() {
112
112
 
113
113
  useEffect(() => {
114
114
  // Register context provider
115
- return aui.modelContext().register({
115
+ return aui.modelContext.register({
116
116
  getModelContext: () => ({
117
117
  system: "You are a helpful search assistant...",
118
118
  tools: { myTool },
@@ -158,7 +158,7 @@ function SmartTransactionHistory({ userProfile }) {
158
158
  const aui = useAui();
159
159
 
160
160
  useEffect(() => {
161
- return aui.modelContext().register({
161
+ return aui.modelContext.register({
162
162
  getModelContext: () => ({
163
163
  system: `
164
164
  User spending patterns:
@@ -503,14 +503,14 @@ Add attachments from external sources (URLs, API data, CMS references) without n
503
503
  const aui = useAui();
504
504
 
505
505
  // Add an attachment from an external source
506
- await aui.composer().addAttachment({
506
+ await aui.composer.addAttachment({
507
507
  name: "report.pdf",
508
508
  contentType: "application/pdf",
509
509
  content: [{ type: "text", text: "Extracted document content..." }],
510
510
  });
511
511
 
512
512
  // Optionally provide id and type
513
- await aui.composer().addAttachment({
513
+ await aui.composer.addAttachment({
514
514
  id: "cms-doc-123",
515
515
  type: "document",
516
516
  name: "Product Spec",
@@ -532,7 +532,7 @@ const handleMultipleFiles = async (files: FileList) => {
532
532
  const filesToAdd = Array.from(files).slice(0, maxFiles);
533
533
 
534
534
  for (const file of filesToAdd) {
535
- await aui.composer().addAttachment(file);
535
+ await aui.composer.addAttachment(file);
536
536
  }
537
537
  };
538
538
  ```
@@ -54,7 +54,7 @@ const AssistantMessage = () => (
54
54
 
55
55
  ## Programmatic Branch Navigation
56
56
 
57
- For headless or keyboard-shortcut flows, navigate directly to a branch by id via `aui.message().switchToBranch`:
57
+ For headless or keyboard-shortcut flows, navigate directly to a branch by id via `aui.message.switchToBranch`:
58
58
 
59
59
  ```tsx
60
60
  import { useAui } from "@assistant-ui/react";
@@ -62,7 +62,7 @@ import { useAui } from "@assistant-ui/react";
62
62
  const SwitchToBranch = ({ branchId }: { branchId: string }) => {
63
63
  const aui = useAui();
64
64
  return (
65
- <button onClick={() => aui.message().switchToBranch({ branchId })}>
65
+ <button onClick={() => aui.message.switchToBranch({ branchId })}>
66
66
  Go to branch
67
67
  </button>
68
68
  );
@@ -122,7 +122,7 @@ const content = useAuiState((s) => s.message.content);
122
122
 
123
123
  ### useAui
124
124
 
125
- Access the API instance for imperative operations and actions. Unlike `useAuiState`, this hook returns a stable object that never changes, making it perfect for event handlers and imperative operations.
125
+ Access the API instance for imperative operations and actions. Unlike `useAuiState`, this hook does not subscribe to state updates: the client's identity only changes on structural changes (a scope resolving to a different instance), making it well suited for event handlers and imperative operations.
126
126
 
127
127
  ```tsx
128
128
  import { useAui } from "@assistant-ui/react";
@@ -132,20 +132,20 @@ function CustomMessageActions() {
132
132
 
133
133
  // Perform actions in event handlers
134
134
  const handleSend = () => {
135
- aui.composer().send();
135
+ aui.composer.send();
136
136
  };
137
137
 
138
138
  const handleReload = () => {
139
- aui.message().reload();
139
+ aui.message.reload();
140
140
  };
141
141
 
142
142
  // Read state imperatively when needed
143
143
  const handleConditionalAction = () => {
144
- const { isRunning } = aui.thread().getState();
145
- const { text } = aui.composer().getState();
144
+ const { isRunning } = aui.thread.getState();
145
+ const { text } = aui.composer.getState();
146
146
 
147
147
  if (!isRunning && text.length > 0) {
148
- aui.composer().send();
148
+ aui.composer.send();
149
149
  }
150
150
  };
151
151
 
@@ -159,7 +159,7 @@ function CustomMessageActions() {
159
159
  }
160
160
  ```
161
161
 
162
- The API object is stable and doesn't cause re-renders. Use it for:
162
+ The API object doesn't cause re-renders on state updates. Use it for:
163
163
 
164
164
  - **Triggering actions** in event handlers and callbacks
165
165
  - **Reading current state** imperatively when you don't need subscriptions
@@ -170,80 +170,80 @@ The API object is stable and doesn't cause re-renders. Use it for:
170
170
 
171
171
  ```tsx
172
172
  // Thread actions
173
- aui.thread().append(message);
174
- aui.thread().startRun(config);
175
- aui.thread().resumeRun(config);
176
- aui.thread().cancelRun();
177
- aui.thread().getState();
178
- aui.thread().message({ index: idx });
179
- aui.thread().message({ id: messageId });
180
- aui.thread().composer();
173
+ aui.thread.append(message);
174
+ aui.thread.startRun(config);
175
+ aui.thread.resumeRun(config);
176
+ aui.thread.cancelRun();
177
+ aui.thread.getState();
178
+ aui.thread.message({ index: idx });
179
+ aui.thread.message({ id: messageId });
180
+ aui.thread.composer();
181
181
 
182
182
  // Message actions
183
- aui.message().reload();
184
- aui.message().speak();
185
- aui.message().stopSpeaking();
186
- aui.message().submitFeedback({ type: "positive" | "negative" });
187
- aui.message().switchToBranch({ position, branchId });
188
- aui.message().getState();
189
- aui.message().part({ index: idx });
190
- aui.message().part({ toolCallId });
191
- aui.message().composer();
183
+ aui.message.reload();
184
+ aui.message.speak();
185
+ aui.message.stopSpeaking();
186
+ aui.message.submitFeedback({ type: "positive" | "negative" });
187
+ aui.message.switchToBranch({ position, branchId });
188
+ aui.message.getState();
189
+ aui.message.part({ index: idx });
190
+ aui.message.part({ toolCallId });
191
+ aui.message.composer();
192
192
 
193
193
  // Part actions
194
- aui.part().addToolResult(result);
195
- aui.part().resumeToolCall(result);
196
- aui.part().getState();
194
+ aui.part.addToolResult(result);
195
+ aui.part.resumeToolCall(result);
196
+ aui.part.getState();
197
197
 
198
198
  // Composer actions
199
- aui.composer().send();
200
- aui.composer().setText(text);
201
- aui.composer().setRole(role);
202
- aui.composer().addAttachment(file); // File object
203
- aui.composer().addAttachment({ name, content }); // external source
204
- await aui.composer().clearAttachments();
205
- await aui.composer().reset();
206
- aui.composer().getState();
199
+ aui.composer.send();
200
+ aui.composer.setText(text);
201
+ aui.composer.setRole(role);
202
+ aui.composer.addAttachment(file); // File object
203
+ aui.composer.addAttachment({ name, content }); // external source
204
+ await aui.composer.clearAttachments();
205
+ await aui.composer.reset();
206
+ aui.composer.getState();
207
207
 
208
208
  // Attachment actions
209
- aui.attachment().remove();
210
- aui.attachment().getState();
209
+ aui.attachment.remove();
210
+ aui.attachment.getState();
211
211
 
212
212
  // ThreadList actions
213
- aui.threads().switchToNewThread();
214
- aui.threads().switchToThread(threadId);
215
- aui.threads().reload();
216
- await aui.threads().getLoadThreadsPromise();
217
- aui.threads().getState();
213
+ aui.threads.switchToNewThread();
214
+ aui.threads.switchToThread(threadId);
215
+ aui.threads.reload();
216
+ await aui.threads.getLoadThreadsPromise();
217
+ aui.threads.getState();
218
218
 
219
219
  // ThreadListItem actions
220
- aui.threadListItem().switchTo();
221
- aui.threadListItem().rename(title);
222
- aui.threadListItem().updateCustom(custom);
223
- aui.threadListItem().archive();
224
- aui.threadListItem().unarchive();
225
- aui.threadListItem().delete();
226
- aui.threadListItem().getState();
220
+ aui.threadListItem.switchTo();
221
+ aui.threadListItem.rename(title);
222
+ aui.threadListItem.updateCustom(custom);
223
+ aui.threadListItem.archive();
224
+ aui.threadListItem.unarchive();
225
+ aui.threadListItem.delete();
226
+ aui.threadListItem.getState();
227
227
 
228
228
  // Suggestions actions
229
- aui.suggestions().getState();
230
- aui.suggestions().suggestion({ index: 0 });
229
+ aui.suggestions.getState();
230
+ aui.suggestions.suggestion({ index: 0 });
231
231
 
232
232
  // Suggestion actions
233
- aui.suggestion().getState();
233
+ aui.suggestion.getState();
234
234
 
235
235
  // ChainOfThought actions
236
- aui.chainOfThought().getState();
237
- aui.chainOfThought().setCollapsed(collapsed);
238
- aui.chainOfThought().part({ index: 0 });
236
+ aui.chainOfThought.getState();
237
+ aui.chainOfThought.setCollapsed(collapsed);
238
+ aui.chainOfThought.part({ index: 0 });
239
239
 
240
240
  // ModelContext actions — see /docs/copilots/model-context for full usage
241
- aui.modelContext().register(provider);
242
- aui.modelContext().getState();
241
+ aui.modelContext.register(provider);
242
+ aui.modelContext.getState();
243
243
 
244
244
  // Tools actions
245
- aui.tools().setToolUI(toolName, render);
246
- aui.tools().getState();
245
+ aui.tools.setToolUI(toolName, render);
246
+ aui.tools.getState();
247
247
  ```
248
248
 
249
249
  ### useAuiEvent
@@ -320,7 +320,7 @@ function MessageButton() {
320
320
 
321
321
  // Automatically uses the current message scope
322
322
  const handleReload = () => {
323
- aui.message().reload();
323
+ aui.message.reload();
324
324
  };
325
325
 
326
326
  return <button onClick={handleReload}>Reload</button>;
@@ -329,16 +329,10 @@ function MessageButton() {
329
329
 
330
330
  ### Checking Scope Availability
331
331
 
332
- Before accessing a scope, check if it's available:
332
+ Read scopes that may be unavailable through the optional view — they resolve to `undefined` instead of throwing:
333
333
 
334
334
  ```tsx
335
- const aui = useAui();
336
-
337
- // Check if message scope exists
338
- if (aui.message.source) {
339
- // Safe to use message scope
340
- const { role } = aui.message().getState();
341
- }
335
+ const role = useAuiState((s) => s.optional.message?.role);
342
336
  ```
343
337
 
344
338
  ### Accessing Nested Scopes
@@ -349,24 +343,24 @@ Navigate through the scope hierarchy programmatically:
349
343
  const aui = useAui();
350
344
 
351
345
  // Access specific message by ID or index
352
- const messageById = aui.thread().message({ id: "msg_123" });
353
- const messageByIndex = aui.thread().message({ index: 0 });
346
+ const messageById = aui.thread.message({ id: "msg_123" });
347
+ const messageByIndex = aui.thread.message({ index: 0 });
354
348
 
355
349
  // Access part by index or tool call ID
356
- const partByIndex = aui.message().part({ index: 0 });
357
- const partByToolCall = aui.message().part({ toolCallId: "call_123" });
350
+ const partByIndex = aui.message.part({ index: 0 });
351
+ const partByToolCall = aui.message.part({ toolCallId: "call_123" });
358
352
 
359
353
  // Access attachment by index
360
- const attachment = aui.composer().attachment({ index: 0 }).getState();
354
+ const attachment = aui.composer.attachment({ index: 0 }).getState();
361
355
 
362
356
  // Access thread list item by ID, index, or the "main" selector
363
- const threadItem = aui.threads().item({ id: "thread_123" });
364
- const threadByIndex = aui.threads().item({ index: 0 });
365
- const archivedThread = aui.threads().item({ index: 0, archived: true });
357
+ const threadItem = aui.threads.item({ id: "thread_123" });
358
+ const threadByIndex = aui.threads.item({ index: 0 });
359
+ const archivedThread = aui.threads.item({ index: 0, archived: true });
366
360
 
367
361
  // Traverse to the main thread directly
368
- const mainThread = aui.threads().thread("main");
369
- const message = aui.threads().thread("main").message({ id: "msg_123" });
362
+ const mainThread = aui.threads.thread("main");
363
+ const message = aui.threads.thread("main").message({ id: "msg_123" });
370
364
  ```
371
365
 
372
366
  ## Common Patterns
@@ -389,7 +383,7 @@ function CopyButton() {
389
383
  const aui = useAui();
390
384
 
391
385
  const handleCopy = () => {
392
- navigator.clipboard.writeText(aui.message().getCopyText());
386
+ navigator.clipboard.writeText(aui.message.getCopyText());
393
387
  };
394
388
 
395
389
  return <button onClick={handleCopy}>Copy</button>;
@@ -410,10 +404,10 @@ function SmartComposer() {
410
404
  <div>
411
405
  <textarea
412
406
  value={text}
413
- onChange={(e) => aui.composer().setText(e.target.value)}
407
+ onChange={(e) => aui.composer.setText(e.target.value)}
414
408
  disabled={isRunning}
415
409
  />
416
- <button onClick={() => aui.composer().send()} disabled={!canSend}>
410
+ <button onClick={() => aui.composer.send()} disabled={!canSend}>
417
411
  Send
418
412
  </button>
419
413
  </div>
@@ -439,24 +433,18 @@ function MessageCounter() {
439
433
 
440
434
  ### Resolution Dynamics
441
435
 
442
- When you call `aui.scope()`, the API resolves the current scope at that moment. This resolution happens each time you call the function, which matters when dealing with changing contexts:
436
+ Scopes are resolved during render and bound into the client. State updates never change the client's identity; a structural change (for example, switching threads) produces a new client and re-renders consumers through React:
443
437
 
444
438
  ```tsx
445
439
  const aui = useAui();
446
440
 
447
- // Get current thread
448
- const thread1 = aui.thread();
449
- thread1.append({ role: "user", content: "Hello" });
441
+ // Bound to the thread current at this render
442
+ aui.thread.append({ role: "user", content: "Hello" });
450
443
 
451
- // User might switch threads here
452
-
453
- // This could be a different thread
454
- const thread2 = aui.thread();
455
- thread2.cancelRun(); // Cancels the current thread's run, not necessarily thread1's
444
+ // After a thread switch, the component re-renders with a new client.
445
+ // Accessors read from a previous render keep pointing at the old thread.
456
446
  ```
457
447
 
458
- For most use cases, this behavior is intuitive. In advanced scenarios where you need to track specific instances, store the resolved reference.
459
-
460
448
  ### Performance Optimization
461
449
 
462
450
  **Selector optimization:**
@@ -570,16 +558,8 @@ These events fire at the same transition you can observe via state. They are kep
570
558
  // ❌ This will throw if not inside a message component
571
559
  const role = useAuiState((s) => s.message.role);
572
560
 
573
- // ✅ Check scope availability first
574
- function SafeMessageButton() {
575
- const aui = useAui();
576
-
577
- const role = useAuiState((s) =>
578
- aui.message.source !== undefined ? s.message.role : "none",
579
- );
580
-
581
- return <div>Role: {role}</div>;
582
- }
561
+ // ✅ Read through the optional view — unavailable scopes resolve to undefined
562
+ const role = useAuiState((s) => s.optional.message?.role ?? "none");
583
563
  ```
584
564
 
585
565
  **"Maximum update depth exceeded" / Infinite re-renders**
@@ -601,7 +581,7 @@ const content = useAuiState((s) => s.message.content);
601
581
  ```tsx
602
582
  // ❌ Storing scope references can lead to stale data
603
583
  const aui = useAui();
604
- const thread = aui.thread(); // This reference might become stale
584
+ const thread = aui.thread; // This reference might become stale
605
585
 
606
586
  useEffect(() => {
607
587
  // This might reference the wrong thread if user switched
@@ -613,7 +593,7 @@ const aui = useAui();
613
593
 
614
594
  useEffect(() => {
615
595
  // Always gets the current thread
616
- aui.thread().cancelRun();
596
+ aui.thread.cancelRun();
617
597
  }, [aui]);
618
598
  ```
619
599
 
@@ -625,19 +605,17 @@ const value = useAuiState((s) => s.scope.property);
625
605
 
626
606
  // Perform action
627
607
  const aui = useAui();
628
- aui.scope().action();
608
+ aui.scope.action();
629
609
 
630
610
  // Listen to events
631
611
  useAuiEvent("source.event", (e) => {});
632
612
 
633
- // Check scope availability
634
- if (aui.scope.source) {
635
- /* scope exists */
636
- }
613
+ // Read a scope that may be unavailable
614
+ const maybe = useAuiState((s) => s.optional.scope?.property);
637
615
 
638
616
  // Get state imperatively
639
- const state = aui.scope().getState();
617
+ const state = aui.scope.getState();
640
618
 
641
619
  // Navigate scopes
642
- aui.thread().message({ id: "..." }).getState();
620
+ aui.thread.message({ id: "..." }).getState();
643
621
  ```
@@ -66,9 +66,9 @@ const AssistantMessage = () => {
66
66
  };
67
67
  ```
68
68
 
69
- `ActionBarPrimitive.Edit` calls `aui.composer().beginEdit()` under the hood and is disabled when the composer is already in edit mode.
69
+ `ActionBarPrimitive.Edit` calls `aui.composer.beginEdit()` under the hood and is disabled when the composer is already in edit mode.
70
70
 
71
- `ComposerPrimitive.Cancel` calls `aui.composer().cancel()`, which exits edit mode and restores the original message content. See [Composer primitives](/docs/primitives/composer) for the full composer API.
71
+ `ComposerPrimitive.Cancel` calls `aui.composer.cancel()`, which exits edit mode and restores the original message content. See [Composer primitives](/docs/primitives/composer) for the full composer API.
72
72
 
73
73
  ## Detecting Edit Mode
74
74
 
@@ -76,7 +76,7 @@ The `isEditing` flag is available on both `ThreadComposerState` and `EditCompose
76
76
 
77
77
  ## Imperative API
78
78
 
79
- `aui.composer().beginEdit()` is the programmatic entry point for entering edit mode on a message. Use it for headless or keyboard-shortcut-driven flows where `ActionBarPrimitive.Edit` is not rendered:
79
+ `aui.composer.beginEdit()` is the programmatic entry point for entering edit mode on a message. Use it for headless or keyboard-shortcut-driven flows where `ActionBarPrimitive.Edit` is not rendered:
80
80
 
81
81
  ```tsx
82
82
  import { useAui } from "@assistant-ui/react";
@@ -84,12 +84,12 @@ import { useAui } from "@assistant-ui/react";
84
84
  const EditButton = () => {
85
85
  const aui = useAui();
86
86
  return (
87
- <button onClick={() => aui.composer().beginEdit()}>Edit</button>
87
+ <button onClick={() => aui.composer.beginEdit()}>Edit</button>
88
88
  );
89
89
  };
90
90
  ```
91
91
 
92
- `aui.composer().cancel()` exits edit mode without re-submitting.
92
+ `aui.composer.cancel()` exits edit mode without re-submitting.
93
93
 
94
94
  ## Editing While Streaming
95
95