ai 7.0.153 → 7.0.154

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 (41) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/index.d.ts +224 -9
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +399 -96
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal/index.d.ts +52 -4
  7. package/dist/internal/index.d.ts.map +1 -1
  8. package/dist/internal/index.js +26 -20
  9. package/dist/internal/index.js.map +1 -1
  10. package/docs/02-getting-started/02-nextjs-app-router.mdx +2 -2
  11. package/docs/02-getting-started/08-angular.mdx +327 -0
  12. package/docs/02-getting-started/{03-nextjs-pages-router.mdx → 10-nextjs-pages-router.mdx} +12 -20
  13. package/docs/02-getting-started/index.mdx +2 -0
  14. package/docs/03-agents/01-overview.mdx +1 -0
  15. package/docs/03-agents/04-loop-control.mdx +117 -0
  16. package/docs/03-agents/06-tool-approvals.mdx +1 -0
  17. package/docs/03-agents/09-chat-platforms.mdx +164 -0
  18. package/docs/03-agents/index.mdx +6 -0
  19. package/docs/03-ai-sdk-core/60-telemetry.mdx +16 -3
  20. package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +10 -0
  21. package/docs/04-ai-sdk-ui/21-transport.mdx +86 -0
  22. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +46 -0
  23. package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +46 -0
  24. package/docs/07-reference/01-ai-sdk-core/16-tool-loop-agent.mdx +35 -0
  25. package/package.json +11 -11
  26. package/src/agent/create-agent-ui-stream-response.ts +3 -0
  27. package/src/agent/create-agent-ui-stream.ts +16 -0
  28. package/src/agent/pipe-agent-ui-stream-to-response.ts +3 -0
  29. package/src/agent/tool-loop-agent-settings.ts +37 -1
  30. package/src/agent/tool-loop-agent.ts +43 -7
  31. package/src/generate-text/generate-text-events.ts +37 -0
  32. package/src/generate-text/generate-text.ts +521 -373
  33. package/src/generate-text/index.ts +8 -0
  34. package/src/generate-text/prepare-step.ts +12 -0
  35. package/src/generate-text/restricted-telemetry-dispatcher.ts +31 -18
  36. package/src/generate-text/steering.ts +293 -0
  37. package/src/generate-text/step-result.ts +18 -0
  38. package/src/generate-text/stream-text.ts +664 -473
  39. package/src/telemetry/create-telemetry-dispatcher.ts +3 -0
  40. package/src/telemetry/telemetry.ts +15 -1
  41. package/src/util/is-caused-by-abort.ts +20 -0
@@ -27,8 +27,8 @@ Start by creating a new Next.js application. This command will create a new dire
27
27
  <div className="mb-4">
28
28
  <Note>
29
29
  Be sure to select yes when prompted to use the App Router and Tailwind CSS.
30
- If you are looking for the Next.js Pages Router quickstart guide, you can
31
- find it [here](/docs/getting-started/nextjs-pages-router).
30
+ If you are adding the AI SDK to an existing Pages Router application, follow
31
+ the [Pages Router guide](/docs/getting-started/nextjs-pages-router).
32
32
  </Note>
33
33
  </div>
34
34
 
