@assistant-ui/mcp-docs-server 0.1.12 → 0.1.14

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 (31) hide show
  1. package/.docs/organized/code-examples/with-ag-ui.md +1089 -0
  2. package/.docs/organized/code-examples/with-ai-sdk-v5.md +12 -21
  3. package/.docs/organized/code-examples/with-assistant-transport.md +10 -19
  4. package/.docs/organized/code-examples/with-cloud.md +7 -16
  5. package/.docs/organized/code-examples/with-external-store.md +6 -15
  6. package/.docs/organized/code-examples/with-ffmpeg.md +14 -21
  7. package/.docs/organized/code-examples/with-langgraph.md +5 -14
  8. package/.docs/organized/code-examples/with-parent-id-grouping.md +6 -15
  9. package/.docs/organized/code-examples/with-react-hook-form.md +10 -19
  10. package/.docs/raw/docs/api-reference/context-providers/AssistantRuntimeProvider.mdx +6 -1
  11. package/.docs/raw/docs/api-reference/integrations/vercel-ai-sdk.mdx +179 -70
  12. package/.docs/raw/docs/cloud/authorization.mdx +2 -2
  13. package/.docs/raw/docs/copilots/model-context.mdx +4 -5
  14. package/.docs/raw/docs/copilots/motivation.mdx +4 -4
  15. package/.docs/raw/docs/getting-started.mdx +8 -4
  16. package/.docs/raw/docs/guides/Attachments.mdx +2 -2
  17. package/.docs/raw/docs/guides/Tools.mdx +5 -5
  18. package/.docs/raw/docs/guides/context-api.mdx +5 -5
  19. package/.docs/raw/docs/migrations/v0-12.mdx +2 -2
  20. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +6 -2
  21. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +9 -0
  22. package/.docs/raw/docs/runtimes/custom/local.mdx +77 -4
  23. package/.docs/raw/docs/runtimes/langgraph/index.mdx +7 -4
  24. package/.docs/raw/docs/runtimes/langserve.mdx +3 -8
  25. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +13 -11
  26. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +50 -31
  27. package/.docs/raw/docs/ui/PartGrouping.mdx +2 -2
  28. package/.docs/raw/docs/ui/Reasoning.mdx +174 -0
  29. package/dist/chunk-M2RKUM66.js +3 -3
  30. package/dist/chunk-NVNFQ5ZO.js +2 -2
  31. package/package.json +5 -6
@@ -5,19 +5,29 @@ title: "@assistant-ui/react-ai-sdk"
5
5
  Vercel AI SDK integration for assistant-ui.
6
6
 
7
7
  import { ParametersTable } from "@/components/docs";
8
+ import { Callout } from "fumadocs-ui/components/callout";
9
+
10
+ <Callout type="info">
11
+ This package provides integration with AI SDK v5. For AI SDK v4, see the [AI
12
+ SDK v4 (Legacy)](/docs/runtimes/ai-sdk/v4-legacy) documentation.
13
+ </Callout>
8
14
 
9
15
  ## API Reference
10
16
 
11
- ### `useVercelUseChatRuntime`
17
+ ### `useChatRuntime`
12
18
 
13
- Convert Vercel AI SDK chat helpers into a `AssistantRuntime`.
19
+ Creates a runtime directly with AI SDK v5's `useChat` hook integration. This is the recommended approach for most use cases.
14
20
 
15
21
  ```tsx
16
- import { useVercelUseChatRuntime } from "@assistant-ui/react-ai-sdk";
22
+ import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
23
+ import { AssistantRuntimeProvider } from "@assistant-ui/react";
17
24
 
18
25
  const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
19
- const chat = useChat();
20
- const runtime = useVercelUseChatRuntime(chat);
26
+ const runtime = useChatRuntime({
27
+ transport: new AssistantChatTransport({
28
+ api: "/api/chat",
29
+ }),
30
+ });
21
31
 
22
32
  return (
23
33
  <AssistantRuntimeProvider runtime={runtime}>
@@ -27,26 +37,86 @@ const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
27
37
  };
28
38
  ```
29
39
 
40
+ To customize the API endpoint, simply change the `api` parameter:
41
+
42
+ ```tsx
43
+ const runtime = useChatRuntime({
44
+ transport: new AssistantChatTransport({
45
+ api: "/my-custom-api/chat",
46
+ }),
47
+ });
48
+ ```
49
+
30
50
  <ParametersTable
