dd-trace 6.13.0 → 6.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  8. package/ci/test-optimization-validation/package-check.js +1 -1
  9. package/ci/test-optimization-validation/report-writer.js +7 -5
  10. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  11. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  12. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  13. package/package.json +13 -10
  14. package/packages/datadog-esbuild/index.js +3 -1
  15. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  16. package/packages/datadog-instrumentations/src/fs.js +24 -20
  17. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  18. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  19. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  20. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  21. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  23. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  24. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  33. package/packages/datadog-instrumentations/src/jest.js +25 -20
  34. package/packages/datadog-instrumentations/src/knex.js +76 -0
  35. package/packages/datadog-instrumentations/src/mariadb.js +6 -0
  36. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  37. package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
  38. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  39. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  40. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  41. package/packages/datadog-instrumentations/src/net.js +14 -17
  42. package/packages/datadog-instrumentations/src/next.js +212 -62
  43. package/packages/datadog-instrumentations/src/openai.js +42 -40
  44. package/packages/datadog-instrumentations/src/pg.js +15 -0
  45. package/packages/datadog-instrumentations/src/playwright.js +109 -13
  46. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  47. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  48. package/packages/datadog-instrumentations/src/undici.js +53 -4
  49. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
  50. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  51. package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
  52. package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
  53. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  54. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  55. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  56. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  57. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  58. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  59. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  60. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  61. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  62. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  63. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  64. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  65. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  66. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  67. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  68. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  69. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  70. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  71. package/packages/datadog-plugin-jest/src/index.js +0 -6
  72. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  73. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  74. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  75. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  76. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  77. package/packages/datadog-plugin-next/src/index.js +63 -29
  78. package/packages/datadog-plugin-playwright/src/index.js +167 -27
  79. package/packages/datadog-plugin-undici/src/index.js +27 -41
  80. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  81. package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
  82. package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
  83. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  84. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  85. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  86. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  87. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  88. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  89. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  90. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  91. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
  92. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  93. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  94. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  95. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  96. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  97. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  98. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
  99. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  100. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  101. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  102. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
  103. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  104. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  105. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  106. package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
  107. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  108. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  109. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  110. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
  111. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  112. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  113. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  114. package/packages/dd-trace/src/config/index.js +73 -13
  115. package/packages/dd-trace/src/config/parsers.js +11 -1
  116. package/packages/dd-trace/src/config/stable.js +3 -3
  117. package/packages/dd-trace/src/config/supported-configurations.json +20 -2
  118. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  119. package/packages/dd-trace/src/debugger/config.js +12 -1
  120. package/packages/dd-trace/src/debugger/constants.js +1 -0
  121. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  122. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
  123. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
  124. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  125. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  126. package/packages/dd-trace/src/debugger/index.js +23 -3
  127. package/packages/dd-trace/src/dogstatsd.js +510 -76
  128. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  129. package/packages/dd-trace/src/encode/0.4.js +5 -5
  130. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  131. package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
  132. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  133. package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
  134. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  135. package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
  136. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  137. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  138. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  139. package/packages/dd-trace/src/exporters/common/request.js +31 -11
  140. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  141. package/packages/dd-trace/src/exporters/common/writer.js +9 -7
  142. package/packages/dd-trace/src/flush.js +6 -3
  143. package/packages/dd-trace/src/id.js +14 -0
  144. package/packages/dd-trace/src/index.js +5 -2
  145. package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
  146. package/packages/dd-trace/src/llmobs/index.js +32 -32
  147. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  148. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  149. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  150. package/packages/dd-trace/src/llmobs/util.js +0 -33
  151. package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
  152. package/packages/dd-trace/src/log/index.js +7 -1
  153. package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
  154. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  155. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  156. package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
  157. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  158. package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
  159. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
  160. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
  161. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  162. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  163. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  164. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  165. package/packages/dd-trace/src/opentracing/propagation/text_map.js +93 -18
  166. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  167. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  168. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  169. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  170. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  171. package/packages/dd-trace/src/plugins/util/test.js +83 -20
  172. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  173. package/packages/dd-trace/src/plugins/util/web.js +4 -9
  174. package/packages/dd-trace/src/profiling/config.js +1 -0
  175. package/packages/dd-trace/src/profiling/oom.js +7 -2
  176. package/packages/dd-trace/src/profiling/profiler.js +23 -13
  177. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  178. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  179. package/packages/dd-trace/src/proxy.js +75 -5
  180. package/packages/dd-trace/src/remote_config/index.js +319 -101
  181. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  182. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  183. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
  184. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
  185. package/packages/dd-trace/src/sampling_rule.js +36 -9
  186. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
  187. package/packages/dd-trace/src/serverless/vercel.js +53 -20
  188. package/packages/dd-trace/src/serverless.js +10 -3
  189. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  190. package/packages/dd-trace/src/span_stats.js +24 -28
  191. package/packages/dd-trace/src/startup-log.js +3 -2
  192. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  193. package/packages/dd-trace/src/telemetry/send-data.js +18 -16
  194. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  195. package/packages/dd-trace/src/tracer.js +4 -5
  196. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  197. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  198. package/vendor/dist/protobufjs/index.js +1 -1
  199. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  200. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  201. package/packages/dd-trace/src/encode/agentless-json.js +0 -210
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const net = require('node:net')
4
+ const { format } = require('node:url')
4
5
 
