ai 7.0.31 → 7.0.32

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.
@@ -85,7 +85,7 @@ import {
85
85
  } from "@ai-sdk/provider-utils";
86
86
 
87
87
  // src/version.ts
88
- var VERSION = true ? "7.0.31" : "0.0.0-test";
88
+ var VERSION = true ? "7.0.32" : "0.0.0-test";
89
89
 
90
90
  // src/util/download/download.ts
91
91
  var download = async ({
@@ -100,6 +100,10 @@ or a message-array `prompt`, `HarnessAgent` takes the latest user message as the
100
100
  fresh input for the turn. It does not replay the full prior conversation into
101
101
  the harness.
102
102
 
103
+ A trailing tool message is handled differently: tool approval responses and
104
+ client-provided tool results continue the unfinished harness turn that produced
105
+ the corresponding request or tool call.
106
+
103
107
  This is different from model calls, where the application usually sends the full
104
108
  message history. In chat routes, persist and resume the harness session instead
105
109
  of relying on message replay.
@@ -116,6 +120,8 @@ End every session explicitly:
116
120
  the turn is unfinished, the resume state includes the continuation state.
117
121
  - `session.suspendTurn()` is for advanced active-turn continuation across a
118
122
  process boundary.
123
+ - `session.hasUnfinishedTurn()` reports whether the current turn must be
124
+ continued or suspended before the session accepts a new prompt.
119
125
 
120
126
  Use `destroy()` for one-off scripts and tests. Use `detach()` or `stop()` for
121
127
  HTTP routes that need multi-turn continuity.
@@ -165,8 +171,10 @@ For advanced workflows that must hand off an active turn across a process
165
171
  boundary, suspend the turn and persist the continuation state:
166
172
 
167
173
  ```ts
168
- const continuationState = await session.suspendTurn();
169
- await persistContinuationState({ chatId, continuationState });
174
+ if (session.hasUnfinishedTurn()) {
175
+ const continuationState = await session.suspendTurn();
176
+ await persistContinuationState({ chatId, continuationState });
177
+ }
170
178
  ```
171
179
 
172
180
  When you only have raw continuation state from `suspendTurn()`, resume with
@@ -91,6 +91,46 @@ const agent = new HarnessAgent({
91
91
  When the harness calls `weather`, `HarnessAgent` executes the tool in your host
92
92
  process, then submits the result back to the harness runtime.
93
93
 
94
+ ## Client-Side Tools
95
+
96
+ Omit `execute` when a browser, user interaction, or another external process
97
+ provides the tool result:
98
+
99
+ ```ts
100
+ const weather = tool({
101
+ description: 'Get the current temperature for a city.',
102
+ inputSchema: z.object({ city: z.string() }),
103
+ });
104
+ ```
105
+
106
+ When the harness calls a tool without `execute`, the returned result slice ends
107
+ after the tool-call step while the underlying turn waits for a result.
108
+ `session.hasUnfinishedTurn()` remains `true`, and `session.suspendTurn()`
109
+ includes the pending tool call in its serializable continuation state.
110
+
111
+ In UI flows, pass the model messages produced after `addToolOutput` to the next
112
+ `stream()` or `generate()` call. `HarnessAgent` extracts the trailing tool
113
+ result and continues the paused turn automatically.
114
+
115
+ For direct agent calls, provide the raw result to `continueStream()` or
116
+ `continueGenerate()`:
117
+
118
+ ```ts
119
+ const continued = await agent.continueStream({
120
+ session,
121
+ toolResultContinuations: [
122
+ {
123
+ toolCallId,
124
+ output: { city: 'Paris', celsius: 12 },
125
+ },
126
+ ],
127
+ });
128
+ ```
129
+
130
+ Set `isError: true` when the external tool failed. To continue in another
131
+ process, call `suspendTurn()`, recreate the session with `continueFrom`, and
132
+ then pass the result to `continueStream()` or `continueGenerate()`.
133
+
94
134
  ## Tool Filtering
95
135
 
96
136
  Use `activeTools` or `inactiveTools` on `HarnessAgent` to control which tools the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.31",
3
+ "version": "7.0.32",
4
4
  "type": "module",
5
5
  "description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
6
6
  "license": "Apache-2.0",
@@ -42,7 +42,7 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@ai-sdk/gateway": "4.0.23",
45
+ "@ai-sdk/gateway": "4.0.24",
46
46
  "@ai-sdk/provider": "4.0.3",
47
47
  "@ai-sdk/provider-utils": "5.0.11"
48
48
  },
@@ -795,6 +795,10 @@ export async function generateText<
795
795
  const pendingDeferredToolCalls = new Map<string, { toolName: string }>();
796
796
 
797
797
  do {
798
+ if (steps.length > 0) {
799
+ mergedAbortSignal?.throwIfAborted();
800
+ }
801
+
798
802
  // Set up step timeout if configured
799
803
  const stepTimeoutId = setAbortTimeout({
800
804
  abortController: stepAbortController,
package/src/ui/chat.ts CHANGED
@@ -208,8 +208,8 @@ export interface ChatInit<UI_MESSAGE extends UIMessage> {
208
208
  * Optional callback function that is invoked when a tool call is received.
209
209
  * Intended for automatic client-side tool execution.
210
210
  *
211
- * You can optionally return a result for the tool call,
212
- * either synchronously or asynchronously.
211
+ * To add the tool output, call `addToolOutput` without awaiting it inside
212
+ * this callback. The callback's return value is not used.
213
213
  */
214
214
  onToolCall?: ChatOnToolCallCallback<UI_MESSAGE>;
215
215
 
@@ -21,29 +21,29 @@ const toolMetadataSchema: z.ZodType<JSONObject> = z.record(
21
21
  );
22
22
 
23
23
  export const uiMessageChunkSchema = lazySchema(() =>
24
- zodSchema(
24
+ zodSchema<UIMessageChunk>(
25
25
  z.union([
26
- z.strictObject({
26
+ z.looseObject({
27
27
  type: z.literal('text-start'),
28
28
  id: z.string(),
29
29
  providerMetadata: providerMetadataSchema.optional(),
30
30
  }),
31
- z.strictObject({
31
+ z.looseObject({
32
32
  type: z.literal('text-delta'),
33
33
  id: z.string(),
34
34
  delta: z.string(),
35
35
  providerMetadata: providerMetadataSchema.optional(),
36
36
  }),
37
- z.strictObject({
37
+ z.looseObject({
38
38
  type: z.literal('text-end'),
39
39
  id: z.string(),
40
40
  providerMetadata: providerMetadataSchema.optional(),
41
41
  }),
42
- z.strictObject({
42
+ z.looseObject({
43
43
  type: z.literal('error'),
44
44
  errorText: z.string(),
45
45
  }),
46
- z.strictObject({
46
+ z.looseObject({
47
47
  type: z.literal('tool-input-start'),
48
48
  toolCallId: z.string(),
49
49
  toolName: z.string(),
@@ -53,12 +53,12 @@ export const uiMessageChunkSchema = lazySchema(() =>
53
53
  dynamic: z.boolean().optional(),
54
54
  title: z.string().optional(),
55
55
  }),
56
- z.strictObject({
56
+ z.looseObject({
57
57
  type: z.literal('tool-input-delta'),
58
58
  toolCallId: z.string(),
59
59
  inputTextDelta: z.string(),
60
60
  }),
61
- z.strictObject({
61
+ z.looseObject({
62
62
  type: z.literal('tool-input-available'),
63
63
  toolCallId: z.string(),
64
64
  toolName: z.string(),
@@ -69,7 +69,7 @@ export const uiMessageChunkSchema = lazySchema(() =>
69
69
  dynamic: z.boolean().optional(),
70
70
  title: z.string().optional(),
71
71
  }),
72
- z.strictObject({
72
+ z.looseObject({
73
73
  type: z.literal('tool-input-error'),
74
74
  toolCallId: z.string(),
75
75
  toolName: z.string(),
@@ -81,14 +81,14 @@ export const uiMessageChunkSchema = lazySchema(() =>
81
81
  errorText: z.string(),
82
82
  title: z.string().optional(),
83
83
  }),
84
- z.strictObject({
84
+ z.looseObject({
85
85
  type: z.literal('tool-approval-request'),
86
86
  approvalId: z.string(),
87
87
  toolCallId: z.string(),
88
88
  isAutomatic: z.boolean().optional(),
89
89
  signature: z.string().optional(),
90
90
  }),
91
- z.strictObject({
91
+ z.looseObject({
92
92
  type: z.literal('tool-approval-response'),
93
93
  approvalId: z.string(),
94
94
  approved: z.boolean(),
@@ -96,7 +96,7 @@ export const uiMessageChunkSchema = lazySchema(() =>
96
96
  providerExecuted: z.boolean().optional(),
97
97
  providerMetadata: providerMetadataSchema.optional(),
98
98
  }),
99
- z.strictObject({
99
+ z.looseObject({
100
100
  type: z.literal('tool-output-available'),
101
101
  toolCallId: z.string(),
102
102
  output: z.unknown(),
@@ -106,7 +106,7 @@ export const uiMessageChunkSchema = lazySchema(() =>
106
106
  dynamic: z.boolean().optional(),
107
107
  preliminary: z.boolean().optional(),
108
108
  }),
109
- z.strictObject({
109
+ z.looseObject({
110
110
  type: z.literal('tool-output-error'),
111
111
  toolCallId: z.string(),
112
112
  errorText: z.string(),
@@ -115,39 +115,39 @@ export const uiMessageChunkSchema = lazySchema(() =>
115
115
  toolMetadata: toolMetadataSchema.optional(),
116
116
  dynamic: z.boolean().optional(),
117
117
  }),
118
- z.strictObject({
118
+ z.looseObject({
119
119
  type: z.literal('tool-output-denied'),
120
120
  toolCallId: z.string(),
121
121
  }),
122
- z.strictObject({
122
+ z.looseObject({
123
123
  type: z.literal('reasoning-start'),
124
124
  id: z.string(),
125
125
  providerMetadata: providerMetadataSchema.optional(),
126
126
  }),
127
- z.strictObject({
127
+ z.looseObject({
128
128
  type: z.literal('reasoning-delta'),
129
129
  id: z.string(),
130
130
  delta: z.string(),
131
131
  providerMetadata: providerMetadataSchema.optional(),
132
132
  }),
133
- z.strictObject({
133
+ z.looseObject({
134
134
  type: z.literal('reasoning-end'),
135
135
  id: z.string(),
136
136
  providerMetadata: providerMetadataSchema.optional(),
137
137
  }),
138
- z.strictObject({
138
+ z.looseObject({
139
139
  type: z.literal('custom'),
140
140
  kind: z.string().transform(value => value as `${string}.${string}`),
141
141
  providerMetadata: providerMetadataSchema.optional(),
142
142
  }),
143
- z.strictObject({
143
+ z.looseObject({
144
144
  type: z.literal('source-url'),
145
145
  sourceId: z.string(),
146
146
  url: z.string(),
147
147
  title: z.string().optional(),
148
148
  providerMetadata: providerMetadataSchema.optional(),
149
149
  }),
150
- z.strictObject({
150
+ z.looseObject({
151
151
  type: z.literal('source-document'),
152
152
  sourceId: z.string(),
153
153
  mediaType: z.string(),
@@ -155,19 +155,19 @@ export const uiMessageChunkSchema = lazySchema(() =>
155
155
  filename: z.string().optional(),
156
156
  providerMetadata: providerMetadataSchema.optional(),
157
157
  }),
158
- z.strictObject({
158
+ z.looseObject({
159
159
  type: z.literal('file'),
160
160
  url: z.string(),
161
161
  mediaType: z.string(),
162
162
  providerMetadata: providerMetadataSchema.optional(),
163
163
  }),
164
- z.strictObject({
164
+ z.looseObject({
165
165
  type: z.literal('reasoning-file'),
166
166
  url: z.string(),
167
167
  mediaType: z.string(),
168
168
  providerMetadata: providerMetadataSchema.optional(),
169
169
  }),
170
- z.strictObject({
170
+ z.looseObject({
171
171
  type: z.custom<`data-${string}`>(
172
172
  (value): value is `data-${string}` =>
173
173
  typeof value === 'string' && value.startsWith('data-'),
@@ -177,18 +177,18 @@ export const uiMessageChunkSchema = lazySchema(() =>
177
177
  data: z.unknown(),
178
178
  transient: z.boolean().optional(),
179
179
  }),
180
- z.strictObject({
180
+ z.looseObject({
181
181
  type: z.literal('start-step'),
182
182
  }),
183
- z.strictObject({
183
+ z.looseObject({
184
184
  type: z.literal('finish-step'),
185
185
  }),
186
- z.strictObject({
186
+ z.looseObject({
187
187
  type: z.literal('start'),
188
188
  messageId: z.string().optional(),
189
189
  messageMetadata: z.unknown().optional(),
190
190
  }),
191
- z.strictObject({
191
+ z.looseObject({
192
192
  type: z.literal('finish'),
193
193
  finishReason: z
194
194
  .enum([
@@ -202,11 +202,11 @@ export const uiMessageChunkSchema = lazySchema(() =>
202
202
  .optional(),
203
203
  messageMetadata: z.unknown().optional(),
204
204
  }),
205
- z.strictObject({
205
+ z.looseObject({
206
206
  type: z.literal('abort'),
207
207
  reason: z.string().optional(),
208
208
  }),
209
- z.strictObject({
209
+ z.looseObject({
210
210
  type: z.literal('message-metadata'),
211
211
  messageMetadata: z.unknown(),
212
212
  }),