agents 0.20.0 → 0.21.0

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 (87) hide show
  1. package/README.md +50 -12
  2. package/dist/{agent-tool-types-Btk9ETS-.d.ts → agent-tool-types-CzGGB-20.d.ts} +403 -115
  3. package/dist/agent-tool-types.d.ts +1 -1
  4. package/dist/{agent-tools-UuScsJg3.d.ts → agent-tools-zR2d5uij.d.ts} +2 -2
  5. package/dist/agent-tools.d.ts +24 -8
  6. package/dist/agent-tools.js +14 -6
  7. package/dist/agent-tools.js.map +1 -1
  8. package/dist/browser/ai.d.ts +5 -3
  9. package/dist/browser/ai.js +86 -7
  10. package/dist/browser/ai.js.map +1 -1
  11. package/dist/browser/index.d.ts +1 -1
  12. package/dist/browser/index.js +1 -1
  13. package/dist/browser/tanstack-ai.js +13 -1
  14. package/dist/browser/tanstack-ai.js.map +1 -1
  15. package/dist/chat/index.d.ts +31 -2
  16. package/dist/chat/index.js +57 -2
  17. package/dist/chat/index.js.map +1 -1
  18. package/dist/chat/react.d.ts +5 -179
  19. package/dist/chat/react.js +18 -555
  20. package/dist/chat/react.js.map +1 -1
  21. package/dist/chat/transport.d.ts +10 -0
  22. package/dist/chat/transport.js +2 -0
  23. package/dist/chat-sdk/index.d.ts +1 -1
  24. package/dist/client.d.ts +1 -1
  25. package/dist/cloudflare-BduZwmYK.js +204 -0
  26. package/dist/cloudflare-BduZwmYK.js.map +1 -0
  27. package/dist/{connector-v2M1zlZp.d.ts → connector-CkQD4MK3.d.ts} +20 -3
  28. package/dist/{connector-KEJnl6e5.js → connector-CptFKzRh.js} +158 -40
  29. package/dist/connector-CptFKzRh.js.map +1 -0
  30. package/dist/{handler-stateless-8hQN_kC3.js → handler-stateless-CIkKPETH.js} +20 -69
  31. package/dist/handler-stateless-CIkKPETH.js.map +1 -0
  32. package/dist/index.d.ts +21 -11
  33. package/dist/index.js +31 -21
  34. package/dist/index.js.map +1 -1
  35. package/dist/mcp/client.d.ts +20 -20
  36. package/dist/mcp/index.d.ts +35 -35
  37. package/dist/mcp/index.js +32 -98
  38. package/dist/mcp/index.js.map +1 -1
  39. package/dist/mcp/server.js +1 -1
  40. package/dist/observability/ai/index.d.ts +10 -131
  41. package/dist/observability/ai/index.js +209 -444
  42. package/dist/observability/ai/index.js.map +1 -1
  43. package/dist/{wire-types-CU9rLoeS.js → protocol-Dqc2MQxo.js} +2 -46
  44. package/dist/protocol-Dqc2MQxo.js.map +1 -0
  45. package/dist/react.d.ts +1 -1
  46. package/dist/react.js +5 -8
  47. package/dist/react.js.map +1 -1
  48. package/dist/serializable.d.ts +1 -1
  49. package/dist/sub-routing.d.ts +18 -6
  50. package/dist/sub-routing.js +92 -2
  51. package/dist/sub-routing.js.map +1 -1
  52. package/dist/wire-types-CnMt6_HR.js +47 -0
  53. package/dist/wire-types-CnMt6_HR.js.map +1 -0
  54. package/dist/workflow-types.d.ts +25 -25
  55. package/dist/workflow-types.js.map +1 -1
  56. package/dist/workflows.d.ts +22 -22
  57. package/dist/ws-chat-transport-CIoOBbO7.js +561 -0
  58. package/dist/ws-chat-transport-CIoOBbO7.js.map +1 -0
  59. package/dist/ws-chat-transport-UNRIS2xl.d.ts +184 -0
  60. package/docs/adding-to-existing-project.md +4 -2
  61. package/docs/agent-class.md +1 -1
  62. package/docs/agent-tools.md +29 -0
  63. package/docs/browse-the-web.md +16 -1
  64. package/docs/chat-agents.md +3 -1
  65. package/docs/client-sdk.md +12 -8
  66. package/docs/configuration.md +7 -1
  67. package/docs/cross-domain-authentication.md +7 -35
  68. package/docs/email.md +2 -13
  69. package/docs/human-in-the-loop.md +15 -12
  70. package/docs/long-running-agents.md +11 -11
  71. package/docs/mcp-client.md +1 -1
  72. package/docs/mcp-servers.md +7 -1
  73. package/docs/mcp-transports.md +18 -12
  74. package/docs/migration-to-ai-sdk-v5.md +2 -2
  75. package/docs/migration-to-ai-sdk-v6.md +5 -1
  76. package/docs/observability.md +24 -67
  77. package/docs/routing.md +27 -0
  78. package/docs/sub-agents.md +47 -2
  79. package/docs/webhooks.md +109 -136
  80. package/docs/workflows.md +9 -1
  81. package/package.json +19 -10
  82. package/dist/cloudflare-BldFV0Pa.js +0 -117
  83. package/dist/cloudflare-BldFV0Pa.js.map +0 -1
  84. package/dist/connector-KEJnl6e5.js.map +0 -1
  85. package/dist/handler-stateless-8hQN_kC3.js.map +0 -1
  86. package/dist/wire-types-CU9rLoeS.js.map +0 -1
  87. package/dist/workflow-types-Baz_PO5v.d.ts +0 -280
@@ -1,4 +1,5 @@
1
- import { n as writeSpanAttributes, t as tracer } from "../../cloudflare-BldFV0Pa.js";
1
+ import { __DO_NOT_USE_WILL_BREAK__agentContext } from "../../internal_context.js";
2
+ import { r as writeSpanAttributes, t as tracer } from "../../cloudflare-BduZwmYK.js";
2
3
  import { AsyncLocalStorage } from "node:async_hooks";
3
4
  //#region src/observability/ai/read.ts
4
5
  /** Narrows an unknown value to a string. */
