ai 7.0.91 → 7.0.93

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 (42) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/index.d.ts +178 -159
  3. package/dist/index.js +143 -25
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal/index.d.ts +1 -1
  6. package/dist/internal/index.js +2 -2
  7. package/dist/internal/index.js.map +1 -1
  8. package/docs/02-foundations/02-providers-and-models.mdx +1 -0
  9. package/docs/03-agents/04-loop-control.mdx +5 -3
  10. package/docs/03-agents/07-workflow-agent.mdx +27 -6
  11. package/docs/03-ai-sdk-core/10-generating-structured-data.mdx +15 -3
  12. package/docs/03-ai-sdk-core/16-mcp-tools.mdx +64 -1
  13. package/docs/03-ai-sdk-core/35-image-generation.mdx +7 -0
  14. package/docs/03-ai-sdk-core/36-transcription.mdx +36 -35
  15. package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +36 -0
  16. package/docs/04-ai-sdk-ui/20-streaming-data.mdx +11 -6
  17. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +14 -0
  18. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +30 -3
  19. package/docs/07-reference/01-ai-sdk-core/28-output.mdx +27 -1
  20. package/docs/07-reference/01-ai-sdk-core/80-smooth-stream.mdx +1 -1
  21. package/docs/07-reference/02-ai-sdk-ui/01-use-chat.mdx +1 -1
  22. package/docs/07-reference/02-ai-sdk-ui/40-create-ui-message-stream.mdx +4 -0
  23. package/docs/07-reference/02-ai-sdk-ui/41-create-ui-message-stream-response.mdx +6 -1
  24. package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +42 -28
  25. package/docs/07-reference/05-ai-sdk-errors/ai-no-image-generated-error.mdx +7 -0
  26. package/package.json +6 -6
  27. package/src/agent/tool-loop-agent-settings.ts +15 -0
  28. package/src/embed/embed-many.ts +27 -2
  29. package/src/error/no-image-generated-error.ts +9 -0
  30. package/src/generate-image/generate-image.ts +1 -1
  31. package/src/generate-text/generate-text-events.ts +1 -1
  32. package/src/generate-text/output.ts +111 -1
  33. package/src/generate-text/smooth-stream.ts +19 -4
  34. package/src/generate-text/stream-text.ts +2 -6
  35. package/src/ui/call-completion-api.ts +1 -1
  36. package/src/ui/chat.ts +1 -1
  37. package/src/ui/convert-to-model-messages.ts +8 -2
  38. package/src/ui/http-chat-transport.ts +2 -2
  39. package/src/ui/validate-ui-messages.ts +14 -0
  40. package/src/util/async-iterable-stream.ts +1 -1
  41. package/src/util/data-url.ts +1 -1
  42. package/src/util/merge-abort-signals.ts +1 -1
@@ -122,6 +122,7 @@ Here are the capabilities of popular models:
122
122
  | [xAI Grok](/providers/ai-sdk-providers/xai) | `grok-4` | <Cross /> | <Check /> | <Check /> | <Check /> |
123
123
  | [xAI Grok](/providers/ai-sdk-providers/xai) | `grok-3` | <Cross /> | <Check /> | <Check /> | <Check /> |
124
124
  | [xAI Grok](/providers/ai-sdk-providers/xai) | `grok-3-mini` | <Cross /> | <Check /> | <Check /> | <Check /> |
125
+ | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-6-astra` | <Check /> | <Check /> | <Check /> | <Check /> |
125
126
  | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5.6` | <Check /> | <Check /> | <Check /> | <Check /> |
126
127
  | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5.6-luna` | <Check /> | <Check /> | <Check /> | <Check /> |
127
128
  | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5.6-sol` | <Check /> | <Check /> | <Check /> | <Check /> |
@@ -16,7 +16,9 @@ The AI SDK provides built-in loop control through two parameters: `stopWhen` for
16
16
 
17
17
  ## Stop Conditions
18
18
 
