ai 7.0.161 → 7.0.162

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.
@@ -296,6 +296,8 @@ In the standard `useChat` pattern, the server rebuilds the conversation from the
296
296
 
297
297
  Tool approvals reconstructed from this history are re-validated before execution: the tool input is checked against the tool's schema, and the approval policy is re-evaluated. However, without additional protection, a client that crafts a valid-looking approval for a schema-conforming input can bypass the human-in-the-loop step.
298
298
 
299
+ During re-validation, `toolCall.toolMetadata` and `toolCall.dynamic` come from the current server-side tool definition. The deprecated `toolCall.title` field is also restored from that definition. A server-side policy denial prevents execution even when the client supplies an approved response with a valid signature.
300
+
299
301
  If your tools perform sensitive operations (modifying data, spending money, calling external APIs, accessing private resources), use `experimental_toolApprovalSecret` to cryptographically bind approvals to the server that issued them.
300
302
 
301
303
  ### Signing approvals with `experimental_toolApprovalSecret`
@@ -253,6 +253,42 @@ registerTelemetry(
253
253
  );
254
254
  ```
255
255
 
256
+ ## Durable WorkflowAgent runs
257
+
258
+ The steps of a durable `WorkflowAgent` run (`@ai-sdk/workflow`) can execute in
259
+ different processes. To report the run as one `invoke_agent` span tree, use the
260
+ same `OpenTelemetryIdGenerator` instance as your tracer provider's ID generator
261
+ and sampler, and pass it to the globally registered `OpenTelemetry` integration:
262
+
263
+ ```ts
264
+ import { OpenTelemetry, OpenTelemetryIdGenerator } from '@ai-sdk/otel';
265
+ import { NodeSDK } from '@opentelemetry/sdk-node';
266
+ import { registerTelemetry } from 'ai';
267
+
268
+ const durable = new OpenTelemetryIdGenerator();
269
+ new NodeSDK({ idGenerator: durable, sampler: durable }).start();
270
+ registerTelemetry(new OpenTelemetry({ durable }));
271
+ ```
272
+
273
+ Model calls are reported live from their durable steps, so spans started by the
274
+ provider call (for example auto-instrumented HTTP spans) nest under their `chat`
275
+ span. The root, step, and tool spans appear when the run ends or suspends (for
276
+ example on a tool approval), using the original event timestamps. The root
277
+ includes the run's prompt and final output, subject to `recordInputs` and
278
+ `recordOutputs`.
279
+
280
+ A preparation step captures the active parent trace and makes the run's sampling
281
+ decision once. That context is carried to subsequent steps, preserving the trace
282
+ ID, sampling decision, and trace state. By default, the bridge uses parent-based
283
+ sampling with roots enabled. To use another sampler, pass it through
284
+ `new OpenTelemetryIdGenerator({ sampler: yourSampler })` and keep the bridge as
285
+ the provider's sampler. Unrelated spans use random IDs and the configured sampler.
286
+
287
+ Install this setup in every process that executes workflow steps, with integrations
288
+ registered in the same order. Without the bridge, `OpenTelemetry` preserves live
289
+ per-model operation traces. Other globally registered telemetry integrations receive
290
+ the recorded run lifecycle when it ends or suspends.
291
+
256
292
  ## Telemetry Integrations
257
293
 
258
294
  Telemetry integrations let you hook into the generation lifecycle to build custom observability — logging, analytics, DevTools, or any other monitoring system. Instead of wiring up individual callbacks on every call, you implement a `Telemetry` once and register it globally or pass it via `telemetry.integrations`.
@@ -395,6 +431,18 @@ export function myIntegration(): Telemetry {
395
431
  }
396
432
  ```
397
433
 