@@ -37,11 +38,11 @@ function readNestedTokenField(value, key) {
37
38
  const TraceAttribute = {
38
39
  Cloudflare: {
39
40
  AIGatewayLogID: "cloudflare.ai_gateway.log.id",
40
- CallID: "cloudflare.agents.call.id",
41
41
  IntegrationName: "cloudflare.agents.integration.name",
42
42
  MetadataPrefix: "cloudflare.agents.metadata.",
43
43
  OperationName: "cloudflare.agents.operation.name",
44
44
  ResponseFinishReason: "cloudflare.agents.response.finish_reason",
45
+ RuntimeContextPrefix: "cloudflare.agents.runtime_context.",
45
46
  ToolApprovalState: "cloudflare.agents.tool.approval.state",
46
47
  ToolCount: "cloudflare.agents.tool.count",
47
48
  TurnAdmission: "cloudflare.agents.turn.admission",
@@ -159,19 +160,24 @@ const CONSUMED_METADATA_KEYS = /* @__PURE__ */ new Set([
159
160
  "gen_ai.conversation.id"
160
161
  ]);
161
162
  /**
162
- * Projects the AI SDK's per-call `experimental_telemetry.metadata` onto root
163
- * span attributes: reserved keys map to their dedicated attributes, any other
164
- * SCALAR entry passes through as `cloudflare.agents.metadata.{key}`, and
165
- * object/array values are dropped (scalar-only attribute rule).
163
+ * Projects a per-call telemetry record onto root span attributes: reserved
164
+ * keys map to their dedicated attributes, any other SCALAR entry passes
165
+ * through under `passthroughPrefix`, and object/array values are dropped
166
+ * (scalar-only attribute rule).
167
+ *
168
+ * v6 passes `experimental_telemetry.metadata` and keeps the default prefix;
169
+ * v7 has no metadata option and passes the included subset of `runtimeContext`
170
+ * under its own prefix. Reserved keys land on the same attribute either way,
171
+ * so turn identity does not move namespace between SDK majors.
166
172
  */
167
- function metadataAttributes(metadata) {
173
+ function metadataAttributes(metadata, passthroughPrefix = TraceAttribute.Cloudflare.MetadataPrefix) {
168
174
  if (metadata === void 0) return {};
169
175
  const attributes = {};
170
176
  for (const [key, value] of Object.entries(metadata)) {
171
177
  if (CONSUMED_METADATA_KEYS.has(key)) continue;
172
178
  if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") continue;
173
179
  const reserved = Object.hasOwn(RESERVED_METADATA_ATTRIBUTES, key) ? RESERVED_METADATA_ATTRIBUTES[key] : void 0;
174
- attributes[reserved ?? `${TraceAttribute.Cloudflare.MetadataPrefix}${key}`] = value;
180
+ attributes[reserved ?? `${passthroughPrefix}${key}`] = value;
175
181
  }
176
182
  return attributes;
177
183
  }
@@ -188,7 +194,7 @@ function operationSpan(input) {
188
194
  return {
189
195
  attributes: {
190
196
  ...input.attributes,
191
- [TraceAttribute.Cloudflare.IntegrationName]: input.integration,
197
+ [TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
192
198
  [TraceAttribute.Cloudflare.OperationName]: input.operation,
193
199
  [TraceAttribute.GenAI.AgentID]: input.context?.agentId,
194
200
  [TraceAttribute.GenAI.AgentName]: input.context?.agentName,
@@ -206,7 +212,7 @@ function modelCallSpan(input) {
206
212
  return {
207
213
  attributes: {
208
214
  ...input.attributes,
209
- [TraceAttribute.Cloudflare.IntegrationName]: input.integration,
215
+ [TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
210
216
  [TraceAttribute.Cloudflare.OperationName]: input.operation,
211
217
  [TraceAttribute.GenAI.OperationName]: TraceAttribute.GenAI.OperationNameValueChat,
212
218
  [TraceAttribute.GenAI.ProviderName]: normalizeProviderName(input.provider),
@@ -233,7 +239,7 @@ function requestAttributes(request, model) {
233
239
  function toolCallSpan(input) {
234
240
  return {
235
241
  attributes: {
236
- [TraceAttribute.Cloudflare.IntegrationName]: input.integration,
242
+ [TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
237
243
  [TraceAttribute.Cloudflare.OperationName]: input.operation,
238
244
  [TraceAttribute.GenAI.OperationName]: TraceAttribute.GenAI.OperationNameValueExecuteTool,
239
245
  [TraceAttribute.GenAI.ToolCallID]: input.toolCallId,
@@ -284,14 +290,14 @@ function totalTokens(usage) {
284
290
  return usage?.inputTokens !== void 0 && usage.outputTokens !== void 0 ? usage.inputTokens + usage.outputTokens : void 0;
285
291
  }
286
292
  //#endregion
287
- //#region src/observability/ai/v6/extract.ts
293
+ //#region src/observability/ai/wrapper/extract.ts
288
294
  function finishAttributesFromResult(result, options = {}) {
289
295
  return finishAttributes({
290
296
  aiGatewayLogId: options.aiGatewayLogId,
291
- finishReason: extractFinishReason$1(result),
297
+ finishReason: extractFinishReason(result),
292
298
  response: options.includeResponse ? extractResponseInfo(result) : void 0,
293
299
  toolCallCount: extractToolCallCount(result),
294
- usage: extractAISDKv6TokenUsage(result)
300
+ usage: extractAISDKTokenUsage(result)
295
301
  });
296
302
  }
297
303
  function extractRequestSummary(params, operation) {
@@ -307,7 +313,7 @@ function extractRequestSummary(params, operation) {
307
313
  topP: readNumber(params.topP)
308
314
  };
309
315
  }
310
- /** Extracts model identity from an AI SDK v6 model object. */
316
+ /** Extracts model identity from an AI SDK model object. */
311
317
  function extractModelInfo(value) {
312
318
  if (typeof value === "string") return value.length > 0 ? {
313
319
  modelId: value,
@@ -324,13 +330,11 @@ function extractModelInfo(value) {
324
330
  };
325
331
  }
326
332
  /**
327
- * Extracts token usage from an AI SDK v6 result or stream chunk.
328
- *
329
- * AI SDK v6 exposes usage as `{ inputTokens, outputTokens, totalTokens }`
330
- * where `inputTokens`/`outputTokens` may be plain numbers or nested objects
331
- * like `{ total, cacheRead, cacheWrite }` / `{ total, reasoning }`.
333
+ * Extracts token usage from an AI SDK result or stream chunk across supported
334
+ * majors. Token counts may be plain numbers or nested objects such as
335
+ * `{ total, cacheRead, cacheWrite }` / `{ total, reasoning }`.
332
336
  */
333
- function extractAISDKv6TokenUsage(value) {
337
+ function extractAISDKTokenUsage(value) {
334
338
  if (typeof value !== "object" || value === null) return;
335
339
  const record = value;
336
340
  const raw = record.totalUsage ?? record.usage;
@@ -358,9 +362,9 @@ function extractToolCallCount(value) {
358
362
  return Array.isArray(toolCalls) && toolCalls.length > 0 ? toolCalls.length : void 0;
359
363
  }
360
364
  /**
361
- * Reads a finish reason from an AI SDK v6 result or `finish`-type stream chunk.
365
+ * Reads a finish reason from an AI SDK result or `finish`-type stream chunk.
362
366
  */
363
- function extractFinishReason$1(value) {
367
+ function extractFinishReason(value) {
364
368
  if (typeof value !== "object" || value === null) return;
365
369
  const finishReason = value.finishReason;
366
370
  if (typeof finishReason === "string") return finishReason;
@@ -798,14 +802,15 @@ function byteLength(value) {
798
802
  return new TextEncoder().encode(value).length;
799
803
  }
800
804
  //#endregion
801
- //#region src/observability/ai/v6/streams.ts
805
+ //#region src/observability/ai/wrapper/streams.ts
802
806
  function finishWhenStreamCompletes(result, span, options = {}) {
803
807
  return patchStreamFields(result, {
804
808
  onComplete: (summary) => {
805
809
  span.finish(finishAttributesFromStreamSummary(summary, options.includeResponse === true, options.includeAIGatewayLog === true, options.aiGatewayLogId, options.storeMessages === true));
806
810
  },
807
- onError: (cause, observedAIGatewayLogId) => {
811
+ onError: (cause, observedAIGatewayLogId, observed) => {
808
812
  if (options.includeAIGatewayLog) writeSpanAttributes(span, aiGatewayLogAttributes(observedAIGatewayLogId ?? extractAIGatewayLogId(cause) ?? options.aiGatewayLogId));
813
+ if (observed !== void 0) writeSpanAttributes(span, finishAttributesFromStreamSummary(observed, options.includeResponse === true, options.includeAIGatewayLog === true, options.aiGatewayLogId, options.storeMessages === true));
809
814
  span.fail(cause);
810
815
  }
811
816
  }, options.startedAtMs, options.includeAIGatewayLog ? options.aiGatewayLogId : void 0, options.storeMessages === true);
@@ -836,10 +841,10 @@ function patchStreamFields(result, hooks, startedAtMs, aiGatewayLogId, storeMess
836
841
  closed = true;
837
842
  hooks.onComplete(summary);
838
843
  };
839
- const errorOnce = (cause, observedAIGatewayLogId) => {
844
+ const errorOnce = (cause, observedAIGatewayLogId, observed) => {
840
845
  if (closed) return;
841
846
  closed = true;
842
- hooks.onError(cause, observedAIGatewayLogId);
847
+ hooks.onError(cause, observedAIGatewayLogId, observed);
843
848
  };
844
849
  try {
845
850
  if (isReadableStream(record.baseStream)) {
@@ -977,13 +982,23 @@ function createStreamState(hooks, startedAtMs, initialAIGatewayLogId, storeMessa
977
982
  let observedAbort = false;
978
983
  let firstChunkAtMs;
979
984
  const output = createStreamMessages();
985
+ /** What the stream reported before it stopped, complete or not. */
986
+ const observedSummary = () => streamSummaryFromParts({
987
+ aiGatewayLogId,
988
+ finishReason,
989
+ outputMessages: storeMessages ? output.messages(finishReason) : void 0,
990
+ response,
991
+ timeToFirstChunkSeconds: firstChunkAtMs === void 0 || startedAtMs === void 0 ? void 0 : (firstChunkAtMs - startedAtMs) / 1e3,
992
+ toolCallCount,
993
+ usage
994
+ });
980
995
  const settleObserved = () => {
981
996
  if (observedError) {
982
- hooks.onError(observedError.cause, aiGatewayLogId);
997
+ hooks.onError(observedError.cause, aiGatewayLogId, observedSummary());
983
998
  return true;
984
999
  }
985
1000
  if (observedAbort) {
986
- hooks.onError({ name: "AbortError" }, aiGatewayLogId);
1001
+ hooks.onError({ name: "AbortError" }, aiGatewayLogId, observedSummary());
987
1002
  return true;
988
1003
  }
989
1004
  return false;
@@ -1025,8 +1040,8 @@ function createStreamState(hooks, startedAtMs, initialAIGatewayLogId, storeMessa
1025
1040
  if (isErrorChunk(chunk)) observedError = { cause: chunk.error };
1026
1041
  if (isAbortChunk(chunk)) observedAbort = true;
1027
1042
  if (isToolCallChunk(chunk)) toolCallCount += 1;
1028
- finishReason = extractFinishReason$1(chunk) ?? finishReason;
1029
- usage = extractAISDKv6TokenUsage(chunk) ?? usage;
1043
+ finishReason = extractFinishReason(chunk) ?? finishReason;
1044
+ usage = extractAISDKTokenUsage(chunk) ?? usage;
1030
1045
  response = extractResponseInfo(chunk) ?? response;
1031
1046
  }
1032
1047
  };
@@ -1069,8 +1084,8 @@ function isAsyncIterable$1(value) {
1069
1084
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
1070
1085
  }
1071
1086
  //#endregion
1072
- //#region src/observability/ai/v6/model.ts
1073
- function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessages) {
1087
+ //#region src/observability/ai/wrapper/model.ts
1088
+ function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessages, boundToInvocation = false) {
1074
1089
  if (!wrapLanguageModel) return model;
1075
1090
  if (typeof model !== "object" || model === null) return model;
1076
1091
  const modelInfo = extractModelInfo(model);
@@ -1096,7 +1111,7 @@ function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessa
1096
1111
  recordAIGatewayLogOnError(modelCall, cause, aiGatewayLog.get());
1097
1112
  throw cause;
1098
1113
  }
1099
- });
1114
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1100
1115
  },
1101
1116
  wrapStream: async ({ doStream, params }) => {
1102
1117
  const span = modelCallSpanForModel("doStream", modelInfo, params, parentOperation, storeMessages);
@@ -1117,7 +1132,7 @@ function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessa
1117
1132
  modelCall.fail(cause);
1118
1133
  throw cause;
1119
1134
  }
1120
- });
1135
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1121
1136
  }
1122
1137
  }
1123
1138
  });
@@ -1128,7 +1143,6 @@ function recordAIGatewayLogOnError(span, cause, capturedLogId) {
1128
1143
  function modelCallSpanForModel(operation, model, params, parentOperation, storeMessages) {
1129
1144
  const record = typeof params === "object" && params !== null ? params : {};
1130
1145
  const span = modelCallSpan({
1131
- integration: "ai-sdk",
1132
1146
  model: model?.modelId,
1133
1147
  operation,
1134
1148
  provider: model?.provider,
@@ -1143,29 +1157,28 @@ function modelCallSpanForModel(operation, model, params, parentOperation, storeM
1143
1157
  };
1144
1158
  }
1145
1159
  //#endregion
1146
- //#region src/observability/ai/v6/tools.ts
1147
- function wrapTools(tracer, tools, storeTools) {
1160
+ //#region src/observability/ai/wrapper/tools.ts
1161
+ function wrapTools(tracer, tools, storeTools, boundToInvocation = false, approvedToolCalls) {
1148
1162
  if (typeof tools !== "object" || tools === null) return tools;
1149
1163
  const toolRecord = tools;
1150
1164
  const wrappedTools = {};
1151
- for (const [toolName, tool] of Object.entries(toolRecord)) wrappedTools[toolName] = wrapTool(tracer, toolName, tool, storeTools);
1165
+ for (const [toolName, tool] of Object.entries(toolRecord)) wrappedTools[toolName] = wrapTool(tracer, toolName, tool, storeTools, boundToInvocation, approvedToolCalls);
1152
1166
  return wrappedTools;
1153
1167
  }
1154
- function wrapTool(tracer, toolName, tool, storeTools) {
1168
+ function wrapTool(tracer, toolName, tool, storeTools, boundToInvocation, approvedToolCalls) {
1155
1169
  if (typeof tool !== "object" || tool === null) return tool;
1156
1170
  const toolRecord = tool;
1157
1171
  const hasExecute = typeof toolRecord.execute === "function";
1158
1172
  const hasApproval = typeof toolRecord.needsApproval === "boolean" || typeof toolRecord.needsApproval === "function";
1159
1173
  if (!hasExecute && !hasApproval) return tool;
1160
1174
  const wrappedTool = Object.assign(Object.create(Object.getPrototypeOf(tool)), tool);
1161
- if (hasApproval) wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName);
1175
+ if (hasApproval) wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName, boundToInvocation);
1162
1176
  if (!hasExecute) return wrappedTool;
1163
1177
  const execute = toolRecord.execute;
1164
1178
  if (typeof execute !== "function") return wrappedTool;
1165
1179
  const originalExecute = execute.bind(tool);
1166
1180
  wrappedTool.execute = (...args) => {
1167
1181
  const span = toolCallSpan({
1168
- integration: "ai-sdk",
1169
1182
  operation: "tool.execute",
1170
1183
  toolCallId: extractToolCallId(args[1]),
1171
1184
  toolName
@@ -1176,53 +1189,74 @@ function wrapTool(tracer, toolName, tool, storeTools) {
1176
1189
  };
1177
1190
  return tracer.openSpan(span.name, attributes, (toolSpan) => {
1178
1191
  const inSpanContext = AsyncLocalStorage.snapshot();
1179
- const approval = approvalResponseForOptions(args[1], extractToolCallId(args[1]));
1180
- if (approval?.approved === true) recordApprovalChild(tracer, toolName, approval.toolCallId, "approved");
1192
+ const toolCallId = extractToolCallId(args[1]);
1193
+ if (approvalResponseForOptions(args[1], toolCallId)?.approved === true || toolCallId !== void 0 && approvedToolCalls?.get(toolCallId) === toolName) {
1194
+ recordApprovalChild(tracer, toolName, toolCallId, "approved", boundToInvocation);
1195
+ if (toolCallId !== void 0) approvedToolCalls?.delete(toolCallId);
1196
+ }
1181
1197
  const result = originalExecute(...args);
1182
1198
  if (isPromiseLike(result)) return Promise.resolve(result).then((resolved) => settleToolResult(resolved, toolSpan, inSpanContext, storeTools), (cause) => {
1183
1199
  toolSpan.fail(cause);
1184
1200
  throw cause;
1185
1201
  });
1186
1202
  return settleToolResult(result, toolSpan, inSpanContext, storeTools);
1187
- });
1203
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1188
1204
  };
1189
1205
  return wrappedTool;
1190
1206
  }
1191
- function wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName) {
1207
+ function wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName, boundToInvocation) {
1192
1208
  const approval = toolRecord.needsApproval;
1193
1209
  const original = typeof approval === "function" ? approval.bind(tool) : void 0;
1194
1210
  wrappedTool.needsApproval = (...args) => {
1195
1211
  const result = original ? original(...args) : approval;
1196
1212
  const recordRequested = (needed) => {
1197
1213
  const toolCallId = extractToolCallId(args[1]);
1198
- if (needed === true && !hasApprovalResponse(args[1], toolCallId)) recordApprovalSegment(tracer, toolName, toolCallId, "requested");
1214
+ if (needed === true && !hasApprovalResponse(args[1], toolCallId)) recordApprovalSegment(tracer, toolName, toolCallId, "requested", boundToInvocation);
1199
1215
  return needed;
1200
1216
  };
1201
1217
  return isPromiseLike(result) ? Promise.resolve(result).then(recordRequested) : recordRequested(result);
1202
1218
  };
1203
1219
  }
1220
+ /** Instruments AI SDK v7's top-level tool approval policy. */
1221
+ function wrapToolApprovalPolicy(tracer, policy, approvedToolCalls, boundToInvocation = false) {
1222
+ if (typeof policy === "function") return (...args) => {
1223
+ const toolCall = recordValue(recordValue(args[0])?.toolCall);
1224
+ return observePolicyResult(policy(...args), readString(toolCall?.toolName) ?? "tool", readString(toolCall?.toolCallId), tracer, approvedToolCalls, boundToInvocation);
1225
+ };
1226
+ if (typeof policy !== "object" || policy === null) return policy;
1227
+ return Object.fromEntries(Object.entries(policy).map(([toolName, setting]) => [toolName, (...args) => observePolicyResult(typeof setting === "function" ? setting(...args) : setting, toolName, extractToolCallId(args[1]), tracer, approvedToolCalls, boundToInvocation)]));
1228
+ }
1229
+ function observePolicyResult(result, toolName, toolCallId, tracer, approvedToolCalls, boundToInvocation = false) {
1230
+ const observe = (status) => {
1231
+ if (toolCallId === void 0) return status;
1232
+ const type = typeof status === "string" ? status : readString(recordValue(status)?.type);
1233
+ if (type === "approved") approvedToolCalls.set(toolCallId, toolName);
1234
+ else if (type === "user-approval" || type === "denied") recordApprovalSegment(tracer, toolName, toolCallId, type === "denied" ? "denied" : "requested", boundToInvocation);
1235
+ return status;
1236
+ };
1237
+ return isPromiseLike(result) ? Promise.resolve(result).then(observe) : observe(result);
1238
+ }
1204
1239
  /** Records denied responses, whose tool never reaches execute(). */
1205
1240
  function recordDeniedApprovalResponses(tracer, messages) {
1206
1241
  for (const response of approvalResponses(messages)) if (!response.approved) recordApprovalSegment(tracer, response.toolName, response.toolCallId, "denied");
1207
1242
  }
1208
- function recordApprovalSegment(tracer, toolName, toolCallId, state) {
1243
+ function recordApprovalSegment(tracer, toolName, toolCallId, state, boundToInvocation = false) {
1209
1244
  const tool = toolCallSpan({
1210
- integration: "ai-sdk",
1211
1245
  operation: "tool.approval",
1212
1246
  toolCallId,
1213
1247
  toolName
1214
1248
  });
1215
1249
  tracer.withSpan(tool.name, tool.attributes, () => {
1216
- recordApprovalChild(tracer, toolName, toolCallId, state);
1217
- });
1250
+ recordApprovalChild(tracer, toolName, toolCallId, state, boundToInvocation);
1251
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1218
1252
  }
1219
- function recordApprovalChild(tracer, toolName, toolCallId, state) {
1253
+ function recordApprovalChild(tracer, toolName, toolCallId, state, boundToInvocation = false) {
1220
1254
  const approval = toolApprovalSpan({
1221
1255
  state,
1222
1256
  toolCallId,
1223
1257
  toolName
1224
1258
  });
1225
- tracer.withSpan(approval.name, approval.attributes, () => void 0);
1259
+ tracer.withSpan(approval.name, approval.attributes, () => void 0, boundToInvocation ? { boundToInvocation: true } : void 0);
1226
1260
  }
1227
1261
  function hasApprovalResponse(options, toolCallId) {
1228
1262
  return approvalResponseForOptions(options, toolCallId) !== void 0;
@@ -1321,16 +1355,19 @@ function extractToolCallId(options) {
1321
1355
  function isAsyncIterable(value) {
1322
1356
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
1323
1357
  }
1358
+ function recordValue(value) {
1359
+ return typeof value === "object" && value !== null ? value : void 0;
1360
+ }
1324
1361
  function isPromiseLike(value) {
1325
1362
  return value !== null && value !== void 0 && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
1326
1363
  }
1327
1364
  //#endregion
1328
- //#region src/observability/ai/v6/wrap.ts
1365
+ //#region src/observability/ai/wrapper/wrap.ts
1329
1366
  /**
1330
- * Wraps an AI SDK namespace object with v6 tracing while preserving its public
1331
- * shape and overloaded call signatures.
1367
+ * Wraps an AI SDK namespace object with tracing while preserving its public
1368
+ * shape and overloaded call signatures across supported majors.
1332
1369
  */
1333
- function createAISDKV6Wrapper(ai, instrumentation) {
1370
+ function createAISDKWrapper(ai, instrumentation) {
1334
1371
  const target = isModuleNamespace(ai) ? Object.setPrototypeOf({}, ai) : ai;
1335
1372
  const wrapperCache = /* @__PURE__ */ new Map();
1336
1373
  return new Proxy(target, { get(proxyTarget, property, receiver) {
@@ -1338,7 +1375,7 @@ function createAISDKV6Wrapper(ai, instrumentation) {
1338
1375
  if (isWrappedOperationName(property) && typeof original === "function") {
1339
1376
  let wrapper = wrapperCache.get(property);
1340
1377
  if (!wrapper) {
1341
- wrapper = createOperationWrapper(property, toAISDKV6Operation(original), readWrapLanguageModel(ai), instrumentation);
1378
+ wrapper = createOperationWrapper(property, toAISDKOperation(original), readWrapLanguageModel(ai), instrumentation);
1342
1379
  wrapperCache.set(property, wrapper);
1343
1380
  }
1344
1381
  return wrapper;
@@ -1351,7 +1388,7 @@ function readWrapLanguageModel(ai) {
1351
1388
  if (typeof value !== "function") return;
1352
1389
  return value;
1353
1390
  }
1354
- function toAISDKV6Operation(value) {
1391
+ function toAISDKOperation(value) {
1355
1392
  return value;
1356
1393
  }
1357
1394
  function isModuleNamespace(value) {
@@ -1371,7 +1408,8 @@ function createOperationWrapper(operationName, operation, wrapLanguageModel, ins
1371
1408
  storeMessages: instrumentation.options?.storeMessages === true,
1372
1409
  storeTools: instrumentation.options?.storeTools === true
1373
1410
  };
1374
- if (isStreamOperation$1(operationName)) return (params, ...args) => {
1411
+ if (isStreamOperation(operationName)) return (params, ...args) => {
1412
+ const boundToInvocation = isAISDKInvocationBounded(params);
1375
1413
  return instrumentation.tracer.openSpan(operationSpanName(agentNameForCall(params)), {}, (operationSpan) => {
1376
1414
  if (!operationSpan.isTraced) {
1377
1415
  operationSpan.finish();
@@ -1380,46 +1418,51 @@ function createOperationWrapper(operationName, operation, wrapLanguageModel, ins
1380
1418
  writeSpanAttributes(operationSpan, operationSpanForCall(operationName, extractModelInfo(params.model), params, instrumentation.options).attributes);
1381
1419
  recordDeniedApprovalResponses(instrumentation.tracer, params.messages);
1382
1420
  const startedAtMs = Date.now();
1383
- const result = operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage), ...args);
1421
+ const result = operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage, boundToInvocation), ...args);
1384
1422
  const hasModelSpan = canWrapModel(wrapLanguageModel, params.model);
1385
1423
  return finishWhenStreamCompletes(result, operationSpan, {
1386
1424
  includeResponse: !hasModelSpan,
1387
1425
  startedAtMs: hasModelSpan ? void 0 : startedAtMs
1388
1426
  });
1389
- });
1427
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1390
1428
  };
1391
1429
  return async (params, ...args) => {
1430
+ const boundToInvocation = isAISDKInvocationBounded(params);
1392
1431
  return instrumentation.tracer.withSpan(operationSpanName(agentNameForCall(params)), {}, async (operationSpan) => {
1393
1432
  if (!operationSpan.isTraced) return operation(params, ...args);
1394
1433
  writeSpanAttributes(operationSpan, operationSpanForCall(operationName, extractModelInfo(params.model), params, instrumentation.options).attributes);
1395
1434
  recordDeniedApprovalResponses(instrumentation.tracer, params.messages);
1396
- const result = await operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage), ...args);
1435
+ const result = await operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage, boundToInvocation), ...args);
1397
1436
  operationSpan.finish(finishAttributesFromResult(result, { includeResponse: !canWrapModel(wrapLanguageModel, params.model) }));
1398
1437
  return result;
1399
- });
1438
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1400
1439
  };
1401
1440
  }
1402
1441
  /**
1403
- * Reads only the agent name (metadata.agentName / gen_ai.agent.name /
1404
- * functionId) for the span name. `functionId` is the AI SDK's canonical
1405
- * projection to `gen_ai.agent.name`; an explicit metadata name takes priority.
1442
+ * Reads only the agent name for the span name, from the same sources and in
1443
+ * the same order as {@link semanticContext}, so the span name and
1444
+ * `gen_ai.agent.name` never disagree. `functionId` is the AI SDK's canonical
1445
+ * projection; an explicit name from v6 metadata or v7 runtime context wins.
1406
1446
  */
1407
1447
  function agentNameForCall(params) {
1408
- const telemetry = typeof params.experimental_telemetry === "object" && params.experimental_telemetry !== null ? params.experimental_telemetry : void 0;
1409
- const metadata = typeof telemetry?.metadata === "object" && telemetry.metadata !== null ? telemetry.metadata : void 0;
1410
- return readString(metadata?.agentName ?? metadata?.["gen_ai.agent.name"]) ?? readString(telemetry?.functionId);
1448
+ const telemetry = telemetryOptions(params);
1449
+ const metadata = telemetryMetadata(params);
1450
+ const runtimeContext = runtimeContextRecord(params);
1451
+ return metadataValue(metadata, "agentName", "gen_ai.agent.name") ?? metadataValue(runtimeContext, "agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId);
1411
1452
  }
1412
- function operationParamsForCall(params, operationName, wrapLanguageModel, tracer, storage) {
1453
+ function operationParamsForCall(params, operationName, wrapLanguageModel, tracer, storage, boundToInvocation = false) {
1454
+ const approvedToolCalls = /* @__PURE__ */ new Map();
1413
1455
  return {
1414
1456
  ...params,
1415
- ...shouldWrapTools(operationName) && params.tools !== void 0 ? { tools: wrapTools(tracer, params.tools, storage.storeTools) } : {},
1416
- ...params.model !== void 0 ? { model: wrapModel(tracer, wrapLanguageModel, params.model, operationName, storage.storeMessages) } : {}
1457
+ ...shouldWrapTools(operationName) && params.tools !== void 0 ? { tools: wrapTools(tracer, params.tools, storage.storeTools, boundToInvocation, approvedToolCalls) } : {},
1458
+ ...params.toolApproval !== void 0 ? { toolApproval: wrapToolApprovalPolicy(tracer, params.toolApproval, approvedToolCalls, boundToInvocation) } : {},
1459
+ ...params.model !== void 0 ? { model: wrapModel(tracer, wrapLanguageModel, params.model, operationName, storage.storeMessages, boundToInvocation) } : {}
1417
1460
  };
1418
1461
  }
1419
1462
  function canWrapModel(wrapLanguageModel, model) {
1420
1463
  return wrapLanguageModel !== void 0 && typeof model === "object" && model !== null;
1421
1464
  }
1422
- function isStreamOperation$1(operationName) {
1465
+ function isStreamOperation(operationName) {
1423
1466
  return operationName === "streamObject" || operationName === "streamText";
1424
1467
  }
1425
1468
  function shouldWrapTools(operationName) {
@@ -1431,11 +1474,11 @@ function isWrappedOperationName(value) {
1431
1474
  function operationSpanForCall(operation, model, params, options) {
1432
1475
  return operationSpan({
1433
1476
  attributes: {
1477
+ ...metadataAttributes(includedRuntimeContext(params), TraceAttribute.Cloudflare.RuntimeContextPrefix),
1434
1478
  ...metadataAttributes(telemetryMetadata(params)),
1435
1479
  ...contextAttributes(params, options)
1436
1480
  },
1437
1481
  context: semanticContext(params),
1438
- integration: "ai-sdk",
1439
1482
  model: model?.modelId,
1440
1483
  operation,
1441
1484
  provider: model?.provider,
@@ -1444,405 +1487,127 @@ function operationSpanForCall(operation, model, params, options) {
1444
1487
  }
1445
1488
  /** Reads the per-call `experimental_telemetry.metadata` record, if present. */
1446
1489
  function telemetryMetadata(params) {
1447
- const telemetry = typeof params.experimental_telemetry === "object" && params.experimental_telemetry !== null ? params.experimental_telemetry : void 0;
1490
+ const telemetry = telemetryOptions(params);
1448
1491
  return typeof telemetry?.metadata === "object" && telemetry.metadata !== null ? telemetry.metadata : void 0;
1449
1492
  }
1450
1493
  /**
1451
- * Reads agent/conversation semantic context from the AI SDK's own
1452
- * `experimental_telemetry` fields. The AI SDK maps `functionId` to
1453
- * `gen_ai.agent.name`; explicit `metadata.agentName` / `gen_ai.agent.name`
1454
- * takes priority. Other semantic fields come only from metadata.
1494
+ * The caller's application-data channel: `runtimeContext` on v7, the
1495
+ * `experimental_context` it replaced on v6. Read through one accessor so the
1496
+ * two majors cannot drift on which one identity and metadata come from.
1455
1497
  */
1456
- function semanticContext(params) {
1457
- const telemetry = typeof params.experimental_telemetry === "object" && params.experimental_telemetry !== null ? params.experimental_telemetry : void 0;
1458
- const metadata = typeof telemetry?.metadata === "object" && telemetry.metadata !== null ? telemetry.metadata : void 0;
1459
- return {
1460
- agentId: metadataValue$1(metadata, "agentId", "gen_ai.agent.id"),
1461
- agentName: metadataValue$1(metadata, "agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId),
1462
- agentVersion: metadataValue$1(metadata, "agentVersion", "gen_ai.agent.version"),
1463
- conversationId: metadataValue$1(metadata, "conversationId", "gen_ai.conversation.id")
1464
- };
1465
- }
1466
- function metadataValue$1(metadata, key, semanticKey) {
1467
- return readString(metadata?.[key] ?? metadata?.[semanticKey]);
1468
- }
1469
- function contextAttributes(params, options) {
1470
- const attributes = {};
1471
- const runtimeContext = typeof params.experimental_context === "object" && params.experimental_context !== null ? params.experimental_context : void 0;
1472
- for (const key of options?.includeRuntimeContext ?? []) {
1473
- const value = runtimeContext?.[key];
1474
- if (isScalarAttributeValue$1(value)) attributes[`cloudflare.agents.runtime_context.${key}`] = value;
1475
- }
1476
- return Object.keys(attributes).length > 0 ? attributes : void 0;
1477
- }
1478
- function isScalarAttributeValue$1(value) {
1479
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
1498
+ function runtimeContextRecord(params) {
1499
+ const value = params.runtimeContext ?? params.experimental_context;
1500
+ return typeof value === "object" && value !== null ? value : void 0;
1480
1501
  }
1481
- //#endregion
1482
- //#region src/observability/ai/v7/extract.ts
1483
- /** Extracts the safe operation name from an AI SDK v7 operation id. */
1484
- function operationNameFromId(operationId) {
1485
- const value = readString(operationId);
1486
- if (value === void 0) return "ai-sdk";
1487
- return value.startsWith("ai.") ? value.slice(3) : value;
1502
+ function telemetryOptions(params) {
1503
+ const telemetryValue = params.telemetry ?? params.experimental_telemetry;
1504
+ return typeof telemetryValue === "object" && telemetryValue !== null ? telemetryValue : void 0;
1488
1505
  }
1489
1506
  /**
1490
- * Extracts safe GenAI semantic context from an AI SDK v7 event. The AI SDK's
1491
- * canonical OpenTelemetry projection maps `functionId` to agent name. v7 has
1492
- * no telemetry metadata bag, so other identity fields come from the SDK-
1493
- * filtered runtime context only when the caller explicitly includes them.
1507
+ * The v7 stand-in for `experimental_telemetry.metadata`.
1508
+ *
1509
+ * v7 dropped `metadata` from its telemetry options; callers put the same
1510
+ * values in `runtimeContext` and mark the telemetry-visible ones through the
1511
+ * SDK's own `telemetry.includeRuntimeContext`. Running the included subset
1512
+ * through {@link metadataAttributes} is what keeps reserved keys — Think's
1513
+ * `cloudflare.agents.turn.*` above all — on the attribute names v6 emits, so
1514
+ * a query written against v6 traces still matches v7 ones. Everything else
1515
+ * passes through as documented, under `cloudflare.agents.runtime_context.*`.
1516
+ *
1517
+ * Runtime context the caller did not mark as included stays off the span as a
1518
+ * passthrough attribute: on v7 this is a general application-data channel, not
1519
+ * a telemetry bag. Identity (`agentId`, `agentName`, `agentVersion`,
1520
+ * `conversationId`) is separate — {@link semanticContext} reads it regardless,
1521
+ * because it names the operation rather than describing it, and v7 left
1522
+ * callers nowhere else to put it.
1494
1523
  */
1495
- function semanticContextFromEvent(event) {
1496
- const record = eventRecord(event);
1497
- const runtimeContext = typeof record.runtimeContext === "object" && record.runtimeContext !== null ? record.runtimeContext : void 0;
1498
- return {
1499
- agentId: metadataValue(runtimeContext, "agentId", "gen_ai.agent.id"),
1500
- agentName: metadataValue(runtimeContext, "agentName", "gen_ai.agent.name") ?? readString(record.functionId),
1501
- agentVersion: metadataValue(runtimeContext, "agentVersion", "gen_ai.agent.version"),
1502
- conversationId: metadataValue(runtimeContext, "conversationId", "gen_ai.conversation.id")
1503
- };
1524
+ function includedRuntimeContext(params) {
1525
+ const runtimeContext = runtimeContextRecord(params);
1526
+ if (runtimeContext === void 0) return;
1527
+ const included = includedContextKeys(telemetryOptions(params));
1528
+ if (included === void 0) return;
1529
+ const projected = {};
1530
+ for (const key of included) if (Object.hasOwn(runtimeContext, key)) projected[key] = runtimeContext[key];
1531
+ return projected;
1504
1532
  }
1505
- /** Extracts safe request settings from an AI SDK v7 event. */
1506
- function requestSummaryFromEvent(event, operationName) {
1507
- const record = eventRecord(event);
1508
- return {
1509
- frequencyPenalty: readNumber(record.frequencyPenalty),
1510
- maxTokens: readNumber(record.maxOutputTokens ?? record.maxTokens),
1511
- outputType: operationName === "generateObject" || operationName === "streamObject" ? "json" : "text",
1512
- presencePenalty: readNumber(record.presencePenalty),
1513
- seed: readNumber(record.seed),
1514
- stream: operationName === "streamText" || operationName === "streamObject",
1515
- temperature: readNumber(record.temperature),
1516
- topK: readNumber(record.topK),
1517
- topP: readNumber(record.topP)
1518
- };
1533
+ /**
1534
+ * The keys a caller marked as telemetry-visible.
1535
+ *
1536
+ * The SDK's own shape is `{ [key]: boolean }`, included only when explicitly
1537
+ * true; a plain array of key names is accepted too, since that is the shape of
1538
+ * the wrapper's option of the same name and silently ignoring it would be
1539
+ * worse than honouring it. There is deliberately no "include everything"
1540
+ * shorthand — runtime context routinely carries credentials and user data that
1541
+ * no one asked to put on a span.
1542
+ */
1543
+ function includedContextKeys(telemetry) {
1544
+ const included = telemetry?.includeRuntimeContext;
1545
+ if (Array.isArray(included)) return included.filter((key) => typeof key === "string");
1546
+ if (typeof included !== "object" || included === null) return;
1547
+ return Object.entries(included).filter(([, enabled]) => enabled === true).map(([key]) => key);
1519
1548
  }
1520
- /** Extracts safe finish attributes from an AI SDK v7 result-like event. */
1521
- function finishAttributesFromEvent(event, options = {}) {
1522
- const record = eventRecord(event);
1523
- return finishAttributes({
1524
- aiGatewayLogId: options.includeAIGatewayLog ? extractAIGatewayLogId(record) : void 0,
1525
- finishReason: extractFinishReason(record),
1526
- response: options.includeResponse ? responseSummaryFromEvent(record) : void 0,
1527
- timeToFirstChunkSeconds: options.includePerformance ? timeToFirstChunkSeconds(record) : void 0,
1528
- usage: tokenUsageFromEvent(record)
1529
- });
1549
+ /**
1550
+ * Whether the caller explicitly opted a key OUT. Identity is read from runtime
1551
+ * context without an opt-in — on v7 there is nowhere else to put it, and
1552
+ * requiring one would silently cost every caller `gen_ai.agent.id` — but an
1553
+ * explicit `false` is a stated intention and is honoured.
1554
+ */
1555
+ function isExcludedFromContext(params, key) {
1556
+ const included = telemetryOptions(params)?.includeRuntimeContext;
1557
+ return typeof included === "object" && included !== null && !Array.isArray(included) && included[key] === false;
1530
1558
  }
1531
- /** Builds correlation attributes for AI SDK v7 callback ids. */
1532
- function correlationAttributes(input) {
1559
+ /**
1560
+ * Reads agent/conversation semantic context from the AI SDK's own telemetry
1561
+ * fields. The AI SDK maps `functionId` to `gen_ai.agent.name`; an explicit
1562
+ * name takes priority. Each field comes from v6 `telemetry.metadata` or, on
1563
+ * v7 where that option no longer exists, from `runtimeContext`.
1564
+ */
1565
+ function semanticContext(params) {
1566
+ const telemetry = telemetryOptions(params);
1567
+ const metadata = telemetryMetadata(params);
1568
+ const runtimeContext = runtimeContextRecord(params);
1569
+ const fromContext = (key, semanticKey) => isExcludedFromContext(params, key) ? void 0 : metadataValue(runtimeContext, key, semanticKey);
1533
1570
  return {
1534
- [TraceAttribute.Cloudflare.CallID]: input.callId,
1535
- [TraceAttribute.GenAI.ToolCallID]: input.toolCallId
1571
+ agentId: metadataValue(metadata, "agentId", "gen_ai.agent.id") ?? fromContext("agentId", "gen_ai.agent.id"),
1572
+ agentName: metadataValue(metadata, "agentName", "gen_ai.agent.name") ?? fromContext("agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId),
1573
+ agentVersion: metadataValue(metadata, "agentVersion", "gen_ai.agent.version") ?? fromContext("agentVersion", "gen_ai.agent.version"),
1574
+ conversationId: metadataValue(metadata, "conversationId", "gen_ai.conversation.id") ?? fromContext("conversationId", "gen_ai.conversation.id")
1536
1575
  };
1537
1576
  }
1538
1577
  function metadataValue(metadata, key, semanticKey) {
1539
1578
  return readString(metadata?.[key] ?? metadata?.[semanticKey]);
1540
1579
  }
1541
- function eventRecord(event) {
1542
- return event;
1543
- }
1544
- function extractFinishReason(event) {
1545
- const finishReason = event.finishReason;
1546
- if (typeof finishReason === "string") return finishReason;
1547
- if (typeof finishReason === "object" && finishReason !== null) return readString(finishReason.unified);
1548
- }
1549
- function responseSummaryFromEvent(event) {
1550
- const response = typeof event.response === "object" && event.response !== null ? event.response : void 0;
1551
- const id = readString(event.responseId ?? response?.id);
1552
- const model = readString(event.responseModel ?? response?.modelId ?? response?.model);
1553
- if (id === void 0 && model === void 0) return;
1554
- return {
1555
- ...id !== void 0 ? { id } : {},
1556
- ...model !== void 0 ? { model } : {}
1557
- };
1558
- }
1559
- function tokenUsageFromEvent(event) {
1560
- const raw = event.totalUsage ?? event.usage;
1561
- if (typeof raw !== "object" || raw === null) return;
1562
- const usage = raw;
1563
- const inputTokens = readTokenCount(usage.inputTokens);
1564
- const outputTokens = readTokenCount(usage.outputTokens);
1565
- const cacheReadInputTokens = readNestedTokenField(usage.inputTokenDetails, "cacheReadTokens") ?? readNestedTokenField(usage.inputTokens, "cacheRead") ?? readNumber(usage.cachedInputTokens);
1566
- const cacheCreationInputTokens = readNestedTokenField(usage.inputTokenDetails, "cacheWriteTokens") ?? readNestedTokenField(usage.inputTokens, "cacheWrite");
1567
- const reasoningTokens = readNestedTokenField(usage.outputTokenDetails, "reasoningTokens") ?? readNestedTokenField(usage.outputTokens, "reasoning") ?? readNumber(usage.reasoningTokens);
1568
- if (inputTokens === void 0 && outputTokens === void 0 && cacheReadInputTokens === void 0 && cacheCreationInputTokens === void 0 && reasoningTokens === void 0) return;
1569
- return {
1570
- ...cacheCreationInputTokens !== void 0 ? { cacheCreationInputTokens } : {},
1571
- ...cacheReadInputTokens !== void 0 ? { cacheReadInputTokens } : {},
1572
- ...inputTokens !== void 0 ? { inputTokens } : {},
1573
- ...outputTokens !== void 0 ? { outputTokens } : {},
1574
- ...reasoningTokens !== void 0 ? { reasoningTokens } : {}
1575
- };
1576
- }
1577
- function timeToFirstChunkSeconds(event) {
1578
- const milliseconds = readNumber((typeof event.performance === "object" && event.performance !== null ? event.performance : void 0)?.timeToFirstOutputMs);
1579
- return milliseconds === void 0 ? void 0 : milliseconds / 1e3;
1580
- }
1581
- //#endregion
1582
- //#region src/observability/ai/v7/telemetry.ts
1583
1580
  /**
1584
- * Creates an AI SDK v7 `Telemetry` object that projects callback events into
1585
- * Cloudflare-compatible GenAI spans without recording raw prompts or outputs.
1581
+ * The wrapper-level allowlist, set once for the instrumentation rather than
1582
+ * per call. It selects from the same context and lands on the same attributes
1583
+ * as the SDK's per-call allowlist: selecting a key through both must produce
1584
+ * one attribute, not a canonical one and a `runtime_context.*` near-duplicate.
1586
1585
  */
1587
- function createAISDKV7Telemetry(instrumentation) {
1588
- const storeMessages = instrumentation.options?.storeMessages === true;
1589
- const storeTools = instrumentation.options?.storeTools === true;
1590
- const operations = /* @__PURE__ */ new Map();
1591
- const modelSpans = /* @__PURE__ */ new Map();
1592
- const toolSpans = /* @__PURE__ */ new Map();
1593
- const toolSpanKey = (callId, toolCallId) => `${callId}:${toolCallId}`;
1594
- const finishOperation = (event) => {
1595
- const state = operations.get(event.callId);
1596
- if (!state) return;
1597
- finishOpenModelSpans(event.callId, void 0, modelSpans, instrumentation.tracer);
1598
- finishOpenToolSpans(event.callId, void 0, toolSpans, instrumentation.tracer);
1599
- state.span.finish(finishAttributesFromEvent(event));
1600
- operations.delete(event.callId);
1601
- };
1602
- return {
1603
- onStart(event) {
1604
- const operationName = supportedOperationName(operationNameFromId(event.operationId));
1605
- if (!operationName) return;
1606
- const span = operationSpan({
1607
- attributes: {
1608
- ...correlationAttributes({ callId: event.callId }),
1609
- ...runtimeContextAttributes(event.runtimeContext)
1610
- },
1611
- context: semanticContextFromEvent(event),
1612
- integration: "ai-sdk",
1613
- model: readString(event.modelId),
1614
- operation: operationName,
1615
- provider: readString(event.provider),
1616
- request: requestSummaryFromEvent(event, operationName)
1617
- });
1618
- const operation = instrumentation.tracer.openSpan(span.name, span.attributes, (activeSpan) => activeSpan);
1619
- operations.set(event.callId, {
1620
- callId: event.callId,
1621
- operationName,
1622
- span: operation
1623
- });
1624
- },
1625
- onLanguageModelCallStart(event) {
1626
- const state = operations.get(event.callId);
1627
- if (!state) return;
1628
- const span = modelCallSpan({
1629
- attributes: {
1630
- ...correlationAttributes({ callId: event.callId }),
1631
- ...inputMessageAttributes(event, storeMessages)
1632
- },
1633
- integration: "ai-sdk",
1634
- model: readString(event.modelId),
1635
- operation: isStreamOperation(state.operationName) ? "doStream" : "doGenerate",
1636
- provider: readString(event.provider),
1637
- request: requestSummaryFromEvent(event, state.operationName)
1638
- });
1639
- const spans = modelSpans.get(event.callId) ?? [];
1640
- spans.push({ spanSpec: span });
1641
- modelSpans.set(event.callId, spans);
1642
- },
1643
- onLanguageModelCallEnd(event) {
1644
- const state = shiftModelSpan(modelSpans, event.callId);
1645
- if (!state) return;
1646
- (state.span ?? instrumentation.tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (activeSpan) => activeSpan)).finish({
1647
- ...finishAttributesFromEvent(event, {
1648
- includeAIGatewayLog: true,
1649
- includePerformance: true,
1650
- includeResponse: true
1651
- }),
1652
- ...outputMessageAttributes(event, storeMessages)
1653
- });
1654
- },
1655
- onToolExecutionStart(event) {
1656
- const toolCallId = readString(event.toolCall.toolCallId);
1657
- if (toolCallId === void 0 || !operations.has(event.callId)) return;
1658
- const toolName = readString(event.toolCall.toolName) ?? "tool";
1659
- const span = toolCallSpan({
1660
- integration: "ai-sdk",
1661
- operation: "tool.execute",
1662
- toolName
1663
- });
1664
- toolSpans.set(toolSpanKey(event.callId, toolCallId), {
1665
- callId: event.callId,
1666
- spanSpec: {
1667
- name: span.name,
1668
- attributes: {
1669
- ...span.attributes,
1670
- ...correlationAttributes({
1671
- callId: event.callId,
1672
- toolCallId
1673
- }),
1674
- ...toolInputAttributes(event.toolCall.input, storeTools),
1675
- ...toolContextAttributes(toolName, event.toolContext)
1676
- }
1677
- }
1678
- });
1679
- },
1680
- onToolExecutionEnd(event) {
1681
- const toolCallId = readString(event.toolCall.toolCallId);
1682
- if (toolCallId === void 0) return;
1683
- const state = toolSpans.get(toolSpanKey(event.callId, toolCallId));
1684
- if (!state) return;
1685
- const span = state.span ?? instrumentation.tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (activeSpan) => activeSpan);
1686
- if (event.toolOutput?.type === "tool-error") span.fail(event.toolOutput.error);
1687
- else span.finish(toolOutputAttributes(event.toolOutput?.output, storeTools));
1688
- toolSpans.delete(toolSpanKey(event.callId, toolCallId));
1689
- },
1690
- onAbort(event) {
1691
- const cause = { name: "AbortError" };
1692
- finishOpenModelSpans(event.callId, cause, modelSpans, instrumentation.tracer);
1693
- finishOpenToolSpans(event.callId, cause, toolSpans, instrumentation.tracer);
1694
- const state = operations.get(event.callId);
1695
- if (!state) return;
1696
- state.span.fail(cause);
1697
- operations.delete(event.callId);
1698
- },
1699
- onEnd: finishOperation,
1700
- onError(event) {
1701
- const errorEvent = eventObject(event);
1702
- const callId = readString(errorEvent.callId);
1703
- if (callId === void 0) return;
1704
- const cause = errorEvent.error ?? event;
1705
- finishOpenModelSpans(callId, cause, modelSpans, instrumentation.tracer);
1706
- finishOpenToolSpans(callId, cause, toolSpans, instrumentation.tracer);
1707
- const state = operations.get(callId);
1708
- if (!state) return;
1709
- state.span.fail(cause);
1710
- operations.delete(callId);
1711
- },
1712
- executeLanguageModelCall(options) {
1713
- const state = modelSpans.get(options.callId)?.find((candidate) => candidate.span === void 0);
1714
- if (!state) return options.execute();
1715
- return instrumentation.tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (span) => {
1716
- state.span = span;
1717
- try {
1718
- return Promise.resolve(options.execute()).catch((cause) => {
1719
- writeSpanAttributes(span, aiGatewayLogAttributes(extractAIGatewayLogId(cause)));
1720
- span.fail(cause);
1721
- removeModelState(modelSpans, options.callId, state);
1722
- throw cause;
1723
- });
1724
- } catch (cause) {
1725
- writeSpanAttributes(span, aiGatewayLogAttributes(extractAIGatewayLogId(cause)));
1726
- span.fail(cause);
1727
- removeModelState(modelSpans, options.callId, state);
1728
- throw cause;
1729
- }
1730
- });
1731
- },
1732
- executeTool(options) {
1733
- const state = toolSpans.get(toolSpanKey(options.callId, options.toolCallId));
1734
- if (!state) return options.execute();
1735
- return instrumentation.tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (span) => {
1736
- state.span = span;
1737
- try {
1738
- return Promise.resolve(options.execute()).catch((cause) => {
1739
- span.fail(cause);
1740
- toolSpans.delete(toolSpanKey(options.callId, options.toolCallId));
1741
- throw cause;
1742
- });
1743
- } catch (cause) {
1744
- span.fail(cause);
1745
- toolSpans.delete(toolSpanKey(options.callId, options.toolCallId));
1746
- throw cause;
1747
- }
1748
- });
1749
- }
1750
- };
1751
- }
1752
- function supportedOperationName(operationName) {
1753
- if (operationName === "generateObject" || operationName === "generateText" || operationName === "streamObject" || operationName === "streamText") return operationName;
1754
- }
1755
- function isStreamOperation(operationName) {
1756
- return operationName === "streamObject" || operationName === "streamText";
1757
- }
1758
- function shiftModelSpan(spansByCallId, callId) {
1759
- const spans = spansByCallId.get(callId);
1760
- const span = spans?.shift();
1761
- if (spans && spans.length === 0) spansByCallId.delete(callId);
1762
- return span;
1763
- }
1764
- function removeModelState(statesByCallId, callId, state) {
1765
- const states = statesByCallId.get(callId);
1766
- if (!states) return;
1767
- const index = states.indexOf(state);
1768
- if (index !== -1) states.splice(index, 1);
1769
- if (states.length === 0) statesByCallId.delete(callId);
1770
- }
1771
- function finishOpenModelSpans(callId, cause, spansByCallId, tracer) {
1772
- const states = spansByCallId.get(callId);
1773
- if (!states) return;
1774
- for (const state of states) {
1775
- const span = state.span ?? tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (activeSpan) => activeSpan);
1776
- if (cause === void 0) span.finish();
1777
- else span.fail(cause);
1778
- }
1779
- spansByCallId.delete(callId);
1780
- }
1781
- function finishOpenToolSpans(callId, cause, spansByToolCallId, tracer) {
1782
- for (const [toolCallId, state] of spansByToolCallId) {
1783
- if (state.callId !== callId) continue;
1784
- const span = state.span ?? tracer.openSpan(state.spanSpec.name, state.spanSpec.attributes, (activeSpan) => activeSpan);
1785
- if (cause === void 0) span.finish();
1786
- else span.fail(cause);
1787
- spansByToolCallId.delete(toolCallId);
1788
- }
1789
- }
1790
- function eventObject(event) {
1791
- return typeof event === "object" && event !== null ? event : {};
1792
- }
1793
- const SEMANTIC_CONTEXT_KEYS = /* @__PURE__ */ new Set([
1794
- "agentId",
1795
- "agentName",
1796
- "agentVersion",
1797
- "conversationId",
1798
- "gen_ai.agent.id",
1799
- "gen_ai.agent.name",
1800
- "gen_ai.agent.version",
1801
- "gen_ai.conversation.id"
1802
- ]);
1803
- function runtimeContextAttributes(runtimeContextValue) {
1804
- const attributes = {};
1805
- const runtimeContext = recordValue(runtimeContextValue);
1806
- for (const [key, value] of Object.entries(runtimeContext ?? {})) if (!SEMANTIC_CONTEXT_KEYS.has(key) && isScalarAttributeValue(value)) attributes[key.startsWith("cloudflare.agents.") ? key : `cloudflare.agents.runtime_context.${key}`] = value;
1807
- return attributes;
1808
- }
1809
- function toolContextAttributes(toolName, toolContextValue) {
1810
- const attributes = {};
1811
- const toolContext = recordValue(toolContextValue);
1812
- for (const [key, value] of Object.entries(toolContext ?? {})) if (isScalarAttributeValue(value)) attributes[`cloudflare.agents.tool_context.${toolName}.${key}`] = value;
1813
- return attributes;
1814
- }
1815
- function recordValue(value) {
1816
- return typeof value === "object" && value !== null ? value : void 0;
1817
- }
1818
- function isScalarAttributeValue(value) {
1819
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
1586
+ function contextAttributes(params, options) {
1587
+ const included = options?.includeRuntimeContext;
1588
+ if (included === void 0 || included.length === 0) return;
1589
+ const runtimeContext = runtimeContextRecord(params);
1590
+ if (runtimeContext === void 0) return;
1591
+ const projected = {};
1592
+ for (const key of included) if (Object.hasOwn(runtimeContext, key)) projected[key] = runtimeContext[key];
1593
+ return metadataAttributes(projected, TraceAttribute.Cloudflare.RuntimeContextPrefix);
1594
+ }
1595
+ const invocationBounded = Symbol.for("cloudflare.agents.ai-sdk.invocation-bounded");
1596
+ function isAISDKInvocationBounded(params) {
1597
+ return params[invocationBounded] === true || __DO_NOT_USE_WILL_BREAK__agentContext.getStore()?.connection !== void 0;
1820
1598
  }
1821
1599
  //#endregion
1822
1600
  //#region src/observability/ai/index.ts
1823
- const agentsAISDKTelemetryBrand = Symbol.for("cloudflare.agents.ai-sdk-telemetry");
1824
1601
  /**
1825
1602
  * Wraps an AI SDK namespace with tracing.
1826
1603
  */
1827
1604
  function wrapAISDK(ai, options = {}) {
1828
- return createAISDKV6Wrapper(ai, {
1829
- options,
1830
- tracer
1831
- });
1832
- }
1833
- /**
1834
- * Creates an AI SDK v7 telemetry adapter for use with `registerTelemetry` or
1835
- * per-call telemetry configuration.
1836
- */
1837
- function createAISDKTelemetry(options = {}) {
1838
- const telemetry = createAISDKV7Telemetry({
1605
+ return createAISDKWrapper(ai, {
1839
1606
  options,
1840
1607
  tracer
1841
1608
  });
1842
- Object.defineProperty(telemetry, agentsAISDKTelemetryBrand, { value: true });
1843
- return telemetry;
1844
1609
  }
1845
1610
  //#endregion
1846
- export { createAISDKTelemetry, wrapAISDK };
1611
+ export { wrapAISDK };
1847
1612
 
1848
1613
  //# sourceMappingURL=index.js.map