@sentry/server-utils
Advanced tools
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const core = require('@sentry/core'); | ||
| const vercelAiDcSubscriber = require('./vercel-ai-dc-subscriber.js'); | ||
| const dc = require('node:diagnostics_channel'); | ||
| const _vercelAiIntegration = ((options = {}) => { | ||
| return { | ||
| name: "VercelAI", | ||
| setupOnce() { | ||
| if (!dc.tracingChannel) { | ||
| return; | ||
| } | ||
| void Promise.resolve().then(() => vercelAiDcSubscriber.subscribeVercelAiTracingChannel(dc.tracingChannel, options)); | ||
| } | ||
| }; | ||
| }); | ||
| const vercelAiIntegration = core.defineIntegration(_vercelAiIntegration); | ||
| exports.vercelAiIntegration = vercelAiIntegration; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../src/vercel-ai/index.ts"],"sourcesContent":["import { defineIntegration, type IntegrationFn } from '@sentry/core';\nimport { subscribeVercelAiTracingChannel } from './vercel-ai-dc-subscriber';\nimport * as dc from 'node:diagnostics_channel';\n\ntype VercelAiOptions = {\n /**\n * Enable or disable input recording. Enabled if `dataCollection.genAI.inputs` (or the deprecated `sendDefaultPii` option) is `true`\n * or if you set `isEnabled` to `true` in your ai SDK method telemetry settings.\n * Integration-level options take precedence over global `dataCollection` config.\n */\n recordInputs?: boolean;\n\n /**\n * Enable or disable output recording. Enabled if `dataCollection.genAI.outputs` (or the deprecated `sendDefaultPii` option) is `true`\n * or if you set `isEnabled` to `true` in your ai SDK method telemetry settings.\n * Integration-level options take precedence over global `dataCollection` config.\n */\n recordOutputs?: boolean;\n\n /**\n * Enable or disable truncation of recorded input messages.\n * Defaults to `true`.\n */\n enableTruncation?: boolean;\n};\n\nconst _vercelAiIntegration = ((options: VercelAiOptions = {}) => {\n return {\n name: 'VercelAI' as const,\n setupOnce() {\n // Bail if this is not available\n if (!dc.tracingChannel) {\n return;\n }\n\n // Subscribe to the `ai` SDK's native telemetry tracing channel (ai >= 7).\n // This is a no-op on versions that don't publish to the channel, so it is always safe to call.\n // The factory needs the Sentry OTel context manager, which `initOpenTelemetry()` registers after `setupOnce`, so defer a tick.\n // Options are passed in here rather than read back off the integration per event.\n void Promise.resolve().then(() => subscribeVercelAiTracingChannel(dc.tracingChannel, options));\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the `ai` SDK's native telemetry tracing channel (ai >= 7).\n */\nexport const vercelAiIntegration = defineIntegration(_vercelAiIntegration);\n"],"names":["subscribeVercelAiTracingChannel","defineIntegration"],"mappings":";;;;;;AA0BA,MAAM,oBAAA,IAAwB,CAAC,OAAA,GAA2B,EAAC,KAAM;AAC/D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,UAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,GAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAMA,MAAA,KAAK,OAAA,CAAQ,SAAQ,CAAE,IAAA,CAAK,MAAMA,oDAAA,CAAgC,EAAA,CAAG,cAAA,EAAgB,OAAO,CAAC,CAAA;AAAA,IAC/F;AAAA,GACF;AACF,CAAA,CAAA;AAKO,MAAM,mBAAA,GAAsBC,uBAAkB,oBAAoB;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const attributes = require('@sentry/conventions/attributes'); | ||
| const op = require('@sentry/conventions/op'); | ||
| const core = require('@sentry/core'); | ||
| const tracingChannel = require('../tracing-channel.js'); | ||
| const AI_SDK_TELEMETRY_TRACING_CHANNEL = "ai:telemetry"; | ||
| const ORIGIN = "auto.vercelai.channel"; | ||
| const GEN_AI_TOOL_CALL_ID_ATTRIBUTE = "gen_ai.tool.call.id"; | ||
| const GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE = "gen_ai.tool.description"; | ||
| const GEN_AI_EMBEDDINGS_OPERATION = "embeddings"; | ||
| const GEN_AI_RERANK_OPERATION = "rerank"; | ||
| const GEN_AI_GENERATE_CONTENT_OPERATION = "generate_content"; | ||
| const VERCEL_AI_OPERATION_ID_ATTRIBUTE = "vercel.ai.operationId"; | ||
| const VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE = "vercel.ai.model.provider"; | ||
| const VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = "vercel.ai.settings.maxRetries"; | ||
| const operationIdByCallId = /* @__PURE__ */ new Map(); | ||
| const toolDescriptionsByCallId = /* @__PURE__ */ new Map(); | ||
| const ROOT_OPERATION_TYPES = /* @__PURE__ */ new Set(["generateText", "streamText", "embed", "rerank"]); | ||
| function clearOperationId(data) { | ||
| if (!ROOT_OPERATION_TYPES.has(data.type)) { | ||
| return; | ||
| } | ||
| const callId = asString(data.event.callId); | ||
| if (callId) { | ||
| operationIdByCallId.delete(callId); | ||
| toolDescriptionsByCallId.delete(callId); | ||
| } | ||
| } | ||
| function recordToolDescriptions(callId, tools) { | ||
| if (!callId || !Array.isArray(tools)) { | ||
| return; | ||
| } | ||
| let descriptions = toolDescriptionsByCallId.get(callId); | ||
| for (const tool of tools) { | ||
| if (isRecord(tool) && typeof tool.name === "string" && typeof tool.description === "string") { | ||
| descriptions = descriptions ?? /* @__PURE__ */ new Map(); | ||
| if (!descriptions.has(tool.name)) { | ||
| descriptions.set(tool.name, tool.description); | ||
| } | ||
| } | ||
| } | ||
| if (descriptions) { | ||
| toolDescriptionsByCallId.set(callId, descriptions); | ||
| } | ||
| } | ||
| function resolveToolDescription(callId, toolName, tools) { | ||
| const fromMap = callId ? toolDescriptionsByCallId.get(callId)?.get(toolName) : void 0; | ||
| if (fromMap) { | ||
| return fromMap; | ||
| } | ||
| if (Array.isArray(tools)) { | ||
| const match = tools.find((tool) => isRecord(tool) && tool.name === toolName); | ||
| return isRecord(match) ? asString(match.description) : void 0; | ||
| } | ||
| if (isRecord(tools)) { | ||
| const tool = tools[toolName]; | ||
| return isRecord(tool) ? asString(tool.description) : void 0; | ||
| } | ||
| return void 0; | ||
| } | ||
| let subscribed = false; | ||
| function subscribeVercelAiTracingChannel(tracingChannel$1, options = {}) { | ||
| if (subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| tracingChannel.bindTracingChannelToSpan( | ||
| tracingChannel$1(AI_SDK_TELEMETRY_TRACING_CHANNEL), | ||
| (data) => createSpanFromMessage(data, options), | ||
| { | ||
| // The helper ends the span; we enrich it from the settled result first (tokens, output messages, | ||
| // finish reasons, response model/id, provider metadata) and drop the per-operation `callId` maps. | ||
| beforeSpanEnd: (span, data) => { | ||
| enrichSpanOnEnd(span, data, options); | ||
| clearOperationId(data); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function createSpanFromMessage(data, channelOptions) { | ||
| const { type, event } = data; | ||
| if (type === "step" || !event || typeof event !== "object") { | ||
| return void 0; | ||
| } | ||
| const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions); | ||
| const provider = asString(event.provider); | ||
| const modelId = asString(event.modelId); | ||
| const callId = asString(event.callId); | ||
| const maxRetries = asNumber(event.maxRetries); | ||
| if (recordInputs) { | ||
| recordToolDescriptions(callId, event.tools); | ||
| } | ||
| const baseAttributes = { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| ...provider ? { [attributes.GEN_AI_SYSTEM]: provider, [VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE]: provider } : {}, | ||
| ...modelId ? { [attributes.GEN_AI_REQUEST_MODEL]: modelId } : {}, | ||
| ...maxRetries !== void 0 ? { [VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE]: maxRetries } : {} | ||
| }; | ||
| switch (type) { | ||
| case "generateText": | ||
| case "streamText": | ||
| return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === "streamText"); | ||
| case "languageModelCall": | ||
| return buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId); | ||
| case "executeTool": | ||
| return buildToolSpan(event, recordInputs); | ||
| case "embed": | ||
| return startGenAiSpan(GEN_AI_EMBEDDINGS_OPERATION, modelId, { | ||
| ...baseAttributes, | ||
| ...recordInputs && event.value !== void 0 ? { [attributes.GEN_AI_EMBEDDINGS_INPUT]: safeStringify(event.value) } : {} | ||
| }); | ||
| case "rerank": | ||
| return startGenAiSpan(GEN_AI_RERANK_OPERATION, modelId, baseAttributes); | ||
| default: | ||
| return void 0; | ||
| } | ||
| } | ||
| function startGenAiSpan(operation, suffix, attributes$1) { | ||
| return core.startInactiveSpan({ | ||
| name: suffix ? `${operation} ${suffix}` : operation, | ||
| op: `gen_ai.${operation}`, | ||
| attributes: { [attributes.GEN_AI_OPERATION_NAME]: operation, ...attributes$1 } | ||
| }); | ||
| } | ||
| function buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, isStream) { | ||
| const functionId = asString(event.functionId); | ||
| const operationId = asString(event.operationId) ?? (isStream ? "ai.streamText" : "ai.generateText"); | ||
| if (callId) { | ||
| operationIdByCallId.set(callId, { operationId, isStream }); | ||
| } | ||
| return startGenAiSpan(op.GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, { | ||
| ...baseAttributes, | ||
| [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId, | ||
| [attributes.GEN_AI_RESPONSE_STREAMING]: isStream, | ||
| ...functionId ? { [attributes.GEN_AI_FUNCTION_ID]: functionId } : {}, | ||
| ...recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {} | ||
| }); | ||
| } | ||
| function buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId) { | ||
| const parent = callId ? operationIdByCallId.get(callId) : void 0; | ||
| const operationId = parent ? `${parent.operationId}.${parent.isStream ? "doStream" : "doGenerate"}` : "ai.generateText.doGenerate"; | ||
| return startGenAiSpan(GEN_AI_GENERATE_CONTENT_OPERATION, modelId, { | ||
| ...baseAttributes, | ||
| [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId, | ||
| ...recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}, | ||
| ...recordInputs && Array.isArray(event.tools) ? { [attributes.GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) } : {} | ||
| }); | ||
| } | ||
| function buildToolSpan(event, recordInputs) { | ||
| const toolCall = isRecord(event.toolCall) ? event.toolCall : {}; | ||
| const toolName = asString(toolCall.toolName); | ||
| const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId); | ||
| const toolInput = toolCall.input ?? toolCall.args; | ||
| const description = recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : void 0; | ||
| return startGenAiSpan(op.GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, { | ||
| [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [attributes.GEN_AI_TOOL_TYPE]: "function", | ||
| ...toolName ? { [attributes.GEN_AI_TOOL_NAME]: toolName } : {}, | ||
| ...toolCallId ? { [GEN_AI_TOOL_CALL_ID_ATTRIBUTE]: toolCallId } : {}, | ||
| ...description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}, | ||
| ...recordInputs && toolInput !== void 0 ? { [attributes.GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {} | ||
| }); | ||
| } | ||
| function enrichSpanOnEnd(span, data, channelOptions) { | ||
| const { type, result } = data; | ||
| if (!isRecord(result)) { | ||
| return; | ||
| } | ||
| const { recordOutputs } = getRecordingOptions(data.event, channelOptions); | ||
| if (type === "executeTool") { | ||
| if (recordOutputs) { | ||
| span.setAttribute(attributes.GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result)); | ||
| } | ||
| const output = isRecord(result.output) ? result.output : void 0; | ||
| if (output?.type === "tool-error") { | ||
| captureToolError(span, data, output.error); | ||
| } | ||
| return; | ||
| } | ||
| const usage = isRecord(result.usage) ? result.usage : void 0; | ||
| if (usage) { | ||
| const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.tokens); | ||
| const outputTokens = tokenCount(usage.outputTokens); | ||
| const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens); | ||
| if (inputTokens !== void 0) { | ||
| span.setAttribute(attributes.GEN_AI_USAGE_INPUT_TOKENS, inputTokens); | ||
| } | ||
| if (outputTokens !== void 0) { | ||
| span.setAttribute(attributes.GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens); | ||
| } | ||
| if (totalTokens !== void 0) { | ||
| span.setAttribute(attributes.GEN_AI_USAGE_TOTAL_TOKENS, totalTokens); | ||
| } | ||
| } | ||
| const finishReason = getFinishReason(result); | ||
| if (finishReason && type === "languageModelCall") { | ||
| span.setAttribute(attributes.GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason])); | ||
| } | ||
| const response = isRecord(result.response) ? result.response : void 0; | ||
| const responseId = asString(response?.id) ?? asString(result.responseId); | ||
| if (responseId) { | ||
| span.setAttribute(attributes.GEN_AI_RESPONSE_ID, responseId); | ||
| } | ||
| const responseModel = asString(response?.modelId) ?? asString(data.event.modelId); | ||
| if (responseModel) { | ||
| span.setAttribute(attributes.GEN_AI_RESPONSE_MODEL, responseModel); | ||
| } | ||
| const providerMetadata = result.providerMetadata; | ||
| const providerAttributes = core.getProviderMetadataAttributes(providerMetadata); | ||
| if (core.GEN_AI_CONVERSATION_ID_ATTRIBUTE in providerAttributes && core.spanToJSON(span).data[core.GEN_AI_CONVERSATION_ID_ATTRIBUTE]) { | ||
| delete providerAttributes[core.GEN_AI_CONVERSATION_ID_ATTRIBUTE]; | ||
| } | ||
| span.setAttributes(providerAttributes); | ||
| if (recordOutputs) { | ||
| const parts = type === "languageModelCall" && Array.isArray(result.content) ? partsFromContent(result.content) : partsFromTextAndToolCalls(result.text, result.toolCalls); | ||
| const outputMessages = buildOutputMessages(parts, finishReason); | ||
| if (outputMessages) { | ||
| span.setAttribute(attributes.GEN_AI_OUTPUT_MESSAGES, outputMessages); | ||
| } | ||
| } | ||
| } | ||
| function normalizeFinishReason(finishReason) { | ||
| return finishReason === "tool-calls" ? "tool_call" : finishReason ?? "stop"; | ||
| } | ||
| function getFinishReason(result) { | ||
| const finishReason = result.finishReason; | ||
| if (typeof finishReason === "string") { | ||
| return finishReason; | ||
| } | ||
| return isRecord(finishReason) ? asString(finishReason.unified) : void 0; | ||
| } | ||
| function tokenCount(value) { | ||
| return asNumber(value) ?? (isRecord(value) ? asNumber(value.total) : void 0); | ||
| } | ||
| function buildOutputMessages(parts, finishReason) { | ||
| if (!parts.length) { | ||
| return void 0; | ||
| } | ||
| return safeStringify([{ role: "assistant", parts, finish_reason: normalizeFinishReason(finishReason) }]); | ||
| } | ||
| function toolCallPart(toolCall) { | ||
| const args = toolCall.input ?? toolCall.args; | ||
| return { | ||
| type: "tool_call", | ||
| id: asString(toolCall.toolCallId), | ||
| name: asString(toolCall.toolName), | ||
| arguments: typeof args === "string" ? args : safeStringify(args ?? {}) | ||
| }; | ||
| } | ||
| function partsFromContent(content) { | ||
| const parts = []; | ||
| for (const item of content) { | ||
| if (!isRecord(item)) { | ||
| continue; | ||
| } | ||
| if (item.type === "text" && typeof item.text === "string") { | ||
| parts.push({ type: "text", content: item.text }); | ||
| } else if (item.type === "tool-call") { | ||
| parts.push(toolCallPart(item)); | ||
| } | ||
| } | ||
| return parts; | ||
| } | ||
| function partsFromTextAndToolCalls(text, toolCalls) { | ||
| const parts = []; | ||
| if (typeof text === "string" && text.length) { | ||
| parts.push({ type: "text", content: text }); | ||
| } | ||
| if (Array.isArray(toolCalls)) { | ||
| for (const toolCall of toolCalls) { | ||
| if (isRecord(toolCall)) { | ||
| parts.push(toolCallPart(toolCall)); | ||
| } | ||
| } | ||
| } | ||
| return parts; | ||
| } | ||
| function captureToolError(span, data, error) { | ||
| span.setStatus({ | ||
| code: core.SPAN_STATUS_ERROR, | ||
| message: error instanceof Error ? error.message : "tool_error" | ||
| }); | ||
| const toolCall = isRecord(data.event.toolCall) ? data.event.toolCall : {}; | ||
| const toolName = asString(toolCall.toolName); | ||
| const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId); | ||
| core.withScope((scope) => { | ||
| scope.setContext("trace", core.spanToTraceContext(span)); | ||
| if (toolName) { | ||
| scope.setTag("vercel.ai.tool.name", toolName); | ||
| } | ||
| if (toolCallId) { | ||
| scope.setTag("vercel.ai.tool.callId", toolCallId); | ||
| } | ||
| scope.setLevel("error"); | ||
| core.captureException( | ||
| error instanceof Error ? error : new Error(typeof error === "string" ? error : "Tool execution failed"), | ||
| { | ||
| mechanism: { type: "auto.vercelai.channel", handled: false } | ||
| } | ||
| ); | ||
| }); | ||
| } | ||
| function getRecordingOptions(event, channelOptions) { | ||
| const genAI = core.getClient()?.getDataCollectionOptions().genAI; | ||
| return { | ||
| recordInputs: resolveRecording(channelOptions.recordInputs, event.recordInputs, genAI?.inputs), | ||
| recordOutputs: resolveRecording(channelOptions.recordOutputs, event.recordOutputs, genAI?.outputs), | ||
| enableTruncation: core.shouldEnableTruncation(channelOptions.enableTruncation) | ||
| }; | ||
| } | ||
| function resolveRecording(integrationOption, perCallOption, globalDefault) { | ||
| if (typeof integrationOption === "boolean") { | ||
| return integrationOption; | ||
| } | ||
| if (typeof perCallOption === "boolean") { | ||
| return perCallOption; | ||
| } | ||
| return globalDefault === true; | ||
| } | ||
| function buildInputMessageAttributes(event, enableTruncation) { | ||
| const attributes$1 = {}; | ||
| const instructions = asString(event.instructions); | ||
| if (instructions) { | ||
| attributes$1[core.GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: "text", content: instructions }]); | ||
| } | ||
| const messages = event.messages ?? event.prompt; | ||
| if (messages !== void 0) { | ||
| attributes$1[attributes.GEN_AI_INPUT_MESSAGES] = enableTruncation ? core.getTruncatedJsonString(messages) : safeStringify(messages); | ||
| attributes$1[core.GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1; | ||
| } | ||
| return attributes$1; | ||
| } | ||
| function asString(value) { | ||
| return typeof value === "string" ? value : void 0; | ||
| } | ||
| function asNumber(value) { | ||
| return typeof value === "number" && !isNaN(value) ? value : void 0; | ||
| } | ||
| function sum(a, b) { | ||
| return a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0); | ||
| } | ||
| function isRecord(value) { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
| function safeStringify(value) { | ||
| if (typeof value === "string") { | ||
| return value; | ||
| } | ||
| try { | ||
| return JSON.stringify(value); | ||
| } catch { | ||
| return "[unserializable]"; | ||
| } | ||
| } | ||
| exports.clearOperationId = clearOperationId; | ||
| exports.createSpanFromMessage = createSpanFromMessage; | ||
| exports.enrichSpanOnEnd = enrichSpanOnEnd; | ||
| exports.subscribeVercelAiTracingChannel = subscribeVercelAiTracingChannel; | ||
| //# sourceMappingURL=vercel-ai-dc-subscriber.js.map |
| {"version":3,"file":"vercel-ai-dc-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"sourcesContent":["/* eslint-disable max-lines */\n// `@sentry/conventions` marks several gen_ai attributes (e.g. `GEN_AI_SYSTEM`, `GEN_AI_TOOL_*`,\n// `GEN_AI_REQUEST_AVAILABLE_TOOLS`) as deprecated in favour of newer semconv names. We intentionally\n// keep emitting the current names so these spans match the OTel-based (v6) integration and what the\n// Sentry product consumes today; migrating to the new names is a separate, coordinated change.\n/* eslint-disable typescript-eslint/no-deprecated */\nimport {\n GEN_AI_EMBEDDINGS_INPUT,\n GEN_AI_FUNCTION_ID,\n GEN_AI_INPUT_MESSAGES,\n GEN_AI_OPERATION_NAME,\n GEN_AI_OUTPUT_MESSAGES,\n GEN_AI_REQUEST_AVAILABLE_TOOLS,\n GEN_AI_REQUEST_MODEL,\n GEN_AI_RESPONSE_FINISH_REASONS,\n GEN_AI_RESPONSE_ID,\n GEN_AI_RESPONSE_MODEL,\n GEN_AI_RESPONSE_STREAMING,\n GEN_AI_SYSTEM,\n GEN_AI_TOOL_INPUT,\n GEN_AI_TOOL_NAME,\n GEN_AI_TOOL_OUTPUT,\n GEN_AI_TOOL_TYPE,\n GEN_AI_USAGE_INPUT_TOKENS,\n GEN_AI_USAGE_OUTPUT_TOKENS,\n GEN_AI_USAGE_TOTAL_TOKENS,\n} from '@sentry/conventions/attributes';\nimport { GEN_AI_EXECUTE_TOOL_SPAN_OP, GEN_AI_INVOKE_AGENT_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span } from '@sentry/core';\nimport {\n captureException,\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n getClient,\n getProviderMetadataAttributes,\n getTruncatedJsonString,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n SPAN_STATUS_ERROR,\n spanToJSON,\n spanToTraceContext,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport type { TracingChannel } from 'node:diagnostics_channel';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n/**\n * The single tracing channel the `ai` package (>= 7) publishes all telemetry lifecycle events to\n * via `node:diagnostics_channel`. Events are discriminated by their `type` field.\n * @see https://github.com/vercel/ai/pull/15660\n */\nconst AI_SDK_TELEMETRY_TRACING_CHANNEL = 'ai:telemetry';\n\nconst ORIGIN = 'auto.vercelai.channel';\n\n// `@sentry/conventions` does not expose these yet, so we keep the literals here.\nconst GEN_AI_TOOL_CALL_ID_ATTRIBUTE = 'gen_ai.tool.call.id';\nconst GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE = 'gen_ai.tool.description';\nconst GEN_AI_EMBEDDINGS_OPERATION = 'embeddings';\nconst GEN_AI_RERANK_OPERATION = 'rerank';\n// The model-call op matches the Vercel AI OTel integration (`gen_ai.generate_content`) rather than\n// the generic `gen_ai.chat`, so v6 (OTel) and v7 (channel) produce the same spans.\nconst GEN_AI_GENERATE_CONTENT_OPERATION = 'generate_content';\n\n// Subset of the `vercel.ai.*` passthrough attributes the OTel integration emits that we reproduce.\nconst VERCEL_AI_OPERATION_ID_ATTRIBUTE = 'vercel.ai.operationId';\nconst VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE = 'vercel.ai.model.provider';\nconst VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = 'vercel.ai.settings.maxRetries';\n\n// Tracks the top-level operationId (and whether it streams) per `callId` so a model-call span can\n// name its `doGenerate`/`doStream` operation the same way the OTel integration does. `isStream` is\n// the authoritative event-type signal rather than a substring check on the (possibly custom)\n// operationId. Cleared when the top-level span ends.\nconst operationIdByCallId = new Map<string, { operationId: string; isStream: boolean }>();\n\n// Per-operation map of tool name → description, harvested from a model-call /\n// top-level event's `tools` (keyed by the shared `callId`). The AI SDK's\n// `executeTool` event doesn't carry the tool's description, so we backfill it\n// onto the tool span here — without relying on the OTel `vercelAiEventProcessor`\n// (which isn't registered in channel/orchestrion mode). Cleared with the\n// operation. Only populated when inputs are recorded, matching the OTel path\n// (which sources descriptions from the recorded `available_tools`).\nconst toolDescriptionsByCallId = new Map<string, Map<string, string>>();\n\n// Only top-level operations own the `callId` → operationId mapping; `step`/`languageModelCall`/\n// `executeTool` share the parent's `callId`, so they must not clear it.\nconst ROOT_OPERATION_TYPES = new Set<ChannelEventType>(['generateText', 'streamText', 'embed', 'rerank']);\n\n/** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */\nexport function clearOperationId(data: VercelAiChannelMessage): void {\n if (!ROOT_OPERATION_TYPES.has(data.type)) {\n return;\n }\n const callId = asString(data.event.callId);\n if (callId) {\n operationIdByCallId.delete(callId);\n toolDescriptionsByCallId.delete(callId);\n }\n}\n\n/** Record tool name → description from an event's `tools`, so tool spans can backfill the description. */\nfunction recordToolDescriptions(callId: string | undefined, tools: unknown): void {\n if (!callId || !Array.isArray(tools)) {\n return;\n }\n let descriptions = toolDescriptionsByCallId.get(callId);\n for (const tool of tools) {\n if (isRecord(tool) && typeof tool.name === 'string' && typeof tool.description === 'string') {\n descriptions = descriptions ?? new Map();\n if (!descriptions.has(tool.name)) {\n descriptions.set(tool.name, tool.description);\n }\n }\n }\n if (descriptions) {\n toolDescriptionsByCallId.set(callId, descriptions);\n }\n}\n\n/**\n * Resolve a tool's description, preferring the per-operation map (populated from the model-call /\n * top-level event's `tools`, v7) and falling back to a `tools` collection on the event itself —\n * which may be an array of `{ name, description }` or a record keyed by tool name (v6).\n */\nfunction resolveToolDescription(callId: string | undefined, toolName: string, tools: unknown): string | undefined {\n const fromMap = callId ? toolDescriptionsByCallId.get(callId)?.get(toolName) : undefined;\n if (fromMap) {\n return fromMap;\n }\n if (Array.isArray(tools)) {\n const match = tools.find(tool => isRecord(tool) && tool.name === toolName);\n return isRecord(match) ? asString(match.description) : undefined;\n }\n if (isRecord(tools)) {\n const tool = tools[toolName];\n return isRecord(tool) ? asString(tool.description) : undefined;\n }\n return undefined;\n}\n\n/** The lifecycle event types the `ai:telemetry` channel can carry. */\nexport type ChannelEventType =\n | 'generateText'\n | 'streamText'\n | 'step'\n | 'languageModelCall'\n | 'executeTool'\n | 'embed'\n | 'rerank';\n\n/**\n * The context object the AI SDK passes through one tracing-channel call. It is the same object\n * identity across `start`/`end`/`asyncEnd`/`error`, and Node's `tracingChannel` attaches\n * `result`/`error` to it as the traced promise settles.\n */\nexport interface VercelAiChannelMessage {\n type: ChannelEventType;\n event: Record<string, unknown>;\n result?: unknown;\n error?: unknown;\n}\n\n/**\n * Platform-provided factory that returns a tracing channel for the given channel name. The factory\n * is responsible for, when `start` fires, calling `transformStart(data)` and storing the returned\n * span on `data._sentrySpan` so the subscriber's `asyncEnd`/`error` handlers can read it.\n *\n * Node passes `@sentry/opentelemetry/tracing-channel`, which uses `bindStore` to additionally make\n * the span the active OTel context for the duration of the traced operation. That is what makes\n * nested AI SDK operations (model calls, tool calls) become children of the enclosing span without\n * any manual parent bookkeeping here.\n */\ntype VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */\ninterface VercelAiChannelOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n enableTruncation?: boolean;\n}\n\nlet subscribed = false;\n\n/**\n * Subscribe Sentry span handlers to the `ai` SDK's native telemetry tracing channel (`ai:telemetry`,\n * available in `ai` >= 7) and emit fully-formed `gen_ai.*` spans directly — no OpenTelemetry span\n * post-processing involved.\n *\n * The integration passes its options in directly (rather than us looking the integration up on every\n * event); the global `dataCollection.genAI` default is still read from the client per event.\n *\n * Safe to always call: on `ai` versions that don't publish to the channel (e.g. < 7) nothing is\n * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based\n * patcher. Idempotent.\n */\nexport function subscribeVercelAiTracingChannel(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n bindTracingChannelToSpan(\n tracingChannel<VercelAiChannelMessage>(AI_SDK_TELEMETRY_TRACING_CHANNEL),\n data => createSpanFromMessage(data, options),\n {\n // The helper ends the span; we enrich it from the settled result first (tokens, output messages,\n // finish reasons, response model/id, provider metadata) and drop the per-operation `callId` maps.\n beforeSpanEnd: (span, data) => {\n enrichSpanOnEnd(span, data, options);\n clearOperationId(data);\n },\n },\n );\n}\n\n/**\n * Transform a channel `start` payload into the span that should be active for the operation. For\n * `step` we deliberately don't open a span (model calls and tool calls are siblings under the\n * invoke_agent span, matching the OTel-based output), so we reuse the active span and mark the\n * payload to skip ending it.\n */\nexport function createSpanFromMessage(\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): Span | undefined {\n const { type, event } = data;\n\n if (type === 'step' || !event || typeof event !== 'object') {\n // Opt out: returning `undefined` leaves the enclosing `invoke_agent` span as the active context\n // (model-call and tool-call events nest under it) without opening — or ending — a span of its own.\n return undefined;\n }\n\n const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions);\n const provider = asString(event.provider);\n const modelId = asString(event.modelId);\n const callId = asString(event.callId);\n const maxRetries = asNumber(event.maxRetries);\n\n // Harvest tool descriptions from the operation/model-call `tools` so tool spans can backfill them.\n // Gated on `recordInputs` to match the OTel path, which only records `available_tools` then.\n if (recordInputs) {\n recordToolDescriptions(callId, event.tools);\n }\n\n const baseAttributes: Record<string, string | number | boolean> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n ...(provider ? { [GEN_AI_SYSTEM]: provider, [VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE]: provider } : {}),\n ...(modelId ? { [GEN_AI_REQUEST_MODEL]: modelId } : {}),\n ...(maxRetries !== undefined ? { [VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE]: maxRetries } : {}),\n };\n\n switch (type) {\n case 'generateText':\n case 'streamText':\n return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === 'streamText');\n case 'languageModelCall':\n return buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId);\n case 'executeTool':\n return buildToolSpan(event, recordInputs);\n case 'embed':\n return startGenAiSpan(GEN_AI_EMBEDDINGS_OPERATION, modelId, {\n ...baseAttributes,\n ...(recordInputs && event.value !== undefined ? { [GEN_AI_EMBEDDINGS_INPUT]: safeStringify(event.value) } : {}),\n });\n case 'rerank':\n return startGenAiSpan(GEN_AI_RERANK_OPERATION, modelId, baseAttributes);\n default:\n // Unknown event type: opt out rather than open a span we can't shape correctly.\n return undefined;\n }\n}\n\ntype Attributes = Record<string, string | number | boolean>;\n\n/** Start a `gen_ai.<operation>` span named `<operation> <suffix>` (or just `<operation>` when no suffix). */\nfunction startGenAiSpan(operation: string, suffix: string | undefined, attributes: Attributes): Span {\n return startInactiveSpan({\n name: suffix ? `${operation} ${suffix}` : operation,\n op: `gen_ai.${operation}`,\n attributes: { [GEN_AI_OPERATION_NAME]: operation, ...attributes },\n });\n}\n\nfunction buildInvokeAgentSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n isStream: boolean,\n): Span {\n const functionId = asString(event.functionId);\n const operationId = asString(event.operationId) ?? (isStream ? 'ai.streamText' : 'ai.generateText');\n if (callId) {\n operationIdByCallId.set(callId, { operationId, isStream });\n }\n return startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n [GEN_AI_RESPONSE_STREAMING]: isStream,\n ...(functionId ? { [GEN_AI_FUNCTION_ID]: functionId } : {}),\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n });\n}\n\nfunction buildModelCallSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n modelId: string | undefined,\n): Span {\n const parent = callId ? operationIdByCallId.get(callId) : undefined;\n const operationId = parent\n ? `${parent.operationId}.${parent.isStream ? 'doStream' : 'doGenerate'}`\n : 'ai.generateText.doGenerate';\n return startGenAiSpan(GEN_AI_GENERATE_CONTENT_OPERATION, modelId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n ...(recordInputs && Array.isArray(event.tools)\n ? { [GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) }\n : {}),\n });\n}\n\nfunction buildToolSpan(event: Record<string, unknown>, recordInputs: boolean): Span {\n const toolCall = isRecord(event.toolCall) ? event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId);\n const toolInput = toolCall.input ?? toolCall.args;\n // The `executeTool` event has no description; backfill it from the operation's recorded tools.\n // Gated on `recordInputs` to match the OTel path (descriptions come from the recorded tools list).\n const description =\n recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : undefined;\n return startGenAiSpan(GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [GEN_AI_TOOL_TYPE]: 'function',\n ...(toolName ? { [GEN_AI_TOOL_NAME]: toolName } : {}),\n ...(toolCallId ? { [GEN_AI_TOOL_CALL_ID_ATTRIBUTE]: toolCallId } : {}),\n ...(description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}),\n ...(recordInputs && toolInput !== undefined ? { [GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {}),\n });\n}\n\n/**\n * Best-effort enrichment from the resolved value the AI SDK attaches to the channel context.\n * Everything here is guarded: when a field is missing or the shape differs across `ai` versions,\n * we simply don't set the attribute rather than emit a malformed span.\n */\nexport function enrichSpanOnEnd(\n span: Span,\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): void {\n const { type, result } = data;\n if (!isRecord(result)) {\n return;\n }\n\n const { recordOutputs } = getRecordingOptions(data.event, channelOptions);\n\n if (type === 'executeTool') {\n if (recordOutputs) {\n span.setAttribute(GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result));\n }\n // From V5 on, tool errors are not rejected (so the `error` channel verb never fires) — they\n // surface as `tool-error` content on the resolved result. Mirror the OTel path by marking the\n // span and capturing the error.\n const output = isRecord(result.output) ? result.output : undefined;\n if (output?.type === 'tool-error') {\n captureToolError(span, data, output.error);\n }\n return;\n }\n\n // `languageModelCall` results report usage as `{ total }` objects; top-level/step results report\n // flat numbers. `tokenCount` handles both.\n const usage = isRecord(result.usage) ? result.usage : undefined;\n if (usage) {\n const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.tokens);\n const outputTokens = tokenCount(usage.outputTokens);\n const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens);\n if (inputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_INPUT_TOKENS, inputTokens);\n }\n if (outputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);\n }\n if (totalTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_TOTAL_TOKENS, totalTokens);\n }\n }\n\n // Match the OTel integration: finish reasons live on the model-call (`generate_content`) span, not\n // on the top-level `invoke_agent` span.\n const finishReason = getFinishReason(result);\n if (finishReason && type === 'languageModelCall') {\n span.setAttribute(GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason]));\n }\n\n const response = isRecord(result.response) ? result.response : undefined;\n const responseId = asString(response?.id) ?? asString(result.responseId);\n if (responseId) {\n span.setAttribute(GEN_AI_RESPONSE_ID, responseId);\n }\n const responseModel = asString(response?.modelId) ?? asString(data.event.modelId);\n if (responseModel) {\n span.setAttribute(GEN_AI_RESPONSE_MODEL, responseModel);\n }\n\n // Provider-specific cache/reasoning/prediction token breakdowns and `gen_ai.conversation.id`.\n // The channel exposes `providerMetadata` as an object (the OTel path parses it from a string);\n // both share `getProviderMetadataAttributes` so the emitted shape is identical.\n const providerMetadata = (result as { providerMetadata?: unknown }).providerMetadata;\n const providerAttributes = getProviderMetadataAttributes(providerMetadata);\n // Don't overwrite a conversation id already set on span start (e.g. by `conversationIdIntegration`\n // from a user-set scope value); the provider-derived id is only a fallback. Matches the OTel path.\n if (\n GEN_AI_CONVERSATION_ID_ATTRIBUTE in providerAttributes &&\n spanToJSON(span).data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]\n ) {\n // oxlint-disable-next-line typescript/no-dynamic-delete\n delete providerAttributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE];\n }\n span.setAttributes(providerAttributes);\n\n if (recordOutputs) {\n // `languageModelCall` exposes the response as a `content` parts array; top-level results expose\n // `text` + `toolCalls`. Both normalize into the OTel `gen_ai.output.messages` assistant message.\n const parts =\n type === 'languageModelCall' && Array.isArray(result.content)\n ? partsFromContent(result.content)\n : partsFromTextAndToolCalls(result.text, result.toolCalls);\n const outputMessages = buildOutputMessages(parts, finishReason);\n if (outputMessages) {\n span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages);\n }\n }\n}\n\n/** Maps a Vercel AI finish reason to the OTel `gen_ai.output.messages` form (`tool-calls` → `tool_call`). */\nfunction normalizeFinishReason(finishReason: string | undefined): string {\n return finishReason === 'tool-calls' ? 'tool_call' : (finishReason ?? 'stop');\n}\n\n/** Reads the finish reason from a result — a string on top-level results, `{ unified }` on model calls. */\nfunction getFinishReason(result: Record<string, unknown>): string | undefined {\n const finishReason = result.finishReason;\n if (typeof finishReason === 'string') {\n return finishReason;\n }\n return isRecord(finishReason) ? asString(finishReason.unified) : undefined;\n}\n\n/** Reads a token count that may be a plain number or a `{ total }` object (model-call usage). */\nfunction tokenCount(value: unknown): number | undefined {\n return asNumber(value) ?? (isRecord(value) ? asNumber(value.total) : undefined);\n}\n\nfunction buildOutputMessages(\n parts: Array<Record<string, unknown>>,\n finishReason: string | undefined,\n): string | undefined {\n if (!parts.length) {\n return undefined;\n }\n return safeStringify([{ role: 'assistant', parts, finish_reason: normalizeFinishReason(finishReason) }]);\n}\n\nfunction toolCallPart(toolCall: Record<string, unknown>): Record<string, unknown> {\n const args = toolCall.input ?? toolCall.args;\n return {\n type: 'tool_call',\n id: asString(toolCall.toolCallId),\n name: asString(toolCall.toolName),\n arguments: typeof args === 'string' ? args : safeStringify(args ?? {}),\n };\n}\n\nfunction partsFromContent(content: unknown[]): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n for (const item of content) {\n if (!isRecord(item)) {\n continue;\n }\n if (item.type === 'text' && typeof item.text === 'string') {\n parts.push({ type: 'text', content: item.text });\n } else if (item.type === 'tool-call') {\n parts.push(toolCallPart(item));\n }\n }\n return parts;\n}\n\nfunction partsFromTextAndToolCalls(text: unknown, toolCalls: unknown): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n if (typeof text === 'string' && text.length) {\n parts.push({ type: 'text', content: text });\n }\n if (Array.isArray(toolCalls)) {\n for (const toolCall of toolCalls) {\n if (isRecord(toolCall)) {\n parts.push(toolCallPart(toolCall));\n }\n }\n }\n return parts;\n}\n\nfunction captureToolError(span: Span, data: VercelAiChannelMessage, error: unknown): void {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: error instanceof Error ? error.message : 'tool_error',\n });\n\n const toolCall = isRecord(data.event.toolCall) ? data.event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId);\n\n withScope(scope => {\n scope.setContext('trace', spanToTraceContext(span));\n if (toolName) {\n scope.setTag('vercel.ai.tool.name', toolName);\n }\n if (toolCallId) {\n scope.setTag('vercel.ai.tool.callId', toolCallId);\n }\n scope.setLevel('error');\n captureException(\n error instanceof Error ? error : new Error(typeof error === 'string' ? error : 'Tool execution failed'),\n {\n mechanism: { type: 'auto.vercelai.channel', handled: false },\n },\n );\n });\n}\n\nfunction getRecordingOptions(\n event: Record<string, unknown>,\n channelOptions: VercelAiChannelOptions,\n): {\n recordInputs: boolean;\n recordOutputs: boolean;\n enableTruncation: boolean;\n} {\n const genAI = getClient()?.getDataCollectionOptions().genAI;\n\n return {\n recordInputs: resolveRecording(channelOptions.recordInputs, event.recordInputs, genAI?.inputs),\n recordOutputs: resolveRecording(channelOptions.recordOutputs, event.recordOutputs, genAI?.outputs),\n enableTruncation: shouldEnableTruncation(channelOptions.enableTruncation),\n };\n}\n\n/**\n * Mirrors the OTel integration's `determineRecordingSettings` precedence: an integration-level option\n * wins, then the per-call `experimental_telemetry.recordInputs/recordOutputs` flag the AI SDK forwards\n * on the channel event, then the global `dataCollection.genAI` default.\n *\n * NOTE: the OTel integration also defaults recording to `true` for a call with\n * `experimental_telemetry: { isEnabled: true }`. The `ai:telemetry` channel does not expose `isEnabled`\n * (nor a resolved recording flag), so that per-call default cannot be reproduced here — v7 users who\n * want inputs/outputs recorded must enable `dataCollection.genAI` or set `recordInputs`/`recordOutputs`.\n */\nfunction resolveRecording(integrationOption: unknown, perCallOption: unknown, globalDefault: unknown): boolean {\n if (typeof integrationOption === 'boolean') {\n return integrationOption;\n }\n if (typeof perCallOption === 'boolean') {\n return perCallOption;\n }\n return globalDefault === true;\n}\n\nfunction buildInputMessageAttributes(\n event: Record<string, unknown>,\n enableTruncation: boolean,\n): Record<string, string | number> {\n const attributes: Record<string, string | number> = {};\n\n // `ai` >= 7 forbids system messages in `messages`/`prompt` and exposes the system prompt as a\n // separate `instructions` field. The OTel path lifts the system message out of the prompt into\n // `gen_ai.system_instructions` as `[{ type: 'text', content }]`; mirror that shape here.\n const instructions = asString(event.instructions);\n if (instructions) {\n attributes[GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: 'text', content: instructions }]);\n }\n\n // The AI SDK start events extend `StandardizedPrompt`; messages live on `messages`, otherwise the\n // simpler `prompt` field is used.\n const messages = event.messages ?? event.prompt;\n if (messages !== undefined) {\n attributes[GEN_AI_INPUT_MESSAGES] = enableTruncation ? getTruncatedJsonString(messages) : safeStringify(messages);\n // The original (pre-truncation) message count, so the product can show how many were dropped.\n attributes[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1;\n }\n\n return attributes;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && !isNaN(value) ? value : undefined;\n}\n\nfunction sum(a: number | undefined, b: number | undefined): number | undefined {\n return a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n}\n"],"names":["tracingChannel","bindTracingChannelToSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","GEN_AI_SYSTEM","GEN_AI_REQUEST_MODEL","GEN_AI_EMBEDDINGS_INPUT","attributes","startInactiveSpan","GEN_AI_OPERATION_NAME","GEN_AI_INVOKE_AGENT_SPAN_OP","GEN_AI_RESPONSE_STREAMING","GEN_AI_FUNCTION_ID","GEN_AI_REQUEST_AVAILABLE_TOOLS","GEN_AI_EXECUTE_TOOL_SPAN_OP","GEN_AI_TOOL_TYPE","GEN_AI_TOOL_NAME","GEN_AI_TOOL_INPUT","GEN_AI_TOOL_OUTPUT","GEN_AI_USAGE_INPUT_TOKENS","GEN_AI_USAGE_OUTPUT_TOKENS","GEN_AI_USAGE_TOTAL_TOKENS","GEN_AI_RESPONSE_FINISH_REASONS","GEN_AI_RESPONSE_ID","GEN_AI_RESPONSE_MODEL","getProviderMetadataAttributes","GEN_AI_CONVERSATION_ID_ATTRIBUTE","spanToJSON","GEN_AI_OUTPUT_MESSAGES","SPAN_STATUS_ERROR","withScope","spanToTraceContext","captureException","getClient","shouldEnableTruncation","GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE","GEN_AI_INPUT_MESSAGES","getTruncatedJsonString","GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE"],"mappings":"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"} |
| import { defineIntegration } from '@sentry/core'; | ||
| import { subscribeVercelAiTracingChannel } from './vercel-ai-dc-subscriber.js'; | ||
| import * as dc from 'node:diagnostics_channel'; | ||
| const _vercelAiIntegration = ((options = {}) => { | ||
| return { | ||
| name: "VercelAI", | ||
| setupOnce() { | ||
| if (!dc.tracingChannel) { | ||
| return; | ||
| } | ||
| void Promise.resolve().then(() => subscribeVercelAiTracingChannel(dc.tracingChannel, options)); | ||
| } | ||
| }; | ||
| }); | ||
| const vercelAiIntegration = defineIntegration(_vercelAiIntegration); | ||
| export { vercelAiIntegration }; | ||
| //# sourceMappingURL=index.js.map |
| {"version":3,"file":"index.js","sources":["../../../src/vercel-ai/index.ts"],"sourcesContent":["import { defineIntegration, type IntegrationFn } from '@sentry/core';\nimport { subscribeVercelAiTracingChannel } from './vercel-ai-dc-subscriber';\nimport * as dc from 'node:diagnostics_channel';\n\ntype VercelAiOptions = {\n /**\n * Enable or disable input recording. Enabled if `dataCollection.genAI.inputs` (or the deprecated `sendDefaultPii` option) is `true`\n * or if you set `isEnabled` to `true` in your ai SDK method telemetry settings.\n * Integration-level options take precedence over global `dataCollection` config.\n */\n recordInputs?: boolean;\n\n /**\n * Enable or disable output recording. Enabled if `dataCollection.genAI.outputs` (or the deprecated `sendDefaultPii` option) is `true`\n * or if you set `isEnabled` to `true` in your ai SDK method telemetry settings.\n * Integration-level options take precedence over global `dataCollection` config.\n */\n recordOutputs?: boolean;\n\n /**\n * Enable or disable truncation of recorded input messages.\n * Defaults to `true`.\n */\n enableTruncation?: boolean;\n};\n\nconst _vercelAiIntegration = ((options: VercelAiOptions = {}) => {\n return {\n name: 'VercelAI' as const,\n setupOnce() {\n // Bail if this is not available\n if (!dc.tracingChannel) {\n return;\n }\n\n // Subscribe to the `ai` SDK's native telemetry tracing channel (ai >= 7).\n // This is a no-op on versions that don't publish to the channel, so it is always safe to call.\n // The factory needs the Sentry OTel context manager, which `initOpenTelemetry()` registers after `setupOnce`, so defer a tick.\n // Options are passed in here rather than read back off the integration per event.\n void Promise.resolve().then(() => subscribeVercelAiTracingChannel(dc.tracingChannel, options));\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Auto-instrument the `ai` SDK's native telemetry tracing channel (ai >= 7).\n */\nexport const vercelAiIntegration = defineIntegration(_vercelAiIntegration);\n"],"names":[],"mappings":";;;;AA0BA,MAAM,oBAAA,IAAwB,CAAC,OAAA,GAA2B,EAAC,KAAM;AAC/D,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,UAAA;AAAA,IACN,SAAA,GAAY;AAEV,MAAA,IAAI,CAAC,GAAG,cAAA,EAAgB;AACtB,QAAA;AAAA,MACF;AAMA,MAAA,KAAK,OAAA,CAAQ,SAAQ,CAAE,IAAA,CAAK,MAAM,+BAAA,CAAgC,EAAA,CAAG,cAAA,EAAgB,OAAO,CAAC,CAAA;AAAA,IAC/F;AAAA,GACF;AACF,CAAA,CAAA;AAKO,MAAM,mBAAA,GAAsB,kBAAkB,oBAAoB;;;;"} |
| import { GEN_AI_EMBEDDINGS_INPUT, GEN_AI_OPERATION_NAME, GEN_AI_FUNCTION_ID, GEN_AI_RESPONSE_STREAMING, GEN_AI_REQUEST_AVAILABLE_TOOLS, GEN_AI_TOOL_INPUT, GEN_AI_TOOL_NAME, GEN_AI_TOOL_TYPE, GEN_AI_TOOL_OUTPUT, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_USAGE_TOTAL_TOKENS, GEN_AI_RESPONSE_FINISH_REASONS, GEN_AI_RESPONSE_ID, GEN_AI_RESPONSE_MODEL, GEN_AI_OUTPUT_MESSAGES, GEN_AI_INPUT_MESSAGES, GEN_AI_REQUEST_MODEL, GEN_AI_SYSTEM } from '@sentry/conventions/attributes'; | ||
| import { GEN_AI_INVOKE_AGENT_SPAN_OP, GEN_AI_EXECUTE_TOOL_SPAN_OP } from '@sentry/conventions/op'; | ||
| import { startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getProviderMetadataAttributes, GEN_AI_CONVERSATION_ID_ATTRIBUTE, spanToJSON, SPAN_STATUS_ERROR, withScope, spanToTraceContext, captureException, getClient, shouldEnableTruncation, getTruncatedJsonString, GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE } from '@sentry/core'; | ||
| import { bindTracingChannelToSpan } from '../tracing-channel.js'; | ||
| const AI_SDK_TELEMETRY_TRACING_CHANNEL = "ai:telemetry"; | ||
| const ORIGIN = "auto.vercelai.channel"; | ||
| const GEN_AI_TOOL_CALL_ID_ATTRIBUTE = "gen_ai.tool.call.id"; | ||
| const GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE = "gen_ai.tool.description"; | ||
| const GEN_AI_EMBEDDINGS_OPERATION = "embeddings"; | ||
| const GEN_AI_RERANK_OPERATION = "rerank"; | ||
| const GEN_AI_GENERATE_CONTENT_OPERATION = "generate_content"; | ||
| const VERCEL_AI_OPERATION_ID_ATTRIBUTE = "vercel.ai.operationId"; | ||
| const VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE = "vercel.ai.model.provider"; | ||
| const VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = "vercel.ai.settings.maxRetries"; | ||
| const operationIdByCallId = /* @__PURE__ */ new Map(); | ||
| const toolDescriptionsByCallId = /* @__PURE__ */ new Map(); | ||
| const ROOT_OPERATION_TYPES = /* @__PURE__ */ new Set(["generateText", "streamText", "embed", "rerank"]); | ||
| function clearOperationId(data) { | ||
| if (!ROOT_OPERATION_TYPES.has(data.type)) { | ||
| return; | ||
| } | ||
| const callId = asString(data.event.callId); | ||
| if (callId) { | ||
| operationIdByCallId.delete(callId); | ||
| toolDescriptionsByCallId.delete(callId); | ||
| } | ||
| } | ||
| function recordToolDescriptions(callId, tools) { | ||
| if (!callId || !Array.isArray(tools)) { | ||
| return; | ||
| } | ||
| let descriptions = toolDescriptionsByCallId.get(callId); | ||
| for (const tool of tools) { | ||
| if (isRecord(tool) && typeof tool.name === "string" && typeof tool.description === "string") { | ||
| descriptions = descriptions ?? /* @__PURE__ */ new Map(); | ||
| if (!descriptions.has(tool.name)) { | ||
| descriptions.set(tool.name, tool.description); | ||
| } | ||
| } | ||
| } | ||
| if (descriptions) { | ||
| toolDescriptionsByCallId.set(callId, descriptions); | ||
| } | ||
| } | ||
| function resolveToolDescription(callId, toolName, tools) { | ||
| const fromMap = callId ? toolDescriptionsByCallId.get(callId)?.get(toolName) : void 0; | ||
| if (fromMap) { | ||
| return fromMap; | ||
| } | ||
| if (Array.isArray(tools)) { | ||
| const match = tools.find((tool) => isRecord(tool) && tool.name === toolName); | ||
| return isRecord(match) ? asString(match.description) : void 0; | ||
| } | ||
| if (isRecord(tools)) { | ||
| const tool = tools[toolName]; | ||
| return isRecord(tool) ? asString(tool.description) : void 0; | ||
| } | ||
| return void 0; | ||
| } | ||
| let subscribed = false; | ||
| function subscribeVercelAiTracingChannel(tracingChannel, options = {}) { | ||
| if (subscribed) { | ||
| return; | ||
| } | ||
| subscribed = true; | ||
| bindTracingChannelToSpan( | ||
| tracingChannel(AI_SDK_TELEMETRY_TRACING_CHANNEL), | ||
| (data) => createSpanFromMessage(data, options), | ||
| { | ||
| // The helper ends the span; we enrich it from the settled result first (tokens, output messages, | ||
| // finish reasons, response model/id, provider metadata) and drop the per-operation `callId` maps. | ||
| beforeSpanEnd: (span, data) => { | ||
| enrichSpanOnEnd(span, data, options); | ||
| clearOperationId(data); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
| function createSpanFromMessage(data, channelOptions) { | ||
| const { type, event } = data; | ||
| if (type === "step" || !event || typeof event !== "object") { | ||
| return void 0; | ||
| } | ||
| const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions); | ||
| const provider = asString(event.provider); | ||
| const modelId = asString(event.modelId); | ||
| const callId = asString(event.callId); | ||
| const maxRetries = asNumber(event.maxRetries); | ||
| if (recordInputs) { | ||
| recordToolDescriptions(callId, event.tools); | ||
| } | ||
| const baseAttributes = { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| ...provider ? { [GEN_AI_SYSTEM]: provider, [VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE]: provider } : {}, | ||
| ...modelId ? { [GEN_AI_REQUEST_MODEL]: modelId } : {}, | ||
| ...maxRetries !== void 0 ? { [VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE]: maxRetries } : {} | ||
| }; | ||
| switch (type) { | ||
| case "generateText": | ||
| case "streamText": | ||
| return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === "streamText"); | ||
| case "languageModelCall": | ||
| return buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId); | ||
| case "executeTool": | ||
| return buildToolSpan(event, recordInputs); | ||
| case "embed": | ||
| return startGenAiSpan(GEN_AI_EMBEDDINGS_OPERATION, modelId, { | ||
| ...baseAttributes, | ||
| ...recordInputs && event.value !== void 0 ? { [GEN_AI_EMBEDDINGS_INPUT]: safeStringify(event.value) } : {} | ||
| }); | ||
| case "rerank": | ||
| return startGenAiSpan(GEN_AI_RERANK_OPERATION, modelId, baseAttributes); | ||
| default: | ||
| return void 0; | ||
| } | ||
| } | ||
| function startGenAiSpan(operation, suffix, attributes) { | ||
| return startInactiveSpan({ | ||
| name: suffix ? `${operation} ${suffix}` : operation, | ||
| op: `gen_ai.${operation}`, | ||
| attributes: { [GEN_AI_OPERATION_NAME]: operation, ...attributes } | ||
| }); | ||
| } | ||
| function buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, isStream) { | ||
| const functionId = asString(event.functionId); | ||
| const operationId = asString(event.operationId) ?? (isStream ? "ai.streamText" : "ai.generateText"); | ||
| if (callId) { | ||
| operationIdByCallId.set(callId, { operationId, isStream }); | ||
| } | ||
| return startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, { | ||
| ...baseAttributes, | ||
| [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId, | ||
| [GEN_AI_RESPONSE_STREAMING]: isStream, | ||
| ...functionId ? { [GEN_AI_FUNCTION_ID]: functionId } : {}, | ||
| ...recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {} | ||
| }); | ||
| } | ||
| function buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId) { | ||
| const parent = callId ? operationIdByCallId.get(callId) : void 0; | ||
| const operationId = parent ? `${parent.operationId}.${parent.isStream ? "doStream" : "doGenerate"}` : "ai.generateText.doGenerate"; | ||
| return startGenAiSpan(GEN_AI_GENERATE_CONTENT_OPERATION, modelId, { | ||
| ...baseAttributes, | ||
| [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId, | ||
| ...recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}, | ||
| ...recordInputs && Array.isArray(event.tools) ? { [GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) } : {} | ||
| }); | ||
| } | ||
| function buildToolSpan(event, recordInputs) { | ||
| const toolCall = isRecord(event.toolCall) ? event.toolCall : {}; | ||
| const toolName = asString(toolCall.toolName); | ||
| const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId); | ||
| const toolInput = toolCall.input ?? toolCall.args; | ||
| const description = recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : void 0; | ||
| return startGenAiSpan(GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN, | ||
| [GEN_AI_TOOL_TYPE]: "function", | ||
| ...toolName ? { [GEN_AI_TOOL_NAME]: toolName } : {}, | ||
| ...toolCallId ? { [GEN_AI_TOOL_CALL_ID_ATTRIBUTE]: toolCallId } : {}, | ||
| ...description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}, | ||
| ...recordInputs && toolInput !== void 0 ? { [GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {} | ||
| }); | ||
| } | ||
| function enrichSpanOnEnd(span, data, channelOptions) { | ||
| const { type, result } = data; | ||
| if (!isRecord(result)) { | ||
| return; | ||
| } | ||
| const { recordOutputs } = getRecordingOptions(data.event, channelOptions); | ||
| if (type === "executeTool") { | ||
| if (recordOutputs) { | ||
| span.setAttribute(GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result)); | ||
| } | ||
| const output = isRecord(result.output) ? result.output : void 0; | ||
| if (output?.type === "tool-error") { | ||
| captureToolError(span, data, output.error); | ||
| } | ||
| return; | ||
| } | ||
| const usage = isRecord(result.usage) ? result.usage : void 0; | ||
| if (usage) { | ||
| const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.tokens); | ||
| const outputTokens = tokenCount(usage.outputTokens); | ||
| const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens); | ||
| if (inputTokens !== void 0) { | ||
| span.setAttribute(GEN_AI_USAGE_INPUT_TOKENS, inputTokens); | ||
| } | ||
| if (outputTokens !== void 0) { | ||
| span.setAttribute(GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens); | ||
| } | ||
| if (totalTokens !== void 0) { | ||
| span.setAttribute(GEN_AI_USAGE_TOTAL_TOKENS, totalTokens); | ||
| } | ||
| } | ||
| const finishReason = getFinishReason(result); | ||
| if (finishReason && type === "languageModelCall") { | ||
| span.setAttribute(GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason])); | ||
| } | ||
| const response = isRecord(result.response) ? result.response : void 0; | ||
| const responseId = asString(response?.id) ?? asString(result.responseId); | ||
| if (responseId) { | ||
| span.setAttribute(GEN_AI_RESPONSE_ID, responseId); | ||
| } | ||
| const responseModel = asString(response?.modelId) ?? asString(data.event.modelId); | ||
| if (responseModel) { | ||
| span.setAttribute(GEN_AI_RESPONSE_MODEL, responseModel); | ||
| } | ||
| const providerMetadata = result.providerMetadata; | ||
| const providerAttributes = getProviderMetadataAttributes(providerMetadata); | ||
| if (GEN_AI_CONVERSATION_ID_ATTRIBUTE in providerAttributes && spanToJSON(span).data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]) { | ||
| delete providerAttributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE]; | ||
| } | ||
| span.setAttributes(providerAttributes); | ||
| if (recordOutputs) { | ||
| const parts = type === "languageModelCall" && Array.isArray(result.content) ? partsFromContent(result.content) : partsFromTextAndToolCalls(result.text, result.toolCalls); | ||
| const outputMessages = buildOutputMessages(parts, finishReason); | ||
| if (outputMessages) { | ||
| span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages); | ||
| } | ||
| } | ||
| } | ||
| function normalizeFinishReason(finishReason) { | ||
| return finishReason === "tool-calls" ? "tool_call" : finishReason ?? "stop"; | ||
| } | ||
| function getFinishReason(result) { | ||
| const finishReason = result.finishReason; | ||
| if (typeof finishReason === "string") { | ||
| return finishReason; | ||
| } | ||
| return isRecord(finishReason) ? asString(finishReason.unified) : void 0; | ||
| } | ||
| function tokenCount(value) { | ||
| return asNumber(value) ?? (isRecord(value) ? asNumber(value.total) : void 0); | ||
| } | ||
| function buildOutputMessages(parts, finishReason) { | ||
| if (!parts.length) { | ||
| return void 0; | ||
| } | ||
| return safeStringify([{ role: "assistant", parts, finish_reason: normalizeFinishReason(finishReason) }]); | ||
| } | ||
| function toolCallPart(toolCall) { | ||
| const args = toolCall.input ?? toolCall.args; | ||
| return { | ||
| type: "tool_call", | ||
| id: asString(toolCall.toolCallId), | ||
| name: asString(toolCall.toolName), | ||
| arguments: typeof args === "string" ? args : safeStringify(args ?? {}) | ||
| }; | ||
| } | ||
| function partsFromContent(content) { | ||
| const parts = []; | ||
| for (const item of content) { | ||
| if (!isRecord(item)) { | ||
| continue; | ||
| } | ||
| if (item.type === "text" && typeof item.text === "string") { | ||
| parts.push({ type: "text", content: item.text }); | ||
| } else if (item.type === "tool-call") { | ||
| parts.push(toolCallPart(item)); | ||
| } | ||
| } | ||
| return parts; | ||
| } | ||
| function partsFromTextAndToolCalls(text, toolCalls) { | ||
| const parts = []; | ||
| if (typeof text === "string" && text.length) { | ||
| parts.push({ type: "text", content: text }); | ||
| } | ||
| if (Array.isArray(toolCalls)) { | ||
| for (const toolCall of toolCalls) { | ||
| if (isRecord(toolCall)) { | ||
| parts.push(toolCallPart(toolCall)); | ||
| } | ||
| } | ||
| } | ||
| return parts; | ||
| } | ||
| function captureToolError(span, data, error) { | ||
| span.setStatus({ | ||
| code: SPAN_STATUS_ERROR, | ||
| message: error instanceof Error ? error.message : "tool_error" | ||
| }); | ||
| const toolCall = isRecord(data.event.toolCall) ? data.event.toolCall : {}; | ||
| const toolName = asString(toolCall.toolName); | ||
| const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId); | ||
| withScope((scope) => { | ||
| scope.setContext("trace", spanToTraceContext(span)); | ||
| if (toolName) { | ||
| scope.setTag("vercel.ai.tool.name", toolName); | ||
| } | ||
| if (toolCallId) { | ||
| scope.setTag("vercel.ai.tool.callId", toolCallId); | ||
| } | ||
| scope.setLevel("error"); | ||
| captureException( | ||
| error instanceof Error ? error : new Error(typeof error === "string" ? error : "Tool execution failed"), | ||
| { | ||
| mechanism: { type: "auto.vercelai.channel", handled: false } | ||
| } | ||
| ); | ||
| }); | ||
| } | ||
| function getRecordingOptions(event, channelOptions) { | ||
| const genAI = getClient()?.getDataCollectionOptions().genAI; | ||
| return { | ||
| recordInputs: resolveRecording(channelOptions.recordInputs, event.recordInputs, genAI?.inputs), | ||
| recordOutputs: resolveRecording(channelOptions.recordOutputs, event.recordOutputs, genAI?.outputs), | ||
| enableTruncation: shouldEnableTruncation(channelOptions.enableTruncation) | ||
| }; | ||
| } | ||
| function resolveRecording(integrationOption, perCallOption, globalDefault) { | ||
| if (typeof integrationOption === "boolean") { | ||
| return integrationOption; | ||
| } | ||
| if (typeof perCallOption === "boolean") { | ||
| return perCallOption; | ||
| } | ||
| return globalDefault === true; | ||
| } | ||
| function buildInputMessageAttributes(event, enableTruncation) { | ||
| const attributes = {}; | ||
| const instructions = asString(event.instructions); | ||
| if (instructions) { | ||
| attributes[GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: "text", content: instructions }]); | ||
| } | ||
| const messages = event.messages ?? event.prompt; | ||
| if (messages !== void 0) { | ||
| attributes[GEN_AI_INPUT_MESSAGES] = enableTruncation ? getTruncatedJsonString(messages) : safeStringify(messages); | ||
| attributes[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1; | ||
| } | ||
| return attributes; | ||
| } | ||
| function asString(value) { | ||
| return typeof value === "string" ? value : void 0; | ||
| } | ||
| function asNumber(value) { | ||
| return typeof value === "number" && !isNaN(value) ? value : void 0; | ||
| } | ||
| function sum(a, b) { | ||
| return a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0); | ||
| } | ||
| function isRecord(value) { | ||
| return typeof value === "object" && value !== null; | ||
| } | ||
| function safeStringify(value) { | ||
| if (typeof value === "string") { | ||
| return value; | ||
| } | ||
| try { | ||
| return JSON.stringify(value); | ||
| } catch { | ||
| return "[unserializable]"; | ||
| } | ||
| } | ||
| export { clearOperationId, createSpanFromMessage, enrichSpanOnEnd, subscribeVercelAiTracingChannel }; | ||
| //# sourceMappingURL=vercel-ai-dc-subscriber.js.map |
| {"version":3,"file":"vercel-ai-dc-subscriber.js","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"sourcesContent":["/* eslint-disable max-lines */\n// `@sentry/conventions` marks several gen_ai attributes (e.g. `GEN_AI_SYSTEM`, `GEN_AI_TOOL_*`,\n// `GEN_AI_REQUEST_AVAILABLE_TOOLS`) as deprecated in favour of newer semconv names. We intentionally\n// keep emitting the current names so these spans match the OTel-based (v6) integration and what the\n// Sentry product consumes today; migrating to the new names is a separate, coordinated change.\n/* eslint-disable typescript-eslint/no-deprecated */\nimport {\n GEN_AI_EMBEDDINGS_INPUT,\n GEN_AI_FUNCTION_ID,\n GEN_AI_INPUT_MESSAGES,\n GEN_AI_OPERATION_NAME,\n GEN_AI_OUTPUT_MESSAGES,\n GEN_AI_REQUEST_AVAILABLE_TOOLS,\n GEN_AI_REQUEST_MODEL,\n GEN_AI_RESPONSE_FINISH_REASONS,\n GEN_AI_RESPONSE_ID,\n GEN_AI_RESPONSE_MODEL,\n GEN_AI_RESPONSE_STREAMING,\n GEN_AI_SYSTEM,\n GEN_AI_TOOL_INPUT,\n GEN_AI_TOOL_NAME,\n GEN_AI_TOOL_OUTPUT,\n GEN_AI_TOOL_TYPE,\n GEN_AI_USAGE_INPUT_TOKENS,\n GEN_AI_USAGE_OUTPUT_TOKENS,\n GEN_AI_USAGE_TOTAL_TOKENS,\n} from '@sentry/conventions/attributes';\nimport { GEN_AI_EXECUTE_TOOL_SPAN_OP, GEN_AI_INVOKE_AGENT_SPAN_OP } from '@sentry/conventions/op';\nimport type { Span } from '@sentry/core';\nimport {\n captureException,\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n getClient,\n getProviderMetadataAttributes,\n getTruncatedJsonString,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n shouldEnableTruncation,\n SPAN_STATUS_ERROR,\n spanToJSON,\n spanToTraceContext,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport type { TracingChannel } from 'node:diagnostics_channel';\nimport { bindTracingChannelToSpan } from '../tracing-channel';\n\n/**\n * The single tracing channel the `ai` package (>= 7) publishes all telemetry lifecycle events to\n * via `node:diagnostics_channel`. Events are discriminated by their `type` field.\n * @see https://github.com/vercel/ai/pull/15660\n */\nconst AI_SDK_TELEMETRY_TRACING_CHANNEL = 'ai:telemetry';\n\nconst ORIGIN = 'auto.vercelai.channel';\n\n// `@sentry/conventions` does not expose these yet, so we keep the literals here.\nconst GEN_AI_TOOL_CALL_ID_ATTRIBUTE = 'gen_ai.tool.call.id';\nconst GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE = 'gen_ai.tool.description';\nconst GEN_AI_EMBEDDINGS_OPERATION = 'embeddings';\nconst GEN_AI_RERANK_OPERATION = 'rerank';\n// The model-call op matches the Vercel AI OTel integration (`gen_ai.generate_content`) rather than\n// the generic `gen_ai.chat`, so v6 (OTel) and v7 (channel) produce the same spans.\nconst GEN_AI_GENERATE_CONTENT_OPERATION = 'generate_content';\n\n// Subset of the `vercel.ai.*` passthrough attributes the OTel integration emits that we reproduce.\nconst VERCEL_AI_OPERATION_ID_ATTRIBUTE = 'vercel.ai.operationId';\nconst VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE = 'vercel.ai.model.provider';\nconst VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE = 'vercel.ai.settings.maxRetries';\n\n// Tracks the top-level operationId (and whether it streams) per `callId` so a model-call span can\n// name its `doGenerate`/`doStream` operation the same way the OTel integration does. `isStream` is\n// the authoritative event-type signal rather than a substring check on the (possibly custom)\n// operationId. Cleared when the top-level span ends.\nconst operationIdByCallId = new Map<string, { operationId: string; isStream: boolean }>();\n\n// Per-operation map of tool name → description, harvested from a model-call /\n// top-level event's `tools` (keyed by the shared `callId`). The AI SDK's\n// `executeTool` event doesn't carry the tool's description, so we backfill it\n// onto the tool span here — without relying on the OTel `vercelAiEventProcessor`\n// (which isn't registered in channel/orchestrion mode). Cleared with the\n// operation. Only populated when inputs are recorded, matching the OTel path\n// (which sources descriptions from the recorded `available_tools`).\nconst toolDescriptionsByCallId = new Map<string, Map<string, string>>();\n\n// Only top-level operations own the `callId` → operationId mapping; `step`/`languageModelCall`/\n// `executeTool` share the parent's `callId`, so they must not clear it.\nconst ROOT_OPERATION_TYPES = new Set<ChannelEventType>(['generateText', 'streamText', 'embed', 'rerank']);\n\n/** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */\nexport function clearOperationId(data: VercelAiChannelMessage): void {\n if (!ROOT_OPERATION_TYPES.has(data.type)) {\n return;\n }\n const callId = asString(data.event.callId);\n if (callId) {\n operationIdByCallId.delete(callId);\n toolDescriptionsByCallId.delete(callId);\n }\n}\n\n/** Record tool name → description from an event's `tools`, so tool spans can backfill the description. */\nfunction recordToolDescriptions(callId: string | undefined, tools: unknown): void {\n if (!callId || !Array.isArray(tools)) {\n return;\n }\n let descriptions = toolDescriptionsByCallId.get(callId);\n for (const tool of tools) {\n if (isRecord(tool) && typeof tool.name === 'string' && typeof tool.description === 'string') {\n descriptions = descriptions ?? new Map();\n if (!descriptions.has(tool.name)) {\n descriptions.set(tool.name, tool.description);\n }\n }\n }\n if (descriptions) {\n toolDescriptionsByCallId.set(callId, descriptions);\n }\n}\n\n/**\n * Resolve a tool's description, preferring the per-operation map (populated from the model-call /\n * top-level event's `tools`, v7) and falling back to a `tools` collection on the event itself —\n * which may be an array of `{ name, description }` or a record keyed by tool name (v6).\n */\nfunction resolveToolDescription(callId: string | undefined, toolName: string, tools: unknown): string | undefined {\n const fromMap = callId ? toolDescriptionsByCallId.get(callId)?.get(toolName) : undefined;\n if (fromMap) {\n return fromMap;\n }\n if (Array.isArray(tools)) {\n const match = tools.find(tool => isRecord(tool) && tool.name === toolName);\n return isRecord(match) ? asString(match.description) : undefined;\n }\n if (isRecord(tools)) {\n const tool = tools[toolName];\n return isRecord(tool) ? asString(tool.description) : undefined;\n }\n return undefined;\n}\n\n/** The lifecycle event types the `ai:telemetry` channel can carry. */\nexport type ChannelEventType =\n | 'generateText'\n | 'streamText'\n | 'step'\n | 'languageModelCall'\n | 'executeTool'\n | 'embed'\n | 'rerank';\n\n/**\n * The context object the AI SDK passes through one tracing-channel call. It is the same object\n * identity across `start`/`end`/`asyncEnd`/`error`, and Node's `tracingChannel` attaches\n * `result`/`error` to it as the traced promise settles.\n */\nexport interface VercelAiChannelMessage {\n type: ChannelEventType;\n event: Record<string, unknown>;\n result?: unknown;\n error?: unknown;\n}\n\n/**\n * Platform-provided factory that returns a tracing channel for the given channel name. The factory\n * is responsible for, when `start` fires, calling `transformStart(data)` and storing the returned\n * span on `data._sentrySpan` so the subscriber's `asyncEnd`/`error` handlers can read it.\n *\n * Node passes `@sentry/opentelemetry/tracing-channel`, which uses `bindStore` to additionally make\n * the span the active OTel context for the duration of the traced operation. That is what makes\n * nested AI SDK operations (model calls, tool calls) become children of the enclosing span without\n * any manual parent bookkeeping here.\n */\ntype VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>;\n\n/** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */\ninterface VercelAiChannelOptions {\n recordInputs?: boolean;\n recordOutputs?: boolean;\n enableTruncation?: boolean;\n}\n\nlet subscribed = false;\n\n/**\n * Subscribe Sentry span handlers to the `ai` SDK's native telemetry tracing channel (`ai:telemetry`,\n * available in `ai` >= 7) and emit fully-formed `gen_ai.*` spans directly — no OpenTelemetry span\n * post-processing involved.\n *\n * The integration passes its options in directly (rather than us looking the integration up on every\n * event); the global `dataCollection.genAI` default is still read from the client per event.\n *\n * Safe to always call: on `ai` versions that don't publish to the channel (e.g. < 7) nothing is\n * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based\n * patcher. Idempotent.\n */\nexport function subscribeVercelAiTracingChannel(\n tracingChannel: VercelAiTracingChannelFactory,\n options: VercelAiChannelOptions = {},\n): void {\n if (subscribed) {\n return;\n }\n subscribed = true;\n\n bindTracingChannelToSpan(\n tracingChannel<VercelAiChannelMessage>(AI_SDK_TELEMETRY_TRACING_CHANNEL),\n data => createSpanFromMessage(data, options),\n {\n // The helper ends the span; we enrich it from the settled result first (tokens, output messages,\n // finish reasons, response model/id, provider metadata) and drop the per-operation `callId` maps.\n beforeSpanEnd: (span, data) => {\n enrichSpanOnEnd(span, data, options);\n clearOperationId(data);\n },\n },\n );\n}\n\n/**\n * Transform a channel `start` payload into the span that should be active for the operation. For\n * `step` we deliberately don't open a span (model calls and tool calls are siblings under the\n * invoke_agent span, matching the OTel-based output), so we reuse the active span and mark the\n * payload to skip ending it.\n */\nexport function createSpanFromMessage(\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): Span | undefined {\n const { type, event } = data;\n\n if (type === 'step' || !event || typeof event !== 'object') {\n // Opt out: returning `undefined` leaves the enclosing `invoke_agent` span as the active context\n // (model-call and tool-call events nest under it) without opening — or ending — a span of its own.\n return undefined;\n }\n\n const { recordInputs, enableTruncation } = getRecordingOptions(event, channelOptions);\n const provider = asString(event.provider);\n const modelId = asString(event.modelId);\n const callId = asString(event.callId);\n const maxRetries = asNumber(event.maxRetries);\n\n // Harvest tool descriptions from the operation/model-call `tools` so tool spans can backfill them.\n // Gated on `recordInputs` to match the OTel path, which only records `available_tools` then.\n if (recordInputs) {\n recordToolDescriptions(callId, event.tools);\n }\n\n const baseAttributes: Record<string, string | number | boolean> = {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n ...(provider ? { [GEN_AI_SYSTEM]: provider, [VERCEL_AI_MODEL_PROVIDER_ATTRIBUTE]: provider } : {}),\n ...(modelId ? { [GEN_AI_REQUEST_MODEL]: modelId } : {}),\n ...(maxRetries !== undefined ? { [VERCEL_AI_SETTINGS_MAX_RETRIES_ATTRIBUTE]: maxRetries } : {}),\n };\n\n switch (type) {\n case 'generateText':\n case 'streamText':\n return buildInvokeAgentSpan(event, baseAttributes, recordInputs, enableTruncation, callId, type === 'streamText');\n case 'languageModelCall':\n return buildModelCallSpan(event, baseAttributes, recordInputs, enableTruncation, callId, modelId);\n case 'executeTool':\n return buildToolSpan(event, recordInputs);\n case 'embed':\n return startGenAiSpan(GEN_AI_EMBEDDINGS_OPERATION, modelId, {\n ...baseAttributes,\n ...(recordInputs && event.value !== undefined ? { [GEN_AI_EMBEDDINGS_INPUT]: safeStringify(event.value) } : {}),\n });\n case 'rerank':\n return startGenAiSpan(GEN_AI_RERANK_OPERATION, modelId, baseAttributes);\n default:\n // Unknown event type: opt out rather than open a span we can't shape correctly.\n return undefined;\n }\n}\n\ntype Attributes = Record<string, string | number | boolean>;\n\n/** Start a `gen_ai.<operation>` span named `<operation> <suffix>` (or just `<operation>` when no suffix). */\nfunction startGenAiSpan(operation: string, suffix: string | undefined, attributes: Attributes): Span {\n return startInactiveSpan({\n name: suffix ? `${operation} ${suffix}` : operation,\n op: `gen_ai.${operation}`,\n attributes: { [GEN_AI_OPERATION_NAME]: operation, ...attributes },\n });\n}\n\nfunction buildInvokeAgentSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n isStream: boolean,\n): Span {\n const functionId = asString(event.functionId);\n const operationId = asString(event.operationId) ?? (isStream ? 'ai.streamText' : 'ai.generateText');\n if (callId) {\n operationIdByCallId.set(callId, { operationId, isStream });\n }\n return startGenAiSpan(GEN_AI_INVOKE_AGENT_SPAN_OP, functionId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n [GEN_AI_RESPONSE_STREAMING]: isStream,\n ...(functionId ? { [GEN_AI_FUNCTION_ID]: functionId } : {}),\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n });\n}\n\nfunction buildModelCallSpan(\n event: Record<string, unknown>,\n baseAttributes: Attributes,\n recordInputs: boolean,\n enableTruncation: boolean,\n callId: string | undefined,\n modelId: string | undefined,\n): Span {\n const parent = callId ? operationIdByCallId.get(callId) : undefined;\n const operationId = parent\n ? `${parent.operationId}.${parent.isStream ? 'doStream' : 'doGenerate'}`\n : 'ai.generateText.doGenerate';\n return startGenAiSpan(GEN_AI_GENERATE_CONTENT_OPERATION, modelId, {\n ...baseAttributes,\n [VERCEL_AI_OPERATION_ID_ATTRIBUTE]: operationId,\n ...(recordInputs ? buildInputMessageAttributes(event, enableTruncation) : {}),\n ...(recordInputs && Array.isArray(event.tools)\n ? { [GEN_AI_REQUEST_AVAILABLE_TOOLS]: safeStringify(event.tools) }\n : {}),\n });\n}\n\nfunction buildToolSpan(event: Record<string, unknown>, recordInputs: boolean): Span {\n const toolCall = isRecord(event.toolCall) ? event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(event.toolCallId) ?? asString(toolCall.toolCallId);\n const toolInput = toolCall.input ?? toolCall.args;\n // The `executeTool` event has no description; backfill it from the operation's recorded tools.\n // Gated on `recordInputs` to match the OTel path (descriptions come from the recorded tools list).\n const description =\n recordInputs && toolName ? resolveToolDescription(asString(event.callId), toolName, event.tools) : undefined;\n return startGenAiSpan(GEN_AI_EXECUTE_TOOL_SPAN_OP, toolName, {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: ORIGIN,\n [GEN_AI_TOOL_TYPE]: 'function',\n ...(toolName ? { [GEN_AI_TOOL_NAME]: toolName } : {}),\n ...(toolCallId ? { [GEN_AI_TOOL_CALL_ID_ATTRIBUTE]: toolCallId } : {}),\n ...(description ? { [GEN_AI_TOOL_DESCRIPTION_ATTRIBUTE]: description } : {}),\n ...(recordInputs && toolInput !== undefined ? { [GEN_AI_TOOL_INPUT]: safeStringify(toolInput) } : {}),\n });\n}\n\n/**\n * Best-effort enrichment from the resolved value the AI SDK attaches to the channel context.\n * Everything here is guarded: when a field is missing or the shape differs across `ai` versions,\n * we simply don't set the attribute rather than emit a malformed span.\n */\nexport function enrichSpanOnEnd(\n span: Span,\n data: VercelAiChannelMessage,\n channelOptions: VercelAiChannelOptions,\n): void {\n const { type, result } = data;\n if (!isRecord(result)) {\n return;\n }\n\n const { recordOutputs } = getRecordingOptions(data.event, channelOptions);\n\n if (type === 'executeTool') {\n if (recordOutputs) {\n span.setAttribute(GEN_AI_TOOL_OUTPUT, safeStringify(result.output ?? result));\n }\n // From V5 on, tool errors are not rejected (so the `error` channel verb never fires) — they\n // surface as `tool-error` content on the resolved result. Mirror the OTel path by marking the\n // span and capturing the error.\n const output = isRecord(result.output) ? result.output : undefined;\n if (output?.type === 'tool-error') {\n captureToolError(span, data, output.error);\n }\n return;\n }\n\n // `languageModelCall` results report usage as `{ total }` objects; top-level/step results report\n // flat numbers. `tokenCount` handles both.\n const usage = isRecord(result.usage) ? result.usage : undefined;\n if (usage) {\n const inputTokens = tokenCount(usage.inputTokens) ?? tokenCount(usage.tokens);\n const outputTokens = tokenCount(usage.outputTokens);\n const totalTokens = tokenCount(usage.totalTokens) ?? sum(inputTokens, outputTokens);\n if (inputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_INPUT_TOKENS, inputTokens);\n }\n if (outputTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_OUTPUT_TOKENS, outputTokens);\n }\n if (totalTokens !== undefined) {\n span.setAttribute(GEN_AI_USAGE_TOTAL_TOKENS, totalTokens);\n }\n }\n\n // Match the OTel integration: finish reasons live on the model-call (`generate_content`) span, not\n // on the top-level `invoke_agent` span.\n const finishReason = getFinishReason(result);\n if (finishReason && type === 'languageModelCall') {\n span.setAttribute(GEN_AI_RESPONSE_FINISH_REASONS, safeStringify([finishReason]));\n }\n\n const response = isRecord(result.response) ? result.response : undefined;\n const responseId = asString(response?.id) ?? asString(result.responseId);\n if (responseId) {\n span.setAttribute(GEN_AI_RESPONSE_ID, responseId);\n }\n const responseModel = asString(response?.modelId) ?? asString(data.event.modelId);\n if (responseModel) {\n span.setAttribute(GEN_AI_RESPONSE_MODEL, responseModel);\n }\n\n // Provider-specific cache/reasoning/prediction token breakdowns and `gen_ai.conversation.id`.\n // The channel exposes `providerMetadata` as an object (the OTel path parses it from a string);\n // both share `getProviderMetadataAttributes` so the emitted shape is identical.\n const providerMetadata = (result as { providerMetadata?: unknown }).providerMetadata;\n const providerAttributes = getProviderMetadataAttributes(providerMetadata);\n // Don't overwrite a conversation id already set on span start (e.g. by `conversationIdIntegration`\n // from a user-set scope value); the provider-derived id is only a fallback. Matches the OTel path.\n if (\n GEN_AI_CONVERSATION_ID_ATTRIBUTE in providerAttributes &&\n spanToJSON(span).data[GEN_AI_CONVERSATION_ID_ATTRIBUTE]\n ) {\n // oxlint-disable-next-line typescript/no-dynamic-delete\n delete providerAttributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE];\n }\n span.setAttributes(providerAttributes);\n\n if (recordOutputs) {\n // `languageModelCall` exposes the response as a `content` parts array; top-level results expose\n // `text` + `toolCalls`. Both normalize into the OTel `gen_ai.output.messages` assistant message.\n const parts =\n type === 'languageModelCall' && Array.isArray(result.content)\n ? partsFromContent(result.content)\n : partsFromTextAndToolCalls(result.text, result.toolCalls);\n const outputMessages = buildOutputMessages(parts, finishReason);\n if (outputMessages) {\n span.setAttribute(GEN_AI_OUTPUT_MESSAGES, outputMessages);\n }\n }\n}\n\n/** Maps a Vercel AI finish reason to the OTel `gen_ai.output.messages` form (`tool-calls` → `tool_call`). */\nfunction normalizeFinishReason(finishReason: string | undefined): string {\n return finishReason === 'tool-calls' ? 'tool_call' : (finishReason ?? 'stop');\n}\n\n/** Reads the finish reason from a result — a string on top-level results, `{ unified }` on model calls. */\nfunction getFinishReason(result: Record<string, unknown>): string | undefined {\n const finishReason = result.finishReason;\n if (typeof finishReason === 'string') {\n return finishReason;\n }\n return isRecord(finishReason) ? asString(finishReason.unified) : undefined;\n}\n\n/** Reads a token count that may be a plain number or a `{ total }` object (model-call usage). */\nfunction tokenCount(value: unknown): number | undefined {\n return asNumber(value) ?? (isRecord(value) ? asNumber(value.total) : undefined);\n}\n\nfunction buildOutputMessages(\n parts: Array<Record<string, unknown>>,\n finishReason: string | undefined,\n): string | undefined {\n if (!parts.length) {\n return undefined;\n }\n return safeStringify([{ role: 'assistant', parts, finish_reason: normalizeFinishReason(finishReason) }]);\n}\n\nfunction toolCallPart(toolCall: Record<string, unknown>): Record<string, unknown> {\n const args = toolCall.input ?? toolCall.args;\n return {\n type: 'tool_call',\n id: asString(toolCall.toolCallId),\n name: asString(toolCall.toolName),\n arguments: typeof args === 'string' ? args : safeStringify(args ?? {}),\n };\n}\n\nfunction partsFromContent(content: unknown[]): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n for (const item of content) {\n if (!isRecord(item)) {\n continue;\n }\n if (item.type === 'text' && typeof item.text === 'string') {\n parts.push({ type: 'text', content: item.text });\n } else if (item.type === 'tool-call') {\n parts.push(toolCallPart(item));\n }\n }\n return parts;\n}\n\nfunction partsFromTextAndToolCalls(text: unknown, toolCalls: unknown): Array<Record<string, unknown>> {\n const parts: Array<Record<string, unknown>> = [];\n if (typeof text === 'string' && text.length) {\n parts.push({ type: 'text', content: text });\n }\n if (Array.isArray(toolCalls)) {\n for (const toolCall of toolCalls) {\n if (isRecord(toolCall)) {\n parts.push(toolCallPart(toolCall));\n }\n }\n }\n return parts;\n}\n\nfunction captureToolError(span: Span, data: VercelAiChannelMessage, error: unknown): void {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: error instanceof Error ? error.message : 'tool_error',\n });\n\n const toolCall = isRecord(data.event.toolCall) ? data.event.toolCall : {};\n const toolName = asString(toolCall.toolName);\n const toolCallId = asString(data.event.toolCallId) ?? asString(toolCall.toolCallId);\n\n withScope(scope => {\n scope.setContext('trace', spanToTraceContext(span));\n if (toolName) {\n scope.setTag('vercel.ai.tool.name', toolName);\n }\n if (toolCallId) {\n scope.setTag('vercel.ai.tool.callId', toolCallId);\n }\n scope.setLevel('error');\n captureException(\n error instanceof Error ? error : new Error(typeof error === 'string' ? error : 'Tool execution failed'),\n {\n mechanism: { type: 'auto.vercelai.channel', handled: false },\n },\n );\n });\n}\n\nfunction getRecordingOptions(\n event: Record<string, unknown>,\n channelOptions: VercelAiChannelOptions,\n): {\n recordInputs: boolean;\n recordOutputs: boolean;\n enableTruncation: boolean;\n} {\n const genAI = getClient()?.getDataCollectionOptions().genAI;\n\n return {\n recordInputs: resolveRecording(channelOptions.recordInputs, event.recordInputs, genAI?.inputs),\n recordOutputs: resolveRecording(channelOptions.recordOutputs, event.recordOutputs, genAI?.outputs),\n enableTruncation: shouldEnableTruncation(channelOptions.enableTruncation),\n };\n}\n\n/**\n * Mirrors the OTel integration's `determineRecordingSettings` precedence: an integration-level option\n * wins, then the per-call `experimental_telemetry.recordInputs/recordOutputs` flag the AI SDK forwards\n * on the channel event, then the global `dataCollection.genAI` default.\n *\n * NOTE: the OTel integration also defaults recording to `true` for a call with\n * `experimental_telemetry: { isEnabled: true }`. The `ai:telemetry` channel does not expose `isEnabled`\n * (nor a resolved recording flag), so that per-call default cannot be reproduced here — v7 users who\n * want inputs/outputs recorded must enable `dataCollection.genAI` or set `recordInputs`/`recordOutputs`.\n */\nfunction resolveRecording(integrationOption: unknown, perCallOption: unknown, globalDefault: unknown): boolean {\n if (typeof integrationOption === 'boolean') {\n return integrationOption;\n }\n if (typeof perCallOption === 'boolean') {\n return perCallOption;\n }\n return globalDefault === true;\n}\n\nfunction buildInputMessageAttributes(\n event: Record<string, unknown>,\n enableTruncation: boolean,\n): Record<string, string | number> {\n const attributes: Record<string, string | number> = {};\n\n // `ai` >= 7 forbids system messages in `messages`/`prompt` and exposes the system prompt as a\n // separate `instructions` field. The OTel path lifts the system message out of the prompt into\n // `gen_ai.system_instructions` as `[{ type: 'text', content }]`; mirror that shape here.\n const instructions = asString(event.instructions);\n if (instructions) {\n attributes[GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE] = safeStringify([{ type: 'text', content: instructions }]);\n }\n\n // The AI SDK start events extend `StandardizedPrompt`; messages live on `messages`, otherwise the\n // simpler `prompt` field is used.\n const messages = event.messages ?? event.prompt;\n if (messages !== undefined) {\n attributes[GEN_AI_INPUT_MESSAGES] = enableTruncation ? getTruncatedJsonString(messages) : safeStringify(messages);\n // The original (pre-truncation) message count, so the product can show how many were dropped.\n attributes[GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE] = Array.isArray(messages) ? messages.length : 1;\n }\n\n return attributes;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asNumber(value: unknown): number | undefined {\n return typeof value === 'number' && !isNaN(value) ? value : undefined;\n}\n\nfunction sum(a: number | undefined, b: number | undefined): number | undefined {\n return a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction safeStringify(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n}\n"],"names":[],"mappings":"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"} |
| type VercelAiOptions = { | ||
| /** | ||
| * Enable or disable input recording. Enabled if `dataCollection.genAI.inputs` (or the deprecated `sendDefaultPii` option) is `true` | ||
| * or if you set `isEnabled` to `true` in your ai SDK method telemetry settings. | ||
| * Integration-level options take precedence over global `dataCollection` config. | ||
| */ | ||
| recordInputs?: boolean; | ||
| /** | ||
| * Enable or disable output recording. Enabled if `dataCollection.genAI.outputs` (or the deprecated `sendDefaultPii` option) is `true` | ||
| * or if you set `isEnabled` to `true` in your ai SDK method telemetry settings. | ||
| * Integration-level options take precedence over global `dataCollection` config. | ||
| */ | ||
| recordOutputs?: boolean; | ||
| /** | ||
| * Enable or disable truncation of recorded input messages. | ||
| * Defaults to `true`. | ||
| */ | ||
| enableTruncation?: boolean; | ||
| }; | ||
| /** | ||
| * Auto-instrument the `ai` SDK's native telemetry tracing channel (ai >= 7). | ||
| */ | ||
| export declare const vercelAiIntegration: (options?: VercelAiOptions | undefined) => import("@sentry/core").Integration; | ||
| export {}; | ||
| //# sourceMappingURL=index.d.ts.map |
| import { Span } from '@sentry/core'; | ||
| import { TracingChannel } from 'node:diagnostics_channel'; | ||
| /** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */ | ||
| export declare function clearOperationId(data: VercelAiChannelMessage): void; | ||
| /** The lifecycle event types the `ai:telemetry` channel can carry. */ | ||
| export type ChannelEventType = 'generateText' | 'streamText' | 'step' | 'languageModelCall' | 'executeTool' | 'embed' | 'rerank'; | ||
| /** | ||
| * The context object the AI SDK passes through one tracing-channel call. It is the same object | ||
| * identity across `start`/`end`/`asyncEnd`/`error`, and Node's `tracingChannel` attaches | ||
| * `result`/`error` to it as the traced promise settles. | ||
| */ | ||
| export interface VercelAiChannelMessage { | ||
| type: ChannelEventType; | ||
| event: Record<string, unknown>; | ||
| result?: unknown; | ||
| error?: unknown; | ||
| } | ||
| /** | ||
| * Platform-provided factory that returns a tracing channel for the given channel name. The factory | ||
| * is responsible for, when `start` fires, calling `transformStart(data)` and storing the returned | ||
| * span on `data._sentrySpan` so the subscriber's `asyncEnd`/`error` handlers can read it. | ||
| * | ||
| * Node passes `@sentry/opentelemetry/tracing-channel`, which uses `bindStore` to additionally make | ||
| * the span the active OTel context for the duration of the traced operation. That is what makes | ||
| * nested AI SDK operations (model calls, tool calls) become children of the enclosing span without | ||
| * any manual parent bookkeeping here. | ||
| */ | ||
| type VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>; | ||
| /** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */ | ||
| interface VercelAiChannelOptions { | ||
| recordInputs?: boolean; | ||
| recordOutputs?: boolean; | ||
| enableTruncation?: boolean; | ||
| } | ||
| /** | ||
| * Subscribe Sentry span handlers to the `ai` SDK's native telemetry tracing channel (`ai:telemetry`, | ||
| * available in `ai` >= 7) and emit fully-formed `gen_ai.*` spans directly — no OpenTelemetry span | ||
| * post-processing involved. | ||
| * | ||
| * The integration passes its options in directly (rather than us looking the integration up on every | ||
| * event); the global `dataCollection.genAI` default is still read from the client per event. | ||
| * | ||
| * Safe to always call: on `ai` versions that don't publish to the channel (e.g. < 7) nothing is | ||
| * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based | ||
| * patcher. Idempotent. | ||
| */ | ||
| export declare function subscribeVercelAiTracingChannel(tracingChannel: VercelAiTracingChannelFactory, options?: VercelAiChannelOptions): void; | ||
| /** | ||
| * Transform a channel `start` payload into the span that should be active for the operation. For | ||
| * `step` we deliberately don't open a span (model calls and tool calls are siblings under the | ||
| * invoke_agent span, matching the OTel-based output), so we reuse the active span and mark the | ||
| * payload to skip ending it. | ||
| */ | ||
| export declare function createSpanFromMessage(data: VercelAiChannelMessage, channelOptions: VercelAiChannelOptions): Span | undefined; | ||
| /** | ||
| * Best-effort enrichment from the resolved value the AI SDK attaches to the channel context. | ||
| * Everything here is guarded: when a field is missing or the shape differs across `ai` versions, | ||
| * we simply don't set the attribute rather than emit a malformed span. | ||
| */ | ||
| export declare function enrichSpanOnEnd(span: Span, data: VercelAiChannelMessage, channelOptions: VercelAiChannelOptions): void; | ||
| export {}; | ||
| //# sourceMappingURL=vercel-ai-dc-subscriber.d.ts.map |
| type VercelAiOptions = { | ||
| /** | ||
| * Enable or disable input recording. Enabled if `dataCollection.genAI.inputs` (or the deprecated `sendDefaultPii` option) is `true` | ||
| * or if you set `isEnabled` to `true` in your ai SDK method telemetry settings. | ||
| * Integration-level options take precedence over global `dataCollection` config. | ||
| */ | ||
| recordInputs?: boolean; | ||
| /** | ||
| * Enable or disable output recording. Enabled if `dataCollection.genAI.outputs` (or the deprecated `sendDefaultPii` option) is `true` | ||
| * or if you set `isEnabled` to `true` in your ai SDK method telemetry settings. | ||
| * Integration-level options take precedence over global `dataCollection` config. | ||
| */ | ||
| recordOutputs?: boolean; | ||
| /** | ||
| * Enable or disable truncation of recorded input messages. | ||
| * Defaults to `true`. | ||
| */ | ||
| enableTruncation?: boolean; | ||
| }; | ||
| /** | ||
| * Auto-instrument the `ai` SDK's native telemetry tracing channel (ai >= 7). | ||
| */ | ||
| export declare const vercelAiIntegration: (options?: VercelAiOptions | undefined) => import("@sentry/core").Integration; | ||
| export {}; | ||
| //# sourceMappingURL=index.d.ts.map |
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/vercel-ai/index.ts"],"names":[],"mappings":"AAIA,KAAK,eAAe,GAAG;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAoBF;;GAEG;AACH,eAAO,MAAM,mBAAmB,+EAA0C,CAAC"} |
| import type { Span } from '@sentry/core'; | ||
| import type { TracingChannel } from 'node:diagnostics_channel'; | ||
| /** Drop the per-operation `callId` maps once the owning top-level operation settles (success or error). */ | ||
| export declare function clearOperationId(data: VercelAiChannelMessage): void; | ||
| /** The lifecycle event types the `ai:telemetry` channel can carry. */ | ||
| export type ChannelEventType = 'generateText' | 'streamText' | 'step' | 'languageModelCall' | 'executeTool' | 'embed' | 'rerank'; | ||
| /** | ||
| * The context object the AI SDK passes through one tracing-channel call. It is the same object | ||
| * identity across `start`/`end`/`asyncEnd`/`error`, and Node's `tracingChannel` attaches | ||
| * `result`/`error` to it as the traced promise settles. | ||
| */ | ||
| export interface VercelAiChannelMessage { | ||
| type: ChannelEventType; | ||
| event: Record<string, unknown>; | ||
| result?: unknown; | ||
| error?: unknown; | ||
| } | ||
| /** | ||
| * Platform-provided factory that returns a tracing channel for the given channel name. The factory | ||
| * is responsible for, when `start` fires, calling `transformStart(data)` and storing the returned | ||
| * span on `data._sentrySpan` so the subscriber's `asyncEnd`/`error` handlers can read it. | ||
| * | ||
| * Node passes `@sentry/opentelemetry/tracing-channel`, which uses `bindStore` to additionally make | ||
| * the span the active OTel context for the duration of the traced operation. That is what makes | ||
| * nested AI SDK operations (model calls, tool calls) become children of the enclosing span without | ||
| * any manual parent bookkeeping here. | ||
| */ | ||
| type VercelAiTracingChannelFactory = <T extends object>(name: string) => TracingChannel<T, T>; | ||
| /** Integration-level recording options, pinned at subscribe time so we never look the integration up per event. */ | ||
| interface VercelAiChannelOptions { | ||
| recordInputs?: boolean; | ||
| recordOutputs?: boolean; | ||
| enableTruncation?: boolean; | ||
| } | ||
| /** | ||
| * Subscribe Sentry span handlers to the `ai` SDK's native telemetry tracing channel (`ai:telemetry`, | ||
| * available in `ai` >= 7) and emit fully-formed `gen_ai.*` spans directly — no OpenTelemetry span | ||
| * post-processing involved. | ||
| * | ||
| * The integration passes its options in directly (rather than us looking the integration up on every | ||
| * event); the global `dataCollection.genAI` default is still read from the client per event. | ||
| * | ||
| * Safe to always call: on `ai` versions that don't publish to the channel (e.g. < 7) nothing is | ||
| * ever emitted and this is inert, so there is no double-instrumentation against the OTel-based | ||
| * patcher. Idempotent. | ||
| */ | ||
| export declare function subscribeVercelAiTracingChannel(tracingChannel: VercelAiTracingChannelFactory, options?: VercelAiChannelOptions): void; | ||
| /** | ||
| * Transform a channel `start` payload into the span that should be active for the operation. For | ||
| * `step` we deliberately don't open a span (model calls and tool calls are siblings under the | ||
| * invoke_agent span, matching the OTel-based output), so we reuse the active span and mark the | ||
| * payload to skip ending it. | ||
| */ | ||
| export declare function createSpanFromMessage(data: VercelAiChannelMessage, channelOptions: VercelAiChannelOptions): Span | undefined; | ||
| /** | ||
| * Best-effort enrichment from the resolved value the AI SDK attaches to the channel context. | ||
| * Everything here is guarded: when a field is missing or the shape differs across `ai` versions, | ||
| * we simply don't set the attribute rather than emit a malformed span. | ||
| */ | ||
| export declare function enrichSpanOnEnd(span: Span, data: VercelAiChannelMessage, channelOptions: VercelAiChannelOptions): void; | ||
| export {}; | ||
| //# sourceMappingURL=vercel-ai-dc-subscriber.d.ts.map |
| {"version":3,"file":"vercel-ai-dc-subscriber.d.ts","sourceRoot":"","sources":["../../../src/vercel-ai/vercel-ai-dc-subscriber.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAiBzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA6C/D,2GAA2G;AAC3G,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CASnE;AA0CD,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GACxB,cAAc,GACd,YAAY,GACZ,MAAM,GACN,mBAAmB,GACnB,aAAa,GACb,OAAO,GACP,QAAQ,CAAC;AAEb;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,KAAK,6BAA6B,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE9F,mHAAmH;AACnH,UAAU,sBAAsB;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,+BAA+B,CAC7C,cAAc,EAAE,6BAA6B,EAC7C,OAAO,GAAE,sBAA2B,GACnC,IAAI,CAkBN;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,sBAAsB,EAC5B,cAAc,EAAE,sBAAsB,GACrC,IAAI,GAAG,SAAS,CA+ClB;AA4ED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,sBAAsB,EAC5B,cAAc,EAAE,sBAAsB,GACrC,IAAI,CAqFN"} |
@@ -5,2 +5,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const tracingChannel = require('./tracing-channel.js'); | ||
| const index = require('./vercel-ai/index.js'); | ||
@@ -16,2 +17,3 @@ | ||
| exports.bindTracingChannelToSpan = tracingChannel.bindTracingChannelToSpan; | ||
| exports.vercelAiIntegration = index.vercelAiIntegration; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"} | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"} |
| Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| const diagnosticsChannel = require('node:diagnostics_channel'); | ||
| const dc = require('node:diagnostics_channel'); | ||
| const core = require('@sentry/core'); | ||
@@ -21,3 +21,3 @@ const debugBuild = require('../../debug-build.js'); | ||
| debugBuild.DEBUG_BUILD && core.debug.log(`[orchestrion:mysql] subscribing to channel "${channels.CHANNELS.MYSQL_QUERY}"`); | ||
| const queryCh = diagnosticsChannel.tracingChannel(channels.CHANNELS.MYSQL_QUERY); | ||
| const queryCh = dc.tracingChannel(channels.CHANNELS.MYSQL_QUERY); | ||
| const spans = /* @__PURE__ */ new WeakMap(); | ||
@@ -24,0 +24,0 @@ const parentScopes = /* @__PURE__ */ new WeakMap(); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mysql.js","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope, Span } from '@sentry/core';\nimport {\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, OTel 'Mysql' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Mysql';\n\n// OpenTelemetry \"OLD\" db/net semantic-conventions. We inline them rather than\n// importing `@opentelemetry/semantic-conventions` to keep this integration's\n// dependency surface free of OTel — orchestrion's whole point is to step away\n// from the OTel auto-instrumentation stack.\n//\n// We emit the OLD conventions to match `@opentelemetry/instrumentation-mysql`'s\n// default (it only emits the stable `db.system.name` / `db.query.text` set when\n// `OTEL_SEMCONV_STABILITY_OPT_IN=database` is opted into) and the rest of the\n// Sentry JS SDK, whose `inferDbSpanData` processor renames spans based on\n// `db.statement`.\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\n\n/**\n * The shape orchestrion's wrapCallback transform attaches to the tracing-channel\n * `context` object. Documented here rather than imported because orchestrion's\n * runtime doesn't export it — see `node_modules/@apm-js-collab/code-transformer/lib/transforms.js`.\n *\n * `arguments` is the *live* args array passed to the wrapped function: orchestrion\n * splices the user's callback out and inserts its own wrapper at the same index\n * before publishing `start`. The `start` hook re-wraps that entry to restore the\n * caller's scope across mysql's async callback dispatch (see below).\n */\ninterface MysqlQueryChannelContext {\n arguments: unknown[];\n self?: MysqlConnection;\n moduleVersion?: string;\n result?: unknown;\n error?: unknown;\n}\n\ninterface MysqlConnectionConfig {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n // Pool connections nest the real config one level deeper.\n connectionConfig?: MysqlConnectionConfig;\n}\n\ninterface MysqlConnection {\n config?: MysqlConnectionConfig;\n}\n\nconst _mysqlChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel \"${CHANNELS.MYSQL_QUERY}\"`);\n const queryCh = diagnosticsChannel.tracingChannel(CHANNELS.MYSQL_QUERY);\n\n // Orchestrion creates one `context` object per call, shared across all\n // lifecycle hooks. We key both maps off that identity; `WeakMap` so an\n // unfinished path can't leak its entries.\n const spans = new WeakMap<object, Span>();\n // The scope active when the query was issued, consumed in `end` to bind\n // the streamed `Query` emitter's listeners to it.\n const parentScopes = new WeakMap<object, Scope>();\n\n // `subscribe()` requires all five lifecycle hooks. The orchestrion\n // `wrapAuto` transform fires events in one of four orders depending on\n // call shape:\n // - sync throw : start → error → end\n // (NO asyncEnd)\n // - async-callback error : start → end → error →\n // asyncStart → asyncEnd\n // - async-callback success : start → end → asyncStart →\n // asyncEnd\n // - no-callback (streamable Query) : start → end\n // (ctx.result is the Query\n // emitter, no async events)\n //\n // Where the span closes depends on the path: `asyncEnd` for callbacks (so\n // it spans the full round-trip + callback), or `end` for the sync-throw\n // and streamable paths. The `end` hook tells those apart via `ctx.error`\n // / `ctx.result` — see there.\n queryCh.subscribe({\n start(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n const sql = extractSql(ctx.arguments[0]);\n const { host, port, database, user } = getConnectionConfig(ctx.self);\n const portNumber = typeof port === 'string' ? parseInt(port, 10) : port;\n const portIsNumber = typeof portNumber === 'number' && !isNaN(portNumber);\n\n const span = startInactiveSpan({\n name: sql ?? 'mysql.query',\n op: 'db',\n attributes: {\n [ATTR_DB_SYSTEM]: 'mysql',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.orchestrion.mysql',\n [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, portIsNumber ? portNumber : undefined, database),\n ...(database ? { [ATTR_DB_NAME]: database } : {}),\n ...(user ? { [ATTR_DB_USER]: user } : {}),\n ...(sql ? { [ATTR_DB_STATEMENT]: sql } : {}),\n ...(host ? { [ATTR_NET_PEER_NAME]: host } : {}),\n ...(portIsNumber ? { [ATTR_NET_PEER_PORT]: portNumber } : {}),\n },\n });\n spans.set(rawCtx, span);\n\n // Capture the scope while we're still synchronously inside the\n // caller's `connection.query` call. mysql v2 drains callbacks and\n // emits streamed-query events from its socket data handler, where the\n // AsyncLocalStorage store backing the active span no longer reflects\n // the caller's context — and `asyncStart`/`asyncEnd` fire from that\n // same lost context, so capturing has to happen now.\n const scope = getCurrentScope();\n parentScopes.set(rawCtx, scope);\n\n // Callback path: orchestrion has spliced the user's callback out of\n // `ctx.arguments` and put its own wrapper (`__apm$wrappedCb`) at the\n // same index. Re-wrap it so the callback — and any nested\n // `connection.query(...)` — runs with the captured scope active.\n if (ctx.arguments.length > 0) {\n const cbIdx = ctx.arguments.length - 1;\n const orchestrionWrappedCb = ctx.arguments[cbIdx];\n if (typeof orchestrionWrappedCb === 'function') {\n const wrapped = orchestrionWrappedCb as (...a: unknown[]) => unknown;\n ctx.arguments[cbIdx] = function (this: unknown, ...args: unknown[]): unknown {\n return withScope(scope, () => wrapped.apply(this, args));\n };\n }\n }\n },\n\n end(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n\n // Sync throw: `end` fires AFTER `error` (both inside the wrapper's\n // `try/catch/finally`), so `ctx.error` is already set. Close the\n // span now since no `asyncEnd` will fire.\n if (ctx.error !== undefined) {\n finishSpan(rawCtx);\n return;\n }\n\n // No-callback (streamable Query) path: orchestrion's `wrapPromise`\n // stores the synchronous return value on `ctx.result` and never\n // fires `asyncStart`/`asyncEnd`. The returned `Query` is an\n // `EventEmitter` that emits `'end'` on success and `'error'` on\n // failure — hook those to close the span.\n // Note: a streamed span never finishes if the connection is destroyed\n // mid-flight — mysql then emits neither `'end'` nor `'error'`, so the\n // span is dropped (the `WeakMap` still prevents a leak). Closing this\n // needs connection-level hooks the per-query context doesn't expose.\n const result = ctx.result;\n if (result && typeof result === 'object' && hasOnMethod(result)) {\n const span = spans.get(rawCtx);\n if (!span) return;\n\n // Bind the captured scope to the streamed `Query` emitter: its\n // `'end'`/`'error'`/`'fields'`/… events fire from mysql's socket\n // handler with the caller's context lost, so without this a span\n // started in a user's stream listener would begin a fresh root trace\n // instead of nesting under the parent. `bindScopeToEmitter` patches\n // `on`/`addListener`/… so listeners added after `query()` returns\n // inherit the scope (like OTel's `context.bind`).\n const parentScope = parentScopes.get(rawCtx);\n if (parentScope) {\n bindScopeToEmitter(result, parentScope);\n }\n\n result.on('error', err => {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: err instanceof Error ? err.message : 'unknown_error',\n });\n // Defensive: end the span here too in case `'end'` never fires\n // (e.g. abrupt socket destruction). `finishSpan` is idempotent —\n // `spans.delete` makes the subsequent `'end'` listener a no-op.\n finishSpan(rawCtx);\n });\n result.on('end', () => finishSpan(rawCtx));\n return;\n }\n\n // Callback path: `asyncEnd` will close the span. Nothing to do here.\n },\n\n error(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n const span = spans.get(rawCtx);\n if (!span) return;\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: ctx.error instanceof Error ? ctx.error.message : 'unknown_error',\n });\n },\n\n asyncStart() {\n // No-op: we end on `asyncEnd` so the span covers the full callback duration.\n },\n\n asyncEnd(rawCtx) {\n finishSpan(rawCtx);\n },\n });\n\n function finishSpan(rawCtx: object): void {\n const span = spans.get(rawCtx);\n if (!span) return;\n span.end();\n spans.delete(rawCtx);\n parentScopes.delete(rawCtx);\n }\n },\n };\n}) satisfies IntegrationFn;\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\nfunction extractSql(firstArg: unknown): string | undefined {\n if (typeof firstArg === 'string') {\n return firstArg;\n }\n if (firstArg && typeof firstArg === 'object' && 'sql' in firstArg) {\n const sql = (firstArg as { sql?: unknown }).sql;\n return typeof sql === 'string' ? sql : undefined;\n }\n return undefined;\n}\n\nfunction getConnectionConfig(connection: MysqlConnection | undefined): {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n} {\n // Pool connections nest the real config under `.connectionConfig`; single\n // connections expose it directly. Matches `@opentelemetry/instrumentation-mysql`.\n const config = connection?.config?.connectionConfig ?? connection?.config ?? {};\n return {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n };\n}\n\nfunction getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined): string {\n let s = `jdbc:mysql://${host || 'localhost'}`;\n if (typeof port === 'number') {\n s += `:${port}`;\n }\n if (database) {\n s += `/${database}`;\n }\n return s;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven mysql integration.\n *\n * Subscribes to the `orchestrion:mysql:query` diagnostics_channel that the\n * orchestrion code transform injects into `mysql/lib/Connection.js`'s\n * `Connection.prototype.query`. Requires the orchestrion runtime hook or\n * bundler plugin to be active — wire that up via `_experimentalSetupOrchestrion`.\n */\nexport const mysqlChannelIntegration = defineIntegration(_mysqlChannelIntegration);\n"],"names":["DEBUG_BUILD","debug","CHANNELS","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","getCurrentScope","withScope","bindScopeToEmitter","SPAN_STATUS_ERROR","defineIntegration"],"mappings":"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"} | ||
| {"version":3,"file":"mysql.js","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope, Span } from '@sentry/core';\nimport {\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, OTel 'Mysql' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Mysql';\n\n// OpenTelemetry \"OLD\" db/net semantic-conventions. We inline them rather than\n// importing `@opentelemetry/semantic-conventions` to keep this integration's\n// dependency surface free of OTel — orchestrion's whole point is to step away\n// from the OTel auto-instrumentation stack.\n//\n// We emit the OLD conventions to match `@opentelemetry/instrumentation-mysql`'s\n// default (it only emits the stable `db.system.name` / `db.query.text` set when\n// `OTEL_SEMCONV_STABILITY_OPT_IN=database` is opted into) and the rest of the\n// Sentry JS SDK, whose `inferDbSpanData` processor renames spans based on\n// `db.statement`.\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\n\n/**\n * The shape orchestrion's wrapCallback transform attaches to the tracing-channel\n * `context` object. Documented here rather than imported because orchestrion's\n * runtime doesn't export it — see `node_modules/@apm-js-collab/code-transformer/lib/transforms.js`.\n *\n * `arguments` is the *live* args array passed to the wrapped function: orchestrion\n * splices the user's callback out and inserts its own wrapper at the same index\n * before publishing `start`. The `start` hook re-wraps that entry to restore the\n * caller's scope across mysql's async callback dispatch (see below).\n */\ninterface MysqlQueryChannelContext {\n arguments: unknown[];\n self?: MysqlConnection;\n moduleVersion?: string;\n result?: unknown;\n error?: unknown;\n}\n\ninterface MysqlConnectionConfig {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n // Pool connections nest the real config one level deeper.\n connectionConfig?: MysqlConnectionConfig;\n}\n\ninterface MysqlConnection {\n config?: MysqlConnectionConfig;\n}\n\nconst _mysqlChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel \"${CHANNELS.MYSQL_QUERY}\"`);\n const queryCh = diagnosticsChannel.tracingChannel(CHANNELS.MYSQL_QUERY);\n\n // Orchestrion creates one `context` object per call, shared across all\n // lifecycle hooks. We key both maps off that identity; `WeakMap` so an\n // unfinished path can't leak its entries.\n const spans = new WeakMap<object, Span>();\n // The scope active when the query was issued, consumed in `end` to bind\n // the streamed `Query` emitter's listeners to it.\n const parentScopes = new WeakMap<object, Scope>();\n\n // `subscribe()` requires all five lifecycle hooks. The orchestrion\n // `wrapAuto` transform fires events in one of four orders depending on\n // call shape:\n // - sync throw : start → error → end\n // (NO asyncEnd)\n // - async-callback error : start → end → error →\n // asyncStart → asyncEnd\n // - async-callback success : start → end → asyncStart →\n // asyncEnd\n // - no-callback (streamable Query) : start → end\n // (ctx.result is the Query\n // emitter, no async events)\n //\n // Where the span closes depends on the path: `asyncEnd` for callbacks (so\n // it spans the full round-trip + callback), or `end` for the sync-throw\n // and streamable paths. The `end` hook tells those apart via `ctx.error`\n // / `ctx.result` — see there.\n queryCh.subscribe({\n start(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n const sql = extractSql(ctx.arguments[0]);\n const { host, port, database, user } = getConnectionConfig(ctx.self);\n const portNumber = typeof port === 'string' ? parseInt(port, 10) : port;\n const portIsNumber = typeof portNumber === 'number' && !isNaN(portNumber);\n\n const span = startInactiveSpan({\n name: sql ?? 'mysql.query',\n op: 'db',\n attributes: {\n [ATTR_DB_SYSTEM]: 'mysql',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.orchestrion.mysql',\n [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, portIsNumber ? portNumber : undefined, database),\n ...(database ? { [ATTR_DB_NAME]: database } : {}),\n ...(user ? { [ATTR_DB_USER]: user } : {}),\n ...(sql ? { [ATTR_DB_STATEMENT]: sql } : {}),\n ...(host ? { [ATTR_NET_PEER_NAME]: host } : {}),\n ...(portIsNumber ? { [ATTR_NET_PEER_PORT]: portNumber } : {}),\n },\n });\n spans.set(rawCtx, span);\n\n // Capture the scope while we're still synchronously inside the\n // caller's `connection.query` call. mysql v2 drains callbacks and\n // emits streamed-query events from its socket data handler, where the\n // AsyncLocalStorage store backing the active span no longer reflects\n // the caller's context — and `asyncStart`/`asyncEnd` fire from that\n // same lost context, so capturing has to happen now.\n const scope = getCurrentScope();\n parentScopes.set(rawCtx, scope);\n\n // Callback path: orchestrion has spliced the user's callback out of\n // `ctx.arguments` and put its own wrapper (`__apm$wrappedCb`) at the\n // same index. Re-wrap it so the callback — and any nested\n // `connection.query(...)` — runs with the captured scope active.\n if (ctx.arguments.length > 0) {\n const cbIdx = ctx.arguments.length - 1;\n const orchestrionWrappedCb = ctx.arguments[cbIdx];\n if (typeof orchestrionWrappedCb === 'function') {\n const wrapped = orchestrionWrappedCb as (...a: unknown[]) => unknown;\n ctx.arguments[cbIdx] = function (this: unknown, ...args: unknown[]): unknown {\n return withScope(scope, () => wrapped.apply(this, args));\n };\n }\n }\n },\n\n end(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n\n // Sync throw: `end` fires AFTER `error` (both inside the wrapper's\n // `try/catch/finally`), so `ctx.error` is already set. Close the\n // span now since no `asyncEnd` will fire.\n if (ctx.error !== undefined) {\n finishSpan(rawCtx);\n return;\n }\n\n // No-callback (streamable Query) path: orchestrion's `wrapPromise`\n // stores the synchronous return value on `ctx.result` and never\n // fires `asyncStart`/`asyncEnd`. The returned `Query` is an\n // `EventEmitter` that emits `'end'` on success and `'error'` on\n // failure — hook those to close the span.\n // Note: a streamed span never finishes if the connection is destroyed\n // mid-flight — mysql then emits neither `'end'` nor `'error'`, so the\n // span is dropped (the `WeakMap` still prevents a leak). Closing this\n // needs connection-level hooks the per-query context doesn't expose.\n const result = ctx.result;\n if (result && typeof result === 'object' && hasOnMethod(result)) {\n const span = spans.get(rawCtx);\n if (!span) return;\n\n // Bind the captured scope to the streamed `Query` emitter: its\n // `'end'`/`'error'`/`'fields'`/… events fire from mysql's socket\n // handler with the caller's context lost, so without this a span\n // started in a user's stream listener would begin a fresh root trace\n // instead of nesting under the parent. `bindScopeToEmitter` patches\n // `on`/`addListener`/… so listeners added after `query()` returns\n // inherit the scope (like OTel's `context.bind`).\n const parentScope = parentScopes.get(rawCtx);\n if (parentScope) {\n bindScopeToEmitter(result, parentScope);\n }\n\n result.on('error', err => {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: err instanceof Error ? err.message : 'unknown_error',\n });\n // Defensive: end the span here too in case `'end'` never fires\n // (e.g. abrupt socket destruction). `finishSpan` is idempotent —\n // `spans.delete` makes the subsequent `'end'` listener a no-op.\n finishSpan(rawCtx);\n });\n result.on('end', () => finishSpan(rawCtx));\n return;\n }\n\n // Callback path: `asyncEnd` will close the span. Nothing to do here.\n },\n\n error(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n const span = spans.get(rawCtx);\n if (!span) return;\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: ctx.error instanceof Error ? ctx.error.message : 'unknown_error',\n });\n },\n\n asyncStart() {\n // No-op: we end on `asyncEnd` so the span covers the full callback duration.\n },\n\n asyncEnd(rawCtx) {\n finishSpan(rawCtx);\n },\n });\n\n function finishSpan(rawCtx: object): void {\n const span = spans.get(rawCtx);\n if (!span) return;\n span.end();\n spans.delete(rawCtx);\n parentScopes.delete(rawCtx);\n }\n },\n };\n}) satisfies IntegrationFn;\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\nfunction extractSql(firstArg: unknown): string | undefined {\n if (typeof firstArg === 'string') {\n return firstArg;\n }\n if (firstArg && typeof firstArg === 'object' && 'sql' in firstArg) {\n const sql = (firstArg as { sql?: unknown }).sql;\n return typeof sql === 'string' ? sql : undefined;\n }\n return undefined;\n}\n\nfunction getConnectionConfig(connection: MysqlConnection | undefined): {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n} {\n // Pool connections nest the real config under `.connectionConfig`; single\n // connections expose it directly. Matches `@opentelemetry/instrumentation-mysql`.\n const config = connection?.config?.connectionConfig ?? connection?.config ?? {};\n return {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n };\n}\n\nfunction getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined): string {\n let s = `jdbc:mysql://${host || 'localhost'}`;\n if (typeof port === 'number') {\n s += `:${port}`;\n }\n if (database) {\n s += `/${database}`;\n }\n return s;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven mysql integration.\n *\n * Subscribes to the `orchestrion:mysql:query` diagnostics_channel that the\n * orchestrion code transform injects into `mysql/lib/Connection.js`'s\n * `Connection.prototype.query`. Requires the orchestrion runtime hook or\n * bundler plugin to be active — wire that up via `_experimentalSetupOrchestrion`.\n */\nexport const mysqlChannelIntegration = defineIntegration(_mysqlChannelIntegration);\n"],"names":["DEBUG_BUILD","debug","CHANNELS","diagnosticsChannel","startInactiveSpan","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","getCurrentScope","withScope","bindScopeToEmitter","SPAN_STATUS_ERROR","defineIntegration"],"mappings":"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"} |
| export { IOREDIS_DC_CHANNEL_COMMAND, IOREDIS_DC_CHANNEL_CONNECT, REDIS_DC_CHANNEL_BATCH, REDIS_DC_CHANNEL_COMMAND, REDIS_DC_CHANNEL_CONNECT, subscribeRedisDiagnosticChannels } from './redis/redis-dc-subscriber.js'; | ||
| export { bindTracingChannelToSpan } from './tracing-channel.js'; | ||
| export { vercelAiIntegration } from './vercel-ai/index.js'; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"} | ||
| {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"} |
@@ -1,2 +0,2 @@ | ||
| import * as diagnosticsChannel from 'node:diagnostics_channel'; | ||
| import * as dc from 'node:diagnostics_channel'; | ||
| import { defineIntegration, debug, SPAN_STATUS_ERROR, bindScopeToEmitter, startInactiveSpan, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getCurrentScope, withScope } from '@sentry/core'; | ||
@@ -19,3 +19,3 @@ import { DEBUG_BUILD } from '../../debug-build.js'; | ||
| DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel "${CHANNELS.MYSQL_QUERY}"`); | ||
| const queryCh = diagnosticsChannel.tracingChannel(CHANNELS.MYSQL_QUERY); | ||
| const queryCh = dc.tracingChannel(CHANNELS.MYSQL_QUERY); | ||
| const spans = /* @__PURE__ */ new WeakMap(); | ||
@@ -22,0 +22,0 @@ const parentScopes = /* @__PURE__ */ new WeakMap(); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"mysql.js","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope, Span } from '@sentry/core';\nimport {\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, OTel 'Mysql' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Mysql';\n\n// OpenTelemetry \"OLD\" db/net semantic-conventions. We inline them rather than\n// importing `@opentelemetry/semantic-conventions` to keep this integration's\n// dependency surface free of OTel — orchestrion's whole point is to step away\n// from the OTel auto-instrumentation stack.\n//\n// We emit the OLD conventions to match `@opentelemetry/instrumentation-mysql`'s\n// default (it only emits the stable `db.system.name` / `db.query.text` set when\n// `OTEL_SEMCONV_STABILITY_OPT_IN=database` is opted into) and the rest of the\n// Sentry JS SDK, whose `inferDbSpanData` processor renames spans based on\n// `db.statement`.\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\n\n/**\n * The shape orchestrion's wrapCallback transform attaches to the tracing-channel\n * `context` object. Documented here rather than imported because orchestrion's\n * runtime doesn't export it — see `node_modules/@apm-js-collab/code-transformer/lib/transforms.js`.\n *\n * `arguments` is the *live* args array passed to the wrapped function: orchestrion\n * splices the user's callback out and inserts its own wrapper at the same index\n * before publishing `start`. The `start` hook re-wraps that entry to restore the\n * caller's scope across mysql's async callback dispatch (see below).\n */\ninterface MysqlQueryChannelContext {\n arguments: unknown[];\n self?: MysqlConnection;\n moduleVersion?: string;\n result?: unknown;\n error?: unknown;\n}\n\ninterface MysqlConnectionConfig {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n // Pool connections nest the real config one level deeper.\n connectionConfig?: MysqlConnectionConfig;\n}\n\ninterface MysqlConnection {\n config?: MysqlConnectionConfig;\n}\n\nconst _mysqlChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel \"${CHANNELS.MYSQL_QUERY}\"`);\n const queryCh = diagnosticsChannel.tracingChannel(CHANNELS.MYSQL_QUERY);\n\n // Orchestrion creates one `context` object per call, shared across all\n // lifecycle hooks. We key both maps off that identity; `WeakMap` so an\n // unfinished path can't leak its entries.\n const spans = new WeakMap<object, Span>();\n // The scope active when the query was issued, consumed in `end` to bind\n // the streamed `Query` emitter's listeners to it.\n const parentScopes = new WeakMap<object, Scope>();\n\n // `subscribe()` requires all five lifecycle hooks. The orchestrion\n // `wrapAuto` transform fires events in one of four orders depending on\n // call shape:\n // - sync throw : start → error → end\n // (NO asyncEnd)\n // - async-callback error : start → end → error →\n // asyncStart → asyncEnd\n // - async-callback success : start → end → asyncStart →\n // asyncEnd\n // - no-callback (streamable Query) : start → end\n // (ctx.result is the Query\n // emitter, no async events)\n //\n // Where the span closes depends on the path: `asyncEnd` for callbacks (so\n // it spans the full round-trip + callback), or `end` for the sync-throw\n // and streamable paths. The `end` hook tells those apart via `ctx.error`\n // / `ctx.result` — see there.\n queryCh.subscribe({\n start(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n const sql = extractSql(ctx.arguments[0]);\n const { host, port, database, user } = getConnectionConfig(ctx.self);\n const portNumber = typeof port === 'string' ? parseInt(port, 10) : port;\n const portIsNumber = typeof portNumber === 'number' && !isNaN(portNumber);\n\n const span = startInactiveSpan({\n name: sql ?? 'mysql.query',\n op: 'db',\n attributes: {\n [ATTR_DB_SYSTEM]: 'mysql',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.orchestrion.mysql',\n [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, portIsNumber ? portNumber : undefined, database),\n ...(database ? { [ATTR_DB_NAME]: database } : {}),\n ...(user ? { [ATTR_DB_USER]: user } : {}),\n ...(sql ? { [ATTR_DB_STATEMENT]: sql } : {}),\n ...(host ? { [ATTR_NET_PEER_NAME]: host } : {}),\n ...(portIsNumber ? { [ATTR_NET_PEER_PORT]: portNumber } : {}),\n },\n });\n spans.set(rawCtx, span);\n\n // Capture the scope while we're still synchronously inside the\n // caller's `connection.query` call. mysql v2 drains callbacks and\n // emits streamed-query events from its socket data handler, where the\n // AsyncLocalStorage store backing the active span no longer reflects\n // the caller's context — and `asyncStart`/`asyncEnd` fire from that\n // same lost context, so capturing has to happen now.\n const scope = getCurrentScope();\n parentScopes.set(rawCtx, scope);\n\n // Callback path: orchestrion has spliced the user's callback out of\n // `ctx.arguments` and put its own wrapper (`__apm$wrappedCb`) at the\n // same index. Re-wrap it so the callback — and any nested\n // `connection.query(...)` — runs with the captured scope active.\n if (ctx.arguments.length > 0) {\n const cbIdx = ctx.arguments.length - 1;\n const orchestrionWrappedCb = ctx.arguments[cbIdx];\n if (typeof orchestrionWrappedCb === 'function') {\n const wrapped = orchestrionWrappedCb as (...a: unknown[]) => unknown;\n ctx.arguments[cbIdx] = function (this: unknown, ...args: unknown[]): unknown {\n return withScope(scope, () => wrapped.apply(this, args));\n };\n }\n }\n },\n\n end(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n\n // Sync throw: `end` fires AFTER `error` (both inside the wrapper's\n // `try/catch/finally`), so `ctx.error` is already set. Close the\n // span now since no `asyncEnd` will fire.\n if (ctx.error !== undefined) {\n finishSpan(rawCtx);\n return;\n }\n\n // No-callback (streamable Query) path: orchestrion's `wrapPromise`\n // stores the synchronous return value on `ctx.result` and never\n // fires `asyncStart`/`asyncEnd`. The returned `Query` is an\n // `EventEmitter` that emits `'end'` on success and `'error'` on\n // failure — hook those to close the span.\n // Note: a streamed span never finishes if the connection is destroyed\n // mid-flight — mysql then emits neither `'end'` nor `'error'`, so the\n // span is dropped (the `WeakMap` still prevents a leak). Closing this\n // needs connection-level hooks the per-query context doesn't expose.\n const result = ctx.result;\n if (result && typeof result === 'object' && hasOnMethod(result)) {\n const span = spans.get(rawCtx);\n if (!span) return;\n\n // Bind the captured scope to the streamed `Query` emitter: its\n // `'end'`/`'error'`/`'fields'`/… events fire from mysql's socket\n // handler with the caller's context lost, so without this a span\n // started in a user's stream listener would begin a fresh root trace\n // instead of nesting under the parent. `bindScopeToEmitter` patches\n // `on`/`addListener`/… so listeners added after `query()` returns\n // inherit the scope (like OTel's `context.bind`).\n const parentScope = parentScopes.get(rawCtx);\n if (parentScope) {\n bindScopeToEmitter(result, parentScope);\n }\n\n result.on('error', err => {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: err instanceof Error ? err.message : 'unknown_error',\n });\n // Defensive: end the span here too in case `'end'` never fires\n // (e.g. abrupt socket destruction). `finishSpan` is idempotent —\n // `spans.delete` makes the subsequent `'end'` listener a no-op.\n finishSpan(rawCtx);\n });\n result.on('end', () => finishSpan(rawCtx));\n return;\n }\n\n // Callback path: `asyncEnd` will close the span. Nothing to do here.\n },\n\n error(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n const span = spans.get(rawCtx);\n if (!span) return;\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: ctx.error instanceof Error ? ctx.error.message : 'unknown_error',\n });\n },\n\n asyncStart() {\n // No-op: we end on `asyncEnd` so the span covers the full callback duration.\n },\n\n asyncEnd(rawCtx) {\n finishSpan(rawCtx);\n },\n });\n\n function finishSpan(rawCtx: object): void {\n const span = spans.get(rawCtx);\n if (!span) return;\n span.end();\n spans.delete(rawCtx);\n parentScopes.delete(rawCtx);\n }\n },\n };\n}) satisfies IntegrationFn;\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\nfunction extractSql(firstArg: unknown): string | undefined {\n if (typeof firstArg === 'string') {\n return firstArg;\n }\n if (firstArg && typeof firstArg === 'object' && 'sql' in firstArg) {\n const sql = (firstArg as { sql?: unknown }).sql;\n return typeof sql === 'string' ? sql : undefined;\n }\n return undefined;\n}\n\nfunction getConnectionConfig(connection: MysqlConnection | undefined): {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n} {\n // Pool connections nest the real config under `.connectionConfig`; single\n // connections expose it directly. Matches `@opentelemetry/instrumentation-mysql`.\n const config = connection?.config?.connectionConfig ?? connection?.config ?? {};\n return {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n };\n}\n\nfunction getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined): string {\n let s = `jdbc:mysql://${host || 'localhost'}`;\n if (typeof port === 'number') {\n s += `:${port}`;\n }\n if (database) {\n s += `/${database}`;\n }\n return s;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven mysql integration.\n *\n * Subscribes to the `orchestrion:mysql:query` diagnostics_channel that the\n * orchestrion code transform injects into `mysql/lib/Connection.js`'s\n * `Connection.prototype.query`. Requires the orchestrion runtime hook or\n * bundler plugin to be active — wire that up via `_experimentalSetupOrchestrion`.\n */\nexport const mysqlChannelIntegration = defineIntegration(_mysqlChannelIntegration);\n"],"names":[],"mappings":";;;;;AAiBA,MAAM,gBAAA,GAAmB,OAAA;AAYzB,MAAM,cAAA,GAAiB,WAAA;AACvB,MAAM,yBAAA,GAA4B,sBAAA;AAClC,MAAM,YAAA,GAAe,SAAA;AACrB,MAAM,YAAA,GAAe,SAAA;AACrB,MAAM,iBAAA,GAAoB,cAAA;AAC1B,MAAM,kBAAA,GAAqB,eAAA;AAC3B,MAAM,kBAAA,GAAqB,eAAA;AAiC3B,MAAM,4BAA4B,MAAM;AACtC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,SAAA,GAAY;AACV,MAAA,WAAA,IAAe,KAAA,CAAM,GAAA,CAAI,CAAA,4CAAA,EAA+C,QAAA,CAAS,WAAW,CAAA,CAAA,CAAG,CAAA;AAC/F,MAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,cAAA,CAAe,QAAA,CAAS,WAAW,CAAA;AAKtE,MAAA,MAAM,KAAA,uBAAY,OAAA,EAAsB;AAGxC,MAAA,MAAM,YAAA,uBAAmB,OAAA,EAAuB;AAmBhD,MAAA,OAAA,CAAQ,SAAA,CAAU;AAAA,QAChB,MAAM,MAAA,EAAQ;AACZ,UAAA,MAAM,GAAA,GAAM,MAAA;AACZ,UAAA,MAAM,GAAA,GAAM,UAAA,CAAW,GAAA,CAAI,SAAA,CAAU,CAAC,CAAC,CAAA;AACvC,UAAA,MAAM,EAAE,MAAM,IAAA,EAAM,QAAA,EAAU,MAAK,GAAI,mBAAA,CAAoB,IAAI,IAAI,CAAA;AACnE,UAAA,MAAM,aAAa,OAAO,IAAA,KAAS,WAAW,QAAA,CAAS,IAAA,EAAM,EAAE,CAAA,GAAI,IAAA;AACnE,UAAA,MAAM,eAAe,OAAO,UAAA,KAAe,QAAA,IAAY,CAAC,MAAM,UAAU,CAAA;AAExE,UAAA,MAAM,OAAO,iBAAA,CAAkB;AAAA,YAC7B,MAAM,GAAA,IAAO,aAAA;AAAA,YACb,EAAA,EAAI,IAAA;AAAA,YACJ,UAAA,EAAY;AAAA,cACV,CAAC,cAAc,GAAG,OAAA;AAAA,cAClB,CAAC,gCAAgC,GAAG,2BAAA;AAAA,cACpC,CAAC,yBAAyB,GAAG,aAAA,CAAc,MAAM,YAAA,GAAe,UAAA,GAAa,QAAW,QAAQ,CAAA;AAAA,cAChG,GAAI,WAAW,EAAE,CAAC,YAAY,GAAG,QAAA,KAAa,EAAC;AAAA,cAC/C,GAAI,OAAO,EAAE,CAAC,YAAY,GAAG,IAAA,KAAS,EAAC;AAAA,cACvC,GAAI,MAAM,EAAE,CAAC,iBAAiB,GAAG,GAAA,KAAQ,EAAC;AAAA,cAC1C,GAAI,OAAO,EAAE,CAAC,kBAAkB,GAAG,IAAA,KAAS,EAAC;AAAA,cAC7C,GAAI,eAAe,EAAE,CAAC,kBAAkB,GAAG,UAAA,KAAe;AAAC;AAC7D,WACD,CAAA;AACD,UAAA,KAAA,CAAM,GAAA,CAAI,QAAQ,IAAI,CAAA;AAQtB,UAAA,MAAM,QAAQ,eAAA,EAAgB;AAC9B,UAAA,YAAA,CAAa,GAAA,CAAI,QAAQ,KAAK,CAAA;AAM9B,UAAA,IAAI,GAAA,CAAI,SAAA,CAAU,MAAA,GAAS,CAAA,EAAG;AAC5B,YAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,SAAA,CAAU,MAAA,GAAS,CAAA;AACrC,YAAA,MAAM,oBAAA,GAAuB,GAAA,CAAI,SAAA,CAAU,KAAK,CAAA;AAChD,YAAA,IAAI,OAAO,yBAAyB,UAAA,EAAY;AAC9C,cAAA,MAAM,OAAA,GAAU,oBAAA;AAChB,cAAA,GAAA,CAAI,SAAA,CAAU,KAAK,CAAA,GAAI,SAAA,GAA4B,IAAA,EAA0B;AAC3E,gBAAA,OAAO,UAAU,KAAA,EAAO,MAAM,QAAQ,KAAA,CAAM,IAAA,EAAM,IAAI,CAAC,CAAA;AAAA,cACzD,CAAA;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAA;AAAA,QAEA,IAAI,MAAA,EAAQ;AACV,UAAA,MAAM,GAAA,GAAM,MAAA;AAKZ,UAAA,IAAI,GAAA,CAAI,UAAU,MAAA,EAAW;AAC3B,YAAA,UAAA,CAAW,MAAM,CAAA;AACjB,YAAA;AAAA,UACF;AAWA,UAAA,MAAM,SAAS,GAAA,CAAI,MAAA;AACnB,UAAA,IAAI,UAAU,OAAO,MAAA,KAAW,QAAA,IAAY,WAAA,CAAY,MAAM,CAAA,EAAG;AAC/D,YAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA;AAC7B,YAAA,IAAI,CAAC,IAAA,EAAM;AASX,YAAA,MAAM,WAAA,GAAc,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AAC3C,YAAA,IAAI,WAAA,EAAa;AACf,cAAA,kBAAA,CAAmB,QAAQ,WAAW,CAAA;AAAA,YACxC;AAEA,YAAA,MAAA,CAAO,EAAA,CAAG,SAAS,CAAA,GAAA,KAAO;AACxB,cAAA,IAAA,CAAK,SAAA,CAAU;AAAA,gBACb,IAAA,EAAM,iBAAA;AAAA,gBACN,OAAA,EAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU;AAAA,eAC/C,CAAA;AAID,cAAA,UAAA,CAAW,MAAM,CAAA;AAAA,YACnB,CAAC,CAAA;AACD,YAAA,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,MAAM,UAAA,CAAW,MAAM,CAAC,CAAA;AACzC,YAAA;AAAA,UACF;AAAA,QAGF,CAAA;AAAA,QAEA,MAAM,MAAA,EAAQ;AACZ,UAAA,MAAM,GAAA,GAAM,MAAA;AACZ,UAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA;AAC7B,UAAA,IAAI,CAAC,IAAA,EAAM;AACX,UAAA,IAAA,CAAK,SAAA,CAAU;AAAA,YACb,IAAA,EAAM,iBAAA;AAAA,YACN,SAAS,GAAA,CAAI,KAAA,YAAiB,KAAA,GAAQ,GAAA,CAAI,MAAM,OAAA,GAAU;AAAA,WAC3D,CAAA;AAAA,QACH,CAAA;AAAA,QAEA,UAAA,GAAa;AAAA,QAEb,CAAA;AAAA,QAEA,SAAS,MAAA,EAAQ;AACf,UAAA,UAAA,CAAW,MAAM,CAAA;AAAA,QACnB;AAAA,OACD,CAAA;AAED,MAAA,SAAS,WAAW,MAAA,EAAsB;AACxC,QAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA;AAC7B,QAAA,IAAI,CAAC,IAAA,EAAM;AACX,QAAA,IAAA,CAAK,GAAA,EAAI;AACT,QAAA,KAAA,CAAM,OAAO,MAAM,CAAA;AACnB,QAAA,YAAA,CAAa,OAAO,MAAM,CAAA;AAAA,MAC5B;AAAA,IACF;AAAA,GACF;AACF,CAAA,CAAA;AAEA,SAAS,YAAY,GAAA,EAA2F;AAC9G,EAAA,OAAO,IAAA,IAAQ,GAAA,IAAO,OAAQ,GAAA,CAAyB,EAAA,KAAO,UAAA;AAChE;AAEA,SAAS,WAAW,QAAA,EAAuC;AACzD,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,IAAI,QAAA,IAAY,OAAO,QAAA,KAAa,QAAA,IAAY,SAAS,QAAA,EAAU;AACjE,IAAA,MAAM,MAAO,QAAA,CAA+B,GAAA;AAC5C,IAAA,OAAO,OAAO,GAAA,KAAQ,QAAA,GAAW,GAAA,GAAM,MAAA;AAAA,EACzC;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,oBAAoB,UAAA,EAK3B;AAGA,EAAA,MAAM,SAAS,UAAA,EAAY,MAAA,EAAQ,gBAAA,IAAoB,UAAA,EAAY,UAAU,EAAC;AAC9E,EAAA,OAAO;AAAA,IACL,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,MAAM,MAAA,CAAO;AAAA,GACf;AACF;AAEA,SAAS,aAAA,CAAc,IAAA,EAA0B,IAAA,EAA0B,QAAA,EAAsC;AAC/G,EAAA,IAAI,CAAA,GAAI,CAAA,aAAA,EAAgB,IAAA,IAAQ,WAAW,CAAA,CAAA;AAC3C,EAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC5B,IAAA,CAAA,IAAK,IAAI,IAAI,CAAA,CAAA;AAAA,EACf;AACA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,CAAA,IAAK,IAAI,QAAQ,CAAA,CAAA;AAAA,EACnB;AACA,EAAA,OAAO,CAAA;AACT;AAUO,MAAM,uBAAA,GAA0B,kBAAkB,wBAAwB;;;;"} | ||
| {"version":3,"file":"mysql.js","sources":["../../../../src/integrations/tracing-channel/mysql.ts"],"sourcesContent":["import * as diagnosticsChannel from 'node:diagnostics_channel';\nimport type { IntegrationFn, Scope, Span } from '@sentry/core';\nimport {\n bindScopeToEmitter,\n debug,\n defineIntegration,\n getCurrentScope,\n SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,\n SPAN_STATUS_ERROR,\n startInactiveSpan,\n withScope,\n} from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { CHANNELS } from '../../orchestrion/channels';\n\n// NOTE: this uses the same name as the OTel integration by design.\n// When enabled, OTel 'Mysql' integration is omitted from the default set.\nconst INTEGRATION_NAME = 'Mysql';\n\n// OpenTelemetry \"OLD\" db/net semantic-conventions. We inline them rather than\n// importing `@opentelemetry/semantic-conventions` to keep this integration's\n// dependency surface free of OTel — orchestrion's whole point is to step away\n// from the OTel auto-instrumentation stack.\n//\n// We emit the OLD conventions to match `@opentelemetry/instrumentation-mysql`'s\n// default (it only emits the stable `db.system.name` / `db.query.text` set when\n// `OTEL_SEMCONV_STABILITY_OPT_IN=database` is opted into) and the rest of the\n// Sentry JS SDK, whose `inferDbSpanData` processor renames spans based on\n// `db.statement`.\nconst ATTR_DB_SYSTEM = 'db.system';\nconst ATTR_DB_CONNECTION_STRING = 'db.connection_string';\nconst ATTR_DB_NAME = 'db.name';\nconst ATTR_DB_USER = 'db.user';\nconst ATTR_DB_STATEMENT = 'db.statement';\nconst ATTR_NET_PEER_NAME = 'net.peer.name';\nconst ATTR_NET_PEER_PORT = 'net.peer.port';\n\n/**\n * The shape orchestrion's wrapCallback transform attaches to the tracing-channel\n * `context` object. Documented here rather than imported because orchestrion's\n * runtime doesn't export it — see `node_modules/@apm-js-collab/code-transformer/lib/transforms.js`.\n *\n * `arguments` is the *live* args array passed to the wrapped function: orchestrion\n * splices the user's callback out and inserts its own wrapper at the same index\n * before publishing `start`. The `start` hook re-wraps that entry to restore the\n * caller's scope across mysql's async callback dispatch (see below).\n */\ninterface MysqlQueryChannelContext {\n arguments: unknown[];\n self?: MysqlConnection;\n moduleVersion?: string;\n result?: unknown;\n error?: unknown;\n}\n\ninterface MysqlConnectionConfig {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n // Pool connections nest the real config one level deeper.\n connectionConfig?: MysqlConnectionConfig;\n}\n\ninterface MysqlConnection {\n config?: MysqlConnectionConfig;\n}\n\nconst _mysqlChannelIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n DEBUG_BUILD && debug.log(`[orchestrion:mysql] subscribing to channel \"${CHANNELS.MYSQL_QUERY}\"`);\n const queryCh = diagnosticsChannel.tracingChannel(CHANNELS.MYSQL_QUERY);\n\n // Orchestrion creates one `context` object per call, shared across all\n // lifecycle hooks. We key both maps off that identity; `WeakMap` so an\n // unfinished path can't leak its entries.\n const spans = new WeakMap<object, Span>();\n // The scope active when the query was issued, consumed in `end` to bind\n // the streamed `Query` emitter's listeners to it.\n const parentScopes = new WeakMap<object, Scope>();\n\n // `subscribe()` requires all five lifecycle hooks. The orchestrion\n // `wrapAuto` transform fires events in one of four orders depending on\n // call shape:\n // - sync throw : start → error → end\n // (NO asyncEnd)\n // - async-callback error : start → end → error →\n // asyncStart → asyncEnd\n // - async-callback success : start → end → asyncStart →\n // asyncEnd\n // - no-callback (streamable Query) : start → end\n // (ctx.result is the Query\n // emitter, no async events)\n //\n // Where the span closes depends on the path: `asyncEnd` for callbacks (so\n // it spans the full round-trip + callback), or `end` for the sync-throw\n // and streamable paths. The `end` hook tells those apart via `ctx.error`\n // / `ctx.result` — see there.\n queryCh.subscribe({\n start(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n const sql = extractSql(ctx.arguments[0]);\n const { host, port, database, user } = getConnectionConfig(ctx.self);\n const portNumber = typeof port === 'string' ? parseInt(port, 10) : port;\n const portIsNumber = typeof portNumber === 'number' && !isNaN(portNumber);\n\n const span = startInactiveSpan({\n name: sql ?? 'mysql.query',\n op: 'db',\n attributes: {\n [ATTR_DB_SYSTEM]: 'mysql',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.db.orchestrion.mysql',\n [ATTR_DB_CONNECTION_STRING]: getJDBCString(host, portIsNumber ? portNumber : undefined, database),\n ...(database ? { [ATTR_DB_NAME]: database } : {}),\n ...(user ? { [ATTR_DB_USER]: user } : {}),\n ...(sql ? { [ATTR_DB_STATEMENT]: sql } : {}),\n ...(host ? { [ATTR_NET_PEER_NAME]: host } : {}),\n ...(portIsNumber ? { [ATTR_NET_PEER_PORT]: portNumber } : {}),\n },\n });\n spans.set(rawCtx, span);\n\n // Capture the scope while we're still synchronously inside the\n // caller's `connection.query` call. mysql v2 drains callbacks and\n // emits streamed-query events from its socket data handler, where the\n // AsyncLocalStorage store backing the active span no longer reflects\n // the caller's context — and `asyncStart`/`asyncEnd` fire from that\n // same lost context, so capturing has to happen now.\n const scope = getCurrentScope();\n parentScopes.set(rawCtx, scope);\n\n // Callback path: orchestrion has spliced the user's callback out of\n // `ctx.arguments` and put its own wrapper (`__apm$wrappedCb`) at the\n // same index. Re-wrap it so the callback — and any nested\n // `connection.query(...)` — runs with the captured scope active.\n if (ctx.arguments.length > 0) {\n const cbIdx = ctx.arguments.length - 1;\n const orchestrionWrappedCb = ctx.arguments[cbIdx];\n if (typeof orchestrionWrappedCb === 'function') {\n const wrapped = orchestrionWrappedCb as (...a: unknown[]) => unknown;\n ctx.arguments[cbIdx] = function (this: unknown, ...args: unknown[]): unknown {\n return withScope(scope, () => wrapped.apply(this, args));\n };\n }\n }\n },\n\n end(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n\n // Sync throw: `end` fires AFTER `error` (both inside the wrapper's\n // `try/catch/finally`), so `ctx.error` is already set. Close the\n // span now since no `asyncEnd` will fire.\n if (ctx.error !== undefined) {\n finishSpan(rawCtx);\n return;\n }\n\n // No-callback (streamable Query) path: orchestrion's `wrapPromise`\n // stores the synchronous return value on `ctx.result` and never\n // fires `asyncStart`/`asyncEnd`. The returned `Query` is an\n // `EventEmitter` that emits `'end'` on success and `'error'` on\n // failure — hook those to close the span.\n // Note: a streamed span never finishes if the connection is destroyed\n // mid-flight — mysql then emits neither `'end'` nor `'error'`, so the\n // span is dropped (the `WeakMap` still prevents a leak). Closing this\n // needs connection-level hooks the per-query context doesn't expose.\n const result = ctx.result;\n if (result && typeof result === 'object' && hasOnMethod(result)) {\n const span = spans.get(rawCtx);\n if (!span) return;\n\n // Bind the captured scope to the streamed `Query` emitter: its\n // `'end'`/`'error'`/`'fields'`/… events fire from mysql's socket\n // handler with the caller's context lost, so without this a span\n // started in a user's stream listener would begin a fresh root trace\n // instead of nesting under the parent. `bindScopeToEmitter` patches\n // `on`/`addListener`/… so listeners added after `query()` returns\n // inherit the scope (like OTel's `context.bind`).\n const parentScope = parentScopes.get(rawCtx);\n if (parentScope) {\n bindScopeToEmitter(result, parentScope);\n }\n\n result.on('error', err => {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: err instanceof Error ? err.message : 'unknown_error',\n });\n // Defensive: end the span here too in case `'end'` never fires\n // (e.g. abrupt socket destruction). `finishSpan` is idempotent —\n // `spans.delete` makes the subsequent `'end'` listener a no-op.\n finishSpan(rawCtx);\n });\n result.on('end', () => finishSpan(rawCtx));\n return;\n }\n\n // Callback path: `asyncEnd` will close the span. Nothing to do here.\n },\n\n error(rawCtx) {\n const ctx = rawCtx as MysqlQueryChannelContext;\n const span = spans.get(rawCtx);\n if (!span) return;\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n message: ctx.error instanceof Error ? ctx.error.message : 'unknown_error',\n });\n },\n\n asyncStart() {\n // No-op: we end on `asyncEnd` so the span covers the full callback duration.\n },\n\n asyncEnd(rawCtx) {\n finishSpan(rawCtx);\n },\n });\n\n function finishSpan(rawCtx: object): void {\n const span = spans.get(rawCtx);\n if (!span) return;\n span.end();\n spans.delete(rawCtx);\n parentScopes.delete(rawCtx);\n }\n },\n };\n}) satisfies IntegrationFn;\n\nfunction hasOnMethod(obj: object): obj is { on: (event: string, listener: (arg?: unknown) => void) => unknown } {\n return 'on' in obj && typeof (obj as { on?: unknown }).on === 'function';\n}\n\nfunction extractSql(firstArg: unknown): string | undefined {\n if (typeof firstArg === 'string') {\n return firstArg;\n }\n if (firstArg && typeof firstArg === 'object' && 'sql' in firstArg) {\n const sql = (firstArg as { sql?: unknown }).sql;\n return typeof sql === 'string' ? sql : undefined;\n }\n return undefined;\n}\n\nfunction getConnectionConfig(connection: MysqlConnection | undefined): {\n host?: string;\n port?: number | string;\n database?: string;\n user?: string;\n} {\n // Pool connections nest the real config under `.connectionConfig`; single\n // connections expose it directly. Matches `@opentelemetry/instrumentation-mysql`.\n const config = connection?.config?.connectionConfig ?? connection?.config ?? {};\n return {\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n };\n}\n\nfunction getJDBCString(host: string | undefined, port: number | undefined, database: string | undefined): string {\n let s = `jdbc:mysql://${host || 'localhost'}`;\n if (typeof port === 'number') {\n s += `:${port}`;\n }\n if (database) {\n s += `/${database}`;\n }\n return s;\n}\n\n/**\n * EXPERIMENTAL — orchestrion-driven mysql integration.\n *\n * Subscribes to the `orchestrion:mysql:query` diagnostics_channel that the\n * orchestrion code transform injects into `mysql/lib/Connection.js`'s\n * `Connection.prototype.query`. Requires the orchestrion runtime hook or\n * bundler plugin to be active — wire that up via `_experimentalSetupOrchestrion`.\n */\nexport const mysqlChannelIntegration = defineIntegration(_mysqlChannelIntegration);\n"],"names":["diagnosticsChannel"],"mappings":"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"} |
@@ -1,1 +0,1 @@ | ||
| {"type":"module","version":"10.61.0","sideEffects":false} | ||
| {"type":"module","version":"10.62.0","sideEffects":false} |
@@ -10,2 +10,3 @@ /** | ||
| export { SentryTracingChannel, TracingChannelLifeCycleOptions, TracingChannelBindingHandle, TracingChannelPayloadWithSpan, } from './tracing-channel'; | ||
| export { vercelAiIntegration } from './vercel-ai'; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -10,2 +10,3 @@ /** | ||
| export type { SentryTracingChannel, TracingChannelLifeCycleOptions, TracingChannelBindingHandle, TracingChannelPayloadWithSpan, } from './tracing-channel'; | ||
| export { vercelAiIntegration } from './vercel-ai'; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,gCAAgC,GACjC,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,kCAAkC,EAClC,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,gCAAgC,GACjC,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,kCAAkC,EAClC,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"} |
+2
-2
| { | ||
| "name": "@sentry/server-utils", | ||
| "version": "10.61.0", | ||
| "version": "10.62.0", | ||
| "description": "Server Utilities for all Sentry JavaScript SDKs", | ||
@@ -89,3 +89,3 @@ "repository": "git://github.com/getsentry/sentry-javascript.git", | ||
| "@sentry/conventions": "^0.12.0", | ||
| "@sentry/core": "10.61.0", | ||
| "@sentry/core": "10.62.0", | ||
| "magic-string": "~0.30.0" | ||
@@ -92,0 +92,0 @@ }, |
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
338582
61.26%95
17.28%2529
57.96%12
33.33%+ Added
- Removed
Updated