19
- The `stopWhen` parameter controls when to stop execution when there are tool results in the last step. By default, agents stop after 20 steps using `isStepCount(20)`. This default is a safety measure to prevent runaway loops that could result in excessive API calls and costs.
19
+ The `stopWhen` parameter controls when to stop execution when there are tool results in the last step. By default, `ToolLoopAgent` stops after 20 steps using `isStepCount(20)`. This default is a safety measure to prevent runaway loops that could result in excessive API calls and costs.
20
+
21
+ `WorkflowAgent` does not apply a default step limit. It continues until the model stops calling tools or another natural termination condition is met. Configure an explicit condition such as `isStepCount(20)` when you need to bound its model calls. See [WorkflowAgent Loop Control](/docs/agents/workflow-agent#loop-control) for details.
20
22
 
21
23
  When you provide `stopWhen`, the agent continues executing after tool calls until a stopping condition is met. When the condition is an array, execution stops when any of the conditions are met.
22
24
 
@@ -39,7 +41,7 @@ const agent = new ToolLoopAgent({
39
41
  tools: {
40
42
  // your tools
41
43
  },
42
- stopWhen: isStepCount(50), // Increasing the default of 20 to 50.
44
+ stopWhen: isStepCount(50), // Increase ToolLoopAgent's default from 20 to 50.
43
45
  });
44
46
 
45
47
  const result = await agent.generate({
@@ -49,7 +51,7 @@ const result = await agent.generate({
49
51
 
50
52
  ### Run Until Finished
51
53
 
52
- If you want the agent to run until the model naturally stops making tool calls, use `isLoopFinished()`. This removes the default step limit:
54
+ If you want a `ToolLoopAgent` to run until the model naturally stops making tool calls, use `isLoopFinished()`. This removes its default step limit:
53
55
 
54
56
  ```ts
55
57
  import { ToolLoopAgent, isLoopFinished } from 'ai';
@@ -399,6 +399,16 @@ The stream-level value overrides the constructor default.
399
399
 
400
400
  ## Loop Control
401
401
 
402
+ Unlike `ToolLoopAgent`, `WorkflowAgent` does not apply a default step limit.
403
+ When `stopWhen` is omitted, it continues until the model stops calling tools or
404
+ another natural termination condition is met.
405
+
406
+ <Note>
407
+ A model that repeatedly calls tools can make an unlimited number of model
408
+ calls. Configure an explicit stop condition when you need to bound execution
409
+ time and cost.
410
+ </Note>
411
+
402
412
  Control how many steps the agent can take:
403
413
 
404
414
  ```ts
@@ -410,7 +420,8 @@ const result = await agent.stream({
410
420
  });
411
421
  ```
412
422
 
413
- If you want the agent to keep running until it has finished calling tools, you can also use `isLoopFinished()`:
423
+ Omitting `stopWhen` already lets `WorkflowAgent` continue until it has finished
424
+ calling tools. You can make that intent explicit with `isLoopFinished()`:
414
425
 
415
426
  ```ts
416
427
  import { isLoopFinished } from 'ai';
@@ -421,9 +432,10 @@ const result = await agent.stream({
421
432
  });
422
433
  ```
423
434
 
424
- `isLoopFinished()` lets the agent run until all tool calls have completed, but you should still pair it with `maxSteps` to avoid runaway loops. See https://ai-sdk.dev/v7/docs/reference/ai-sdk-core/loop-finished#isloopfinished.
425
-
426
- By default, the agent loops until the model stops calling tools (no maximum).
435
+ `isLoopFinished()` is equivalent to omitting `stopWhen` for `WorkflowAgent`.
436
+ Use it with caution because a model that keeps calling tools can run
437
+ indefinitely and incur significant costs. See
438
+ [`isLoopFinished()`](/docs/reference/ai-sdk-core/loop-finished).
427
439
 
428
440
  ## Structured Output
429
441
 
@@ -599,8 +611,11 @@ const agent = new WorkflowAgent({
599
611
  console.log(`Calling tool: ${toolCall.toolName}`);
600
612
  },
601
613
 
602
- onToolExecutionEnd({ toolCall, toolOutput }) {
603
- console.log(`Tool finished: ${toolCall.toolName}`);
614
+ onToolExecutionEnd({ toolCall, success, durationMs }) {
615
+ console.log(`Tool finished: ${toolCall.toolName}`, {
616
+ success,
617
+ durationMs,
618
+ });
604
619
  },
605
620
 
606
621
  onStepEnd({ usage, finishReason }) {
@@ -613,6 +628,12 @@ const agent = new WorkflowAgent({
613
628
  });
614
629
  ```
615
630
 
631
+ For concrete tool sets, `WorkflowAgentToolExecutionStartEvent` and
632
+ `WorkflowAgentToolExecutionEndEvent` preserve the relationship between each
633
+ tool name and its input, context, and output types. TypeScript narrows the
634
+ nested `toolCall` directly, but use `Extract` or a user-defined type guard when
635
+ you need to narrow the correlated `toolContext` or `output` fields by tool name.
636
+
616
637
  Tool input callbacks (`onInputStart`, `onInputDelta`, and
617
638
  `onInputAvailable`) are also preserved by `WorkflowAgent`. The model call runs
618
639
  inside a durable step, while callback functions remain in the workflow
@@ -177,7 +177,9 @@ const { output } = await generateText({
177
177
 
178
178
  ### `Output.array()`
179
179
 
180
- Use `Output.array({ element })` to specify that you expect an array of typed objects from the model, where each element should conform to a schema (defined in the `element` property).
180
+ Use `Output.array({ element, minItems, maxItems })` to specify that you expect
181
+ an array of typed objects from the model. Each element must conform to the
182
+ `element` schema, and the optional bounds constrain the number of elements.
181
183
 
182
184
  ```ts
183
185
  import { generateText, Output } from 'ai';
@@ -191,6 +193,8 @@ const { output } = await generateText({
191
193
  temperature: z.number(),
192
194
  condition: z.string(),
193
195
  }),
196
+ minItems: 2,
197
+ maxItems: 2,
194
198
  }),
195
199
  prompt: 'List the weather for San Francisco and Paris.',
196
200
  });
@@ -201,6 +205,11 @@ const { output } = await generateText({
201
205
  // ]
202
206
  ```
203
207
 
208
+ `minItems` and `maxItems` must be non-negative integers, and `minItems` cannot
209
+ be greater than `maxItems`. Use the same value for both options to require an
210
+ exact length. The bounds are sent to providers as part of the structured output
211
+ schema when supported, and the AI SDK independently validates the final output.
212
+
204
213
  When streaming arrays with `streamText`, you can use `elementStream` to receive each completed element as it is generated:
205
214
 
206
215
  ```ts
@@ -227,7 +236,10 @@ for await (const hero of elementStream) {
227
236
  <Note>
228
237
  Each element emitted by `elementStream` is complete and validated against your
229
238
  element schema. This differs from `partialOutputStream`, which streams the
230
- entire partial array including incomplete elements.
239
+ entire partial array including incomplete elements. If the model generates
240
+ more than `maxItems`, `elementStream` errors before emitting the first excess
241
+ element. This does not automatically abort provider generation, and the final
242
+ `output` promise rejects.
231
243
  </Note>
232
244
 
233
245
  ### `Output.choice()`
@@ -378,7 +390,7 @@ const { output } = await generateText({
378
390
  This works with all output types that support structured generation:
379
391
 
380
392
  - `Output.object({ name, description, schema })`
381
- - `Output.array({ name, description, element })`
393
+ - `Output.array({ name, description, element, minItems, maxItems })`
382
394
  - `Output.choice({ name, description, options })`
383
395
  - `Output.json({ name, description })`
384
396
 
@@ -320,6 +320,67 @@ const tools = await mcpClient.tools();
320
320
 
321
321
  This approach is simpler to implement and automatically stays in sync with server changes. However, you won't have TypeScript type safety during development, and all tools from the server will be loaded
322
322
 
323
+ ### Tool Annotations and Approval
324
+
325
+ MCP servers can describe tool behavior with annotations such as
326
+ `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. The
327
+ MCP client exposes these annotations on each tool's `metadata.annotations` and
328
+ on the resulting tool call's `toolMetadata.annotations`.
329
+
330
+ Annotations are untrusted, server-provided hints. The MCP client does not turn
331
+ them into an approval policy automatically. Applications should combine them
332
+ with deterministic controls such as tool allowlists, scoped credentials, and
333
+ their own [`toolApproval`](/docs/agents/tool-approvals) policy.
334
+
335
+ The following conservative policy allows tools to run automatically only when
336
+ the server explicitly marks them as read-only. Tools with `readOnlyHint: false`
337
+ or no `readOnlyHint` require user approval:
338
+
339
+ ```typescript
340
+ import { type McpProviderMetadata } from '@ai-sdk/mcp';
341
+
342
+ const result = streamText({
343
+ model: __MODEL__,
344
+ tools,
345
+ toolApproval: ({ toolCall }) => {
346
+ const annotations = (
347
+ toolCall.toolMetadata as McpProviderMetadata | undefined
348
+ )?.annotations;
349
+
350
+ return annotations?.readOnlyHint === true
351
+ ? 'not-applicable'
352
+ : {
353
+ type: 'user-approval',
354
+ reason:
355
+ annotations?.destructiveHint === true
356
+ ? 'The MCP server marks this tool as destructive.'
357
+ : 'The MCP server does not mark this tool as read-only.',
358
+ };
359
+ },
360
+ prompt,
361
+ });
362
+ ```
363
+
364
+ See the
365
+ [local tool annotations example](https://github.com/vercel/ai/tree/main/examples/mcp/src/tool-annotations)
366
+ for a complete annotated MCP server and application-layer approval
367
+ configuration. Start the server and client in separate terminals:
368
+
369
+ ```bash
370
+ cd examples/mcp
371
+ pnpm server:tool-annotations
372
+ ```
373
+
374
+ ```bash
375
+ cd examples/mcp
376
+ pnpm client:tool-annotations
377
+ ```
378
+
379
+ Ask the client to read, delete, or create a note. The read-only tool executes
380
+ immediately. The destructive and unannotated tools prompt for approval; after
381
+ you enter `y` or `n`, the client sends the decision back and prints the model's
382
+ response.
383
+
323
384
  ### Schema Definition
324
385
 
325
386
  For better type safety and control, you can define the tools and their input schemas explicitly in your client code:
@@ -347,7 +408,7 @@ This approach provides full TypeScript type safety and IDE autocompletion, letti
347
408
 
348
409
  ### Typed Tool Outputs
349
410
 
350
- When MCP servers return `structuredContent` (per the [MCP specification](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content)), you can define an `outputSchema` to get typed tool results:
411
+ When MCP servers return `structuredContent` (per the [MCP specification](https://modelcontextprotocol.io/specification/2026-07-28/server/tools#structured-content)), you can define an `outputSchema` to get typed tool results:
351
412
 
352
413
  ```typescript
353
414
  import { z } from 'zod';
@@ -384,6 +445,8 @@ When `outputSchema` is provided:
384
445
 
385
446
  If the server doesn't return `structuredContent`, the client falls back to parsing JSON from the text content. If neither is available or validation fails, an error is thrown.
386
447
 
448
+ If a server returns `structuredContent` without the backwards-compatible `content` field, the client adds a text content block containing the serialized JSON before returning the result. This compatibility behavior handles servers that omit the text mirror recommended by the MCP specification.
449
+
387
450
  <Note>
388
451
  Without `outputSchema`, the tool returns the raw `CallToolResult` object
389
452
  containing `content` and optional `isError` fields.
@@ -230,6 +230,7 @@ This error occurs when the AI provider fails to generate an image. It can arise
230
230
 
231
231
  The error preserves the following information to help you log the issue:
232
232
 
233
+ - `calls`: Results from the underlying image model calls, including generated images, provider metadata, response metadata, warnings, and usage.
233
234
  - `responses`: Metadata about the image model responses, including timestamp, model, and headers.
234
235
  - `cause`: The cause of the error. You can use this for more detailed error handling
235
236
 
@@ -243,6 +244,12 @@ try {
243
244
  console.log('NoImageGeneratedError');
244
245
  console.log('Cause:', error.cause);
245
246
  console.log('Responses:', error.responses);
247
+
248
+ for (const call of error.calls ?? []) {
249
+ console.log('Provider metadata:', call.providerMetadata);
250
+ console.log('Warnings:', call.warnings);
251
+ console.log('Usage:', call.usage);
252
+ }
246
253
  }
247
254
  }
248
255
  ```
@@ -293,40 +293,41 @@ try {
293
293
 
294
294
  ## Transcription Models
295
295
 
296
- | Provider | Model |
297
- | ----------------------------------------------------------------------------------- | ------------------------ |
298
- | [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `whisper-1` |
299
- | [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-transcribe` |
300
- | [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-mini-transcribe` |
301
- | [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v1` |
302
- | [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v1_experimental` |
303
- | [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v2` |
304
- | [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#streaming-transcription-models) | `scribe_v2_realtime` |
305
- | [Groq](/providers/ai-sdk-providers/groq#transcription-models) | `whisper-large-v3-turbo` |
306
- | [Groq](/providers/ai-sdk-providers/groq#transcription-models) | `whisper-large-v3` |
307
- | [Mistral](/providers/ai-sdk-providers/mistral#transcription-models) | `voxtral-mini-latest` |
308
- | [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `whisper-1` |
309
- | [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-transcribe` |
310
- | [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-mini-transcribe` |
311
- | [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `machine` |
312
- | [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `low_cost` |
313
- | [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `fusion` |
314
- | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `base` (+ variants) |
315
- | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `enhanced` (+ variants) |
316
- | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova` (+ variants) |
317
- | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-2` (+ variants) |
318
- | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-3` (+ variants) |
319
- | [Gladia](/providers/ai-sdk-providers/gladia#transcription-models) | `default` |
320
- | [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-5-pro` |
321
- | [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-pro` |
322
- | [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `whisper` |
323
- | [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `wizper` |
324
- | [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_2` |
325
- | [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_3` |
326
- | [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `telephony` |
327
- | [xAI](/providers/ai-sdk-providers/xai#transcription-models) | `default` |
328
- | [Cartesia](/providers/ai-sdk-providers/cartesia#transcription-models) | `ink-whisper` |
329
- | [Cartesia](/providers/ai-sdk-providers/cartesia#streaming-transcription-models) | `ink-2` |
330
- | [Fish Audio](/providers/ai-sdk-providers/fish-audio#transcription-models) | `transcribe-1` |
296
+ | Provider | Model |
297
+ | ----------------------------------------------------------------------------------- | --------------------------- |
298
+ | [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `whisper-1` |
299
+ | [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-transcribe` |
300
+ | [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-mini-transcribe` |
301
+ | [OpenAI](/providers/ai-sdk-providers/openai#transcription-models) | `gpt-4o-transcribe-diarize` |
302
+ | [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v1` |
303
+ | [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v1_experimental` |
304
+ | [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#transcription-models) | `scribe_v2` |
305
+ | [ElevenLabs](/providers/ai-sdk-providers/elevenlabs#streaming-transcription-models) | `scribe_v2_realtime` |
306
+ | [Groq](/providers/ai-sdk-providers/groq#transcription-models) | `whisper-large-v3-turbo` |
307
+ | [Groq](/providers/ai-sdk-providers/groq#transcription-models) | `whisper-large-v3` |
308
+ | [Mistral](/providers/ai-sdk-providers/mistral#transcription-models) | `voxtral-mini-latest` |
309
+ | [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `whisper-1` |
310
+ | [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-transcribe` |
311
+ | [Azure OpenAI](/providers/ai-sdk-providers/azure#transcription-models) | `gpt-4o-mini-transcribe` |
312
+ | [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `machine` |
313
+ | [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `low_cost` |
314
+ | [Rev.ai](/providers/ai-sdk-providers/revai#transcription-models) | `fusion` |
315
+ | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `base` (+ variants) |
316
+ | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `enhanced` (+ variants) |
317
+ | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova` (+ variants) |
318
+ | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-2` (+ variants) |
319
+ | [Deepgram](/providers/ai-sdk-providers/deepgram#transcription-models) | `nova-3` (+ variants) |
320
+ | [Gladia](/providers/ai-sdk-providers/gladia#transcription-models) | `default` |
321
+ | [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-5-pro` |
322
+ | [AssemblyAI](/providers/ai-sdk-providers/assemblyai#transcription-models) | `universal-3-pro` |
323
+ | [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `whisper` |
324
+ | [Fal](/providers/ai-sdk-providers/fal#transcription-models) | `wizper` |
325
+ | [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_2` |
326
+ | [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `chirp_3` |
327
+ | [Google Vertex](/providers/ai-sdk-providers/google-vertex#transcription-models) | `telephony` |
328
+ | [xAI](/providers/ai-sdk-providers/xai#transcription-models) | `default` |
329
+ | [Cartesia](/providers/ai-sdk-providers/cartesia#transcription-models) | `ink-whisper` |
330
+ | [Cartesia](/providers/ai-sdk-providers/cartesia#streaming-transcription-models) | `ink-2` |
331
+ | [Fish Audio](/providers/ai-sdk-providers/fish-audio#transcription-models) | `transcribe-1` |
331
332
 
332
333
  Above are a small subset of the transcription models supported by the AI SDK providers. For more, see the respective provider documentation.
@@ -98,6 +98,39 @@ try {
98
98
  }
99
99
  ```
100
100
 
101
+ ## Lifecycle Callbacks
102
+
103
+ Configure lifecycle callbacks on `HarnessAgent` to observe agent calls, model
104
+ steps, and tool executions:
105
+
106
+ ```ts
107
+ const agent = new HarnessAgent({
108
+ harness: claudeCode,
109
+ sandbox,
110
+ tools: { weather },
111
+ onStart: event => console.log('call started', event.callId),
112
+ onStepStart: event => console.log('step started', event.stepNumber),
113
+ onLanguageModelCallStart: event =>
114
+ console.log('model call started', event.modelId),
115
+ onLanguageModelCallEnd: event =>
116
+ console.log('model call ended', event.finishReason),
117
+ onToolExecutionStart: event =>
118
+ console.log('tool started', event.toolCall.toolName),
119
+ onToolExecutionEnd: event => console.log('tool ended', event.toolOutput.type),
120
+ onStepEnd: step => console.log('step ended', step.stepNumber),
121
+ onEnd: event => console.log('call ended', event.finishReason),
122
+ });
123
+ ```
124
+
125
+ Callbacks configured on individual `generate()` and `stream()` calls are
126
+ invoked in addition to settings callbacks, with settings callbacks invoked
127
+ first. Callback errors are ignored and do not change agent execution.
128
+
129
+ Harness runtimes execute their built-in tools internally. For those tools,
130
+ `onToolExecutionStart` and `onToolExecutionEnd` describe the logical tool
131
+ lifecycle after the runtime reports the result. The callbacks are still
132
+ delivered before the tool result is published to the result stream.
133
+
101
134
  ## Generate Structured Output
102
135
 
103
136
  Set `output` when constructing `HarnessAgent` to require the same typed output
@@ -510,6 +543,9 @@ console.log(preparation.identity);
510
543
  - `id`: optional stable agent identifier.
511
544
  - `instructions`: instructions appended to the runtime's system or developer
512
545
  prompt when supported, or prepended to the user prompt otherwise.
546
+ - `headers`: additional headers sent with model requests. Headers are fixed at
547
+ construction time. `authorization`, `x-api-key`, `user-agent`, and
548
+ `x-client-app` are not allowed.
513
549
  - `callOptionsSchema` and `prepareCall`: validate custom call options and derive
514
550
  model, skills, instructions, and tools for each new turn.
515
551
  - `output`: typed output specification applied to every turn.
@@ -63,14 +63,17 @@ export async function POST(req: Request) {
63
63
 
64
64
  const stream = createUIMessageStream<MyUIMessage>({
65
65
  execute: ({ writer }) => {
66
- // 1. Send initial status (transient - won't be added to message history)
66
+ // 1. Start the assistant message before writing any message parts.
67
+ writer.write({ type: 'start' });
68
+
69
+ // 2. Send initial status (transient - won't be added to message history)
67
70
  writer.write({
68
71
  type: 'data-notification',
69
72
  data: { message: 'Processing your request...', level: 'info' },
70
73
  transient: true, // This part won't be added to message history
71
74
  });
72
75
 
73
- // 2. Send sources (useful for RAG use cases)
76
+ // 3. Send sources (useful for RAG use cases)
74
77
  writer.write({
75
78
  type: 'source',
76
79
  value: {
@@ -82,7 +85,7 @@ export async function POST(req: Request) {
82
85
  },
83
86
  });
84
87
 
85
- // 3. Send data parts with loading state
88
+ // 4. Send data parts with loading state
86
89
  writer.write({
87
90
  type: 'data-weather',
88
91
  id: 'weather-1',
@@ -93,7 +96,7 @@ export async function POST(req: Request) {
93
96
  model: __MODEL__,
94
97
  messages: await convertToModelMessages(messages),
95
98
  onEnd() {
96
- // 4. Update the same data part (reconciliation)
99
+ // 5. Update the same data part (reconciliation)
97
100
  writer.write({
98
101
  type: 'data-weather',
99
102
  id: 'weather-1', // Same ID = update existing part
@@ -104,7 +107,7 @@ export async function POST(req: Request) {
104
107
  },
105
108
  });
106
109
 
107
- // 5. Send completion notification (transient)
110
+ // 6. Send completion notification (transient)
108
111
  writer.write({
109
112
  type: 'data-notification',
110
113
  data: { message: 'Request completed', level: 'info' },
@@ -113,7 +116,9 @@ export async function POST(req: Request) {
113
116
  },
114
117
  });
115
118
 
116
- writer.merge(toUIMessageStream({ stream: result.stream }));
119
+ writer.merge(
120
+ toUIMessageStream({ stream: result.stream, sendStart: false }),
121
+ );
117
122
  },
118
123
  });
119
124
 
@@ -996,6 +996,20 @@ To see `generateText` in action, check out [these examples](#examples).
996
996
  description:
997
997
  'The schema of the array elements to generate.',
998
998
  },
999
+ {
1000
+ name: 'minItems',
1001
+ type: 'number',
1002
+ isOptional: true,
1003
+ description:
1004
+ 'Optional minimum number of array elements. Must be a non-negative integer.',
1005
+ },
1006
+ {
1007
+ name: 'maxItems',
1008
+ type: 'number',
1009
+ isOptional: true,
1010
+ description:
1011
+ 'Optional maximum number of array elements. Must be a non-negative integer and greater than or equal to minItems.',
1012
+ },
999
1013
  {
1000
1014
  name: 'name',
1001
1015
  type: 'string',
@@ -1272,6 +1272,20 @@ To see `streamText` in action, check out [these examples](#examples).
1272
1272
  description:
1273
1273
  'The schema of the array elements to generate.',
1274
1274
  },
1275
+ {
1276
+ name: 'minItems',
1277
+ type: 'number',
1278
+ isOptional: true,
1279
+ description:
1280
+ 'Optional minimum number of array elements. Must be a non-negative integer.',
1281
+ },
1282
+ {
1283
+ name: 'maxItems',
1284
+ type: 'number',
1285
+ isOptional: true,
1286
+ description:
1287
+ 'Optional maximum number of array elements. Must be a non-negative integer and greater than or equal to minItems.',
1288
+ },
1275
1289
  {
1276
1290
  name: 'name',
1277
1291
  type: 'string',
@@ -2069,11 +2083,24 @@ To see `streamText` in action, check out [these examples](#examples).
2069
2083
  {
2070
2084
  type: 'OnAbortResult',
2071
2085
  parameters: [
2086
+ {
2087
+ name: 'callId',
2088
+ type: 'string',
2089
+ description:
2090
+ 'Unique identifier for this generation call, used to correlate events.',
2091
+ },
2072
2092
  {
2073
2093
  name: 'steps',
2074
2094
  type: 'Array<StepResult>',
2075
2095
  description: 'Details for all previously finished steps.',
2076
2096
  },
2097
+ {
2098
+ name: 'reason',
2099
+ type: 'unknown',
2100
+ isOptional: true,
2101
+ description:
2102
+ 'The raw abort reason from the AbortSignal, when one is available.',
2103
+ },
2077
2104
  ],
2078
2105
  },
2079
2106
  ],
@@ -3979,10 +4006,10 @@ To see `streamText` in action, check out [these examples](#examples).
3979
4006
  },
3980
4007
  {
3981
4008
  name: 'reason',
3982
- type: 'unknown',
4009
+ type: 'string',
3983
4010
  isOptional: true,
3984
4011
  description:
3985
- 'Optional abort reason (from AbortSignal.reason) when the stream is aborted.',
4012
+ 'Optional serialized abort reason when the stream is aborted.',
3986
4013
  },
3987
4014
  ],
3988
4015
  },
@@ -4004,7 +4031,7 @@ To see `streamText` in action, check out [these examples](#examples).
4004
4031
  name: 'elementStream',
4005
4032
  type: 'AsyncIterableStream<ELEMENT_OUTPUT>',
4006
4033
  description:
4007
- 'A stream of individual array elements as they complete. Only available when using `output: Output.array()`. Each element is complete and validated against the element schema. AsyncIterableStream is defined as AsyncIterable<T> & ReadableStream<T>.',
4034
+ 'A stream of individual array elements as they complete. Only available when using `output: Output.array()`. Each element is complete and validated against the element schema. When `maxItems` is set, the stream errors before emitting the first excess element. AsyncIterableStream is defined as AsyncIterable<T> & ReadableStream<T>.',
4008
4035
  },
4009
4036
  {
4010
4037
  name: 'output',
@@ -135,6 +135,8 @@ const { output } = await generateText({
135
135
  temperature: z.number(),
136
136
  condition: z.string(),
137
137
  }),
138
+ minItems: 2,
139
+ maxItems: 2,
138
140
  }),
139
141
  prompt: 'List the weather for San Francisco and Paris.',
140
142
  });
@@ -151,6 +153,20 @@ const { output } = await generateText({
151
153
  description:
152
154
  'The schema that defines the structure of each array element. Supports Zod schemas, Valibot schemas, or JSON schemas.',
153
155
  },
156
+ {
157
+ name: 'minItems',
158
+ type: 'number',
159
+ isOptional: true,
160
+ description:
161
+ 'The minimum number of elements to generate. Must be a non-negative integer.',
162
+ },
163
+ {
164
+ name: 'maxItems',
165
+ type: 'number',
166
+ isOptional: true,
167
+ description:
168
+ 'The maximum number of elements to generate. Must be a non-negative integer and greater than or equal to minItems.',
169
+ },
154
170
  {
155
171
  name: 'name',
156
172
  type: 'string',
@@ -173,8 +189,15 @@ const { output } = await generateText({
173
189
  An `Output<Array<ELEMENT>, Array<ELEMENT>>` specification where:
174
190
 
175
191
  - Complete output is an array with all elements validated
192
+ - Complete output is validated against `minItems` and `maxItems`
176
193
  - Partial output contains only fully validated elements (incomplete elements are excluded)
177
194
 
195
+ Set `minItems` and `maxItems` to the same value to require an exact number of
196
+ elements. The bounds are included in the provider-facing schema when the
197
+ provider supports them. The AI SDK also validates the completed output, so
198
+ providers that ignore these schema keywords cannot return an out-of-bounds
199
+ result.
200
+
178
201
  #### Streaming with `elementStream`
179
202
 
180
203
  When using `streamText` with `Output.array()`, you can iterate over elements as they are generated using `elementStream`:
@@ -202,7 +225,10 @@ for await (const hero of elementStream) {
202
225
 
203
226
  <Note>
204
227
  Each element emitted by `elementStream` is complete and validated against your
205
- element schema, ensuring type safety for each item as it is generated.
228
+ element schema, ensuring type safety for each item as it is generated. If the
229
+ model generates more than `maxItems`, `elementStream` errors before emitting
230
+ the first excess element. Provider generation is not aborted automatically,
231
+ and the final `output` promise also rejects.
206
232
  </Note>
207
233
 
208
234
  ---
@@ -38,7 +38,7 @@ const result = streamText({
38
38
  type: 'number | null',
39
39
  isOptional: true,
40
40
  description:
41
- 'The delay in milliseconds between outputting each chunk. Defaults to 10ms. Set to `null` to disable delays.',
41
+ 'The delay in milliseconds between outputting each chunk. Defaults to 10ms. Set to `null` to disable delays. The delay is skipped while the document is hidden (e.g. browser background tabs), where timer throttling would otherwise stall the stream.',
42
42
  },
43
43
  {
44
44
  name: 'chunking',
@@ -408,7 +408,7 @@ Allows you to easily create a conversational user interface for your chatbot app
408
408
  name: 'sendMessage',
409
409
  type: '(message?: { text: string; files?: FileList | FileUIPart[]; metadata?; messageId?: string } | CreateUIMessage, options?: ChatRequestOptions) => Promise<void>',
410
410
  description:
411
- 'Function to send a new message to the chat. This will trigger an API call to generate the assistant response. If a messageId is provided, the message will be replaced (useful for editing). If no message is provided, resubmits the current messages (useful after adding tool outputs).',
411
+ 'Function to send a new message to the chat. This will trigger an API call to generate the assistant response. If a messageId is provided, the message will be replaced (useful for editing). When replacing with a CreateUIMessage, provide its id to assign a new ID to the replacement. If no message is provided, resubmits the current messages (useful after adding tool outputs).',
412
412
  properties: [
413
413
  {
414
414
  type: 'ChatRequestOptions',
@@ -20,6 +20,9 @@ const existingMessages: UIMessage[] = [
20
20
 
21
21
  const stream = createUIMessageStream({
22
22
  async execute({ writer }) {
23
+ // The outer stream owns the assistant message lifecycle.
24
+ writer.write({ type: 'start' });
25
+
23
26
  // Start a text message
24
27
  // Note: The id must be consistent across text-start, text-delta, and text-end steps
25
28
  // This allows the system to correctly identify they belong to the same text block
@@ -50,6 +53,7 @@ const stream = createUIMessageStream({
50
53
  writer.merge(
51
54
  toUIMessageStream({
52
55
  stream: result.stream,
56
+ sendStart: false,
53
57
  onEnd: ({ outcome }) => {
54
58
  // The composer decides that the model stream outcome is also the
55
59
  // aggregate stream outcome.