5
6
  const { urlToHttpOptions } = require('./url-to-http-options-polyfill')
6
7
 
@@ -16,6 +17,44 @@ function isLoopbackHost (hostname) {
16
17
  (hostname.startsWith('127.') && net.isIPv4(hostname))
17
18
  }
18
19
 
20
+ /**
21
+ * @param {string|undefined} protocol
22
+ * @param {string|undefined} hostname
23
+ * @returns {boolean}
24
+ */
25
+ function canSendApiKey (protocol, hostname) {
26
+ return protocol === 'https:' || protocol === 'unix:' ||
27
+ typeof hostname === 'string' && isLoopbackHost(hostname)
28
+ }
29
+
30
+ /**
31
+ * @param {string} site
32
+ * @param {string} [intake]
33
+ * @returns {URL | undefined}
34
+ */
35
+ function createSiteUrl (site, intake) {
36
+ const hostname = `${intake === undefined ? '' : `${intake}.`}${site}`.toLowerCase()
37
+
38
+ try {
39
+ const url = new URL(format({
40
+ protocol: 'https:',
41
+ hostname,
42
+ }))
43
+ if (
44
+ url.hostname !== hostname ||
45
+ url.username ||
46
+ url.password ||
47
+ url.port ||
48
+ url.pathname !== '/' ||
49
+ url.search ||
50
+ url.hash
51
+ ) {
52
+ return
53
+ }
54
+ return url
55
+ } catch {}
56
+ }
57
+
19
58
  /**
20
59
  * Convert an agent/intake URL into Node http(s) request options.
21
60
  *
@@ -43,4 +82,4 @@ function parseUrl (urlObjOrString) {
43
82
  return url
44
83
  }
45
84
 
46
- module.exports = { isLoopbackHost, parseUrl }
85
+ module.exports = { canSendApiKey, createSiteUrl, isLoopbackHost, parseUrl }
@@ -8,13 +8,16 @@ const request = require('./request')
8
8
  const { safeJSONStringify } = require('./util')
9
9
 
10
10
  const firstFlushChannel = channel('dd-trace:exporter:first-flush')
11
+ const noop = () => {}
11
12
 
12
13
  class Writer {
13
14
  #deliveryTracker
15
+ #retainOnBackpressure
14
16
 
15
- constructor ({ url, beforeFirstFlush, deliveryTracker }) {
17
+ constructor ({ url, beforeFirstFlush, deliveryTracker, retainOnBackpressure = false }) {
16
18
  this._url = url
17
19
  this._beforeFirstFlush = beforeFirstFlush
20
+ this.#retainOnBackpressure = retainOnBackpressure
18
21
  this.#deliveryTracker = deliveryTracker
19
22
  }
20
23
 
@@ -39,10 +42,10 @@ class Writer {
39
42
  * @param {{ deadline?: number }} [options]
40
43
  * @returns {void}
41
44
  */