31
51
  parameters={[
32
52
  {
33
- name: "chat",
34
- type: "ReturnType<typeof useChat>",
35
- description: "The UseChatHelpers from @ai-sdk/react.",
53
+ name: "options",
54
+ type: "UseChatRuntimeOptions",
55
+ description: "Configuration options for the chat runtime.",
56
+ children: [
57
+ {
58
+ type: "UseChatRuntimeOptions",
59
+ parameters: [
60
+ {
61
+ name: "api",
62
+ type: "string",
63
+ description: "The API endpoint URL. Defaults to '/api/chat'.",
64
+ },
65
+ {
66
+ name: "transport",
67
+ type: "ChatTransport",
68
+ description:
69
+ "Custom transport implementation. Defaults to AssistantChatTransport which forwards system messages and tools.",
70
+ },
71
+ {
72
+ name: "cloud",
73
+ type: "AssistantCloud",
74
+ description:
75
+ "Optional AssistantCloud instance for chat persistence.",
76
+ },
77
+ {
78
+ name: "initialMessages",
79
+ type: "UIMessage[]",
80
+ description: "Initial messages to populate the chat.",
81
+ },
82
+ {
83
+ name: "onFinish",
84
+ type: "(message: UIMessage) => void",
85
+ description: "Callback when a message completes streaming.",
86
+ },
87
+ {
88
+ name: "onError",
89
+ type: "(error: Error) => void",
90
+ description: "Callback for handling errors.",
91
+ },
92
+ ],
93
+ },
94
+ ],
36
95
  },
37
96
  ]}
38
97
  />
39
98
 
40
- ### `useVercelUseAssistantRuntime`
99
+ <Callout type="info">
100
+ By default, `useChatRuntime` uses `AssistantChatTransport` which automatically
101
+ forwards system messages and frontend tools to your backend API. This enables
102
+ your backend to receive the full context from the assistant-ui.
103
+ </Callout>
104
+
105
+ ### `useAISDKRuntime`
41
106
 
42
- Convert Vercel AI SDK assistant helpers into a `AssistantRuntime`.
107
+ For advanced use cases where you need direct access to the `useChat` hook from AI SDK.
43
108
 
44
109
  ```tsx
45
- import { useVercelUseAssistantRuntime } from "@assistant-ui/react-ai-sdk";
110
+ import { useChat } from "@ai-sdk/react";
111
+ import { useAISDKRuntime } from "@assistant-ui/react-ai-sdk";
112
+ import { AssistantRuntimeProvider } from "@assistant-ui/react";
46
113
 
47
114
  const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
48
- const assistant = useAssistant();
49
- const runtime = useVercelUseAssistantRuntime(assistant);
115
+ const chat = useChat({
116
+ api: "/api/chat",
117
+ });
118
+
119
+ const runtime = useAISDKRuntime(chat);
50
120
 
51
121
  return (
52
122
  <AssistantRuntimeProvider runtime={runtime}>
@@ -59,83 +129,76 @@ const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
59
129
  <ParametersTable
60
130
  parameters={[
61
131
  {
62
- name: "assistant",
63
- type: "ReturnType<typeof useAssistant>",
64
- description: "The UseAssistantHelpers from @ai-sdk/react.",
132
+ name: "chat",
133
+ type: "ReturnType<typeof useChat>",
134
+ description: "The chat helpers from @ai-sdk/react's useChat hook.",
135
+ },
136
+ {
137
+ name: "options",
138
+ type: "AISDKRuntimeOptions",
139
+ description: "Optional configuration options.",
140
+ children: [
141
+ {
142
+ type: "AISDKRuntimeOptions",
143
+ parameters: [
144
+ {
145
+ name: "cloud",
146
+ type: "AssistantCloud",
147
+ description:
148
+ "Optional AssistantCloud instance for chat persistence.",
149
+ },
150
+ {
151
+ name: "adapters",
152
+ type: "RuntimeAdapters",
153
+ description:
154
+ "Optional runtime adapters for attachments, feedback, speech, etc.",
155
+ },
156
+ ],
157
+ },
158
+ ],
65
159
  },
66
160
  ]}
67
161
  />
68
162
 
69
- ### `useVercelRSCRuntime`
163
+ ### `AssistantChatTransport`
70
164
 
71
- Convert Vercel RSC runtime into a `AssistantRuntime`.
165
+ A transport that extends the default AI SDK transport to automatically forward system messages and frontend tools to your backend.
72
166
 
73
167
  ```tsx
