agents 0.20.0 → 0.20.1

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.
@@ -1,2 +1,2 @@
1
- import { n as getMcpAuthContext, t as createStatelessMcpHandler } from "../handler-stateless-8hQN_kC3.js";
1
+ import { n as getMcpAuthContext, t as createStatelessMcpHandler } from "../handler-stateless-CIkKPETH.js";
2
2
  export { createStatelessMcpHandler as createMcpHandler, getMcpAuthContext };
@@ -6,7 +6,15 @@ type AISDKStorageOptions = {
6
6
  };
7
7
  /** Instrumentation options for the AI SDK v6 adapter. */
8
8
  type AISDKInstrumentationOptions = AISDKStorageOptions & {
9
- /** AI SDK v6 `experimental_context` keys to emit as scalar attributes. */ readonly includeRuntimeContext?: readonly string[];
9
+ /**
10
+ * Context keys to emit as `cloudflare.agents.runtime_context.{key}`, set
11
+ * once for the wrapper rather than per call.
12
+ *
13
+ * Distinct from the AI SDK's own per-call `telemetry.includeRuntimeContext`,
14
+ * which shares the name but selects keys that map onto the canonical
15
+ * `cloudflare.agents.turn.*` / `cloudflare.agents.metadata.*` attributes.
16
+ */
17
+ readonly includeRuntimeContext?: readonly string[];
10
18
  };
11
19
  //#endregion
12
20
  //#region src/observability/ai/v7/types.d.ts