42
- flushDirect (done = () => {}, options) {
45
+ flushDirect (done = noop, options) {
43
46
  const count = this._encoder.count()
44
47
 
45
- if (!request.writable && options?.deadline === undefined) {
48
+ if (!request.writable && options?.deadline === undefined && !this.#retainOnBackpressure) {
46
49
  this._encoder.reset()
47
50
  done()
48
51
  } else if (count > 0) {
@@ -77,7 +80,7 @@ class Writer {
77
80
  }
78
81
 
79
82
  append (payload, options) {
80
- if (!request.writable && options?.deadline === undefined) {
83
+ if (!request.writable && options?.deadline === undefined && !this.#retainOnBackpressure) {
81
84
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
82
85
  log.debug(() => `Maximum number of active requests reached. Payload discarded: ${safeJSONStringify(payload)}`)
83
86
  return false
@@ -86,12 +89,11 @@ class Writer {
86
89
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
87
90
  log.debug(() => `Encoding payload: ${safeJSONStringify(payload)}`)
88
91
 
89
- this._encode(payload)
90
- return true
92
+ return this._encode(payload) !== false
91
93
  }
92
94
 
93
95
  _encode (payload) {
94
- this._encoder.encode(payload)
96
+ return this._encoder.encode(payload)
95
97
  }
96
98
 
97
99
  setUrl (url) {
@@ -43,12 +43,16 @@ function registerTelemetryFlusher (flusher, options) {
43
43
  * @param {{ timeout?: number }} [options]
44
44
  * @param {TraceFlushers} [traceFlushers]
45
45
  */
46
- function flushServerlessTelemetry (done, options, traceFlushers = {}) {
47
- const { trace: traceFlusher, spanStats: spanStatsFlusher } = traceFlushers
46
+ function flushServerlessTelemetry (done, options, traceFlushers) {
47
+ const traceFlusher = traceFlushers?.trace
48
+ const spanStatsFlusher = traceFlushers?.spanStats
48
49
  // TODO: Include DSM after DataStreamsProcessor exposes a completion-aware flush API.
49
50
  let pending = telemetryFlushers.size + postTraceTelemetryFlushers.size +
50
51
  (typeof traceFlusher === 'function' ? 1 : 0) +
51
52
  (typeof spanStatsFlusher === 'function' ? 1 : 0)
53
+
54
+ if (pending === 0) return done?.()
55
+
52
56
  let completed = false
53
57
  let timeout
54
58
 
@@ -62,7 +66,6 @@ function flushServerlessTelemetry (done, options, traceFlushers = {}) {
62
66
  if (--pending === 0) finish()
63
67
  }
64
68
 
65
- if (pending === 0) return finish()
66
69
  if (options?.timeout) {
67
70
  timeout = setTimeout(() => {
68
71
  log.warn('Timed out waiting for telemetry flush after %dms', options.timeout)
@@ -2,6 +2,8 @@
2
2
 
3
3
  const { randomFillSync } = require('crypto')
4
4
 
5
+ const { channel } = require('dc-polyfill')
6
+
5
7
  const UINT_MAX = 4_294_967_296
6
8
 
7
9
  const data = new Uint8Array(8 * 8192)
@@ -9,6 +11,8 @@ const zeroId = new Uint8Array(8)
9
11
 
10
12
  let batch = 0
11
13
 
14
+ channel('datadog:identity:update').subscribe(reseed)
15
+
12
16
  // Internal representation of a trace or span ID.
13
17
  class Identifier {
14
18
  /** @type {number[] | Uint8Array} */
@@ -254,6 +258,16 @@ function writeUInt32BE (buffer, value, offset) {
254
258
  buffer[0 + offset] = value & 255
255
259
  }
256
260
 
261
+ /**
262
+ * Resets the batch cursor, forcing the next ID batch to draw a fresh
263
+ * randomFillSync() call on MicroVM clone resume. Node's crypto RNG is
264
+ * re-seeded from the kernel CSPRNG on snapshot resume, so re-invoking it
265
+ * is sufficient — no need to read /dev/urandom directly.
266
+ */
267
+ function reseed () {
268
+ batch = 0
269
+ }
270
+
257
271
  /**
258
272
  * @param {string} [value]
259
273
  * @param {number} [radix]
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const { getValueFromEnvSources } = require('./config/helper')
3
+ const { getEnvironmentVariable, getValueFromEnvSources } = require('./config/helper')
4
+ const { isTrue } = require('./util')
4
5
 
5
6
  // Global `jest` is only present in Jest workers.
6
7
  const inJestWorker = typeof jest !== 'undefined'
@@ -8,8 +9,10 @@ const inJestWorker = typeof jest !== 'undefined'
8
9
  // skipDefault: distinguish an unset DD_TRACE_ENABLED (fall back to the OTel signal) from an
9
10
  // explicit value; the registered default would otherwise mask the OTEL_TRACES_EXPORTER check.
10
11
  const ddTraceEnabled = getValueFromEnvSources('DD_TRACE_ENABLED', true)
12
+ const agentlessTracingEnabled = getValueFromEnvSources('DD_AGENTLESS_ENABLED') ||
13
+ isTrue(getEnvironmentVariable('_DD_APM_TRACING_AGENTLESS_ENABLED'))
11
14
  const ddTraceDisabled = ddTraceEnabled === undefined
12
- ? getValueFromEnvSources('OTEL_TRACES_EXPORTER') === 'none'
15
+ ? !agentlessTracingEnabled && getValueFromEnvSources('OTEL_TRACES_EXPORTER') === 'none'
13
16
  : ddTraceEnabled === false
14
17
 
15
18
  module.exports = ddTraceDisabled || inJestWorker
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const { randomUUID } = require('node:crypto')
4
- const snapshotPayload = require('../../../../../vendor/dist/rfdc')({ proto: false, circles: false })
4
+ const createRfdc = require('../../../../../vendor/dist/rfdc')
5
+ const snapshotPayload = createRfdc({ proto: false, circles: false })
5
6
 
6
7
  /** @typedef {{add?: string[], remove?: string[], replace?: string[]}} TagOperations */
7
8
  /**
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { channel } = require('dc-polyfill')
4
4
 
5
- const { readDatadogTags, writeDatadogTags } = require('../carrier')
6
5
  const { registerTelemetryFlusher } = require('../flush')
7
6
  const log = require('../log')
8
7
  const { createServerlessDeliveryTracker } = require('../serverless')
@@ -25,7 +24,7 @@ const {
25
24
  PROPAGATED_TRACE_ID_KEY,
26
25
  } = require('./constants/tags')
27
26
  const { storage } = require('./storage')
28
- const { agentNameWireSafe, appendOptionalPropagatedTag, resolveAgentAttribution, stripTagsetEntry } = require('./util')
27
+ const { agentNameWireSafe, resolveAgentAttribution } = require('./util')
29
28
  const telemetry = require('./telemetry')
30
29
  const LLMObsSpanProcessor = require('./span_processor')
31
30
  const LLMObsEvalMetricsWriter = require('./writers/evaluations')
@@ -64,6 +63,13 @@ let unregisterTelemetryFlusher
64
63
  /** @type {import('../config/config-base')} */
65
64
  let globalTracerConfig
66
65
 
66
+ /**
67
+ * @typedef {object} TraceTagInjection
68
+ * @property {import('../opentracing/span_context')} spanContext
69
+ * @property {Array<string | undefined>} [traceTagReplacements]
70
+ * @property {number} [optionalTraceTagCount]
71
+ */
72
+
67
73
  /**
68
74
  * @param {@type import('../config/config-base')} config
69
75
  */
@@ -163,7 +169,8 @@ function retireWriters (retiredSpanWriter, retiredEvalWriter) {
163
169
 
164
170
  // since LLMObs traces can extend between services and be the same trace,
165
171
  // we need to propagate the parent id, mlApp, session id, and sampling rate/decision.
166
- function handleLLMObsInjection ({ carrier }) {
172
+ /** @param {TraceTagInjection} injection */
173
+ function handleLLMObsInjection (injection) {
167
174
  // Respect the standard propagator's gate: when trace tag propagation is
168
175
  // disabled, don't write `x-datadog-tags` for LLMObs either.
169
176
  if (globalTracerConfig.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH === 0) return
@@ -201,37 +208,30 @@ function handleLLMObsInjection ({ carrier }) {
201
208
  mlObsSpanTags, parent
202
209
  )
203
210
 
204
- // `_injectTags` only writes `x-datadog-tags` when the trace has `_dd.p.*`
205
- // tags, so it may be undefined here — coalesce before appending.
206
- const existing = readDatadogTags(carrier)
207
- let tags = existing || ''
208
- if (parentId) tags += `${tags ? ',' : ''}${PROPAGATED_PARENT_ID_KEY}=${parentId}`
209
- if (mlApp) tags += `${tags ? ',' : ''}${PROPAGATED_ML_APP_KEY}=${mlApp}`
210
- if (sessionId) tags += `${tags ? ',' : ''}${PROPAGATED_SESSION_ID_KEY}=${sessionId}`
211
- if (sampleRate != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLE_RATE_KEY}=${sampleRate}`
212
- if (samplingDecision != null) tags += `${tags ? ',' : ''}${PROPAGATED_SAMPLING_DECISION_KEY}=${samplingDecision}`
213
- if (propagatedTraceId != null) tags += `${tags ? ',' : ''}${PROPAGATED_TRACE_ID_KEY}=${propagatedTraceId}`
214
- // When a local agent attribution is resolved, strip any stale upstream pagent entries that
215
- // `_injectTags` may have already written into the carrier (it propagates all `_dd.p.*` from
216
- // `_trace.tags`). This ensures the downstream sees a consistent id-only or id+name pair
217
- // rather than a mix from different hops. The id is always digit-safe; an unsafe name is wiped.
218
- if (parentAgentSpanId) {
219
- tags = stripTagsetEntry(tags, PROPAGATED_PARENT_AGENT_ID_KEY)
220
- tags = stripTagsetEntry(tags, PROPAGATED_PARENT_AGENT_NAME_KEY)
211
+ const traceTagReplacements = []
212
+ if (parentId) traceTagReplacements.push(PROPAGATED_PARENT_ID_KEY, parentId)
213
+ if (mlApp) traceTagReplacements.push(PROPAGATED_ML_APP_KEY, mlApp)
214
+ if (sessionId) traceTagReplacements.push(PROPAGATED_SESSION_ID_KEY, sessionId)
215
+ if (sampleRate != null) traceTagReplacements.push(PROPAGATED_SAMPLE_RATE_KEY, sampleRate.toString())
216
+ if (samplingDecision != null) {
217
+ traceTagReplacements.push(PROPAGATED_SAMPLING_DECISION_KEY, samplingDecision.toString())
221
218
  }
222
- const maxLength = globalTracerConfig.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH
223
- const tagsWithId = appendOptionalPropagatedTag(
224
- tags, PROPAGATED_PARENT_AGENT_ID_KEY, parentAgentSpanId, null, maxLength
225
- )
226
- // Only append the name when the id fit: a name without an id is unresolvable by the backend.
227
- if (tagsWithId === tags) {
228
- tags = tagsWithId
229
- } else {
230
- tags = appendOptionalPropagatedTag(
231
- tagsWithId, PROPAGATED_PARENT_AGENT_NAME_KEY, parentAgentName, agentNameWireSafe, maxLength
232
- )
219
+ if (propagatedTraceId != null) traceTagReplacements.push(PROPAGATED_TRACE_ID_KEY, propagatedTraceId)
220
+
221
+ let optionalTraceTagCount = 0
222
+ if (parentAgentSpanId) {
223
+ traceTagReplacements.push(PROPAGATED_PARENT_AGENT_ID_KEY, parentAgentSpanId)
224
+ optionalTraceTagCount++
225
+ if (parentAgentName && agentNameWireSafe(parentAgentName)) {
226
+ traceTagReplacements.push(PROPAGATED_PARENT_AGENT_NAME_KEY, parentAgentName)
227
+ } else {
228
+ traceTagReplacements.push(PROPAGATED_PARENT_AGENT_NAME_KEY, undefined)
229
+ }
230
+ optionalTraceTagCount++
233
231
  }
234
- if (tags !== existing) writeDatadogTags(carrier, tags)
232
+
233
+ injection.traceTagReplacements = traceTagReplacements
234
+ injection.optionalTraceTagCount = optionalTraceTagCount
235
235
  }
236
236
 
237
237
  /**
@@ -4,7 +4,6 @@ const { channel } = require('dc-polyfill')
4
4
  const BaseLLMObsPlugin = require('../base')
5
5
  const { getModelProvider } = require('../../../../../datadog-plugin-ai/src/utils')
6
6
 
7
- const toolCreationCh = channel('tracing:orchestrion:ai:tool:start')
8
7
  const setAttributesCh = channel('dd-trace:vercel-ai:span:setAttributes')
9
8
 
10
9
  const { MODEL_NAME, MODEL_PROVIDER, NAME } = require('../../constants/tags')
@@ -21,16 +20,10 @@ const {
21
20
  getTelemetryMetadata,
22
21
  } = require('./util')
23
22
 
24
- /**
25
- * @typedef {Record<string, unknown> & { description?: string, id?: string }} AvailableToolArgs
26
- */
27
-
28
23
  /**
29
24
  * @typedef {string | number | boolean | null | undefined | string[] | number[] | boolean[]} TagValue
30
25
  * @typedef {Record<string, TagValue>} SpanTags
31
26
  *
32
- * @typedef {{ name?: string, description?: string }} ToolForModel
33
- *
34
27
  * @typedef {{ type: 'text' | 'reasoning' | 'redacted-reasoning', text?: string, data?: string }} TextPart
35
28
  * @typedef {{ type: 'tool-call', toolName: string, toolCallId: string, args?: unknown, input?: unknown }} ToolCallPart
36
29
  * @typedef {(
@@ -76,58 +69,14 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
76
69
  static integration = 'ai'
77
70
  static prefix = 'tracing:dd-trace:vercel-ai'
78
71
 
79
- /**
80
- * The available tools within the runtime scope of this integration.
81
- * This essentially acts as a global registry for all tools made through the Vercel AI SDK.
82
- * @type {Set<AvailableToolArgs>}
83
- */
84
- #availableTools
85
-
86
- /**
87
- * A mapping of tool call IDs to tool names.
88
- * This is used to map the tool call ID to the tool name for the output message.
89
- * @type {Record<string, string>}
90
- */
91
- #toolCallIdsToName
92
-
93
72
  constructor (...args) {
94
73
  super(...args)
95
74
 
96
- this.#toolCallIdsToName = {}
97
- this.#availableTools = new Set()
98
- toolCreationCh.subscribe(ctx => {
99
- const toolArgs = ctx.arguments
100
- const tool = toolArgs[0] ?? {}
101
- this.#availableTools.add(tool)
102
- })
103
-
104
75
  setAttributesCh.subscribe(({ ctx, attributes }) => {
105
76
  Object.assign(ctx.attributes, attributes)
106
77
  })
107
78
  }
108
79
 
109
- /**
110
- * Does a best-effort attempt to find the right tool name for the given tool description.
111
- * This is because the Vercel AI SDK does not tag tools by name properly, but
112
- * rather by the index they were passed in. Tool names appear nowhere in the span tags.
113
- *
114
- * We use the tool description as the next best identifier for a tool.
115
- *
116
- * @param {string} toolName
117
- * @param {string | undefined} toolDescription
118
- * @returns {string | undefined}
119
- */
120
- findToolName (toolName, toolDescription) {
121
- if (Number.isNaN(Number.parseInt(toolName, 10))) return toolName
122
-
123
- for (const availableTool of this.#availableTools) {
124
- const description = availableTool.description
125
- if (description === toolDescription && availableTool.id) {
126
- return availableTool.id
127
- }
128
- }
129
- }
130
-
131
80
  /**
132
81
  * @override
133
82
  */
@@ -232,7 +181,13 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
232
181
  const lastUserPrompt =
233
182
  promptInfo.prompt ??
234
183
  promptInfo.messages.reverse().find(message => message.role === 'user')?.content
235
- const prompt = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
184
+ let prompt = lastUserPrompt
185
+ if (Array.isArray(lastUserPrompt)) {
186
+ prompt = ''
187
+ for (const part of lastUserPrompt) {
188
+ if (typeof part.text === 'string') prompt += part.text
189
+ }
190
+ }
236
191
 
237
192
  const output = tags['ai.response.object']
238
193
 
@@ -248,7 +203,13 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
248
203
  const lastUserPrompt =
249
204
  promptInfo.prompt ??
250
205
  promptInfo.messages.reverse().find(message => message.role === 'user')?.content
251
- const prompt = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
206
+ let prompt = lastUserPrompt
207
+ if (Array.isArray(lastUserPrompt)) {
208
+ prompt = ''
209
+ for (const part of lastUserPrompt) {
210
+ if (typeof part.text === 'string') prompt += part.text
211
+ }
212
+ }
252
213
 
253
214
  const output = tags['ai.response.text']
254
215
 
@@ -263,16 +224,14 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
263
224
  * @param {SpanTags} tags
264
225
  */
265
226
  setLLMOperationTags (span, tags) {
266
- const toolsForModel = tags['ai.prompt.tools']?.map(getJsonStringValue)
267
-
268
227
  const inputMessages = getJsonStringValue(tags['ai.prompt.messages'], [])
269
228
  const parsedInputMessages = []
270
229
  for (const message of inputMessages) {
271
- const formattedMessages = this.formatMessage(message, toolsForModel)
230
+ const formattedMessages = this.formatMessage(message)
272
231
  parsedInputMessages.push(...formattedMessages)
273
232
  }
274
233
 
275
- const outputMessage = this.formatOutputMessage(tags, toolsForModel)
234
+ const outputMessage = this.formatOutputMessage(tags)
276
235
 
277
236
  this._tagger.tagLLMIO(span, parsedInputMessages, outputMessage)
278
237
 
@@ -284,8 +243,8 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
284
243
  }
285
244
 
286
245
  setToolTags (span, tags) {
287
- const toolCallId = tags['ai.toolCall.id']
288
- const name = getToolNameFromTags(tags) ?? this.#toolCallIdsToName[toolCallId]
246
+ const name = getToolNameFromTags(tags)
247
+
289
248
  if (name) this._tagger._setTag(span, NAME, name)
290
249
 
291
250
  const input = tags['ai.toolCall.args']
@@ -294,7 +253,7 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
294
253
  this._tagger.tagTextIO(span, input, output)
295
254
  }
296
255
 
297
- formatOutputMessage (tags, toolsForModel) {
256
+ formatOutputMessage (tags) {
298
257
  const outputMessageText = tags['ai.response.text'] ?? tags['ai.response.object']
299
258
  const outputMessageToolCalls = getJsonStringValue(tags['ai.response.toolCalls'], [])
300
259
 
@@ -302,13 +261,9 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
302
261
  for (const toolCall of outputMessageToolCalls) {
303
262
  const toolArgs = toolCall.args ?? toolCall.input
304
263
  const toolCallArgs = typeof toolArgs === 'string' ? getJsonStringValue(toolArgs, {}) : toolArgs
305
- const toolDescription = toolsForModel?.find(tool => toolCall.toolName === tool.name)?.description
306
- const name = this.findToolName(toolCall.toolName, toolDescription)
307
- this.#toolCallIdsToName[toolCall.toolCallId] = name
308
-
309
264
  formattedToolCalls.push({
310
265
  arguments: toolCallArgs,
311
- name,
266
+ name: toolCall.toolName,
312
267
  toolId: toolCall.toolCallId,
313
268
  type: toolCall.toolCallType ?? 'function',
314
269
  })
@@ -328,11 +283,10 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
328
283
  * need to split into multiple messages.
329
284
  *
330
285
  * @param {AiSdkMessage} message
331
- * @param {ToolForModel[] | null | undefined} toolsForModel
332
286
  * @returns {Array<{role: string, content: string, toolId?: string,
333
287
  * toolCalls?: Array<{arguments: string, name: string, toolId: string, type: string}>}>}
334
288
  */
335
- formatMessage (message, toolsForModel) {
289
+ formatMessage (message) {
336
290
  const { role, content } = message
337
291
 
338
292
  if (role === 'system') {
@@ -357,12 +311,9 @@ class DdTelemetryPlugin extends BaseLLMObsPlugin {
357
311
  if (['text', 'reasoning', 'redacted-reasoning'].includes(type)) {
358
312
  finalContent += part.text ?? part.data
359
313
  } else if (type === 'tool-call') {
360
- const toolDescription = toolsForModel?.find(tool => part.toolName === tool.name)?.description
361
- const name = this.findToolName(part.toolName, toolDescription)
362
-
363
314
  toolCalls.push({
364
315
  arguments: part.args ?? part.input,
365
- name,
316
+ name: part.toolName,
366
317
  toolId: part.toolCallId,
367
318
  type: 'function',
368
319
  })
@@ -38,11 +38,15 @@ function formatLanguageModelInputMessages (instructions, messages) {
38
38
  const inputMessages = []
39
39
 
40
40
  if (instructions) {
41
- const systemPrompt = typeof instructions === 'string'
42
- ? instructions
43
- : Array.isArray(instructions)
44
- ? instructions.map(instruction => instruction.content).join('')
45
- : instructions.content
41
+ let systemPrompt = instructions
42
+ if (typeof instructions !== 'string') {
43
+ if (Array.isArray(instructions)) {
44
+ systemPrompt = ''
45
+ for (const instruction of instructions) systemPrompt += instruction.content
46
+ } else {
47
+ systemPrompt = instructions.content
48
+ }
49
+ }
46
50
 
47
51
  inputMessages.push({ role: 'system', content: systemPrompt })
48
52
  }
@@ -53,13 +57,13 @@ function formatLanguageModelInputMessages (instructions, messages) {
53
57
  if (role === 'system') {
54
58
  inputMessages.push({ role, content })
55
59
  } else if (role === 'user') {
56
- const userMessageContent =
57
- typeof content === 'string'
58
- ? content
59
- : content
60
- .filter(part => part.type === 'text')
61
- .map(part => part.text)
62
- .join('')
60
+ let userMessageContent = content
61
+ if (typeof content !== 'string') {
62
+ userMessageContent = ''
63
+ for (const part of content) {
64
+ if (part.type === 'text') userMessageContent += part.text
65
+ }
66
+ }
63
67
 
64
68
  inputMessages.push({ role, content: userMessageContent })
65
69
  } else if (role === 'assistant') {
@@ -292,7 +296,13 @@ class VercelAiTelemetryPlugin extends BaseLLMObsPlugin {
292
296
  const { event, result } = ctx
293
297
 
294
298
  const lastUserPrompt = event.messages.findLast(message => message.role === 'user')?.content
295
- const input = Array.isArray(lastUserPrompt) ? lastUserPrompt.map(part => part.text ?? '').join('') : lastUserPrompt
299
+ let input = lastUserPrompt
300
+ if (Array.isArray(lastUserPrompt)) {
301
+ input = ''
302
+ for (const part of lastUserPrompt) {
303
+ if (part.type === 'text') input += part.text
304
+ }
305
+ }
296
306
 
297
307
  const output =
298
308
  ctx.isStream
@@ -46,6 +46,13 @@ class BaseLangChainLLMObsPlugin extends LLMObsPlugin {
46
46
  }
47
47
  }
48
48
 
49
+ error (ctx) {
50
+ // NodeInterrupt or GraphInterrupt "errors" for control flow, do not mark as real errors
51
+ if (ctx.error?.is_bubble_up) return
52
+
53
+ super.error(ctx)
54
+ }
55
+
49
56
  getLLMObsSpanRegisterOptions (ctx) {
50
57
  const span = ctx.currentStore?.span
51
58
  const spanContext = span?.context()
@@ -337,37 +337,6 @@ function resolveAgentAttribution (tags, span) {
337
337
  return { name: tags[PARENT_AGENT_NAME], spanId: tags[PARENT_AGENT_SPAN_ID] }
338
338
  }
339
339
 
340
- /**
341
- * Removes all `key=value` entries for the given key from a comma-separated tagset string.
342
- *
343
- * @param {string} tags - Existing tagset string (may be empty).
344
- * @param {string} key
345
- * @returns {string}
346
- */
347
- function stripTagsetEntry (tags, key) {
348
- if (!tags.includes(key)) return tags
349
- return tags.split(',').filter(entry => !entry.startsWith(`${key}=`)).join(',')
350
- }
351
-
352
- /**
353
- * Appends `key=value` to the tagset string with a comma separator, but only when `value` is
354
- * truthy, passes the optional `safeguard` predicate, and fits within `maxTagSetLength`. Returns
355
- * the original `tags` unchanged when the value is absent, unsafe, or would overflow the budget.
356
- *
357
- * @param {string} tags - Existing tagset string (may be empty).
358
- * @param {string} key
359
- * @param {string | undefined} value
360
- * @param {((v: string) => boolean) | null} [safeguard]
361
- * @param {number} [maxTagSetLength]
362
- * @returns {string}
363
- */
364
- function appendOptionalPropagatedTag (tags, key, value, safeguard, maxTagSetLength) {
365
- if (!value || (safeguard && !safeguard(value))) return tags
366
- const entry = `${tags ? ',' : ''}${key}=${value}`
367
- if (maxTagSetLength != null && tags.length + entry.length > maxTagSetLength) return tags
368
- return `${tags}${entry}`
369
- }
370
-
371
340
  function spanHasError (span) {
372
341
  const spanContext = span.context()
373
342
  return !!(spanContext.getTag('error') || spanContext.getTag('error.type'))
@@ -518,7 +487,6 @@ function formatAudioPart (data, mimeType) {
518
487
 
519
488
  module.exports = {
520
489
  agentNameWireSafe,
521
- appendOptionalPropagatedTag,
522
490
  audioMimeTypeFromFormat,
523
491
  encodeUnicode,
524
492
  findGenAIAncestorSpanId,
@@ -527,7 +495,6 @@ module.exports = {
527
495
  normalizeLlmObsTraceId,
528
496
  formatAudioPart,
529
497
  resolveAgentAttribution,
530
- stripTagsetEntry,
531
498
  validateCostTags,
532
499
  validateKind,
533
500
  getFunctionArguments,