@@ -0,0 +1,327 @@
1
+ ---
2
+ title: Angular
3
+ description: Learn how to build your first agent with the AI SDK and Angular.
4
+ ---
5
+
6
+ # Angular Quickstart
7
+
8
+ The AI SDK is a powerful TypeScript library designed to help developers build AI-powered applications.
9
+
10
+ In this quickstart tutorial, you'll build a simple agent with a streaming chat user interface using Angular and an Express backend. Along the way, you'll learn key concepts and techniques that are fundamental to using the AI SDK in your own projects.
11
+
12
+ If you are unfamiliar with the concepts of [Prompt Engineering](/docs/advanced/prompt-engineering) and [HTTP Streaming](/docs/foundations/streaming), you can optionally read these documents first.
13
+
14
+ ## Prerequisites
15
+
16
+ To follow this quickstart, you'll need:
17
+
18
+ - Node.js 24.15+ (or another [version supported by Angular](https://angular.dev/reference/versions)) and pnpm installed on your local development machine.
19
+ - A [Vercel AI Gateway](https://vercel.com/ai-gateway) API key.
20
+
21
+ If you haven't obtained your Vercel AI Gateway API key, you can do so by [signing up](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai&title=Go+to+AI+Gateway) on the Vercel website.
22
+
23
+ ## Set Up Your Application
24
+
25
+ Create a new Angular application using the Angular CLI:
26
+
27
+ <Snippet text="pnpm dlx @angular/cli@latest new my-ai-app --defaults --ssr=false --package-manager=pnpm" />
28
+
29
+ This creates a standalone Angular application. This guide uses client-side rendering, with a separate Express server for the chat API.
30
+
31
+ Navigate to the newly created directory:
32
+
33
+ <Snippet text="cd my-ai-app" />
34
+
35
+ ### Install Dependencies
36
+
37
+ Install `ai` and `@ai-sdk/angular`, the AI package and AI SDK's Angular bindings. You'll also install `express` for the backend, `dotenv` to load environment variables, and `zod` to define tool inputs.
38
+
39
+ <InstallPackages packages="ai @ai-sdk/angular express dotenv zod" />
40
+
41
+ Install the TypeScript runner and server type definitions:
42
+
43
+ <InstallPackages packages="tsx @types/express @types/node" dev />
44
+
45
+ <Note>
46
+ This guide uses the Vercel AI Gateway provider, which ships with the `ai`
47
+ package. You can access hundreds of models with one API key, or switch to
48
+ another [AI SDK provider](/providers/ai-sdk-providers).
49
+ </Note>
50
+
51
+ ### Configure Your AI Gateway API Key
52
+
53
+ Create a `.env` file in your project root:
54
+
55
+ ```env filename=".env"
56
+ AI_GATEWAY_API_KEY=xxxxxxxxx
57
+ ```
58
+
59
+ Replace `xxxxxxxxx` with your actual Vercel AI Gateway API key. The Express server loads this file using `dotenv`, and the AI SDK's Gateway provider reads `AI_GATEWAY_API_KEY` automatically. Keep the API key on the server and add `.env` to `.gitignore`.
60
+
61
+ ## Create an API Route
62
+
63
+ Create `server.ts` in your project root and add the following code:
64
+
65
+ ```ts filename="server.ts"
66
+ import {
67
+ streamText,
68
+ type UIMessage,
69
+ convertToModelMessages,
70
+ pipeUIMessageStreamToResponse,
71
+ toUIMessageStream,
72
+ } from 'ai';
73
+ __PROVIDER_IMPORT__;
74
+ import 'dotenv/config';
75
+ import express from 'express';
76
+
77
+ const app = express();
78
+ app.use(express.json());
79
+
80
+ app.post('/api/chat', async (req, res) => {
81
+ const { messages }: { messages: UIMessage[] } = req.body;
82
+
83
+ const result = streamText({
84
+ model: __MODEL__,
85
+ messages: await convertToModelMessages(messages),
86
+ });
87
+
88
+ await pipeUIMessageStreamToResponse({
89
+ response: res,
90
+ stream: toUIMessageStream({ stream: result.stream }),
91
+ });
92
+ });
93
+
94
+ app.listen(3000, '127.0.0.1', () => {
95
+ console.log('Chat API listening on http://127.0.0.1:3000');
96
+ });
97
+ ```
98
+
99
+ The server listens on `127.0.0.1` to keep the development API local to your machine. This creates a `POST /api/chat` endpoint. Let's take a look at what happens when it receives a request:
100
+
101
+ 1. `express.json()` parses the request body, which contains the conversation's `UIMessage` objects.
102
+ 2. [`convertToModelMessages`](/docs/reference/ai-sdk-ui/convert-to-model-messages) converts those UI messages to the format expected by the model.
103
+ 3. [`streamText`](/docs/reference/ai-sdk-core/stream-text) calls the model through AI Gateway.
104
+ 4. `toUIMessageStream` converts the model's output to the UI message stream format, and `pipeUIMessageStreamToResponse` writes it to the Express response as server-sent events.
105
+
106
+ ### Configure the Development Proxy
107
+
108
+ Create `proxy.conf.json` in your project root:
109
+
110
+ ```json filename="proxy.conf.json"
111
+ {
112
+ "/api/**": {
113
+ "target": "http://127.0.0.1:3000"
114
+ }
115
+ }
116
+ ```
117
+
118
+ When you start Angular with this proxy configuration, requests to `/api/chat` are forwarded to the Express server. The browser can use the relative `/api/chat` URL, which is also the default endpoint for the AI SDK's `Chat` class.
119
+
120
+ <Note>
121
+ This proxy is for local development. When deploying, route `/api/chat` to your
122
+ backend, or configure a different API URL using
123
+ [`DefaultChatTransport`](/docs/ai-sdk-ui/transport).
124
+ </Note>
125
+
126
+ ## Choosing a Provider
127
+
128
+ The AI SDK supports dozens of model providers through [first-party](/providers/ai-sdk-providers), [OpenAI-compatible](/providers/openai-compatible-providers), and [community](/providers/community-providers) packages.
129
+
130
+ This quickstart uses the [Vercel AI Gateway](https://vercel.com/ai-gateway) provider, which is the default [global provider](/docs/ai-sdk-core/provider-management#global-provider-configuration). You can select a model using a string in your server's configuration:
131
+
132
+ ```ts
133
+ model: __MODEL__;
134
+ ```
135
+
136
+ To use a provider directly, install its package and create a provider instance. Learn more about [provider management](/docs/ai-sdk-core/provider-management#global-provider-configuration).
137
+
138
+ ## Wire Up the UI
139
+
140
+ The `@ai-sdk/angular` package provides a `Chat` class that manages messages, streams responses, and exposes the current request status. Its state is backed by Angular signals, so the template updates as new content arrives.
141
+
142
+ Replace `src/app/app.ts` with the following component:
143
+
144
+ ```ts filename="src/app/app.ts"
145
+ import { JsonPipe } from '@angular/common';
146
+ import { Component, signal } from '@angular/core';
147
+ import { Chat } from '@ai-sdk/angular';
148
+
149
+ @Component({
150
+ selector: 'app-root',
151
+ imports: [JsonPipe],
152
+ template: `
153
+ <main>
154
+ @for (message of chat.messages; track message.id) {
155
+ <div>
156
+ <strong>{{ message.role === 'user' ? 'User' : 'AI' }}:</strong>
157
+ @for (part of message.parts; track $index) {
158
+ @if (part.type === 'text') {
159
+ <p style="white-space: pre-wrap">{{ part.text }}</p>
160
+ } @else if (part.type.startsWith('tool-')) {
161
+ <pre>{{ part | json }}</pre>
162
+ }
163
+ }
164
+ </div>
165
+ }
166
+
167
+ @if (chat.status === 'submitted') {
168
+ <p>Waiting for a response...</p>
169
+ }
170
+
171
+ @if (chat.error; as error) {
172
+ <p role="alert">{{ error.message }}</p>
173
+ }
174
+
175
+ <form (submit)="sendMessage($event)">
176
+ <label for="message">Your message</label>
177
+ <input
178
+ id="message"
179
+ #messageInput
180
+ [value]="input()"
181
+ (input)="input.set(messageInput.value)"
182
+ />
183
+ <button
184
+ type="submit"
185
+ [disabled]="
186
+ chat.status === 'submitted' || chat.status === 'streaming'
187
+ "
188
+ >
189
+ Send
190
+ </button>
191
+ @if (chat.status === 'submitted' || chat.status === 'streaming') {
192
+ <button type="button" (click)="chat.stop()">Stop</button>
193
+ }
194
+ </form>
195
+ </main>
196
+ `,
197
+ })
198
+ export class App {
199
+ readonly chat = new Chat({});
200
+ readonly input = signal('');
201
+
202
+ sendMessage(event: Event) {
203
+ event.preventDefault();
204
+ const text = this.input().trim();
205
+
206
+ if (
207
+ !text ||
208
+ this.chat.status === 'submitted' ||
209
+ this.chat.status === 'streaming'
210
+ ) {
211
+ return;
212
+ }
213
+
214
+ this.chat.sendMessage({ text });
215
+ this.input.set('');
216
+ }
217
+ }
218
+ ```
219
+
220
+ <Note>
221
+ Older Angular projects may name the root component file `app.component.ts` and
222
+ its class `AppComponent`. If you are using an existing project, keep its file
223
+ and class names so the imports in `src/main.ts` continue to work.
224
+ </Note>
225
+
226
+ The component creates one `Chat` instance and uses its reactive properties:
227
+
228
+ - `messages` contains the conversation. Each message has a `role` and an ordered array of `parts`.
229
+ - `sendMessage` sends a user message to `/api/chat` and streams the response into `messages`.
230
+ - `status` indicates whether the chat is ready, submitted, streaming, or in an error state.
231
+ - `error` contains the latest request error.
232
+ - `stop` cancels the current response.
233
+
234
+ Read these properties as `chat.messages`, `chat.status`, and `chat.error`; the `Chat` class manages the underlying signals. The input field uses a separate Angular signal, which is read with `input()`.
235
+
236
+ The template renders text parts as they arrive and uses Angular's `JsonPipe` to display tool calls and results, which you'll add next.
237
+
238
+ ## Running Your Application
239
+
240
+ In one terminal, start the Express backend from your project root:
241
+
242
+ <Snippet text="pnpm exec tsx watch server.ts" />
243
+
244
+ In a second terminal, start the Angular development server from the same directory:
245
+
246
+ <Snippet text="pnpm exec ng serve --proxy-config proxy.conf.json" />
247
+
248
+ Open [http://localhost:4200](http://localhost:4200), enter a message, and watch the response stream into the chat interface.
249
+
250
+ <Note>
251
+ If you change `proxy.conf.json`, restart the Angular development server so it
252
+ picks up the new configuration.
253
+ </Note>
254
+
255
+ ## Enhance Your Chatbot with Tools
256
+
257
+ [Tools](/docs/ai-sdk-core/tools-and-tool-calling) let a model request actions such as looking up the weather. The server executes the action and returns the result to the model.
258
+
259
+ Replace `server.ts` with the following code to add a weather tool:
260
+
261
+ ```ts filename="server.ts"
262
+ import {
263
+ streamText,
264
+ type UIMessage,
265
+ convertToModelMessages,
266
+ pipeUIMessageStreamToResponse,
267
+ toUIMessageStream,
268
+ tool,
269
+ isStepCount,
270
+ } from 'ai';
271
+ __PROVIDER_IMPORT__;
272
+ import 'dotenv/config';
273
+ import express from 'express';
274
+ import * as z4 from 'zod/v4';
275
+
276
+ const app = express();
277
+ app.use(express.json());
278
+
279
+ app.post('/api/chat', async (req, res) => {
280
+ const { messages }: { messages: UIMessage[] } = req.body;
281
+
282
+ const result = streamText({
283
+ model: __MODEL__,
284
+ messages: await convertToModelMessages(messages),
285
+ stopWhen: isStepCount(5),
286
+ tools: {
287
+ weather: tool({
288
+ description: 'Get the weather in a location (fahrenheit)',
289
+ inputSchema: z4.object({
290
+ location: z4.string().describe('The location to get the weather for'),
291
+ }),
292
+ execute: async ({ location }) => {
293
+ const temperature = Math.round(Math.random() * (90 - 32) + 32);
294
+ return { location, temperature };
295
+ },
296
+ }),
297
+ },
298
+ });
299
+
300
+ await pipeUIMessageStreamToResponse({
301
+ response: res,
302
+ stream: toUIMessageStream({ stream: result.stream }),
303
+ });
304
+ });
305
+
306
+ app.listen(3000, '127.0.0.1', () => {
307
+ console.log('Chat API listening on http://127.0.0.1:3000');
308
+ });
309
+ ```
310
+
311
+ The `weather` tool has a description, an input schema, and an `execute` function. The model uses the description to decide when to call the tool and the schema to generate its input. The server then executes the function and returns its result.
312
+
313
+ This example returns a simulated temperature. You can replace the function with a call to a weather API to return real data.
314
+
315
+ ### Enabling Multi-Step Tool Calls
316
+
317
+ By default, a generation stops after one step. Setting `stopWhen: isStepCount(5)` allows the model to continue after receiving a tool result, for up to five steps. It can use the returned weather data to answer the original question.
318
+
319
+ Ask "What's the weather in San Francisco?" in your chat interface. The template displays the `tool-weather` part as JSON, followed by the model's text response. No frontend changes are required: the `Chat` class already handles tool parts in the UI message stream.
320
+
321
+ ## Where to Go Next
322
+
323
+ You've built an Angular chat interface that streams responses, calls tools, and uses their results in multi-step generations.
324
+
325
+ - Explore the [Angular example](https://github.com/vercel/ai/tree/main/examples/angular) for chat, completion, and structured output examples.
326
+ - Learn more about [AI SDK UI](/docs/ai-sdk-ui/overview) and [tools and tool calling](/docs/ai-sdk-core/tools-and-tool-calling).
327
+ - Use the [transport API](/docs/ai-sdk-ui/transport) to connect your Angular frontend to an existing backend.
@@ -1,40 +1,32 @@
1
1
  ---
2
2
  title: Next.js Pages Router
3
- description: Learn how to build your first agent with the AI SDK and Next.js Pages Router.
3
+ description: Add an agent with a streaming chat interface to an existing Next.js Pages Router application.
4
4
  ---
5
5
 
6
- # Next.js Pages Router Quickstart
6
+ # Next.js Pages Router Guide
7
7
 
8
- The AI SDK is a powerful TypeScript library designed to help developers build AI-powered applications.
8
+ This guide shows how to add an agent with a streaming chat user interface to an existing Next.js Pages Router application.
9
9
 
10
- In this quickstart tutorial, you'll build a simple agent with a streaming chat user interface. Along the way, you'll learn key concepts and techniques that are fundamental to using the AI SDK in your own projects.
10
+ <Note>
11
+ For new applications, we recommend the [Next.js App Router
12
+ quickstart](/docs/getting-started/nextjs-app-router).
13
+ </Note>
11
14
 
12
15
  If you are unfamiliar with the concepts of [Prompt Engineering](/docs/advanced/prompt-engineering) and [HTTP Streaming](/docs/foundations/streaming), you can optionally read these documents first.
13
16
 
14
17
  ## Prerequisites
15
18
 
16
- To follow this quickstart, you'll need:
19
+ To follow this guide, you'll need:
17
20
 
21
+ - An existing Next.js 13+ application using the Pages Router.
18
22
  - Node.js 22+ and pnpm installed on your local development machine.
19
23
  - A [Vercel AI Gateway](https://vercel.com/ai-gateway) API key.
20
24
 
21
25
  If you haven't obtained your Vercel AI Gateway API key, you can do so by [signing up](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai&title=Go+to+AI+Gateway) on the Vercel website.
22
26
 
23
- ## Setup Your Application
24
-
25
- Start by creating a new Next.js application. This command will create a new directory named `my-ai-app` and set up a basic Next.js application inside it.
26
-
27
- <Note>
28
- Be sure to select no when prompted to use the App Router. If you are looking
29
- for the Next.js App Router quickstart guide, you can find it
30
- [here](/docs/getting-started/nextjs-app-router).
31
- </Note>
32
-
33
- <Snippet text="pnpm create next-app@latest my-ai-app" />
34
-
35
- Navigate to the newly created directory:
27
+ ## Set Up Your Application
36
28
 
37
- <Snippet text="cd my-ai-app" />
29
+ Open a terminal in the root directory of your existing Next.js Pages Router application.
38
30
 
39
31
  ### Install dependencies
40
32
 
@@ -115,7 +107,7 @@ This Route Handler creates a POST request endpoint at `/api/chat`.
115
107
 
116
108
  The AI SDK supports dozens of model providers through [first-party](/providers/ai-sdk-providers), [OpenAI-compatible](/providers/openai-compatible-providers), and [community](/providers/community-providers) packages.
117
109
 
118
- This quickstart uses the [Vercel AI Gateway](https://vercel.com/ai-gateway) provider, which is the default [global provider](/docs/ai-sdk-core/provider-management#global-provider-configuration). This means you can access models using a simple string in the model configuration:
110
+ This guide uses the [Vercel AI Gateway](https://vercel.com/ai-gateway) provider, which is the default [global provider](/docs/ai-sdk-core/provider-management#global-provider-configuration). This means you can access models using a simple string in the model configuration:
119
111
 
120
112
  ```ts
121
113
  model: __MODEL__;
@@ -9,6 +9,8 @@ The following guides are intended to provide you with an introduction to some of
9
9
 
10
10
  <QuickstartFrameworkCards />
11
11
 
12
+ [Using an existing Next.js Pages Router app?](/docs/getting-started/nextjs-pages-router)
13
+
12
14
  ## Backend Framework Examples
13
15
 
14
16
  You can also use [AI SDK Core](/docs/ai-sdk-core/overview) and [AI SDK UI](/docs/ai-sdk-ui/overview) with the following backend frameworks:
@@ -139,3 +139,4 @@ Agents are flexible and powerful, but non-deterministic. When you need reliable,
139
139
  - **[Runtime and Tool Context](/docs/ai-sdk-core/runtime-and-tool-context)** - How to pass shared agent state and per-tool context
140
140
  - **[Workflow Patterns](/docs/agents/workflows)** - Structured patterns using core functions for complex workflows
141
141
  - **[Loop Control](/docs/agents/loop-control)** - Execution control with stopWhen and prepareStep
142
+ - **[Chat Platforms](/docs/agents/chat-platforms)** - Run agents in Slack, Microsoft Teams, Discord, and more with Chat SDK
@@ -12,6 +12,8 @@ You can control both the execution flow and the settings at each step of the age
12
12
  - A tool call needs approval, or
13
13
  - A stop condition is met
14
14
 
15
+ A [steering message](#steering) that is waiting when the model finishes its answer keeps the loop going for another step.
16
+
15
17
  The AI SDK provides built-in loop control through two parameters: `stopWhen` for defining stopping conditions and `prepareStep` for modifying settings (model, tools, messages, and more) between steps.
16
18
 
17
19
  ## Stop Conditions
@@ -402,6 +404,121 @@ Unlike `runtimeContext` and `toolsContext`, an experimental sandbox returned fro
402
404
  only applies to tool execution in that step. Later steps use the top-level
403
405
  `experimental_sandbox` unless they return their own experimental sandbox override.
404
406
 
407
+ ## Steering
408
+
409
+ <Note type="warning">Steering is an experimental feature.</Note>
410
+
411
+ Steering adds a user message to a running loop. Use it when a user sends a correction or a follow-up while the agent is still working. The message is added at the next step boundary, after the current step and its tool calls are done, so running tools are never interrupted.
412
+
413
+ Create a steering object with `experimental_createSteering()`, pass it with the `experimental_steering` option, and call `steer()` while the loop is running:
414
+
415
+ ```ts
416
+ import { experimental_createSteering, isStepCount, streamText } from 'ai';
417
+
418
+ const steering = experimental_createSteering();
419
+
420
+ const result = streamText({
421
+ model: __MODEL__,
422
+ tools: { weather },
423
+ stopWhen: isStepCount(10),
424
+ prompt: 'What is the weather in Paris?',
425
+ experimental_steering: steering,
426
+ });
427
+
428
+ // e.g. when the user sends another message while the agent is running:
429
+ const outcome = await steering.steer('I meant Paris, Texas.');
430
+ ```
431
+
432
+ Steering works the same way with `generateText` and `ToolLoopAgent`, because you create the steering object before the call starts:
433
+
434
+ ```ts
435
+ const steering = experimental_createSteering();
436
+
437
+ const run = agent.generate({
438
+ prompt: 'What is the weather in Paris?',
439
+ experimental_steering: steering,
440
+ });
441
+
442
+ await steering.steer('I meant Paris, Texas.');
443
+
444
+ const result = await run;
445
+ ```
446
+
447
+ ### How Steering Messages Are Delivered
448
+
449
+ - Messages are added in the order they are sent. Each message is its own user message.
450
+ - If the model finishes its answer while a message is waiting, the loop continues for another step.
451
+ - Stop conditions still apply. `generateText` and `streamText` stop after one step by default, so set `stopWhen` to steer them. Agents run up to 20 steps by default.
452
+ - Messages that are sent before the loop starts are added to the first step.
453
+ - `prepareStep` and `onStepStart` receive the steering messages as part of `messages`.
454
+ - A steering object can only be used for one call. Create a new one with `experimental_createSteering()` for each call.
455
+ - For agents, steering is supported by `ToolLoopAgent`. `createAgentUIStream` throws when it gets a steering object with another agent.
456
+
457
+ <Note type="warning">
458
+ `messages` already contains the steering messages in the right place. If
459
+ `prepareStep` returns a `messages` override that is rebuilt from other fields,
460
+ e.g. `initialMessages` and `responseMessages`, the steering messages are
461
+ dropped and `steer()` reports them as not delivered with the reason
462
+ `removed-by-prepare-step`. Build overrides from `messages`, or add the
463
+ steering messages of every step back in order.
464
+ </Note>
465
+
466
+ ```ts
467
+ prepareStep: ({ initialMessages, steps, experimental_steeringMessages }) => ({
468
+ messages: [
469
+ ...initialMessages,
470
+ ...steps.flatMap(step => [
471
+ ...(step.experimental_steeringMessages ?? []),
472
+ ...step.response.messages,
473
+ ]),
474
+ // steering messages for the current step:
475
+ ...(experimental_steeringMessages ?? []),
476
+ ],
477
+ }),
478
+ ```
479
+
480
+ ### Handling the Outcome
481
+
482
+ `steer()` resolves with the outcome of the message and never rejects:
483
+
484
+ ```ts
485
+ const outcome = await steering.steer('I meant Paris, Texas.', {
486
+ id: messageId,
487
+ });
488
+
489
+ if (outcome.type === 'delivered') {
490
+ console.log(`added to step ${outcome.stepNumber}`);
491
+ } else {
492
+ // the message was not added, e.g. send it as a new message instead:
493
+ console.log(outcome.reason, outcome.message);
494
+ }
495
+ ```
496
+
497
+ A message is delivered when it is part of the messages that are sent to the model, after `prepareStep`. It is not delivered when the loop ends before the next step boundary, or when `prepareStep` removes it. The `reason` is one of:
498
+
499
+ - `finished`: the loop ended before the message could be added.
500
+ - `removed-by-prepare-step`: `prepareStep` returned messages without it.
501
+ - `stop-condition`: a stop condition ended the loop.
502
+ - `waiting-for-tool-results`: the loop ended to wait for tool results, e.g. a [tool approval](/docs/agents/tool-approvals).
503
+ - `aborted`: the loop was aborted.
504
+ - `error`: the loop failed.
505
+
506
+ Pass an `id` to make retries safe. Sending a message again with the same `id` returns the outcome of the first message.
507
+
508
+ ### Saving Steering Messages
509
+
510
+ Steering messages are not part of `responseMessages`. Each step lists the steering messages that were added to it in `experimental_steeringMessages`, so you can rebuild the full conversation in order:
511
+
512
+ ```ts
513
+ const messages = [
514
+ ...initialMessages,
515
+ ...result.steps.flatMap(step => [
516
+ ...(step.experimental_steeringMessages ?? []),
517
+ ...step.response.messages,
518
+ ]),
519
+ ];
520
+ ```
521
+
405
522
  ## Access Step Information
406
523
 
407
524
  Both `stopWhen` and `prepareStep` receive detailed information about the current execution:
@@ -352,3 +352,4 @@ Every serverless instance that might handle a request needs the same secret, sin
352
352
  - Author approval rules as code with [Policy-Based Tool Approvals](/docs/agents/policy-tool-approvals) (`@ai-sdk/policy-opa`).
353
353
  - Use `needsApproval` only with [`WorkflowAgent`](/docs/agents/workflow-agent), where approvals suspend and resume durable workflow execution.
354
354
  - Subagent tools cannot use `toolApproval`; see [Subagents](/docs/agents/subagents#no-tool-approvals-in-subagents).
355
+ - Chat SDK's `createChatTools` marks its write tools and `getUser` as needing approval. A `toolApproval` rule overrides those defaults; see [Chat Platforms](/docs/agents/chat-platforms#give-the-agent-chat-tools).