@@ -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. */
@@ -42,6 +43,7 @@ const TraceAttribute = {
42
43
  MetadataPrefix: "cloudflare.agents.metadata.",
43
44
  OperationName: "cloudflare.agents.operation.name",
44
45
  ResponseFinishReason: "cloudflare.agents.response.finish_reason",
46
+ RuntimeContextPrefix: "cloudflare.agents.runtime_context.",
45
47
  ToolApprovalState: "cloudflare.agents.tool.approval.state",
46
48
  ToolCount: "cloudflare.agents.tool.count",
47
49
  TurnAdmission: "cloudflare.agents.turn.admission",
@@ -159,19 +161,24 @@ const CONSUMED_METADATA_KEYS = /* @__PURE__ */ new Set([
159
161
  "gen_ai.conversation.id"
160
162
  ]);
161
163
  /**
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).
164
+ * Projects a per-call telemetry record onto root span attributes: reserved
165
+ * keys map to their dedicated attributes, any other SCALAR entry passes
166
+ * through under `passthroughPrefix`, and object/array values are dropped
167
+ * (scalar-only attribute rule).
168
+ *
169
+ * v6 passes `experimental_telemetry.metadata` and keeps the default prefix;
170
+ * v7 has no metadata option and passes the included subset of `runtimeContext`
171
+ * under its own prefix. Reserved keys land on the same attribute either way,
172
+ * so turn identity does not move namespace between SDK majors.
166
173
  */
167
- function metadataAttributes(metadata) {
174
+ function metadataAttributes(metadata, passthroughPrefix = TraceAttribute.Cloudflare.MetadataPrefix) {
168
175
  if (metadata === void 0) return {};
169
176
  const attributes = {};
170
177
  for (const [key, value] of Object.entries(metadata)) {
171
178
  if (CONSUMED_METADATA_KEYS.has(key)) continue;
172
179
  if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") continue;
173
180
  const reserved = Object.hasOwn(RESERVED_METADATA_ATTRIBUTES, key) ? RESERVED_METADATA_ATTRIBUTES[key] : void 0;
174
- attributes[reserved ?? `${TraceAttribute.Cloudflare.MetadataPrefix}${key}`] = value;
181
+ attributes[reserved ?? `${passthroughPrefix}${key}`] = value;
175
182
  }
176
183
  return attributes;
177
184
  }
@@ -804,8 +811,9 @@ function finishWhenStreamCompletes(result, span, options = {}) {
804
811
  onComplete: (summary) => {
805
812
  span.finish(finishAttributesFromStreamSummary(summary, options.includeResponse === true, options.includeAIGatewayLog === true, options.aiGatewayLogId, options.storeMessages === true));
806
813
  },
807
- onError: (cause, observedAIGatewayLogId) => {
814
+ onError: (cause, observedAIGatewayLogId, observed) => {
808
815
  if (options.includeAIGatewayLog) writeSpanAttributes(span, aiGatewayLogAttributes(observedAIGatewayLogId ?? extractAIGatewayLogId(cause) ?? options.aiGatewayLogId));
816
+ if (observed !== void 0) writeSpanAttributes(span, finishAttributesFromStreamSummary(observed, options.includeResponse === true, options.includeAIGatewayLog === true, options.aiGatewayLogId, options.storeMessages === true));
809
817
  span.fail(cause);
810
818
  }
811
819
  }, options.startedAtMs, options.includeAIGatewayLog ? options.aiGatewayLogId : void 0, options.storeMessages === true);
@@ -836,10 +844,10 @@ function patchStreamFields(result, hooks, startedAtMs, aiGatewayLogId, storeMess
836
844
  closed = true;
837
845
  hooks.onComplete(summary);
838
846
  };
839
- const errorOnce = (cause, observedAIGatewayLogId) => {
847
+ const errorOnce = (cause, observedAIGatewayLogId, observed) => {
840
848
  if (closed) return;
841
849
  closed = true;
842
- hooks.onError(cause, observedAIGatewayLogId);
850
+ hooks.onError(cause, observedAIGatewayLogId, observed);
843
851
  };
844
852
  try {
845
853
  if (isReadableStream(record.baseStream)) {
@@ -977,13 +985,23 @@ function createStreamState(hooks, startedAtMs, initialAIGatewayLogId, storeMessa
977
985
  let observedAbort = false;
978
986
  let firstChunkAtMs;
979
987
  const output = createStreamMessages();
988
+ /** What the stream reported before it stopped, complete or not. */
989
+ const observedSummary = () => streamSummaryFromParts({
990
+ aiGatewayLogId,
991
+ finishReason,
992
+ outputMessages: storeMessages ? output.messages(finishReason) : void 0,
993
+ response,
994
+ timeToFirstChunkSeconds: firstChunkAtMs === void 0 || startedAtMs === void 0 ? void 0 : (firstChunkAtMs - startedAtMs) / 1e3,
995
+ toolCallCount,
996
+ usage
997
+ });
980
998
  const settleObserved = () => {
981
999
  if (observedError) {
982
- hooks.onError(observedError.cause, aiGatewayLogId);
1000
+ hooks.onError(observedError.cause, aiGatewayLogId, observedSummary());
983
1001
  return true;
984
1002
  }
985
1003
  if (observedAbort) {
986
- hooks.onError({ name: "AbortError" }, aiGatewayLogId);
1004
+ hooks.onError({ name: "AbortError" }, aiGatewayLogId, observedSummary());
987
1005
  return true;
988
1006
  }
989
1007
  return false;
@@ -1070,7 +1088,7 @@ function isAsyncIterable$1(value) {
1070
1088
  }
1071
1089
  //#endregion
1072
1090
  //#region src/observability/ai/v6/model.ts
1073
- function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessages) {
1091
+ function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessages, boundToInvocation = false) {
1074
1092
  if (!wrapLanguageModel) return model;
1075
1093
  if (typeof model !== "object" || model === null) return model;
1076
1094
  const modelInfo = extractModelInfo(model);
@@ -1096,7 +1114,7 @@ function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessa
1096
1114
  recordAIGatewayLogOnError(modelCall, cause, aiGatewayLog.get());
1097
1115
  throw cause;
1098
1116
  }
1099
- });
1117
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1100
1118
  },
1101
1119
  wrapStream: async ({ doStream, params }) => {
1102
1120
  const span = modelCallSpanForModel("doStream", modelInfo, params, parentOperation, storeMessages);
@@ -1117,7 +1135,7 @@ function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessa
1117
1135
  modelCall.fail(cause);
1118
1136
  throw cause;
1119
1137
  }
1120
- });
1138
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1121
1139
  }
1122
1140
  }
1123
1141
  });
@@ -1144,21 +1162,21 @@ function modelCallSpanForModel(operation, model, params, parentOperation, storeM
1144
1162
  }