434
+ For durable workflows, integrations normally receive recorded events after the
435
+ run ends. Events can include `timestamp` (Unix milliseconds); use it for span
436
+ start and end times instead of the current time. Integrations that can reconstruct
437
+ traces across processes can implement the experimental
438
+ `experimental_durable.captureContext(event)` hook. It runs in a preparation step
439
+ and returns a serializable `DurableTelemetryContext`. Live model-start events
440
+ then carry that context in `event.durableContext` with `phase: 'live'`; the
441
+ recorded operation-start event carries it with `phase: 'replay'`. Such integrations
442
+ should skip replayed model events to avoid duplicate spans. Returning `undefined`
443
+ selects live per-model operations instead, with no deferred replay to that
444
+ integration.
445
+
398
446
  ### Available lifecycle methods
399
447
 
400
448
  <PropertiesTable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.161",
3
+ "version": "7.0.162",
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,20 +42,20 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@ai-sdk/gateway": "4.0.134",
45
+ "@ai-sdk/gateway": "4.0.135",
46
46
  "@ai-sdk/provider": "4.0.35",
47
- "@ai-sdk/provider-utils": "5.0.73"
47
+ "@ai-sdk/provider-utils": "5.0.74"
48
48
  },
49
49
  "devDependencies": {
50
- "@ai-sdk/amazon-bedrock": "5.0.131",
51
- "@ai-sdk/deepseek": "3.0.78",
52
- "@ai-sdk/google": "4.0.110",
53
- "@ai-sdk/groq": "4.0.75",
54
- "@ai-sdk/huggingface": "2.0.83",
55
- "@ai-sdk/moonshotai": "3.0.83",
56
- "@ai-sdk/openai": "4.0.107",
50
+ "@ai-sdk/amazon-bedrock": "5.0.132",
51
+ "@ai-sdk/deepseek": "3.0.79",
52
+ "@ai-sdk/google": "4.0.111",
53
+ "@ai-sdk/groq": "4.0.76",
54
+ "@ai-sdk/huggingface": "2.0.84",
55
+ "@ai-sdk/moonshotai": "3.0.84",
56
+ "@ai-sdk/openai": "4.0.108",
57
57
  "@ai-sdk/test-server": "2.0.4",
58
- "@ai-sdk/xai": "5.0.37",
58
+ "@ai-sdk/xai": "5.0.38",
59
59
  "@edge-runtime/vm": "^5.0.0",
60
60
  "@smithy/eventstream-codec": "^4.3.3",
61
61
  "@smithy/util-utf8": "^4.3.3",
@@ -57,13 +57,31 @@ export async function validateApprovedToolApprovals<
57
57
  CollectedToolApprovals<TOOLS> & { error: InvalidToolInputError }
58
58
  > = [];
59
59
 
60
- for (const approval of approvedToolApprovals) {
61
- const { approvalRequest, toolCall } = approval;
60
+ for (const collectedApproval of approvedToolApprovals) {
62
61
  // Look up the tool by own property only: `toolName` comes from
63
62
  // client-supplied history, so a name matching an inherited object property
64
63
  // (e.g. `constructor`, `toString`) must resolve to "no such tool" rather
65
64
  // than a prototype value that would silently skip input validation below.
66
- const tool = getOwn(tools, toolCall.toolName);
65
+ const tool = getOwn(tools, collectedApproval.toolCall.toolName);
66
+
67
+ // History can omit or alter these fields. Rebuild them from the current
68
+ // server-side definition before input refinement and approval policy checks,
69
+ // just as parsing a new model tool call does.
70
+ const {
71
+ toolMetadata: _toolMetadata,
72
+ dynamic: _dynamic,
73
+ ...toolCallFromHistory
74
+ } = collectedApproval.toolCall;
75
+ const approval: CollectedToolApprovals<TOOLS> = {
76
+ ...collectedApproval,
77
+ toolCall: {
78
+ ...toolCallFromHistory,
79
+ ...(tool?.metadata != null ? { toolMetadata: tool.metadata } : {}),
80
+ ...(tool?.type === 'dynamic' ? { dynamic: true } : {}),
81
+ title: tool?.title,
82
+ } as typeof collectedApproval.toolCall,
83
+ };
84
+ const { approvalRequest, toolCall } = approval;
67
85
 
68
86
  if (toolApprovalSecret != null) {
69
87
  if (approvalRequest.signature == null) {
@@ -1,5 +1,10 @@
1
1
  export type { TelemetryOptions } from './telemetry-options';
2
- export type { InferTelemetryEvent, Telemetry } from './telemetry';
2
+ export type {
3
+ DurableTelemetryContext,
4
+ DurableTelemetryEventContext,
5
+ InferTelemetryEvent,
6
+ Telemetry,
7
+ } from './telemetry';
3
8
  export { registerTelemetry } from './telemetry-registry';
4
9
  export {
5
10
  AI_SDK_TELEMETRY_TRACING_CHANNEL,
@@ -56,6 +56,43 @@ import type { TelemetryOptions } from '../telemetry/telemetry-options';
56
56
  import type { TelemetryTracingEventType } from './tracing-channel';
57
57
  import type { TracingChannelContext } from './tracing-channel-publisher';
58
58
 
59
+ /** Serializable tracing identity shared by the processes of a durable run. */
60
+ export type DurableTelemetryContext = {
61
+ traceId: string;
62
+ rootSpanId: string;
63
+ /** The integration's root-span data, reused when the run is reported. */
64
+ rootSpanName?: string;
65
+ rootAttributes?: Record<
66
+ string,
67
+ | string
68
+ | number
69
+ | boolean
70
+ | Array<string | null | undefined>
71
+ | Array<number | null | undefined>
72
+ | Array<boolean | null | undefined>
73
+ | undefined
74
+ >;
75
+ traceFlags: number;
76
+ traceState?: string;
77
+ samplingDecision: 0 | 1 | 2;
78
+ parentSpanContext?: {
79
+ traceId: string;
80
+ spanId: string;
81
+ traceFlags: number;
82
+ traceState?: string;
83
+ };
84
+ };
85
+
86
+ /** Identifies live model events and the run events reported after completion. */
87
+ export type DurableTelemetryEventContext = {
88
+ run: DurableTelemetryContext;
89
+ stepNumber?: number;
90
+ phase: 'live' | 'replay';
91
+ /** Context has already been filtered before crossing the step boundary. */
92
+ runtimeContext?: Record<string, unknown>;
93
+ headers?: Record<string, string | undefined>;
94
+ };
95
+
59
96
  export type InferTelemetryEvent<EVENT> = EVENT &
60
97
  Omit<
61
98
  TelemetryOptions,
@@ -66,6 +103,16 @@ export type InferTelemetryEvent<EVENT> = EVENT &
66
103
  * Absent in older releases.
67
104
  */
68
105
  readonly aiVersion?: string;
106
+
107
+ /**
108
+ * Time at which the event occurred, in milliseconds since the Unix epoch.
109
+ *
110
+ * Set when events are reported after they happened, for example when a
111
+ * durable workflow reports a run that spanned several processes.
112
+ * Integrations should use it instead of the current time when present.
113
+ */
114
+ readonly timestamp?: number;
115
+ readonly durableContext?: DurableTelemetryEventContext;
69
116
  };
70
117
 
71
118
  type BivariantCallback<EVENT> = {
@@ -142,6 +189,21 @@ export interface TelemetryDispatcher {
142
189
  * Methods can be sync or return a PromiseLike.
143
190
  */
144
191
  export interface Telemetry {
192
+ /**
193
+ * Capture a serializable tracing identity before a durable run's model calls.
194
+ * Return undefined to preserve live per-model operations when the integration
195
+ * cannot reconstruct a run span across processes. Integrations without this
196
+ * hook receive the recorded lifecycle after completion.
197
+ */
198
+ experimental_durable?: {
199
+ captureContext: (
200
+ event: InferTelemetryEvent<GenerateTextStartEvent>,
201
+ ) =>
202
+ | DurableTelemetryContext
203
+ | undefined
204
+ | PromiseLike<DurableTelemetryContext | undefined>;
205
+ };
206
+
145
207
  /**
146
208
  * Called when an operation begins. Fired for text generation
147
209
  * (generateText/streamText), object generation (generateObject/streamObject),