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.
- package/dist/{agent-tool-types-Btk9ETS-.d.ts → agent-tool-types-BC-WFlsz.d.ts} +30 -45
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-UuScsJg3.d.ts → agent-tools-DeHe9Xov.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/chat/index.d.ts +2 -2
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/cloudflare-BduZwmYK.js +204 -0
- package/dist/cloudflare-BduZwmYK.js.map +1 -0
- package/dist/{handler-stateless-8hQN_kC3.js → handler-stateless-CIkKPETH.js} +20 -69
- package/dist/handler-stateless-CIkKPETH.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +27 -18
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +32 -98
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/observability/ai/index.d.ts +9 -1
- package/dist/observability/ai/index.js +188 -63
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sub-routing.d.ts +1 -1
- package/dist/workflows.d.ts +1 -1
- package/docs/mcp-client.md +1 -1
- package/docs/mcp-servers.md +1 -1
- package/docs/observability.md +2 -1
- package/package.json +7 -7
- package/dist/cloudflare-BldFV0Pa.js +0 -117
- package/dist/cloudflare-BldFV0Pa.js.map +0 -1
- package/dist/handler-stateless-8hQN_kC3.js.map +0 -1
package/dist/mcp/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as getMcpAuthContext, t as createStatelessMcpHandler } from "../handler-stateless-
|
|
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
|
-
/**
|
|
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 {
|
|
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
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
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 ?? `${
|
|
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
|
|
1180
|
-
if (
|
|
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
|
|
1404
|
-
*
|
|
1405
|
-
*
|
|
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 =
|
|
1409
|
-
const metadata =
|
|
1410
|
-
|
|
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.
|
|
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 =
|
|
1497
|
+
const telemetry = telemetryOptions(params);
|
|
1448
1498
|
return typeof telemetry?.metadata === "object" && telemetry.metadata !== null ? telemetry.metadata : void 0;
|
|
1449
1499
|
}
|
|
1450
1500
|
/**
|
|
1451
|
-
*
|
|
1452
|
-
* `
|
|
1453
|
-
*
|
|
1454
|
-
|
|
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 =
|
|
1458
|
-
const metadata =
|
|
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
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
return
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
|
|
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
|