1145
1163
  //#endregion
1146
1164
  //#region src/observability/ai/v6/tools.ts
1147
- function wrapTools(tracer, tools, storeTools) {
1165
+ function wrapTools(tracer, tools, storeTools, boundToInvocation = false, approvedToolCalls) {
1148
1166
  if (typeof tools !== "object" || tools === null) return tools;
1149
1167
  const toolRecord = tools;
1150
1168
  const wrappedTools = {};
1151
- for (const [toolName, tool] of Object.entries(toolRecord)) wrappedTools[toolName] = wrapTool(tracer, toolName, tool, storeTools);
1169
+ for (const [toolName, tool] of Object.entries(toolRecord)) wrappedTools[toolName] = wrapTool(tracer, toolName, tool, storeTools, boundToInvocation, approvedToolCalls);
1152
1170
  return wrappedTools;
1153
1171
  }
1154
- function wrapTool(tracer, toolName, tool, storeTools) {
1172
+ function wrapTool(tracer, toolName, tool, storeTools, boundToInvocation, approvedToolCalls) {
1155
1173
  if (typeof tool !== "object" || tool === null) return tool;
1156
1174
  const toolRecord = tool;
1157
1175
  const hasExecute = typeof toolRecord.execute === "function";
1158
1176
  const hasApproval = typeof toolRecord.needsApproval === "boolean" || typeof toolRecord.needsApproval === "function";
1159
1177
  if (!hasExecute && !hasApproval) return tool;
1160
1178
  const wrappedTool = Object.assign(Object.create(Object.getPrototypeOf(tool)), tool);
1161
- if (hasApproval) wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName);
1179
+ if (hasApproval) wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName, boundToInvocation);
1162
1180
  if (!hasExecute) return wrappedTool;
1163
1181
  const execute = toolRecord.execute;
1164
1182
  if (typeof execute !== "function") return wrappedTool;
@@ -1176,36 +1194,58 @@ function wrapTool(tracer, toolName, tool, storeTools) {
1176
1194
  };
1177
1195
  return tracer.openSpan(span.name, attributes, (toolSpan) => {
1178
1196
  const inSpanContext = AsyncLocalStorage.snapshot();
1179
- const approval = approvalResponseForOptions(args[1], extractToolCallId(args[1]));
1180
- if (approval?.approved === true) recordApprovalChild(tracer, toolName, approval.toolCallId, "approved");
1197
+ const toolCallId = extractToolCallId(args[1]);
1198
+ if (approvalResponseForOptions(args[1], toolCallId)?.approved === true || toolCallId !== void 0 && approvedToolCalls?.get(toolCallId) === toolName) {
1199
+ recordApprovalChild(tracer, toolName, toolCallId, "approved", boundToInvocation);
1200
+ if (toolCallId !== void 0) approvedToolCalls?.delete(toolCallId);
1201
+ }
1181
1202
  const result = originalExecute(...args);
1182
1203
  if (isPromiseLike(result)) return Promise.resolve(result).then((resolved) => settleToolResult(resolved, toolSpan, inSpanContext, storeTools), (cause) => {
1183
1204
  toolSpan.fail(cause);
1184
1205
  throw cause;
1185
1206
  });
1186
1207
  return settleToolResult(result, toolSpan, inSpanContext, storeTools);
1187
- });
1208
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1188
1209
  };
1189
1210
  return wrappedTool;
1190
1211
  }
1191
- function wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName) {
1212
+ function wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName, boundToInvocation) {
1192
1213
  const approval = toolRecord.needsApproval;
1193
1214
  const original = typeof approval === "function" ? approval.bind(tool) : void 0;
1194
1215
  wrappedTool.needsApproval = (...args) => {
1195
1216
  const result = original ? original(...args) : approval;
1196
1217
  const recordRequested = (needed) => {
1197
1218
  const toolCallId = extractToolCallId(args[1]);
1198
- if (needed === true && !hasApprovalResponse(args[1], toolCallId)) recordApprovalSegment(tracer, toolName, toolCallId, "requested");
1219
+ if (needed === true && !hasApprovalResponse(args[1], toolCallId)) recordApprovalSegment(tracer, toolName, toolCallId, "requested", boundToInvocation);
1199
1220
  return needed;
1200
1221
  };
1201
1222
  return isPromiseLike(result) ? Promise.resolve(result).then(recordRequested) : recordRequested(result);
1202
1223
  };
1203
1224
  }
1225
+ /** Instruments AI SDK v7's top-level tool approval policy. */
1226
+ function wrapToolApprovalPolicy(tracer, policy, approvedToolCalls, boundToInvocation = false) {
1227
+ if (typeof policy === "function") return (...args) => {
1228
+ const toolCall = recordValue$1(recordValue$1(args[0])?.toolCall);
1229
+ return observePolicyResult(policy(...args), readString(toolCall?.toolName) ?? "tool", readString(toolCall?.toolCallId), tracer, approvedToolCalls, boundToInvocation);
1230
+ };
1231
+ if (typeof policy !== "object" || policy === null) return policy;
1232
+ 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)]));
1233
+ }
1234
+ function observePolicyResult(result, toolName, toolCallId, tracer, approvedToolCalls, boundToInvocation = false) {
1235
+ const observe = (status) => {
1236
+ if (toolCallId === void 0) return status;
1237
+ const type = typeof status === "string" ? status : readString(recordValue$1(status)?.type);
1238
+ if (type === "approved") approvedToolCalls.set(toolCallId, toolName);
1239
+ else if (type === "user-approval" || type === "denied") recordApprovalSegment(tracer, toolName, toolCallId, type === "denied" ? "denied" : "requested", boundToInvocation);
1240
+ return status;
1241
+ };
1242
+ return isPromiseLike(result) ? Promise.resolve(result).then(observe) : observe(result);
1243
+ }
1204
1244
  /** Records denied responses, whose tool never reaches execute(). */
1205
1245
  function recordDeniedApprovalResponses(tracer, messages) {
1206
1246
  for (const response of approvalResponses(messages)) if (!response.approved) recordApprovalSegment(tracer, response.toolName, response.toolCallId, "denied");
1207
1247
  }
1208
- function recordApprovalSegment(tracer, toolName, toolCallId, state) {
1248
+ function recordApprovalSegment(tracer, toolName, toolCallId, state, boundToInvocation = false) {
1209
1249
  const tool = toolCallSpan({
1210
1250
  integration: "ai-sdk",
1211
1251
  operation: "tool.approval",
@@ -1213,16 +1253,16 @@ function recordApprovalSegment(tracer, toolName, toolCallId, state) {
1213
1253
  toolName
1214
1254
  });
1215
1255
  tracer.withSpan(tool.name, tool.attributes, () => {
1216
- recordApprovalChild(tracer, toolName, toolCallId, state);
1217
- });
1256
+ recordApprovalChild(tracer, toolName, toolCallId, state, boundToInvocation);
1257
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1218
1258
  }
1219
- function recordApprovalChild(tracer, toolName, toolCallId, state) {
1259
+ function recordApprovalChild(tracer, toolName, toolCallId, state, boundToInvocation = false) {
1220
1260
  const approval = toolApprovalSpan({
1221
1261
  state,
1222
1262
  toolCallId,
1223
1263
  toolName
1224
1264
  });
1225
- tracer.withSpan(approval.name, approval.attributes, () => void 0);
1265
+ tracer.withSpan(approval.name, approval.attributes, () => void 0, boundToInvocation ? { boundToInvocation: true } : void 0);
1226
1266
  }
1227
1267
  function hasApprovalResponse(options, toolCallId) {
1228
1268
  return approvalResponseForOptions(options, toolCallId) !== void 0;
@@ -1321,6 +1361,9 @@ function extractToolCallId(options) {
1321
1361
  function isAsyncIterable(value) {
1322
1362
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
1323
1363
  }
1364
+ function recordValue$1(value) {
1365
+ return typeof value === "object" && value !== null ? value : void 0;
1366
+ }
1324
1367
  function isPromiseLike(value) {
1325
1368
  return value !== null && value !== void 0 && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
1326
1369
  }
@@ -1372,6 +1415,7 @@ function createOperationWrapper(operationName, operation, wrapLanguageModel, ins
1372
1415
  storeTools: instrumentation.options?.storeTools === true
1373
1416
  };
1374
1417
  if (isStreamOperation$1(operationName)) return (params, ...args) => {
1418
+ const boundToInvocation = isAISDKInvocationBounded(params);
1375
1419
  return instrumentation.tracer.openSpan(operationSpanName(agentNameForCall(params)), {}, (operationSpan) => {
1376
1420
  if (!operationSpan.isTraced) {
1377
1421
  operationSpan.finish();
@@ -1380,40 +1424,45 @@ function createOperationWrapper(operationName, operation, wrapLanguageModel, ins
1380
1424
  writeSpanAttributes(operationSpan, operationSpanForCall(operationName, extractModelInfo(params.model), params, instrumentation.options).attributes);
1381
1425
  recordDeniedApprovalResponses(instrumentation.tracer, params.messages);
1382
1426
  const startedAtMs = Date.now();
1383
- const result = operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage), ...args);
1427
+ const result = operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage, boundToInvocation), ...args);
1384
1428
  const hasModelSpan = canWrapModel(wrapLanguageModel, params.model);
1385
1429
  return finishWhenStreamCompletes(result, operationSpan, {
1386
1430
  includeResponse: !hasModelSpan,
1387
1431
  startedAtMs: hasModelSpan ? void 0 : startedAtMs
1388
1432
  });
1389
- });
1433
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1390
1434
  };
1391
1435
  return async (params, ...args) => {
1436
+ const boundToInvocation = isAISDKInvocationBounded(params);
1392
1437
  return instrumentation.tracer.withSpan(operationSpanName(agentNameForCall(params)), {}, async (operationSpan) => {
1393
1438
  if (!operationSpan.isTraced) return operation(params, ...args);
1394
1439
  writeSpanAttributes(operationSpan, operationSpanForCall(operationName, extractModelInfo(params.model), params, instrumentation.options).attributes);
1395
1440
  recordDeniedApprovalResponses(instrumentation.tracer, params.messages);
1396
- const result = await operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage), ...args);
1441
+ const result = await operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage, boundToInvocation), ...args);
1397
1442
  operationSpan.finish(finishAttributesFromResult(result, { includeResponse: !canWrapModel(wrapLanguageModel, params.model) }));
1398
1443
  return result;
1399
- });
1444
+ }, boundToInvocation ? { boundToInvocation: true } : void 0);
1400
1445
  };
1401
1446
  }
1402
1447
  /**
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.
1448
+ * Reads only the agent name for the span name, from the same sources and in
1449
+ * the same order as {@link semanticContext}, so the span name and
1450
+ * `gen_ai.agent.name` never disagree. `functionId` is the AI SDK's canonical
1451
+ * projection; an explicit name from v6 metadata or v7 runtime context wins.
1406
1452
  */
1407
1453
  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);
1454
+ const telemetry = telemetryOptions(params);
1455
+ const metadata = telemetryMetadata(params);
1456
+ const runtimeContext = runtimeContextRecord(params);
1457
+ return metadataValue$1(metadata, "agentName", "gen_ai.agent.name") ?? metadataValue$1(runtimeContext, "agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId);
1411
1458
  }
1412
- function operationParamsForCall(params, operationName, wrapLanguageModel, tracer, storage) {
1459
+ function operationParamsForCall(params, operationName, wrapLanguageModel, tracer, storage, boundToInvocation = false) {
1460
+ const approvedToolCalls = /* @__PURE__ */ new Map();
1413
1461
  return {
1414
1462
  ...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) } : {}
1463
+ ...shouldWrapTools(operationName) && params.tools !== void 0 ? { tools: wrapTools(tracer, params.tools, storage.storeTools, boundToInvocation, approvedToolCalls) } : {},
1464
+ ...params.toolApproval !== void 0 ? { toolApproval: wrapToolApprovalPolicy(tracer, params.toolApproval, approvedToolCalls) } : {},
1465
+ ...params.model !== void 0 ? { model: wrapModel(tracer, wrapLanguageModel, params.model, operationName, storage.storeMessages, boundToInvocation) } : {}
1417
1466
  };
1418
1467
  }
1419
1468
  function canWrapModel(wrapLanguageModel, model) {
@@ -1431,6 +1480,7 @@ function isWrappedOperationName(value) {
1431
1480
  function operationSpanForCall(operation, model, params, options) {
1432
1481
  return operationSpan({
1433
1482
  attributes: {
1483
+ ...metadataAttributes(includedRuntimeContext(params), TraceAttribute.Cloudflare.RuntimeContextPrefix),
1434
1484
  ...metadataAttributes(telemetryMetadata(params)),
1435
1485
  ...contextAttributes(params, options)
1436
1486
  },
@@ -1444,39 +1494,114 @@ function operationSpanForCall(operation, model, params, options) {
1444
1494
  }
1445
1495
  /** Reads the per-call `experimental_telemetry.metadata` record, if present. */
1446
1496
  function telemetryMetadata(params) {
1447
- const telemetry = typeof params.experimental_telemetry === "object" && params.experimental_telemetry !== null ? params.experimental_telemetry : void 0;
1497
+ const telemetry = telemetryOptions(params);
1448
1498
  return typeof telemetry?.metadata === "object" && telemetry.metadata !== null ? telemetry.metadata : void 0;
1449
1499
  }
1450
1500
  /**
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.
1501
+ * The caller's application-data channel: `runtimeContext` on v7, the
1502
+ * `experimental_context` it replaced on v6. Read through one accessor so the
1503
+ * two majors cannot drift on which one identity and metadata come from.
1504
+ */
1505
+ function runtimeContextRecord(params) {
1506
+ const value = params.runtimeContext ?? params.experimental_context;
1507
+ return typeof value === "object" && value !== null ? value : void 0;
1508
+ }
1509
+ function telemetryOptions(params) {
1510
+ const telemetryValue = params.telemetry ?? params.experimental_telemetry;
1511
+ return typeof telemetryValue === "object" && telemetryValue !== null ? telemetryValue : void 0;
1512
+ }
1513
+ /**
1514
+ * The v7 stand-in for `experimental_telemetry.metadata`.
1515
+ *
1516
+ * v7 dropped `metadata` from its telemetry options; callers put the same
1517
+ * values in `runtimeContext` and mark the telemetry-visible ones through the
1518
+ * SDK's own `telemetry.includeRuntimeContext`. Running the included subset
1519
+ * through {@link metadataAttributes} is what keeps reserved keys — Think's
1520
+ * `cloudflare.agents.turn.*` above all — on the attribute names v6 emits, so
1521
+ * a query written against v6 traces still matches v7 ones. Everything else
1522
+ * passes through as documented, under `cloudflare.agents.runtime_context.*`.
1523
+ *
1524
+ * Runtime context the caller did not mark as included stays off the span as a
1525
+ * passthrough attribute: on v7 this is a general application-data channel, not
1526
+ * a telemetry bag. Identity (`agentId`, `agentName`, `agentVersion`,
1527
+ * `conversationId`) is separate — {@link semanticContext} reads it regardless,
1528
+ * because it names the operation rather than describing it, and v7 left
1529
+ * callers nowhere else to put it.
1530
+ */
1531
+ function includedRuntimeContext(params) {
1532
+ const runtimeContext = runtimeContextRecord(params);
1533
+ if (runtimeContext === void 0) return;
1534
+ const included = includedContextKeys(telemetryOptions(params));
1535
+ if (included === void 0) return;
1536
+ const projected = {};
1537
+ for (const key of included) if (Object.hasOwn(runtimeContext, key)) projected[key] = runtimeContext[key];
1538
+ return projected;
1539
+ }
1540
+ /**
1541
+ * The keys a caller marked as telemetry-visible.
1542
+ *
1543
+ * The SDK's own shape is `{ [key]: boolean }`, included only when explicitly
1544
+ * true; a plain array of key names is accepted too, since that is the shape of
1545
+ * the wrapper's option of the same name and silently ignoring it would be
1546
+ * worse than honouring it. There is deliberately no "include everything"
1547
+ * shorthand — runtime context routinely carries credentials and user data that
1548
+ * no one asked to put on a span.
1549
+ */
1550
+ function includedContextKeys(telemetry) {
1551
+ const included = telemetry?.includeRuntimeContext;
1552
+ if (Array.isArray(included)) return included.filter((key) => typeof key === "string");
1553
+ if (typeof included !== "object" || included === null) return;
1554
+ return Object.entries(included).filter(([, enabled]) => enabled === true).map(([key]) => key);
1555
+ }
1556
+ /**
1557
+ * Whether the caller explicitly opted a key OUT. Identity is read from runtime
1558
+ * context without an opt-in — on v7 there is nowhere else to put it, and
1559
+ * requiring one would silently cost every caller `gen_ai.agent.id` — but an
1560
+ * explicit `false` is a stated intention and is honoured.
1561
+ */
1562
+ function isExcludedFromContext(params, key) {
1563
+ const included = telemetryOptions(params)?.includeRuntimeContext;
1564
+ return typeof included === "object" && included !== null && !Array.isArray(included) && included[key] === false;
1565
+ }
1566
+ /**
1567
+ * Reads agent/conversation semantic context from the AI SDK's own telemetry
1568
+ * fields. The AI SDK maps `functionId` to `gen_ai.agent.name`; an explicit
1569
+ * name takes priority. Each field comes from v6 `telemetry.metadata` or, on
1570
+ * v7 where that option no longer exists, from `runtimeContext`.
1455
1571
  */
1456
1572
  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;
1573
+ const telemetry = telemetryOptions(params);
1574
+ const metadata = telemetryMetadata(params);
1575
+ const runtimeContext = runtimeContextRecord(params);
1576
+ const fromContext = (key, semanticKey) => isExcludedFromContext(params, key) ? void 0 : metadataValue$1(runtimeContext, key, semanticKey);
1459
1577
  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")
1578
+ agentId: metadataValue$1(metadata, "agentId", "gen_ai.agent.id") ?? fromContext("agentId", "gen_ai.agent.id"),
1579
+ agentName: metadataValue$1(metadata, "agentName", "gen_ai.agent.name") ?? fromContext("agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId),
1580
+ agentVersion: metadataValue$1(metadata, "agentVersion", "gen_ai.agent.version") ?? fromContext("agentVersion", "gen_ai.agent.version"),
1581
+ conversationId: metadataValue$1(metadata, "conversationId", "gen_ai.conversation.id") ?? fromContext("conversationId", "gen_ai.conversation.id")
1464
1582
  };
1465
1583
  }
1466
1584
  function metadataValue$1(metadata, key, semanticKey) {
1467
1585
  return readString(metadata?.[key] ?? metadata?.[semanticKey]);
1468
1586
  }
1587
+ /**
1588
+ * The wrapper-level allowlist, set once for the instrumentation rather than
1589
+ * per call. It selects from the same context and lands on the same attributes
1590
+ * as the SDK's per-call allowlist: selecting a key through both must produce
1591
+ * one attribute, not a canonical one and a `runtime_context.*` near-duplicate.
1592
+ */
1469
1593
  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";
1594
+ const included = options?.includeRuntimeContext;
1595
+ if (included === void 0 || included.length === 0) return;
1596
+ const runtimeContext = runtimeContextRecord(params);
1597
+ if (runtimeContext === void 0) return;
1598
+ const projected = {};
1599
+ for (const key of included) if (Object.hasOwn(runtimeContext, key)) projected[key] = runtimeContext[key];
1600
+ return metadataAttributes(projected, TraceAttribute.Cloudflare.RuntimeContextPrefix);
1601
+ }
1602
+ const invocationBounded = Symbol.for("cloudflare.agents.ai-sdk.invocation-bounded");
1603
+ function isAISDKInvocationBounded(params) {
1604
+ return params[invocationBounded] === true || __DO_NOT_USE_WILL_BREAK__agentContext.getStore()?.connection !== void 0;
1480
1605
  }
1481
1606
  //#endregion
1482
1607
  //#region src/observability/ai/v7/extract.ts