create-agentic-app 1.1.60 → 1.1.64

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.
@@ -0,0 +1,443 @@
1
+ ---
2
+ title: Common Errors
3
+ description: Reference for common AI SDK errors and how to resolve them.
4
+ ---
5
+
6
+ # Common Errors
7
+
8
+ ## `maxTokens` → `maxOutputTokens`
9
+
10
+ ```typescript
11
+ // ❌ Incorrect
12
+ const result = await generateText({
13
+ model: 'anthropic/claude-opus-4.5',
14
+ maxTokens: 512, // deprecated: use `maxOutputTokens` instead
15
+ prompt: 'Write a short story',
16
+ });
17
+
18
+ // ✅ Correct
19
+ const result = await generateText({
20
+ model: 'anthropic/claude-opus-4.5',
21
+ maxOutputTokens: 512,
22
+ prompt: 'Write a short story',
23
+ });
24
+ ```
25
+
26
+ ## `maxSteps` → `stopWhen: isStepCount(n)`
27
+
28
+ ```typescript
29
+ // ❌ Incorrect
30
+ const result = await generateText({
31
+ model: 'anthropic/claude-opus-4.5',
32
+ tools: { weather },
33
+ maxSteps: 5, // deprecated: use `stopWhen: isStepCount(n)` instead
34
+ prompt: 'What is the weather in NYC?',
35
+ });
36
+
37
+ // ✅ Correct
38
+ import { generateText, isStepCount } from 'ai';
39
+
40
+ const result = await generateText({
41
+ model: 'anthropic/claude-opus-4.5',
42
+ tools: { weather },
43
+ stopWhen: isStepCount(5),
44
+ prompt: 'What is the weather in NYC?',
45
+ });
46
+ ```
47
+
48
+ ## `parameters` → `inputSchema` (in tool definition)
49
+
50
+ ```typescript
51
+ // ❌ Incorrect
52
+ const weatherTool = tool({
53
+ description: 'Get weather for a location',
54
+ parameters: z.object({
55
+ // deprecated: use `inputSchema` instead
56
+ location: z.string(),
57
+ }),
58
+ execute: async ({ location }) => ({ location, temp: 72 }),
59
+ });
60
+
61
+ // ✅ Correct
62
+ const weatherTool = tool({
63
+ description: 'Get weather for a location',
64
+ inputSchema: z.object({
65
+ location: z.string(),
66
+ }),
67
+ execute: async ({ location }) => ({ location, temp: 72 }),
68
+ });
69
+ ```
70
+
71
+ ## `generateObject` → `generateText` with `output`
72
+
73
+ `generateObject` is deprecated. Use `generateText` with the `output` option instead.
74
+
75
+ ```typescript
76
+ // ❌ Deprecated
77
+ import { generateObject } from 'ai'; // deprecated: use `generateText` with `output` instead
78
+
79
+ const result = await generateObject({
80
+ // deprecated function
81
+ model: 'anthropic/claude-opus-4.5',
82
+ schema: z.object({
83
+ // deprecated: use `Output.object({ schema })` instead
84
+ recipe: z.object({
85
+ name: z.string(),
86
+ ingredients: z.array(z.string()),
87
+ }),
88
+ }),
89
+ prompt: 'Generate a recipe for chocolate cake',
90
+ });
91
+
92
+ // ✅ Correct
93
+ import { generateText, Output } from 'ai';
94
+
95
+ const result = await generateText({
96
+ model: 'anthropic/claude-opus-4.5',
97
+ output: Output.object({
98
+ schema: z.object({
99
+ recipe: z.object({
100
+ name: z.string(),
101
+ ingredients: z.array(z.string()),
102
+ }),
103
+ }),
104
+ }),
105
+ prompt: 'Generate a recipe for chocolate cake',
106
+ });
107
+
108
+ console.log(result.output); // typed object
109
+ ```
110
+
111
+ ## Manual JSON parsing → `generateText` with `output`
112
+
113
+ ```typescript
114
+ // ❌ Incorrect
115
+ const result = await generateText({
116
+ model: 'anthropic/claude-opus-4.5',
117
+ prompt: `Extract the user info as JSON: { "name": string, "age": number }
118
+
119
+ Input: John is 25 years old`,
120
+ });
121
+ const parsed = JSON.parse(result.text);
122
+
123
+ // ✅ Correct
124
+ import { generateText, Output } from 'ai';
125
+
126
+ const result = await generateText({
127
+ model: 'anthropic/claude-opus-4.5',
128
+ output: Output.object({
129
+ schema: z.object({
130
+ name: z.string(),
131
+ age: z.number(),
132
+ }),
133
+ }),
134
+ prompt: 'Extract the user info: John is 25 years old',
135
+ });
136
+
137
+ console.log(result.output); // { name: 'John', age: 25 }
138
+ ```
139
+
140
+ ## Other `output` options
141
+
142
+ ```typescript
143
+ // Output.array - for generating arrays of items
144
+ const result = await generateText({
145
+ model: 'anthropic/claude-opus-4.5',
146
+ output: Output.array({
147
+ element: z.object({
148
+ city: z.string(),
149
+ country: z.string(),
150
+ }),
151
+ }),
152
+ prompt: 'List 5 capital cities',
153
+ });
154
+
155
+ // Output.choice - for selecting from predefined options
156
+ const result = await generateText({
157
+ model: 'anthropic/claude-opus-4.5',
158
+ output: Output.choice({
159
+ options: ['positive', 'negative', 'neutral'] as const,
160
+ }),
161
+ prompt: 'Classify the sentiment: I love this product!',
162
+ });
163
+
164
+ // Output.json - for untyped JSON output
165
+ const result = await generateText({
166
+ model: 'anthropic/claude-opus-4.5',
167
+ output: Output.json(),
168
+ prompt: 'Return some JSON data',
169
+ });
170
+ ```
171
+
172
+ ## `toDataStreamResponse` → `toUIMessageStreamResponse`
173
+
174
+ When using `useChat` on the frontend, use `toUIMessageStreamResponse()` instead of `toDataStreamResponse()`. The UI message stream format is designed to work with the chat UI components and handles message state correctly.
175
+
176
+ ```typescript
177
+ // ❌ Incorrect (when using useChat)
178
+ const result = streamText({
179
+ // config
180
+ });
181
+
182
+ return result.toDataStreamResponse(); // deprecated for useChat: use toUIMessageStreamResponse
183
+
184
+ // ✅ Correct
185
+ const result = streamText({
186
+ // config
187
+ });
188
+
189
+ return result.toUIMessageStreamResponse();
190
+ ```
191
+
192
+ ## Removed managed input state in `useChat`
193
+
194
+ The `useChat` hook no longer manages input state internally. You must now manage input state manually.
195
+
196
+ ```tsx
197
+ // ❌ Deprecated
198
+ import { useChat } from '@ai-sdk/react';
199
+
200
+ export default function Page() {
201
+ const {
202
+ input, // deprecated: manage input state manually with useState
203
+ handleInputChange, // deprecated: use custom onChange handler
204
+ handleSubmit, // deprecated: use sendMessage() instead
205
+ } = useChat({
206
+ api: '/api/chat', // deprecated: use `transport: new DefaultChatTransport({ api })` instead
207
+ });
208
+
209
+ return (
210
+ <form onSubmit={handleSubmit}>
211
+ <input value={input} onChange={handleInputChange} />
212
+ <button type="submit">Send</button>
213
+ </form>
214
+ );
215
+ }
216
+
217
+ // ✅ Correct
218
+ import { useChat } from '@ai-sdk/react';
219
+ import { DefaultChatTransport } from 'ai';
220
+ import { useState } from 'react';
221
+
222
+ export default function Page() {
223
+ const [input, setInput] = useState('');
224
+ const { sendMessage } = useChat({
225
+ transport: new DefaultChatTransport({ api: '/api/chat' }),
226
+ });
227
+
228
+ const handleSubmit = e => {
229
+ e.preventDefault();
230
+ sendMessage({ text: input });
231
+ setInput('');
232
+ };
233
+
234
+ return (
235
+ <form onSubmit={handleSubmit}>
236
+ <input value={input} onChange={e => setInput(e.target.value)} />
237
+ <button type="submit">Send</button>
238
+ </form>
239
+ );
240
+ }
241
+ ```
242
+
243
+ ## `tool-invocation` → `tool-{toolName}` (typed tool parts)
244
+
245
+ When rendering messages with `useChat`, use the typed tool part names (`tool-{toolName}`) instead of the generic `tool-invocation` type. This provides better type safety and access to tool-specific input/output types.
246
+
247
+ > For end-to-end type-safety, see [Type-Safe Agents](type-safe-agents.md).
248
+
249
+ Typed tool parts also use different property names:
250
+
251
+ - `part.args` → `part.input`
252
+ - `part.result` → `part.output`
253
+
254
+ ```tsx
255
+ // ❌ Incorrect - using generic tool-invocation
256
+ {
257
+ message.parts.map((part, i) => {
258
+ switch (part.type) {
259
+ case 'text':
260
+ return <div key={`${message.id}-${i}`}>{part.text}</div>;
261
+ case 'tool-invocation': // deprecated: use typed tool parts instead
262
+ return (
263
+ <pre key={`${message.id}-${i}`}>
264
+ {JSON.stringify(part.toolInvocation, null, 2)}
265
+ </pre>
266
+ );
267
+ }
268
+ });
269
+ }
270
+
271
+ // ✅ Correct - using typed tool parts (recommended)
272
+ {
273
+ message.parts.map(part => {
274
+ switch (part.type) {
275
+ case 'text':
276
+ return part.text;
277
+ case 'tool-askForConfirmation':
278
+ // handle askForConfirmation tool
279
+ break;
280
+ case 'tool-getWeatherInformation':
281
+ // handle getWeatherInformation tool
282
+ break;
283
+ }
284
+ });
285
+ }
286
+
287
+ // ✅ Alternative - using isToolUIPart as a catch-all
288
+ import { isToolUIPart } from 'ai';
289
+
290
+ {
291
+ message.parts.map(part => {
292
+ if (part.type === 'text') {
293
+ return part.text;
294
+ }
295
+ if (isToolUIPart(part)) {
296
+ // handle any tool part generically
297
+ return (
298
+ <div key={part.toolCallId}>
299
+ {part.toolName}: {part.state}
300
+ </div>
301
+ );
302
+ }
303
+ });
304
+ }
305
+ ```
306
+
307
+ ## `useChat` state-dependent property access
308
+
309
+ Tool part properties are only available in certain states. TypeScript will error if you access them without checking state first.
310
+
311
+ ```tsx
312
+ // ❌ Incorrect - input may be undefined during streaming
313
+ // TS18048: 'part.input' is possibly 'undefined'
314
+ if (part.type === 'tool-getWeather') {
315
+ const location = part.input.location;
316
+ }
317
+
318
+ // ✅ Correct - check for input-available or output-available
319
+ if (
320
+ part.type === 'tool-getWeather' &&
321
+ (part.state === 'input-available' || part.state === 'output-available')
322
+ ) {
323
+ const location = part.input.location;
324
+ }
325
+
326
+ // ❌ Incorrect - output is only available after execution
327
+ // TS18048: 'part.output' is possibly 'undefined'
328
+ if (part.type === 'tool-getWeather') {
329
+ const weather = part.output;
330
+ }
331
+
332
+ // ✅ Correct - check for output-available
333
+ if (part.type === 'tool-getWeather' && part.state === 'output-available') {
334
+ const location = part.input.location;
335
+ const weather = part.output;
336
+ }
337
+ ```
338
+
339
+ ## `part.toolInvocation.args` → `part.input`
340
+
341
+ ```tsx
342
+ // ❌ Incorrect
343
+ if (part.type === 'tool-invocation') {
344
+ // deprecated: use `part.input` on typed tool parts instead
345
+ const location = part.toolInvocation.args.location;
346
+ }
347
+
348
+ // ✅ Correct
349
+ if (
350
+ part.type === 'tool-getWeather' &&
351
+ (part.state === 'input-available' || part.state === 'output-available')
352
+ ) {
353
+ const location = part.input.location;
354
+ }
355
+ ```
356
+
357
+ ## `part.toolInvocation.result` → `part.output`
358
+
359
+ ```tsx
360
+ // ❌ Incorrect
361
+ if (part.type === 'tool-invocation') {
362
+ // deprecated: use `part.output` on typed tool parts instead
363
+ const weather = part.toolInvocation.result;
364
+ }
365
+
366
+ // ✅ Correct
367
+ if (part.type === 'tool-getWeather' && part.state === 'output-available') {
368
+ const weather = part.output;
369
+ }
370
+ ```
371
+
372
+ ## `part.toolInvocation.toolCallId` → `part.toolCallId`
373
+
374
+ ```tsx
375
+ // ❌ Incorrect
376
+ if (part.type === 'tool-invocation') {
377
+ // deprecated: use `part.toolCallId` on typed tool parts instead
378
+ const id = part.toolInvocation.toolCallId;
379
+ }
380
+
381
+ // ✅ Correct
382
+ if (part.type === 'tool-getWeather') {
383
+ const id = part.toolCallId;
384
+ }
385
+ ```
386
+
387
+ ## Tool invocation states renamed
388
+
389
+ ```tsx
390
+ // ❌ Incorrect
391
+ switch (part.toolInvocation.state) {
392
+ case 'partial-call': // deprecated: use `input-streaming` instead
393
+ return <div>Loading...</div>;
394
+ case 'call': // deprecated: use `input-available` instead
395
+ return <div>Executing...</div>;
396
+ case 'result': // deprecated: use `output-available` instead
397
+ return <div>Done</div>;
398
+ }
399
+
400
+ // ✅ Correct
401
+ switch (part.state) {
402
+ case 'input-streaming':
403
+ return <div>Loading...</div>;
404
+ case 'input-available':
405
+ return <div>Executing...</div>;
406
+ case 'output-available':
407
+ return <div>Done</div>;
408
+ }
409
+ ```
410
+
411
+ ## `addToolResult` → `addToolOutput`
412
+
413
+ ```tsx
414
+ // ❌ Incorrect
415
+ addToolResult({
416
+ // deprecated: use `addToolOutput` instead
417
+ toolCallId: part.toolInvocation.toolCallId,
418
+ result: 'Yes, confirmed.', // deprecated: use `output` instead
419
+ });
420
+
421
+ // ✅ Correct
422
+ addToolOutput({
423
+ tool: 'askForConfirmation',
424
+ toolCallId: part.toolCallId,
425
+ output: 'Yes, confirmed.',
426
+ });
427
+ ```
428
+
429
+ ## `messages` → `uiMessages` in `createAgentUIStreamResponse`
430
+
431
+ ```typescript
432
+ // ❌ Incorrect
433
+ return createAgentUIStreamResponse({
434
+ agent: myAgent,
435
+ messages, // incorrect: use `uiMessages` instead
436
+ });
437
+
438
+ // ✅ Correct
439
+ return createAgentUIStreamResponse({
440
+ agent: myAgent,
441
+ uiMessages: messages,
442
+ });
443
+ ```
@@ -0,0 +1,52 @@
1
+ ---
2
+ title: AI SDK DevTools
3
+ description: Debug AI SDK calls by inspecting captured runs and steps.
4
+ ---
5
+
6
+ # AI SDK DevTools
7
+
8
+ ## Why Use DevTools
9
+
10
+ DevTools captures all AI SDK calls (`generateText`, `streamText`, `ToolLoopAgent`) to a local JSON file. This lets you inspect LLM requests, responses, tool calls, and multi-step interactions without manually logging.
11
+
12
+ ## Setup
13
+
14
+ Requires AI SDK 6. Install `@ai-sdk/devtools` using your project's package manager.
15
+
16
+ Wrap your model with the middleware:
17
+
18
+ ```ts
19
+ import { wrapLanguageModel, gateway } from 'ai';
20
+ import { devToolsMiddleware } from '@ai-sdk/devtools';
21
+
22
+ const model = wrapLanguageModel({
23
+ model: gateway('anthropic/claude-sonnet-4.5'),
24
+ middleware: devToolsMiddleware(),
25
+ });
26
+ ```
27
+
28
+ ## Viewing Captured Data
29
+
30
+ All runs and steps are saved to:
31
+
32
+ ```
33
+ .devtools/generations.json
34
+ ```
35
+
36
+ Read this file directly to inspect captured data:
37
+
38
+ ```bash
39
+ cat .devtools/generations.json | jq
40
+ ```
41
+
42
+ Or launch the web UI:
43
+
44
+ ```bash
45
+ npx @ai-sdk/devtools
46
+ # Open http://localhost:4983
47
+ ```
48
+
49
+ ## Data Structure
50
+
51
+ - **Run**: A complete multi-step interaction grouped by initial prompt
52
+ - **Step**: A single LLM call within a run (includes input, output, tool calls, token usage)
@@ -0,0 +1,204 @@
1
+ ---
2
+ title: Type-Safe useChat with Agents
3
+ description: Build end-to-end type-safe agents by inferring UIMessage types from your agent definition.
4
+ ---
5
+
6
+ # Type-Safe useChat with Agents
7
+
8
+ Build end-to-end type-safe agents by inferring `UIMessage` types from your agent definition for type-safe UI rendering with `useChat`.
9
+
10
+ ## Recommended Structure
11
+
12
+ ```
13
+ lib/
14
+ agents/
15
+ my-agent.ts # Agent definition + type export
16
+ tools/
17
+ weather-tool.ts # Individual tool definitions
18
+ calculator-tool.ts
19
+ ```
20
+
21
+ ## Define Tools
22
+
23
+ ```ts
24
+ // lib/tools/weather-tool.ts
25
+ import { tool } from 'ai';
26
+ import { z } from 'zod';
27
+
28
+ export const weatherTool = tool({
29
+ description: 'Get current weather for a location',
30
+ inputSchema: z.object({
31
+ location: z.string().describe('City name'),
32
+ }),
33
+ execute: async ({ location }) => {
34
+ return { temperature: 72, condition: 'sunny', location };
35
+ },
36
+ });
37
+ ```
38
+
39
+ ## Define Agent and Export Type
40
+
41
+ ```ts
42
+ // lib/agents/my-agent.ts
43
+ import { ToolLoopAgent, InferAgentUIMessage } from 'ai';
44
+ import { weatherTool } from '../tools/weather-tool';
45
+ import { calculatorTool } from '../tools/calculator-tool';
46
+
47
+ export const myAgent = new ToolLoopAgent({
48
+ model: 'anthropic/claude-sonnet-4',
49
+ instructions: 'You are a helpful assistant.',
50
+ tools: {
51
+ weather: weatherTool,
52
+ calculator: calculatorTool,
53
+ },
54
+ });
55
+
56
+ // Infer the UIMessage type from the agent
57
+ export type MyAgentUIMessage = InferAgentUIMessage<typeof myAgent>;
58
+ ```
59
+
60
+ ### With Custom Metadata
61
+
62
+ ```ts
63
+ // lib/agents/my-agent.ts
64
+ import { z } from 'zod';
65
+
66
+ const metadataSchema = z.object({
67
+ createdAt: z.number(),
68
+ model: z.string().optional(),
69
+ });
70
+
71
+ type MyMetadata = z.infer<typeof metadataSchema>;
72
+
73
+ export type MyAgentUIMessage = InferAgentUIMessage<typeof myAgent, MyMetadata>;
74
+ ```
75
+
76
+ ## Use with `useChat`
77
+
78
+ ```tsx
79
+ // app/chat.tsx
80
+ import { useChat } from '@ai-sdk/react';
81
+ import type { MyAgentUIMessage } from '@/lib/agents/my-agent';
82
+
83
+ export function Chat() {
84
+ const { messages } = useChat<MyAgentUIMessage>();
85
+
86
+ return (
87
+ <div>
88
+ {messages.map(message => (
89
+ <Message key={message.id} message={message} />
90
+ ))}
91
+ </div>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ## Rendering Parts with Type Safety
97
+
98
+ Tool parts are typed as `tool-{toolName}` based on your agent's tools:
99
+
100
+ ```tsx
101
+ function Message({ message }: { message: MyAgentUIMessage }) {
102
+ return (
103
+ <div>
104
+ {message.parts.map((part, i) => {
105
+ switch (part.type) {
106
+ case 'text':
107
+ return <p key={i}>{part.text}</p>;
108
+
109
+ case 'tool-weather':
110
+ // part.input and part.output are fully typed
111
+ if (part.state === 'output-available') {
112
+ return (
113
+ <div key={i}>
114
+ Weather in {part.input.location}: {part.output.temperature}F
115
+ </div>
116
+ );
117
+ }
118
+ return <div key={i}>Loading weather...</div>;
119
+
120
+ case 'tool-calculator':
121
+ // TypeScript knows this is the calculator tool
122
+ return <div key={i}>Calculating...</div>;
123
+
124
+ default:
125
+ return null;
126
+ }
127
+ })}
128
+ </div>
129
+ );
130
+ }
131
+ ```
132
+
133
+ The `part.type` discriminant narrows the type, giving you autocomplete and type checking for `input` and `output` based on each tool's schema.
134
+
135
+ ## Splitting Tool Rendering into Components
136
+
137
+ When rendering many tools, you may want to split each tool into its own component. Use `UIToolInvocation<TOOL>` to derive a typed invocation from your tool and export it alongside the tool definition:
138
+
139
+ ```ts
140
+ // lib/tools/weather-tool.ts
141
+ import { tool, UIToolInvocation } from 'ai';
142
+ import { z } from 'zod';
143
+
144
+ export const weatherTool = tool({
145
+ description: 'Get current weather for a location',
146
+ inputSchema: z.object({
147
+ location: z.string().describe('City name'),
148
+ }),
149
+ execute: async ({ location }) => {
150
+ return { temperature: 72, condition: 'sunny', location };
151
+ },
152
+ });
153
+
154
+ // Export the invocation type for use in UI components
155
+ export type WeatherToolInvocation = UIToolInvocation<typeof weatherTool>;
156
+ ```
157
+
158
+ Then import only the type in your component:
159
+
160
+ ```tsx
161
+ // components/weather-tool.tsx
162
+ import type { WeatherToolInvocation } from '@/lib/tools/weather-tool';
163
+
164
+ export function WeatherToolComponent({
165
+ invocation,
166
+ }: {
167
+ invocation: WeatherToolInvocation;
168
+ }) {
169
+ // invocation.input and invocation.output are fully typed
170
+ if (invocation.state === 'output-available') {
171
+ return (
172
+ <div>
173
+ Weather in {invocation.input.location}: {invocation.output.temperature}F
174
+ </div>
175
+ );
176
+ }
177
+ return <div>Loading weather for {invocation.input?.location}...</div>;
178
+ }
179
+ ```
180
+
181
+ Use the component in your message renderer:
182
+
183
+ ```tsx
184
+ function Message({ message }: { message: MyAgentUIMessage }) {
185
+ return (
186
+ <div>
187
+ {message.parts.map((part, i) => {
188
+ switch (part.type) {
189
+ case 'text':
190
+ return <p key={i}>{part.text}</p>;
191
+ case 'tool-weather':
192
+ return <WeatherToolComponent key={i} invocation={part} />;
193
+ case 'tool-calculator':
194
+ return <CalculatorToolComponent key={i} invocation={part} />;
195
+ default:
196
+ return null;
197
+ }
198
+ })}
199
+ </div>
200
+ );
201
+ }
202
+ ```
203
+
204
+ This approach keeps your tool rendering logic organized while maintaining full type safety, without needing to import the tool implementation into your UI components.
@@ -23,6 +23,7 @@
23
23
 
24
24
  - Whenever you make changes to the database schema, ALWAYS run the drizzle generate and migrate commands
25
25
  - NEVER run drizzle push!
26
+ - For all ID columns NOT related to BetterAuth, use UUID for the ID columns and be randomly generated
26
27
 
27
28
  ## TESTING
28
29