74
- import { useVercelRSCRuntime } from "@assistant-ui/react-ai-sdk";
75
-
76
- const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
77
- const [messages, setMessages] = useUIState<typeof AI>();
78
-
79
- const onNew = async (m: AppendMessage) => {
80
- if (m.content[0]?.type !== "text")
81
- throw new Error("Only text messages are supported");
82
-
83
- const input = m.content[0].text;
84
- setMessages((currentConversation) => [
85
- ...currentConversation,
86
- { id: nanoid(), role: "user", display: input },
87
- ]);
88
-
89
- const message = await continueConversation(input);
90
-
91
- setMessages((currentConversation) => [...currentConversation, message]);
92
- };
93
-
94
- const runtime = useVercelRSCRuntime({ messages, onNew });
95
-
96
- return (
97
- <AssistantRuntimeProvider runtime={runtime}>
98
- {children}
99
- </AssistantRuntimeProvider>
100
- );
101
- };
168
+ import { AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
169
+ import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
170
+
171
+ const runtime = useChatRuntime({
172
+ transport: new AssistantChatTransport({
173
+ api: "/my-custom-api/chat",
174
+ }),
175
+ });
102
176
  ```
103
177
 
104
178
  <ParametersTable
105
179
  parameters={[
106
180
  {
107
- name: "adapter",
108
- type: "VercelRSCAdapter<TMessage>",
109
- description: "The Vercel RSC adapter to use.",
181
+ name: "options",
182
+ type: "HttpChatTransportInitOptions",
183
+ description: "Transport configuration options.",
110
184
  children: [
111
185
  {
112
- type: "VercelRSCAdapter<TMessage>",
186
+ type: "HttpChatTransportInitOptions",
113
187
  parameters: [
114
188
  {
115
- name: "messages",
116
- type: "readonly ThreadMessage[]",
117
- description: "The messages in the thread.",
189
+ name: "api",
190
+ type: "string",
191
+ description: "The API endpoint URL.",
118
192
  },
119
193
  {
120
- name: "onNew",
121
- type: "(message: AppendMessage) => Promise<void>",
122
- description: "A function to append a message to the thread.",
194
+ name: "headers",
195
+ type: "Record<string, string> | Headers",
196
+ description: "Optional headers to include in requests.",
123
197
  },
124
198
  {
125
- name: "onEdit",
126
- type: "(message: AppendMessage) => Promise<void>",
127
- description: "A function to edit a message.",
128
- },
129
- {
130
- name: "onReload",
131
- type: "(parentId: string | null) => Promise<void>",
132
- description: "A function to reload a message.",
133
- },
134
- {
135
- name: "convertMessage",
136
- type: "(message: TMessage) => VercelRSCMessage",
137
- description:
138
- "A function to convert messages to the VercelRSCMessage format. Only required if your message objects are not already compatible with Vercel RSC.",
199
+ name: "credentials",
200
+ type: "RequestCredentials",
201
+ description: "Optional credentials mode for fetch requests.",
139
202
  },
140
203
  ],
141
204
  },
@@ -143,3 +206,49 @@ const MyRuntimeProvider = ({ children }: { children: React.ReactNode }) => {
143
206
  },
144
207
  ]}
145
208
  />
209
+
210
+ ### `frontendTools`
211
+
212
+ Helper function to convert frontend tool definitions to AI SDK format for use in your backend.
213
+
214
+ ```tsx
215
+ import { frontendTools } from "@assistant-ui/react-ai-sdk";
216
+ import { streamText, convertToModelMessages } from "ai";
217
+ import { openai } from "@ai-sdk/openai";
218
+
219
+ export async function POST(req: Request) {
220
+ const { messages, system, tools } = await req.json();
221
+
222
+ const result = streamText({
223
+ model: openai("gpt-4o"),
224
+ system,
225
+ messages: convertToModelMessages(messages),
226
+ tools: {
227
+ // Wrap frontend tools with the helper
228
+ ...frontendTools(tools),
229
+ // Your backend tools
230
+ myBackendTool: tool({
231
+ // ...
232
+ }),
233
+ },
234
+ });
235
+
236
+ return result.toUIMessageStreamResponse();
237
+ }
238
+ ```
239
+
240
+ <ParametersTable
241
+ parameters={[
242
+ {
243
+ name: "tools",
244
+ type: "Record<string, unknown>",
245
+ description:
246
+ "Frontend tools object forwarded from AssistantChatTransport.",
247
+ },
248
+ ]}
249
+ />
250
+
251
+ <Callout type="info">
252
+ The `frontendTools` helper converts frontend tool definitions to the AI SDK
253
+ format and ensures they are properly handled by the streaming response.
254
+ </Callout>
@@ -70,7 +70,7 @@ export const POST = async (req: Request) => {
70
70
 
71
71
  if (!userId) throw new Error("User not authenticated");
72
72
 
73
- const workspaceId = orgId ? `${orgId}:${userId}` : userId;
73
+ const workspaceId = orgId ? `${orgId}_${userId}` : userId;
74
74
  const assistantCloud = new AssistantCloud({
75
75
  apiKey: process.env["ASSISTANT_API_KEY"]!,
76
76
  userId,
@@ -91,7 +91,7 @@ const cloud = new AssistantCloud({
91
91
  baseUrl: process.env["NEXT_PUBLIC_ASSISTANT_BASE_URL"]!,
92
92
  authToken: () =>
93
93
  fetch("/api/assistant-ui-token", { method: "POST" }).then((r) =>
94
- r.json().then((data) => data.token)
94
+ r.text(),
95
95
  ),
96
96
  });
97
97
 
@@ -33,7 +33,6 @@ import {
33
33
  makeAssistantVisible,
34
34
  makeAssistantTool,
35
35
  tool,
36
- useAssistantRuntime,
37
36
  } from "@assistant-ui/react";
38
37
  import { z } from "zod";
39
38
 
@@ -75,12 +74,12 @@ function Form() {
75
74
  The context provider system allows components to contribute to the model context. Here's a typical usage pattern:
76
75
 
77
76
  ```tsx
78
- import { useAssistantRuntime, tool } from "@assistant-ui/react";
77
+ import { useAssistantApi, tool } from "@assistant-ui/react";
79
78
  import { useEffect } from "react";
80
79
  import { z } from "zod";
81
80
 
82
81
  function MyComponent() {
83
- const assistantRuntime = useAssistantRuntime();
82
+ const api = useAssistantApi();
84
83
 
85
84
  // Define tool using the tool() helper
86
85
  const myTool = tool({
@@ -95,13 +94,13 @@ function MyComponent() {
95
94
 
96
95
  useEffect(() => {
97
96
  // Register context provider
98
- return assistantRuntime.registerModelContextProvider({
97
+ return api.modelContext().register({
99
98
  getModelContext: () => ({
100
99
  system: "You are a helpful search assistant...",
101
100
  tools: { myTool },
102
101
  }),
103
102
  });
104
- }, [assistantRuntime]); // Re-register if runtime changes
103
+ }, [api]); // Re-register if api changes
105
104
 
106
105
  return <div>{/* component content */}</div>;
107
106
  }
@@ -143,14 +143,14 @@ function SmartTransactionHistory() {
143
143
  Finally, let's add dynamic context based on the user's transaction patterns:
144
144
 
145
145
  ```tsx
146
- import { useAssistantRuntime } from "@assistant-ui/react";
146
+ import { useAssistantApi } from "@assistant-ui/react";
147
147
  import { useEffect } from "react";
148
148
 
149
149
  function SmartTransactionHistory({ userProfile }) {
150
- const assistantRuntime = useAssistantRuntime();
150
+ const api = useAssistantApi();
151
151
 
152
152
  useEffect(() => {
153
- return assistantRuntime.registerModelContextProvider({
153
+ return api.modelContext().register({
154
154
  getModelContext: () => ({
155
155
  system: `
156
156
  User spending patterns:
@@ -160,7 +160,7 @@ function SmartTransactionHistory({ userProfile }) {
160
160
  `,
161
161
  }),
162
162
  });
163
- }, [assistantRuntime, userProfile]);
163
+ }, [api, userProfile]);
164
164
 
165
165
  // Previous components...
166
166
  }
@@ -1453,13 +1453,15 @@ If you aren't using Next.js, you can also deploy this endpoint to Cloudflare Wor
1453
1453
 
1454
1454
  ```tsx title="/app/page.tsx" tab="Thread"
1455
1455
  import { AssistantRuntimeProvider } from "@assistant-ui/react";
1456
- import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
1456
+ import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
1457
1457
  import { ThreadList } from "@/components/assistant-ui/thread-list";
1458
1458
  import { Thread } from "@/components/assistant-ui/thread";
1459
1459
 
1460
1460
  const MyApp = () => {
1461
1461
  const runtime = useChatRuntime({
1462
- api: "/api/chat",
1462
+ transport: new AssistantChatTransport({
1463
+ api: "/api/chat",
1464
+ }),
1463
1465
  });
1464
1466
 
1465
1467
  return (
@@ -1477,12 +1479,14 @@ const MyApp = () => {
1477
1479
  // run `npx shadcn@latest add "https://r.assistant-ui.com/assistant-modal"`
1478
1480
 
1479
1481
  import { AssistantRuntimeProvider } from "@assistant-ui/react";
1480
- import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
1482
+ import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
1481
1483
  import { AssistantModal } from "@/components/assistant-ui/assistant-modal";
1482
1484
 
1483
1485
  const MyApp = () => {
1484
1486
  const runtime = useChatRuntime({
1485
- api: "/api/chat",
1487
+ transport: new AssistantChatTransport({
1488
+ api: "/api/chat",
1489
+ }),
1486
1490
  });
1487
1491
 
1488
1492
  return (
@@ -508,14 +508,14 @@ class ValidatedImageAdapter implements AttachmentAdapter {
508
508
  Enable multi-file selection with custom limits:
509
509
 
510
510
  ```tsx
511
- const composer = useComposer();
511
+ const api = useAssistantApi();
512
512
 
513
513
  const handleMultipleFiles = async (files: FileList) => {
514
514
  const maxFiles = 5;
515
515
  const filesToAdd = Array.from(files).slice(0, maxFiles);
516
516
 
517
517
  for (const file of filesToAdd) {
518
- await composer.addAttachment({ file });
518
+ await api.composer().addAttachment({ file });
519
519
  }
520
520
  };
521
521
  ```
@@ -165,15 +165,15 @@ function App() {
165
165
 
166
166
  ### 4. Advanced: Direct Context Registration
167
167
 
168
- Use `registerModelContextProvider` when you need to configure more than just tools:
168
+ Use `api.modelContext().register()` when you need to configure more than just tools:
169
169
 
170
170
  ```tsx
171
- import { tool, useAssistantRuntime } from "@assistant-ui/react";
171
+ import { tool, useAssistantApi } from "@assistant-ui/react";
172
172
  import { useEffect, useState } from "react";
173
173
  import { z } from "zod";
174
174
 
175
175
  function MyComponent() {
176
- const runtime = useAssistantRuntime();
176
+ const api = useAssistantApi();
177
177
  const [isCreativeMode, setIsCreativeMode] = useState(false);
178
178
 
179
179
  useEffect(() => {
@@ -188,7 +188,7 @@ function MyComponent() {
188
188
  });
189
189
 
190
190
  // Register tools with model configuration
191
- return runtime.registerModelContextProvider({
191
+ return api.modelContext().register({
192
192
  getModelContext: () => ({
193
193
  tools: { calculate: calculateTool },
194
194
  callSettings: {
@@ -198,7 +198,7 @@ function MyComponent() {
198
198
  priority: 10, // Higher priority overrides other providers
199
199
  }),
200
200
  });
201
- }, [runtime, isCreativeMode]);
201
+ }, [api, isCreativeMode]);
202
202
 
203
203
  return <div>{/* Your component */}</div>;
204
204
  }
@@ -35,7 +35,7 @@ assistant-ui organizes state into **scopes** - logical boundaries that provide a
35
35
  └── ✏️ Composer (composer) - New message input
36
36
  └── 📎 Attachment (attachment) - Files being added
37
37
 
38
- 🔧 ToolUIs (toolUIs) - Custom UI components for tool calls
38
+ 🔧 Tools (tools) - Custom UI components for tool calls
39
39
  ```
40
40
 
41
41
  **How scopes work:**
@@ -212,9 +212,9 @@ api.threadListItem().unarchive();
212
212
  api.threadListItem().delete();
213
213
  api.threads().getState();
214
214
 
215
- // ToolUIs actions
216
- api.toolUIs().setToolUI(toolName, render);
217
- api.toolUIs().getState();
215
+ // Tools actions
216
+ api.tools().setToolUI(toolName, render);
217
+ api.tools().getState();
218
218
  ```
219
219
 
220
220
  ### useAssistantEvent
@@ -272,7 +272,7 @@ Each scope provides access to specific state and actions:
272
272
  - **Part** (`part`): Content part within a message (text, tool calls, etc.)
273
273
  - **Composer** (`composer`): Text input for sending or editing messages
274
274
  - **Attachment** (`attachment`): File or media attached to a message or composer
275
- - **ToolUIs** (`toolUIs`): Tool UI components
275
+ - **Tools** (`tools`): Tool UI components
276
276
 
277
277
  ### Scope Resolution
278
278
 
@@ -20,8 +20,8 @@ The following hooks have been removed:
20
20
 
21
21
  - `useMessageUtils` → Use `useAssistantState(({ message }) => message.isHovering)` / `useAssistantState(({ message }) => message.isCopied)`
22
22
  - `useMessageUtilsStore` → Use `useAssistantApi()` with `api.message().setIsHovering()` / `api.message().setIsCopied()`
23
- - `useToolUIs` → Use `useAssistantState(({ toolUIs }) => toolUIs)` and `useAssistantApi()` with `api.toolUIs()`
24
- - `useToolUIsStore` → Use `useAssistantApi()` with `api.toolUIs()`
23
+ - `useToolUIs` → Use `useAssistantState(({ tools }) => tools)` and `useAssistantApi()` with `api.tools()`
24
+ - `useToolUIsStore` → Use `useAssistantApi()` with `api.tools()`
25
25
 
26
26
  **Deprecated Hooks:**
27
27
 
@@ -94,10 +94,14 @@ export async function POST(req: Request) {
94
94
 
95
95
  import { Thread } from "@/components/assistant-ui/thread";
96
96
  import { AssistantRuntimeProvider } from "@assistant-ui/react";
97
- import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
97
+ import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
98
98
 
99
99
  export default function Home() {
100
- const runtime = useChatRuntime();
100
+ const runtime = useChatRuntime({
101
+ transport: new AssistantChatTransport({
102
+ api: "/api/chat",
103
+ }),
104
+ });
101
105
 
102
106
  return (
103
107
  <AssistantRuntimeProvider runtime={runtime}>
@@ -109,6 +109,15 @@ When the hook mounts it calls `list()` on your adapter, hydrates existing thread
109
109
  async delete(remoteId) {
110
110
  await fetch(`/api/threads/${remoteId}`, { method: "DELETE" });
111
111
  },
112
+ async fetch(remoteId) {
113
+ const response = await fetch(`/api/threads/${remoteId}`);
114
+ const thread = await response.json();
115
+ return {
116
+ status: thread.is_archived ? "archived" : "regular",
117
+ remoteId: thread.id,
118
+ title: thread.title,
119
+ };
120
+ },
112
121
  async generateTitle(remoteId, messages) {
113
122
  return createAssistantStream(async (controller) => {
114
123
  const response = await fetch(`/api/threads/${remoteId}/title`, {
@@ -538,7 +538,12 @@ export function MyRuntimeProvider({ children }) {
538
538
  ```
539
539
 
540
540
  <Callout type="info" title="Returning a title from generateTitle">
541
- The `generateTitle` method must return an <code>AssistantStream</code> containing the title text. The easiest, type-safe way is to use <code>createAssistantStream</code> and call <code>controller.appendText(newTitle)</code> followed by <code>controller.close()</code>. Returning a raw <code>ReadableStream</code> won't update the thread list UI.
541
+ The `generateTitle` method must return an <code>AssistantStream</code>{" "}
542
+ containing the title text. The easiest, type-safe way is to use{" "}
543
+ <code>createAssistantStream</code> and call{" "}
544
+ <code>controller.appendText(newTitle)</code> followed by{" "}
545
+ <code>controller.close()</code>. Returning a raw <code>ReadableStream</code>{" "}
546
+ won't update the thread list UI.
542
547
  </Callout>
543
548
 
544
549
  #### Understanding the Architecture
@@ -854,9 +859,9 @@ function MyCustomRuntimeProvider({ children }) {
854
859
  </Callout>
855
860
 
856
861
  <Callout type="warning">
857
- **`useThreadRuntime` vs `useLocalThreadRuntime`:**
858
- - `useThreadRuntime` - Access the current thread's runtime from within components
859
- - `useLocalThreadRuntime` - Create a new single-thread runtime instance
862
+ **`useThreadRuntime` vs `useLocalThreadRuntime`:** - `useThreadRuntime` -
863
+ Access the current thread's runtime from within components -
864
+ `useLocalThreadRuntime` - Create a new single-thread runtime instance
860
865
  </Callout>
861
866
 
862
867
  ## Integration Examples
@@ -999,6 +1004,74 @@ async run({ messages }) { // ❌ Wrong for streaming
999
1004
  ```
1000
1005
  </Callout>
1001
1006
 
1007
+ ### Tool UI Flickers or Disappears During Streaming
1008
+
1009
+ A common issue when implementing a streaming `ChatModelAdapter` is seeing a tool's UI appear for a moment and then disappear. This is caused by failing to accumulate the `tool_calls` correctly across multiple stream chunks. State must be stored **outside** the streaming loop to persist.
1010
+
1011
+ **❌ Incorrect: Forgetting Previous Tool Calls**
1012
+
1013
+ This implementation incorrectly re-creates the `content` array for every chunk. If a later chunk contains only text, tool calls from previous chunks are lost, causing the UI to disappear.
1014
+
1015
+ ```tsx
1016
+ // This implementation incorrectly re-creates the `content` array for every chunk.
1017
+ // If a later chunk contains only text, tool calls from previous chunks are lost.
1018
+ async *run({ messages, abortSignal, context }) {
1019
+ const stream = await backendApi({ messages, abortSignal, context });
1020
+ let text = "";
1021
+
1022
+ for await (const chunk of stream) {
1023
+ // ❌ DON'T: This overwrites toolCalls with only the current chunk's data
1024
+ const toolCalls = chunk.tool_calls || [];
1025
+ const content = [{ type: "text", text }];
1026
+ for (const toolCall of toolCalls) {
1027
+ content.push({
1028
+ type: "tool-call",
1029
+ toolName: toolCall.name,
1030
+ toolCallId: toolCall.id,
1031
+ args: toolCall.args,
1032
+ });
1033
+ }
1034
+ yield { content }; // This yield might not contain the tool call anymore
1035
+ }
1036
+ }
1037
+ ```
1038
+
1039
+ **✅ Correct: Accumulating State**
1040
+
1041
+ This implementation uses a `Map` outside the loop to remember all tool calls.
1042
+
1043
+ ```tsx
1044
+ // This implementation uses a Map outside the loop to remember all tool calls.
1045
+ async *run({ messages, abortSignal, context }) {
1046
+ const stream = await backendApi({ messages, abortSignal, context });
1047
+ let text = "";
1048
+ // ✅ DO: Declare state outside the loop
1049
+ const toolCallsMap = new Map();
1050
+
1051
+ for await (const chunk of stream) {
1052
+ text += chunk.content || "";
1053
+
1054
+ // ✅ DO: Add/update tool calls in the persistent map
1055
+ for (const toolCall of chunk.tool_calls || []) {
1056
+ toolCallsMap.set(toolCall.toolCallId, {
1057
+ type: "tool-call",
1058
+ toolName: toolCall.name,
1059
+ toolCallId: toolCall.toolCallId,
1060
+ args: toolCall.args,
1061
+ });
1062
+ }
1063
+
1064
+ // ✅ DO: Build content from accumulated state
1065
+ const content = [
1066
+ ...(text ? [{ type: "text", text }] : []),
1067
+ ...Array.from(toolCallsMap.values()),
1068
+ ];
1069
+
1070
+ yield { content }; // Yield the complete, correct state every time
1071
+ }
1072
+ }
1073
+ ```
1074
+
1002
1075
  ### Debug Tips
1003
1076
 
1004
1077
  1. **Log adapter calls** to trace execution: