dd-trace 6.16.0 → 6.18.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 (356) hide show
  1. package/ci/diagnose.js +0 -23
  2. package/ci/test-optimization-validation/approval-artifacts.js +0 -3
  3. package/ci/test-optimization-validation/approval.js +0 -6
  4. package/ci/test-optimization-validation/artifact-id.js +0 -1
  5. package/ci/test-optimization-validation/bounded-json.js +0 -1
  6. package/ci/test-optimization-validation/cli.js +0 -12
  7. package/ci/test-optimization-validation/command-output-policy.js +0 -1
  8. package/ci/test-optimization-validation/command-runner.js +0 -3
  9. package/ci/test-optimization-validation/environment.js +0 -4
  10. package/ci/test-optimization-validation/executable-approval.js +0 -1
  11. package/ci/test-optimization-validation/executable.js +0 -5
  12. package/ci/test-optimization-validation/framework-adapters/cucumber.js +0 -6
  13. package/ci/test-optimization-validation/framework-adapters/cypress.js +0 -3
  14. package/ci/test-optimization-validation/framework-adapters/playwright.js +0 -6
  15. package/ci/test-optimization-validation/framework-adapters/vitest.js +0 -4
  16. package/ci/test-optimization-validation/generated-test-contract.js +0 -5
  17. package/ci/test-optimization-validation/generated-verifier.js +0 -1
  18. package/ci/test-optimization-validation/literal-environment.js +0 -1
  19. package/ci/test-optimization-validation/manifest-scaffold.js +0 -17
  20. package/ci/test-optimization-validation/manifest-schema.js +0 -21
  21. package/ci/test-optimization-validation/offline-fixtures.js +0 -4
  22. package/ci/test-optimization-validation/plan-writer.js +0 -11
  23. package/ci/test-optimization-validation/preflight-runner.js +0 -1
  24. package/ci/test-optimization-validation/redaction.js +0 -4
  25. package/ci/test-optimization-validation/report-writer.js +0 -16
  26. package/ci/test-optimization-validation/runner-command.js +0 -3
  27. package/ci/test-optimization-validation/runner-contract.js +0 -4
  28. package/ci/test-optimization-validation/safe-files.js +0 -1
  29. package/ci/test-optimization-validation/scenarios/basic-reporting.js +0 -1
  30. package/ci/test-optimization-validation/scenarios/ci-wiring.js +0 -1
  31. package/ci/test-optimization-validation/source-text.js +0 -3
  32. package/ci/vitest-no-worker-init-setup.mjs +41 -16
  33. package/index.d.ts +208 -0
  34. package/package.json +17 -16
  35. package/packages/datadog-esbuild/src/utils.js +0 -2
  36. package/packages/datadog-instrumentations/src/aerospike.js +28 -6
  37. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -2
  38. package/packages/datadog-instrumentations/src/aws-sdk.js +0 -2
  39. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +0 -2
  40. package/packages/datadog-instrumentations/src/console.js +97 -0
  41. package/packages/datadog-instrumentations/src/cucumber.js +77 -12
  42. package/packages/datadog-instrumentations/src/cypress-config.js +27 -38
  43. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +6 -0
  44. package/packages/datadog-instrumentations/src/helpers/channel.js +0 -2
  45. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +2 -1
  46. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +0 -5
  47. package/packages/datadog-instrumentations/src/helpers/hooks.js +8 -6
  48. package/packages/datadog-instrumentations/src/helpers/instrumentation-utils.js +0 -3
  49. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +0 -6
  50. package/packages/datadog-instrumentations/src/helpers/register.js +24 -0
  51. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +111 -26
  52. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +41 -0
  53. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +3 -17
  54. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +55 -0
  55. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +36 -0
  56. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/supabase.js +128 -0
  57. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +13 -0
  58. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +28 -3
  59. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +20 -0
  60. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +388 -0
  61. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +0 -5
  62. package/packages/datadog-instrumentations/src/helpers/router-helper.js +0 -1
  63. package/packages/datadog-instrumentations/src/jest.js +165 -51
  64. package/packages/datadog-instrumentations/src/knex.js +0 -1
  65. package/packages/datadog-instrumentations/src/mariadb-bundle.js +0 -24
  66. package/packages/datadog-instrumentations/src/mariadb.js +0 -2
  67. package/packages/datadog-instrumentations/src/mocha/common.js +0 -1
  68. package/packages/datadog-instrumentations/src/mocha/main.js +25 -26
  69. package/packages/datadog-instrumentations/src/mocha/utils.js +119 -49
  70. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +0 -6
  71. package/packages/datadog-instrumentations/src/mocha/worker.js +14 -17
  72. package/packages/datadog-instrumentations/src/mysql2.js +0 -2
  73. package/packages/datadog-instrumentations/src/openai-realtime/audio-accumulator.js +241 -0
  74. package/packages/datadog-instrumentations/src/openai-realtime/events.js +76 -0
  75. package/packages/datadog-instrumentations/src/openai-realtime/index.js +301 -0
  76. package/packages/datadog-instrumentations/src/openai-realtime/session.js +998 -0
  77. package/packages/datadog-instrumentations/src/openai-realtime/turns.js +211 -0
  78. package/packages/datadog-instrumentations/src/openai.js +51 -12
  79. package/packages/datadog-instrumentations/src/oracledb.js +9 -3
  80. package/packages/datadog-instrumentations/src/playwright-reporter.js +6 -20
  81. package/packages/datadog-instrumentations/src/playwright.js +280 -56
  82. package/packages/datadog-instrumentations/src/{azure-cosmos.js → postgres.js} +1 -1
  83. package/packages/datadog-instrumentations/src/router.js +0 -2
  84. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +0 -2
  85. package/packages/datadog-instrumentations/src/sequelize.js +0 -1
  86. package/packages/datadog-instrumentations/src/supabase.js +15 -0
  87. package/packages/datadog-instrumentations/src/undici.js +7 -3
  88. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +18 -18
  89. package/packages/datadog-instrumentations/src/vitest-main.js +121 -30
  90. package/packages/datadog-instrumentations/src/vitest-util.js +7 -1
  91. package/packages/datadog-instrumentations/src/vitest-worker.js +49 -13
  92. package/packages/datadog-instrumentations/src/webdriverio.js +44 -47
  93. package/packages/datadog-instrumentations/src/ws.js +41 -2
  94. package/packages/datadog-plugin-ai/src/utils.js +0 -2
  95. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +0 -1
  96. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +0 -2
  97. package/packages/datadog-plugin-aws-sdk/src/base.js +0 -1
  98. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +0 -1
  99. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +0 -1
  100. package/packages/datadog-plugin-aws-sdk/src/util.js +0 -1
  101. package/packages/datadog-plugin-azure-durable-functions/src/index.js +0 -1
  102. package/packages/datadog-plugin-cucumber/src/index.js +9 -0
  103. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +211 -85
  104. package/packages/datadog-plugin-cypress/src/finalization.js +0 -3
  105. package/packages/datadog-plugin-cypress/src/index.js +3 -1
  106. package/packages/datadog-plugin-cypress/src/source-map-utils.js +0 -4
  107. package/packages/datadog-plugin-cypress/src/support.js +52 -7
  108. package/packages/datadog-plugin-graphql/src/resolve-error.js +1 -2
  109. package/packages/datadog-plugin-graphql/src/utils.js +0 -5
  110. package/packages/datadog-plugin-jest/src/index.js +12 -0
  111. package/packages/datadog-plugin-kafkajs/src/producer.js +0 -1
  112. package/packages/datadog-plugin-mocha/src/index.js +38 -11
  113. package/packages/datadog-plugin-mongodb-core/src/query.js +0 -1
  114. package/packages/datadog-plugin-openai/src/index.js +16 -1
  115. package/packages/datadog-plugin-openai/src/realtime.js +144 -0
  116. package/packages/datadog-plugin-openai-agents/src/integration.js +0 -4
  117. package/packages/datadog-plugin-openai-agents/src/util.js +0 -1
  118. package/packages/datadog-plugin-playwright/src/index.js +81 -9
  119. package/packages/datadog-plugin-postgres/src/index.js +167 -0
  120. package/packages/datadog-plugin-prisma/src/datadog-tracing-helper.js +0 -1
  121. package/packages/datadog-plugin-redis/src/index.js +15 -1
  122. package/packages/datadog-plugin-supabase/src/error.js +18 -0
  123. package/packages/datadog-plugin-supabase/src/index.js +21 -0
  124. package/packages/datadog-plugin-supabase/src/supabase-auth-js-gotrueclient-getuser.js +69 -0
  125. package/packages/datadog-plugin-supabase/src/supabase-functions-js-functionsclient-invoke.js +70 -0
  126. package/packages/datadog-plugin-supabase/src/supabase-postgrest-js-postgrestbuilder-then.js +122 -0
  127. package/packages/datadog-plugin-supabase/src/supabase-realtime-js-realtimechannel-send.js +66 -0
  128. package/packages/datadog-plugin-supabase/src/supabase-storage-js-handle-request.js +93 -0
  129. package/packages/datadog-plugin-supabase/src/url.js +17 -0
  130. package/packages/datadog-plugin-vitest/src/index.js +9 -0
  131. package/packages/datadog-plugin-ws/src/producer.js +1 -1
  132. package/packages/datadog-plugin-ws/src/util.js +0 -4
  133. package/packages/datadog-shimmer/src/shimmer.js +14 -10
  134. package/packages/datadog-turbopack/index.js +4 -7
  135. package/packages/datadog-turbopack/src/loader.js +6 -18
  136. package/packages/datadog-webpack/src/loader.js +0 -1
  137. package/packages/dd-trace/src/agent/info.js +5 -4
  138. package/packages/dd-trace/src/aiguard/client.js +2 -3
  139. package/packages/dd-trace/src/aiguard/evaluation.js +6 -12
  140. package/packages/dd-trace/src/aiguard/index.js +2 -2
  141. package/packages/dd-trace/src/aiguard/integrations/index.js +1 -1
  142. package/packages/dd-trace/src/aiguard/integrations/openai.js +81 -7
  143. package/packages/dd-trace/src/aiguard/messages/anthropic.js +0 -2
  144. package/packages/dd-trace/src/aiguard/messages/openai.js +64 -1
  145. package/packages/dd-trace/src/aiguard/messages/utils.js +0 -1
  146. package/packages/dd-trace/src/aiguard/redaction.js +0 -1
  147. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  148. package/packages/dd-trace/src/appsec/activation.js +1 -1
  149. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +0 -5
  150. package/packages/dd-trace/src/appsec/api_security/sampler.js +0 -4
  151. package/packages/dd-trace/src/appsec/blocking/index.js +3 -3
  152. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -4
  153. package/packages/dd-trace/src/appsec/iast/index.js +1 -1
  154. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +5 -5
  155. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +1 -1
  156. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -1
  157. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +3 -3
  158. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +8 -10
  159. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -2
  160. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +4 -1
  161. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -7
  162. package/packages/dd-trace/src/appsec/index.js +2 -2
  163. package/packages/dd-trace/src/appsec/lambda.js +71 -2
  164. package/packages/dd-trace/src/appsec/rasp/utils.js +5 -1
  165. package/packages/dd-trace/src/appsec/remote_config.js +8 -6
  166. package/packages/dd-trace/src/appsec/reporter.js +10 -6
  167. package/packages/dd-trace/src/appsec/rule_manager.js +3 -3
  168. package/packages/dd-trace/src/appsec/sdk/track_event.js +0 -2
  169. package/packages/dd-trace/src/appsec/waf/index.js +2 -2
  170. package/packages/dd-trace/src/appsec/waf/waf_manager.js +8 -5
  171. package/packages/dd-trace/src/carrier.js +0 -5
  172. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +104 -0
  173. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +0 -2
  174. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +34 -3
  175. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +0 -1
  176. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +0 -1
  177. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +0 -1
  178. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +0 -4
  179. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +0 -1
  180. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +0 -2
  181. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +37 -15
  182. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +0 -3
  183. package/packages/dd-trace/src/ci-visibility/exporters/request.js +0 -11
  184. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +0 -1
  185. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +0 -1
  186. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +0 -6
  187. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -2
  188. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +0 -1
  189. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +0 -5
  190. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +0 -1
  191. package/packages/dd-trace/src/ci-visibility/telemetry.js +13 -0
  192. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +0 -1
  193. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +0 -2
  194. package/packages/dd-trace/src/ci-visibility/test-video.js +0 -1
  195. package/packages/dd-trace/src/config/defaults.js +3 -2
  196. package/packages/dd-trace/src/config/generated-config-types.d.ts +88 -82
  197. package/packages/dd-trace/src/config/helper.js +0 -1
  198. package/packages/dd-trace/src/config/index.js +31 -30
  199. package/packages/dd-trace/src/config/parsers.js +34 -6
  200. package/packages/dd-trace/src/config/remote_config.js +25 -109
  201. package/packages/dd-trace/src/config/supported-configurations.json +150 -10
  202. package/packages/dd-trace/src/datastreams/encoding.js +0 -1
  203. package/packages/dd-trace/src/datastreams/pathway.js +0 -1
  204. package/packages/dd-trace/src/debugger/constants.js +10 -0
  205. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +62 -10
  206. package/packages/dd-trace/src/debugger/devtools_client/config.js +4 -1
  207. package/packages/dd-trace/src/debugger/devtools_client/guardrail-metrics.js +15 -0
  208. package/packages/dd-trace/src/debugger/devtools_client/index.js +103 -27
  209. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +50 -7
  210. package/packages/dd-trace/src/debugger/devtools_client/log.js +3 -1
  211. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +21 -8
  212. package/packages/dd-trace/src/debugger/devtools_client/send.js +41 -15
  213. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +53 -13
  214. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +118 -71
  215. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +5 -3
  216. package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +0 -3
  217. package/packages/dd-trace/src/debugger/devtools_client/status.js +21 -7
  218. package/packages/dd-trace/src/debugger/guardrail-metrics.js +167 -0
  219. package/packages/dd-trace/src/debugger/index.js +51 -3
  220. package/packages/dd-trace/src/debugger/inspect-segment.js +0 -3
  221. package/packages/dd-trace/src/debugger/probe_sampler.js +133 -9
  222. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +31 -1
  223. package/packages/dd-trace/src/dogstatsd.js +0 -23
  224. package/packages/dd-trace/src/encode/0.4-cross-payload.js +0 -3
  225. package/packages/dd-trace/src/encode/0.4.js +0 -7
  226. package/packages/dd-trace/src/encode/0.5.js +0 -2
  227. package/packages/dd-trace/src/encode/tags-processors.js +0 -1
  228. package/packages/dd-trace/src/evp_proxy/constants.js +2 -0
  229. package/packages/dd-trace/src/evp_proxy/direct.js +17 -13
  230. package/packages/dd-trace/src/evp_proxy/discovery.js +14 -11
  231. package/packages/dd-trace/src/evp_proxy/path.js +21 -9
  232. package/packages/dd-trace/src/exporters/agent/writer.js +0 -1
  233. package/packages/dd-trace/src/exporters/agentless/index.js +0 -1
  234. package/packages/dd-trace/src/exporters/agentless/intake.js +0 -1
  235. package/packages/dd-trace/src/exporters/agentless/writer.js +0 -3
  236. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +0 -6
  237. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  238. package/packages/dd-trace/src/exporters/common/url.js +33 -4
  239. package/packages/dd-trace/src/exporters/common/writer.js +0 -2
  240. package/packages/dd-trace/src/id.js +0 -11
  241. package/packages/dd-trace/src/llmobs/audio-codec.js +192 -0
  242. package/packages/dd-trace/src/llmobs/audio-utils.js +121 -0
  243. package/packages/dd-trace/src/llmobs/constants/audio.js +35 -0
  244. package/packages/dd-trace/src/llmobs/constants/prompts.js +14 -0
  245. package/packages/dd-trace/src/llmobs/constants/tags.js +17 -0
  246. package/packages/dd-trace/src/llmobs/eval-metric.js +0 -6
  247. package/packages/dd-trace/src/llmobs/experiments/dataset.js +0 -4
  248. package/packages/dd-trace/src/llmobs/experiments/experiment.js +0 -3
  249. package/packages/dd-trace/src/llmobs/experiments/index.js +7 -6
  250. package/packages/dd-trace/src/llmobs/experiments/util.js +1 -11
  251. package/packages/dd-trace/src/llmobs/index.js +1 -3
  252. package/packages/dd-trace/src/llmobs/noop.js +10 -0
  253. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +0 -4
  254. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +0 -1
  255. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +0 -4
  256. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +0 -2
  257. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +0 -2
  258. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +4 -6
  259. package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +285 -0
  260. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +16 -3
  261. package/packages/dd-trace/src/llmobs/prompts/cache.js +272 -0
  262. package/packages/dd-trace/src/llmobs/prompts/manager.js +647 -0
  263. package/packages/dd-trace/src/llmobs/prompts/noop.js +29 -0
  264. package/packages/dd-trace/src/llmobs/prompts/prompt.js +158 -0
  265. package/packages/dd-trace/src/llmobs/sdk.js +21 -6
  266. package/packages/dd-trace/src/llmobs/span_processor.js +81 -2
  267. package/packages/dd-trace/src/llmobs/tagger.js +39 -11
  268. package/packages/dd-trace/src/llmobs/telemetry.js +36 -4
  269. package/packages/dd-trace/src/llmobs/util.js +0 -34
  270. package/packages/dd-trace/src/llmobs/writers/base.js +0 -1
  271. package/packages/dd-trace/src/llmobs/writers/util.js +1 -2
  272. package/packages/dd-trace/src/log/channels.js +3 -0
  273. package/packages/dd-trace/src/log/index.js +23 -5
  274. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +74 -6
  275. package/packages/dd-trace/src/msgpack/chunk.js +0 -1
  276. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +0 -12
  277. package/packages/dd-trace/src/openfeature/configuration_source.js +4 -1
  278. package/packages/dd-trace/src/openfeature/encoding.js +0 -2
  279. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +0 -1
  280. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -3
  281. package/packages/dd-trace/src/openfeature/index.js +6 -6
  282. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +0 -1
  283. package/packages/dd-trace/src/openfeature/span-enrichment.js +0 -4
  284. package/packages/dd-trace/src/openfeature/writers/base.js +90 -23
  285. package/packages/dd-trace/src/openfeature/writers/exposures.js +4 -5
  286. package/packages/dd-trace/src/openfeature/writers/util.js +104 -37
  287. package/packages/dd-trace/src/opentelemetry/logs/logger.js +0 -1
  288. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +0 -1
  289. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +0 -1
  290. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +0 -2
  291. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +0 -1
  292. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +0 -1
  293. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +0 -1
  294. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -14
  295. package/packages/dd-trace/src/opentelemetry/metrics/time.js +0 -1
  296. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +30 -13
  297. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +0 -1
  298. package/packages/dd-trace/src/opentelemetry/span-helpers.js +0 -1
  299. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +0 -1
  300. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +0 -3
  301. package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -6
  302. package/packages/dd-trace/src/opentracing/span_context.js +0 -1
  303. package/packages/dd-trace/src/opentracing/tracer.js +3 -3
  304. package/packages/dd-trace/src/otel-thread-ctx.js +38 -11
  305. package/packages/dd-trace/src/payload-tagging/tagging.js +15 -3
  306. package/packages/dd-trace/src/plugin_manager.js +27 -8
  307. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -2
  308. package/packages/dd-trace/src/plugins/database.js +2 -6
  309. package/packages/dd-trace/src/plugins/index.js +7 -0
  310. package/packages/dd-trace/src/plugins/plugin.js +0 -4
  311. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  312. package/packages/dd-trace/src/plugins/util/git.js +1 -3
  313. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +0 -1
  314. package/packages/dd-trace/src/plugins/util/llm.js +0 -2
  315. package/packages/dd-trace/src/plugins/util/status-validator.js +0 -1
  316. package/packages/dd-trace/src/plugins/util/test.js +26 -20
  317. package/packages/dd-trace/src/plugins/util/url.js +0 -5
  318. package/packages/dd-trace/src/priority_sampler.js +0 -6
  319. package/packages/dd-trace/src/process-tags/index.js +0 -1
  320. package/packages/dd-trace/src/profiler.js +132 -35
  321. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +0 -1
  322. package/packages/dd-trace/src/profiling/profiler.js +101 -41
  323. package/packages/dd-trace/src/profiling/ssi-heuristics.js +29 -15
  324. package/packages/dd-trace/src/propagation-hash/index.js +0 -1
  325. package/packages/dd-trace/src/proxy.js +23 -47
  326. package/packages/dd-trace/src/random_sampler.js +0 -4
  327. package/packages/dd-trace/src/rate_limiter.js +0 -4
  328. package/packages/dd-trace/src/remote_config/capabilities.js +1 -9
  329. package/packages/dd-trace/src/remote_config/index.js +0 -1
  330. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +0 -3
  331. package/packages/dd-trace/src/sampler.js +0 -4
  332. package/packages/dd-trace/src/sampling_rule.js +0 -7
  333. package/packages/dd-trace/src/serverless/vercel.js +0 -1
  334. package/packages/dd-trace/src/serverless.js +0 -1
  335. package/packages/dd-trace/src/service-naming/index.js +0 -1
  336. package/packages/dd-trace/src/service-naming/schemas/util.js +0 -1
  337. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +7 -0
  338. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +5 -0
  339. package/packages/dd-trace/src/span_processor.js +2 -2
  340. package/packages/dd-trace/src/span_sampler.js +0 -1
  341. package/packages/dd-trace/src/standalone/product.js +2 -2
  342. package/packages/dd-trace/src/standalone/tracesource.js +0 -1
  343. package/packages/dd-trace/src/startup-log.js +1 -1
  344. package/packages/dd-trace/src/telemetry/endpoints.js +0 -8
  345. package/packages/dd-trace/src/telemetry/logs/index.js +16 -5
  346. package/packages/dd-trace/src/telemetry/logs/log-collector.js +16 -6
  347. package/packages/dd-trace/src/telemetry/metrics.js +0 -2
  348. package/packages/dd-trace/src/telemetry/send-data.js +2 -1
  349. package/packages/dd-trace/src/telemetry/telemetry.js +4 -1
  350. package/packages/dd-trace/src/tracer.js +5 -4
  351. package/packages/dd-trace/src/util.js +13 -1
  352. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  353. package/packages/datadog-instrumentations/src/bullmq.js +0 -11
  354. package/packages/datadog-instrumentations/src/langchain.js +0 -7
  355. package/packages/datadog-instrumentations/src/langgraph.js +0 -7
  356. package/packages/datadog-instrumentations/src/mercurius.js +0 -11
@@ -14,6 +14,9 @@ const {
14
14
  hasEfdRetries,
15
15
  shouldSkipEfdRetry,
16
16
  } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
17
+ const {
18
+ getDynamicAtrRetryCount,
19
+ } = require('../../dd-trace/src/ci-visibility/dynamic-atr-retries')
17
20
  const {
18
21
  TEST_STATUS,
19
22
  setRumTestTags,
@@ -86,6 +89,7 @@ const {
86
89
  getPullRequestBaseBranch,
87
90
  TEST_FINAL_STATUS,
88
91
  getTestOptimizationRequestResults,
92
+ setExpectedEmptyTestSessionTags,
89
93
  } = require('../../dd-trace/src/plugins/util/test')
90
94
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
91
95
  const { RESOURCE_NAME } = require('../../../ext/tags')
@@ -229,11 +233,16 @@ function getTestScreenshots (cypressTest, attemptIndex, specScreenshots) {
229
233
  return specScreenshots.filter(screenshot => isScreenshotForTestAttempt(screenshot, titleParts, attemptIndex))
230
234
  }
231
235
 
232
- function getSessionStatus (summary) {
233
- if (summary.totalFailed !== undefined && summary.totalFailed > 0) {
236
+ function getSessionStatus (summary, testSuiteStatuses) {
237
+ if (!summary) {
238
+ if (testSuiteStatuses.has('fail')) return 'fail'
239
+ if (testSuiteStatuses.has('skip') && !testSuiteStatuses.has('pass')) return 'skip'
240
+ return 'pass'
241
+ }
242
+ if (summary.status === 'failed' || summary.failures > 0 || summary.totalFailed > 0) {
234
243
  return 'fail'
235
244
  }
236
- if (summary.totalSkipped !== undefined && summary.totalSkipped === summary.totalTests) {
245
+ if ((summary.totalSkipped ?? 0) + (summary.totalPending ?? 0) === summary.totalTests) {
237
246
  return 'skip'
238
247
  }
239
248
  return 'pass'
@@ -372,39 +381,16 @@ function getSuiteStatus (suiteStats) {
372
381
  return 'fail'
373
382
  }
374
383
  if (suiteStats.tests !== undefined &&
375
- (suiteStats.tests === suiteStats.pending || suiteStats.tests === suiteStats.skipped)) {
384
+ suiteStats.tests === (suiteStats.pending ?? 0) + (suiteStats.skipped ?? 0)) {
376
385
  return 'skip'
377
386
  }
378
387
  return 'pass'
379
388
  }
380
389
 
381
- function getMatchingCypressTest (cypressTests, testName, attemptIndex, testStatus, preferIndexedMatch = false) {
382
- let matchingTestByIndex
383
- let matchingTestByStatus
384
- let matchingTestIndex = 0
385
-
386
- for (const cypressTest of cypressTests) {
387
- if (cypressTest.title.join(' ') !== testName) {
388
- continue
389
- }
390
-
391
- if (matchingTestIndex === attemptIndex) {
392
- matchingTestByIndex = cypressTest
393
- }
394
- matchingTestIndex++
395
-
396
- if (!matchingTestByStatus && CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state] === testStatus) {
397
- matchingTestByStatus = cypressTest
398
- }
399
- }
400
-
401
- return preferIndexedMatch
402
- ? matchingTestByIndex || matchingTestByStatus
403
- : matchingTestByStatus || matchingTestByIndex
404
- }
405
-
406
390
  function isCypressHookFailure (cypressTest) {
407
- return CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state] === 'fail' &&
391
+ // Duplicate titles can leave the aggregate state passed even when the final attempt has a hook failure.
392
+ const lastAttempt = cypressTest.attempts?.at(-1)
393
+ return (cypressTest.state === 'failed' || lastAttempt?.state === 'failed') &&
408
394
  /\bhook\b/.test(String(cypressTest.displayError || ''))
409
395
  }
410
396
 
@@ -415,7 +401,12 @@ const FINAL_STATUS_RETRY_KIND = {
415
401
  atf: 'atf',
416
402
  }
417
403
 
418
- function getFinalStatusRetryKind ({ finishedTest, finishedTestAttempts, flakyTestRetriesCount }) {
404
+ function getFinalStatusRetryKind ({
405
+ finishedTest,
406
+ finishedTestAttempts,
407
+ flakyTestRetriesCount,
408
+ isDynamicAtrEnabled,
409
+ }) {
419
410
  // Infer retry kind from the executions we actually saw so ATR enabled with
420
411
  // a retry count of 0 is still treated as a single final execution.
421
412
  if (finishedTest.isAttemptToFix) {
@@ -426,7 +417,7 @@ function getFinalStatusRetryKind ({ finishedTest, finishedTestAttempts, flakyTes
426
417
  return FINAL_STATUS_RETRY_KIND.efd
427
418
  }
428
419
 
429
- if (finishedTestAttempts.length > 1 && flakyTestRetriesCount > 0) {
420
+ if (finishedTestAttempts.length > 1 && (isDynamicAtrEnabled || flakyTestRetriesCount > 0)) {
430
421
  return FINAL_STATUS_RETRY_KIND.atr
431
422
  }
432
423
 
@@ -436,7 +427,7 @@ function getFinalStatusRetryKind ({ finishedTest, finishedTestAttempts, flakyTes
436
427
  function getFinalStatus ({
437
428
  status,
438
429
  retryKind,
439
- hasFailedAllRetries,
430
+ hasFailedAllAttempts,
440
431
  hasPassedAllAtfRetries,
441
432
  isQuarantined,
442
433
  isDisabled,
@@ -450,7 +441,7 @@ function getFinalStatus ({
450
441
  case FINAL_STATUS_RETRY_KIND.atr:
451
442
  case FINAL_STATUS_RETRY_KIND.efd:
452
443
  // These modes report the aggregate result across attempts.
453
- return hasFailedAllRetries ? 'fail' : 'pass'
444
+ return hasFailedAllAttempts ? 'fail' : 'pass'
454
445
  case FINAL_STATUS_RETRY_KIND.atf:
455
446
  // Attempt-to-fix only passes if every execution passed.
456
447
  return hasPassedAllAtfRetries ? 'pass' : 'fail'
@@ -460,10 +451,14 @@ function getFinalStatus ({
460
451
  }
461
452
 
462
453
  class CypressPlugin {
454
+ #pendingTestSuiteSpans = new Map()
455
+
463
456
  _isInit = false
464
457
  testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
465
458
 
466
459
  finishedTestsByFile = {}
460
+ hasTestsReported = false
461
+ testSuiteStatuses = new Set()
467
462
  testStatuses = {}
468
463
  hasLibraryConfiguration = false
469
464
  isItrEnabled = false
@@ -473,6 +468,9 @@ class CypressPlugin {
473
468
  isCoverageReportUploadEnabled = false
474
469
  isFlakyTestRetriesEnabled = false
475
470
  flakyTestRetriesCount = 0
471
+ isDynamicAtrEnabled = false
472
+ dynamicAtrBuckets = undefined
473
+ dynamicAtrRetryCountByTest = new Map()
476
474
  isEarlyFlakeDetectionEnabled = false
477
475
  isEarlyFlakeDetectionFaulty = false
478
476
  isKnownTestsEnabled = false
@@ -550,11 +548,13 @@ class CypressPlugin {
550
548
  * Resets state that is scoped to a single Cypress run so the singleton plugin
551
549
  * can be reused safely across multiple programmatic cypress.run() calls.
552
550
  *
553
- * @returns {void}
554
551
  */
555
552
  resetRunState () {
553
+ this.#pendingTestSuiteSpans.clear()
556
554
  this._isInit = false
557
555
  this.finishedTestsByFile = {}
556
+ this.hasTestsReported = false
557
+ this.testSuiteStatuses = new Set()
558
558
  this.testStatuses = {}
559
559
  this.hasLibraryConfiguration = false
560
560
  this.isItrEnabled = false
@@ -564,6 +564,9 @@ class CypressPlugin {
564
564
  this.isCoverageReportUploadEnabled = false
565
565
  this.isFlakyTestRetriesEnabled = false
566
566
  this.flakyTestRetriesCount = 0
567
+ this.isDynamicAtrEnabled = false
568
+ this.dynamicAtrBuckets = undefined
569
+ this.dynamicAtrRetryCountByTest = new Map()
567
570
  this.isEarlyFlakeDetectionEnabled = false
568
571
  this.isEarlyFlakeDetectionFaulty = false
569
572
  this.isKnownTestsEnabled = false
@@ -630,7 +633,6 @@ class CypressPlugin {
630
633
  *
631
634
  * @param {string} traceId - Test trace id used for the upload
632
635
  * @param {Promise<string|undefined>} uploadPromise - Promise resolving to the upload outcome
633
- * @returns {void}
634
636
  */
635
637
  addScreenshotUploadPromise (traceId, uploadPromise) {
636
638
  const uploadPromises = this.screenshotUploadPromisesByTraceId.get(traceId)
@@ -659,7 +661,6 @@ class CypressPlugin {
659
661
  * Cancels screenshot work that must not outlive an errored after:spec finalization boundary.
660
662
  *
661
663
  * @param {Error} error - Error that triggered finalization
662
- * @returns {void}
663
664
  */
664
665
  abortPendingScreenshotUploads (error) {
665
666
  for (const controller of this.screenshotUploadAbortControllers) controller.abort(error)
@@ -673,7 +674,6 @@ class CypressPlugin {
673
674
  * start/finish. Captured at session span creation so it shares the same
674
675
  * epoch as the trace without reaching into span internals.
675
676
  *
676
- * @returns {number}
677
677
  */
678
678
  _now () {
679
679
  return this._timeOrigin + performance.now() - this._perfOrigin
@@ -682,7 +682,6 @@ class CypressPlugin {
682
682
  /**
683
683
  * Returns the directory used to normalize coverage file names.
684
684
  *
685
- * @returns {string}
686
685
  */
687
686
  getCoverageRootDir () {
688
687
  return this.repositoryRoot || this.rootDir || process.cwd()
@@ -691,7 +690,6 @@ class CypressPlugin {
691
690
  /**
692
691
  * Returns whether skipped test coverage should be backfilled into the session coverage map.
693
692
  *
694
- * @returns {boolean}
695
693
  */
696
694
  shouldBackfillSkippedCoverage () {
697
695
  return this.isItrEnabled &&
@@ -704,7 +702,6 @@ class CypressPlugin {
704
702
  * Adds a test's Istanbul coverage to the aggregated session coverage map.
705
703
  *
706
704
  * @param {object} coverage
707
- * @returns {void}
708
705
  */
709
706
  addTestSessionCoverage (coverage) {
710
707
  mergeCoverage(coverage, this.testSessionCoverageMap)
@@ -713,7 +710,6 @@ class CypressPlugin {
713
710
  /**
714
711
  * Applies backend skipped-test coverage to the aggregated session coverage map.
715
712
  *
716
- * @returns {boolean}
717
713
  */
718
714
  applySkippedCoverageToTestSessionCoverage () {
719
715
  if (!this.shouldBackfillSkippedCoverage()) {
@@ -756,7 +752,6 @@ class CypressPlugin {
756
752
  /**
757
753
  * Uploads executable-line coverage for the test session when backend configuration enables it.
758
754
  *
759
- * @returns {void}
760
755
  */
761
756
  reportTestSessionCoverage () {
762
757
  const exporter = this.tracer._tracer._exporter
@@ -785,7 +780,6 @@ class CypressPlugin {
785
780
  * @param {object} cypressConfig - Cypress resolved config
786
781
  * @param {object} tracer - dd-trace proxy tracer
787
782
  * @param {object} testOptimizationConfig - Test Optimization config
788
- * @returns {void}
789
783
  */
790
784
  warnIfMisconfiguredTestFailureScreenshots (cypressConfig, tracer, testOptimizationConfig) {
791
785
  if (!testOptimizationConfig.DD_TEST_FAILURE_SCREENSHOTS_ENABLED) {
@@ -816,7 +810,6 @@ class CypressPlugin {
816
810
  * @param {object} cypressConfig - Cypress resolved config
817
811
  * @param {object} tracer - dd-trace proxy tracer
818
812
  * @param {object} testOptimizationConfig - Test Optimization config
819
- * @returns {void}
820
813
  */
821
814
  warnIfMisconfiguredTestFailureVideos (cypressConfig, tracer, testOptimizationConfig) {
822
815
  if (!testOptimizationConfig.DD_TEST_FAILURE_VIDEOS_ENABLED) return
@@ -897,6 +890,8 @@ class CypressPlugin {
897
890
  earlyFlakeDetectionFaultyThreshold,
898
891
  isFlakyTestRetriesEnabled,
899
892
  flakyTestRetriesCount,
893
+ isDynamicAtrEnabled,
894
+ dynamicAtrBuckets,
900
895
  isKnownTestsEnabled,
901
896
  isTestManagementEnabled,
902
897
  testManagementAttemptToFixRetries,
@@ -914,13 +909,26 @@ class CypressPlugin {
914
909
  if (isFlakyTestRetriesEnabled && this.isTestIsolationEnabled) {
915
910
  this.isFlakyTestRetriesEnabled = true
916
911
  this.flakyTestRetriesCount = flakyTestRetriesCount ?? 0
912
+ this.isDynamicAtrEnabled = isDynamicAtrEnabled ?? false
913
+ this.dynamicAtrBuckets = dynamicAtrBuckets
917
914
  if (typeof this.cypressConfig.retries === 'number') {
918
915
  this.cypressConfig.retries = {
919
916
  openMode: this.cypressConfig.retries,
920
917
  runMode: this.cypressConfig.retries,
921
918
  }
922
919
  }
923
- this.cypressConfig.retries.runMode = this.flakyTestRetriesCount
920
+ // Dynamic ATR starts at the maximum budget, then narrows each test
921
+ // after its initial duration is known.
922
+ if (this.isDynamicAtrEnabled) {
923
+ this.cypressConfig.retries.runMode = Math.max(
924
+ 1,
925
+ this.dynamicAtrBuckets
926
+ ? Math.max(...this.dynamicAtrBuckets)
927
+ : this.earlyFlakeDetectionRetryPolicy.schedulingRetryCount
928
+ )
929
+ } else {
930
+ this.cypressConfig.retries.runMode = this.flakyTestRetriesCount
931
+ }
924
932
  } else {
925
933
  this.flakyTestRetriesCount = 0
926
934
  }
@@ -962,7 +970,6 @@ class CypressPlugin {
962
970
  * @param {string} testSuite
963
971
  * @param {string} testName
964
972
  * @param {number | undefined} duration
965
- * @returns {number}
966
973
  */
967
974
  setEfdRetryCountForTest (testSuite, testName, duration) {
968
975
  if (!this.efdRetryCountByTest[testSuite]) {
@@ -979,19 +986,51 @@ class CypressPlugin {
979
986
  return retryCount
980
987
  }
981
988
 
989
+ /**
990
+ * Stores the dynamic ATR retry count for a test after its first execution duration is known.
991
+ *
992
+ * @param {string} testSuite
993
+ * @param {string} testId
994
+ * @param {number | undefined} duration
995
+ */
996
+ setDynamicAtrRetryCountForTest (testSuite, testId, duration) {
997
+ if (!this.dynamicAtrRetryCountByTest) {
998
+ this.dynamicAtrRetryCountByTest = new Map()
999
+ }
1000
+ let retryCountByTestId = this.dynamicAtrRetryCountByTest.get(testSuite)
1001
+ if (!retryCountByTestId) {
1002
+ retryCountByTestId = new Map()
1003
+ this.dynamicAtrRetryCountByTest.set(testSuite, retryCountByTestId)
1004
+ }
1005
+ const retryCount = getDynamicAtrRetryCount(
1006
+ duration ?? 0,
1007
+ this.earlyFlakeDetectionRetryPolicy,
1008
+ this.dynamicAtrBuckets
1009
+ )
1010
+ retryCountByTestId.set(testId, retryCount)
1011
+ return retryCount
1012
+ }
1013
+
982
1014
  /**
983
1015
  * Returns whether an EFD retry clone is beyond the selected retry count and should be discarded.
984
1016
  *
985
1017
  * @param {string} testSuite
986
1018
  * @param {string} testName
987
1019
  * @param {number} efdRetryIndex
988
- * @returns {boolean}
989
1020
  */
990
1021
  shouldSkipEfdRetry (testSuite, testName, efdRetryIndex) {
991
1022
  const testSuiteRetries = this.efdRetryCountByTest[testSuite]
992
1023
  return shouldSkipEfdRetry(efdRetryIndex, testSuiteRetries?.[testName])
993
1024
  }
994
1025
 
1026
+ /**
1027
+ * @param {string} testSuite
1028
+ * @param {string} testId
1029
+ */
1030
+ getDynamicAtrRetryCountForTest (testSuite, testId) {
1031
+ return this.dynamicAtrRetryCountByTest.get(testSuite)?.get(testId)
1032
+ }
1033
+
995
1034
  getTestSuiteSpan ({ testSuite, testSuiteAbsolutePath }) {
996
1035
  const testSuiteSpanMetadata = {
997
1036
  ...getTestSuiteCommonTags(this.command, this.frameworkVersion, testSuite, TEST_FRAMEWORK_NAME),
@@ -1012,7 +1051,7 @@ class CypressPlugin {
1012
1051
  }
1013
1052
  }
1014
1053
 
1015
- return this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_suite`, {
1054
+ const testSuiteSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_suite`, {
1016
1055
  childOf: this.testModuleSpan,
1017
1056
  tags: {
1018
1057
  [COMPONENT]: TEST_FRAMEWORK_NAME,
@@ -1021,9 +1060,13 @@ class CypressPlugin {
1021
1060
  },
1022
1061
  integrationName: TEST_FRAMEWORK_NAME,
1023
1062
  })
1063
+ this.#pendingTestSuiteSpans.set(testSuite, testSuiteSpan)
1064
+ return testSuiteSpan
1024
1065
  }
1025
1066
 
1026
- getTestSpan ({ testName, testSuite, isUnskippable, isForcedToRun, testSourceFile, isDisabled, isQuarantined }) {
1067
+ getTestSpan ({
1068
+ testName, testSuite, isUnskippable, isForcedToRun, testSourceFile, isDisabled, isQuarantined, startTime,
1069
+ }) {
1027
1070
  const testSuiteTags = {
1028
1071
  [TEST_MODULE]: TEST_FRAMEWORK_NAME,
1029
1072
  }
@@ -1080,6 +1123,7 @@ class CypressPlugin {
1080
1123
 
1081
1124
  return this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test`, {
1082
1125
  childOf,
1126
+ startTime,
1083
1127
  tags: {
1084
1128
  [COMPONENT]: TEST_FRAMEWORK_NAME,
1085
1129
  [ORIGIN_KEY]: CI_APP_ORIGIN,
@@ -1312,6 +1356,20 @@ class CypressPlugin {
1312
1356
  }
1313
1357
 
1314
1358
  afterRun (suiteStats, error) {
1359
+ const pendingFinalizations = []
1360
+ // Late manual initialization cannot intercept a later replacement of after:spec.
1361
+ // Run results let us finalize the suites whose browser hooks already created spans.
1362
+ if (this._isInit && suiteStats?.runs) {
1363
+ for (const result of suiteStats.runs) {
1364
+ const testSuiteSpan = this.#pendingTestSuiteSpans.get(result.spec?.relative)
1365
+ if (!testSuiteSpan) continue
1366
+ this.testSuiteSpan = testSuiteSpan
1367
+ const endedAt = Date.parse(result.stats?.endedAt)
1368
+ const finalization = this.afterSpec(result.spec, result, undefined,
1369
+ Number.isFinite(endedAt) ? endedAt : undefined)
1370
+ if (finalization) pendingFinalizations.push(finalization)
1371
+ }
1372
+ }
1315
1373
  const hasPendingVideoSpans = this.pendingVideoUploads.length > 0
1316
1374
  const videoUploadsPromise = this.uploadPendingTestSuiteVideos()
1317
1375
  if (!this._isInit) {
@@ -1322,7 +1380,11 @@ class CypressPlugin {
1322
1380
  }
1323
1381
  return
1324
1382
  }
1325
- const finalizationPromise = this.#finalizeRun(suiteStats, error, hasPendingVideoSpans)
1383
+ // Recovered screenshot-only tests must finish before the run closes any remaining trace spans.
1384
+ const finalizeRun = () => this.#finalizeRun(suiteStats, error, hasPendingVideoSpans)
1385
+ const finalizationPromise = pendingFinalizations.length > 0
1386
+ ? Promise.all(pendingFinalizations).then(finalizeRun)
1387
+ : finalizeRun()
1326
1388
  if (videoUploadsPromise) {
1327
1389
  return Promise.all([videoUploadsPromise, finalizationPromise])
1328
1390
  .then(() => this.#flushExporter(false))
@@ -1340,12 +1402,21 @@ class CypressPlugin {
1340
1402
  */
1341
1403
  #finalizeRun (suiteStats, error, hasPendingVideoSpans = false) {
1342
1404
  if (this.testSessionSpan && this.testModuleSpan) {
1343
- const testStatus = error ? 'fail' : getSessionStatus(suiteStats)
1405
+ const testStatus = error ? 'fail' : getSessionStatus(suiteStats, this.testSuiteStatuses)
1406
+ const hasNoTests = suiteStats?.totalTests === 0 ||
1407
+ (suiteStats?.totalTests === undefined && !this.hasTestsReported)
1344
1408
  const hasBackfilledCoverage = this.applySkippedCoverageToTestSessionCoverage()
1345
1409
  const testCodeCoverageLinesTotal = this.getTestCodeCoverageLinesTotal(hasBackfilledCoverage)
1346
1410
 
1347
1411
  this.testModuleSpan.setTag(TEST_STATUS, testStatus)
1348
1412
  this.testSessionSpan.setTag(TEST_STATUS, testStatus)
1413
+ if (testStatus !== 'fail' && (hasNoTests || testStatus === 'skip')) {
1414
+ setExpectedEmptyTestSessionTags(
1415
+ this.testSessionSpan,
1416
+ this.testModuleSpan,
1417
+ hasNoTests ? 'zero_tests' : 'all_tests_skipped'
1418
+ )
1419
+ }
1349
1420
  if (error) {
1350
1421
  this.testModuleSpan.setTag('error', error)
1351
1422
  this.testSessionSpan.setTag('error', error)
@@ -1434,7 +1505,6 @@ class CypressPlugin {
1434
1505
  * Uploads failure screenshots as soon as Cypress creates them.
1435
1506
  *
1436
1507
  * @param {object} details - Cypress screenshot details
1437
- * @returns {void}
1438
1508
  */
1439
1509
  afterScreenshot (details) {
1440
1510
  const lastFailedTestSpan = this.lastFinishedTest?.testStatus === 'fail'
@@ -1459,9 +1529,10 @@ class CypressPlugin {
1459
1529
  }
1460
1530
  }
1461
1531
 
1462
- afterSpec (spec, results, error) {
1463
- const { tests, stats, screenshots, video } = results || {}
1532
+ afterSpec (spec, results, error, finishTime) {
1533
+ const { tests, stats, screenshots, video, error: resultError } = results || {}
1464
1534
  const cypressTests = tests || []
1535
+ if (cypressTests.length > 0 || stats?.tests > 0) this.hasTestsReported = true
1465
1536
  const specScreenshots = screenshots || []
1466
1537
  const finishedTests = this.finishedTestsByFile[spec.relative] || []
1467
1538
  const screenshotUploadPromises = []
@@ -1494,7 +1565,10 @@ class CypressPlugin {
1494
1565
  ? getTestSuitePath(spec.absolute, this.repositoryRoot)
1495
1566
  : spec.relative
1496
1567
 
1497
- const skippedTestSpan = this.getTestSpan({ testName: cypressTestName, testSuite: spec.relative, testSourceFile })
1568
+ // Recovered tests that never ran have no execution time; keep them at the recorded suite end.
1569
+ const skippedTestSpan = this.getTestSpan({
1570
+ testName: cypressTestName, testSuite: spec.relative, testSourceFile, startTime: finishTime,
1571
+ })
1498
1572
  skippedTestSpan.setTag(TEST_FINAL_STATUS, 'skip')
1499
1573
 
1500
1574
  skippedTestSpan.setTag(TEST_STATUS, 'skip')
@@ -1532,7 +1606,7 @@ class CypressPlugin {
1532
1606
  })
1533
1607
  }
1534
1608
 
1535
- testSpanFinishes.push({ testSpan: skippedTestSpan, finishTime: this._now() })
1609
+ testSpanFinishes.push({ testSpan: skippedTestSpan, finishTime: finishTime ?? this._now() })
1536
1610
  }
1537
1611
 
1538
1612
  // Make sure that reported test statuses are the same as Cypress reports.
@@ -1540,33 +1614,41 @@ class CypressPlugin {
1540
1614
  // Cypress will report the last run test as failed, but we don't know that yet at `dd:afterEach`
1541
1615
  let latestError
1542
1616
 
1543
- const finishedTestsByTestName = finishedTests.reduce((acc, finishedTest) => {
1544
- if (!acc[finishedTest.testName]) {
1545
- acc[finishedTest.testName] = []
1617
+ // Cypress preserves IDs across retry clones, so duplicate titles remain independent.
1618
+ const finishedTestsByTest = new Map()
1619
+ for (const finishedTest of finishedTests) {
1620
+ const testIdentifier = finishedTest.testId ?? finishedTest.testName
1621
+ let finishedTestAttempts = finishedTestsByTest.get(testIdentifier)
1622
+ if (!finishedTestAttempts) {
1623
+ finishedTestAttempts = []
1624
+ finishedTestsByTest.set(testIdentifier, finishedTestAttempts)
1546
1625
  }
1547
- acc[finishedTest.testName].push(finishedTest)
1548
- return acc
1549
- }, {})
1626
+ finishedTestAttempts.push(finishedTest)
1627
+ }
1550
1628
 
1551
- for (const [testName, finishedTestAttempts] of Object.entries(finishedTestsByTestName)) {
1629
+ const matchedCypressTests = new Set()
1630
+ for (const finishedTestAttempts of finishedTestsByTest.values()) {
1631
+ const { testName } = finishedTestAttempts[0]
1632
+ // Cypress reports one result per original runnable, including Datadog-managed clones.
1633
+ // Public results omit runnable IDs but retain declaration order.
1634
+ const cypressTest = cypressTests.find(test =>
1635
+ test.title.join(' ') === testName && !matchedCypressTests.has(test)
1636
+ )
1637
+ matchedCypressTests.add(cypressTest)
1552
1638
  for (const [attemptIndex, finishedTest] of finishedTestAttempts.entries()) {
1553
1639
  // We can check if this is the last attempt regardless of the retry mechanism
1554
1640
  const isLastAttempt = attemptIndex === finishedTestAttempts.length - 1
1555
1641
  const isDatadogManagedAttempt = finishedTest.isEfdManagedTest || finishedTest.isAttemptToFix
1556
- const cypressTest = isDatadogManagedAttempt
1557
- ? getMatchingCypressTest(cypressTests, testName, attemptIndex, finishedTest.testStatus, isLastAttempt) ||
1558
- cypressTests.find(test => test.title.join(' ') === testName)
1559
- : cypressTests.find(test => test.title.join(' ') === testName)
1560
1642
  if (!cypressTest) {
1561
1643
  continue
1562
1644
  }
1563
1645
  // finishedTests can include multiple tests with the same name if they have been retried
1564
1646
  // by early flake detection. Cypress is unaware of this so .attempts does not necessarily have
1565
1647
  // the same length as `finishedTestAttempts`
1566
- const shouldUseCapturedStatus = isDatadogManagedAttempt && !(isLastAttempt && isCypressHookFailure(cypressTest))
1567
- let cypressTestStatus = shouldUseCapturedStatus
1568
- ? finishedTest.testStatus
1569
- : CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state]
1648
+ let cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state]
1649
+ if (isDatadogManagedAttempt) {
1650
+ cypressTestStatus = isLastAttempt && isCypressHookFailure(cypressTest) ? 'fail' : finishedTest.testStatus
1651
+ }
1570
1652
  if (!finishedTest.isEfdManagedTest && !finishedTest.isAttemptToFix &&
1571
1653
  cypressTest.attempts && cypressTest.attempts[attemptIndex]) {
1572
1654
  cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.attempts[attemptIndex].state]
@@ -1648,9 +1730,22 @@ class CypressPlugin {
1648
1730
  finishedTest,
1649
1731
  finishedTestAttempts,
1650
1732
  flakyTestRetriesCount: this.flakyTestRetriesCount,
1733
+ isDynamicAtrEnabled: this.isDynamicAtrEnabled,
1651
1734
  })
1652
1735
 
1653
- const hasFailedAllRetries = testSpanTags[TEST_HAS_FAILED_ALL_RETRIES] === 'true'
1736
+ let hasFailedAllAttempts = testSpanTags[TEST_HAS_FAILED_ALL_RETRIES] === 'true'
1737
+ if (retryKind === FINAL_STATUS_RETRY_KIND.atr) {
1738
+ // Late hooks can change every attempt after dd:afterEach has captured its status.
1739
+ hasFailedAllAttempts = finishedTestAttempts.every(attempt =>
1740
+ attempt.testSpan.context().getTag(TEST_STATUS) === 'fail'
1741
+ )
1742
+ const atrRetryCount = this.isDynamicAtrEnabled
1743
+ ? this.getDynamicAtrRetryCountForTest(spec.relative, finishedTest.testId)
1744
+ : this.flakyTestRetriesCount
1745
+ // An aborted run can fail without using the entire selected retry budget.
1746
+ const hasFailedAllRetries = hasFailedAllAttempts && finishedTestAttempts.length > atrRetryCount
1747
+ finishedTest.testSpan.setTag(TEST_HAS_FAILED_ALL_RETRIES, hasFailedAllRetries ? 'true' : undefined)
1748
+ }
1654
1749
  const hasPassedAllAtfRetries =
1655
1750
  testSpanTags[TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED] === 'true'
1656
1751
  const isQuarantined = testSpanTags[TEST_MANAGEMENT_IS_QUARANTINED] === 'true'
@@ -1659,7 +1754,7 @@ class CypressPlugin {
1659
1754
  const finalStatus = getFinalStatus({
1660
1755
  status: cypressTestStatus,
1661
1756
  retryKind,
1662
- hasFailedAllRetries,
1757
+ hasFailedAllAttempts,
1663
1758
  hasPassedAllAtfRetries,
1664
1759
  isQuarantined,
1665
1760
  isDisabled,
@@ -1682,8 +1777,13 @@ class CypressPlugin {
1682
1777
  }
1683
1778
  }
1684
1779
 
1685
- const testSuiteFinishTime = this._now()
1686
- const suiteFailed = error || latestError || getSuiteStatus(stats) === 'fail'
1780
+ const testSuiteFinishTime = finishTime ?? this._now()
1781
+ const suiteError = error || resultError || latestError
1782
+ const suiteStatus = suiteError ||
1783
+ cypressTests.some(test => CYPRESS_STATUS_TO_TEST_STATUS[test.state] === 'fail')
1784
+ ? 'fail'
1785
+ : getSuiteStatus(stats)
1786
+ this.testSuiteStatuses.add(suiteStatus)
1687
1787
  const testSuiteSpan = this.testSuiteSpan
1688
1788
  const uploadOptions = {
1689
1789
  filePath: video,
@@ -1694,11 +1794,12 @@ class CypressPlugin {
1694
1794
  ? testSuiteSpan.context().toSpanId()
1695
1795
  : undefined,
1696
1796
  }
1697
- const shouldUploadVideo = suiteFailed && this.#canUploadTestSuiteVideo(uploadOptions)
1797
+ const shouldUploadVideo = suiteStatus === 'fail' && this.#canUploadTestSuiteVideo(uploadOptions)
1698
1798
  if (testSuiteSpan) {
1699
- testSuiteSpan.setTag(TEST_STATUS, error ? 'fail' : getSuiteStatus(stats))
1700
- if (error || latestError) testSuiteSpan.setTag('error', error || latestError)
1799
+ testSuiteSpan.setTag(TEST_STATUS, suiteStatus)
1800
+ if (suiteError) testSuiteSpan.setTag('error', suiteError)
1701
1801
  this.testSuiteSpan = null
1802
+ this.#pendingTestSuiteSpans.delete(spec.relative)
1702
1803
  }
1703
1804
 
1704
1805
  let testSpansPromise
@@ -1838,7 +1939,6 @@ class CypressPlugin {
1838
1939
  * @param {string|undefined} options.filePath - Cypress video path
1839
1940
  * @param {string|undefined} options.testSessionId - Test session id
1840
1941
  * @param {string|undefined} options.testSuiteId - Test suite id
1841
- * @returns {boolean}
1842
1942
  */
1843
1943
  #canUploadTestSuiteVideo ({ filePath, testSessionId, testSuiteId }) {
1844
1944
  const exporter = this.tracer?._tracer?._exporter
@@ -1936,7 +2036,14 @@ class CypressPlugin {
1936
2036
 
1937
2037
  getTasks () {
1938
2038
  return {
1939
- 'dd:testSuiteStart': ({ testSuite, testSuiteAbsolutePath }) => {
2039
+ 'dd:testSuiteStart': ({ testSuite, testSuiteAbsolutePath, isTextTerminal: browserIsTextTerminal }) => {
2040
+ // Cypress 6 omits the execution mode from the Node plugin configuration.
2041
+ const isTextTerminal = this.cypressConfig.isTextTerminal ?? browserIsTextTerminal
2042
+ if (!isTextTerminal) {
2043
+ this.isFlakyTestRetriesEnabled = false
2044
+ this.isDynamicAtrEnabled = false
2045
+ this.flakyTestRetriesCount = 0
2046
+ }
1940
2047
  const suitePayload = {
1941
2048
  isEarlyFlakeDetectionEnabled:
1942
2049
  this.isEarlyFlakeDetectionEnabled && hasEfdRetries(this.earlyFlakeDetectionRetryPolicy),
@@ -1950,14 +2057,18 @@ class CypressPlugin {
1950
2057
  isModifiedTest: this.getIsTestModified(testSuiteAbsolutePath),
1951
2058
  repositoryRoot: this.repositoryRoot,
1952
2059
  isTestIsolationEnabled: this.isTestIsolationEnabled,
2060
+ isDynamicAtrEnabled: this.isDynamicAtrEnabled,
2061
+ isTextTerminal,
1953
2062
  rumFlushWaitMillis: this.rumFlushWaitMillis,
1954
2063
  rumTestExecutionIdCookieName: RUM_TEST_EXECUTION_ID_COOKIE_NAME,
1955
2064
  }
1956
2065
 
1957
- this.testSuiteSpan ||= this.getTestSuiteSpan({ testSuite, testSuiteAbsolutePath })
2066
+ this.testSuiteSpan = this.#pendingTestSuiteSpans.get(testSuite) ||
2067
+ this.getTestSuiteSpan({ testSuite, testSuiteAbsolutePath })
1958
2068
  return suitePayload
1959
2069
  },
1960
2070
  'dd:beforeEach': (test) => {
2071
+ this.hasTestsReported = true
1961
2072
  const { testId, testName, testSuite, isEfdRetry, efdRetryIndex } = test
1962
2073
  if (isEfdRetry && this.shouldSkipEfdRetry(testSuite, testName, efdRetryIndex)) {
1963
2074
  return { shouldSkip: true, shouldDiscard: true }
@@ -2016,6 +2127,7 @@ class CypressPlugin {
2016
2127
  testSourceStack,
2017
2128
  testSuite,
2018
2129
  testSuiteAbsolutePath,
2130
+ testId,
2019
2131
  testName,
2020
2132
  testItTitle,
2021
2133
  isNew,
@@ -2064,12 +2176,23 @@ class CypressPlugin {
2064
2176
  this.activeTestSpan.setTag(TEST_EARLY_FLAKE_ABORT_REASON, 'slow')
2065
2177
  }
2066
2178
  }
2179
+ // Dynamic ATR: after the first attempt, compute the duration-based retry budget.
2180
+ if (
2181
+ this.isDynamicAtrEnabled &&
2182
+ this.isFlakyTestRetriesEnabled &&
2183
+ !isAttemptToFix &&
2184
+ !isEfdRetry &&
2185
+ !isEfdManagedTest &&
2186
+ this.getDynamicAtrRetryCountForTest(testSuite, testId) === undefined
2187
+ ) {
2188
+ this.setDynamicAtrRetryCountForTest(testSuite, testId, duration)
2189
+ }
2067
2190
  if (didAbortSlowEfdRetries && testStatus === 'skip' && !error && duration > 0) {
2068
2191
  testStatus = 'pass'
2069
2192
  }
2070
2193
  this.activeTestSpan.setTag(TEST_STATUS, testStatus)
2071
2194
 
2072
- const testIdentifier = `${testSuite}\0${testName}`
2195
+ const testIdentifier = `${testSuite}\0${testId ?? testName}`
2073
2196
  let testStatuses = this.testStatuses[testIdentifier]
2074
2197
  if (testStatuses) {
2075
2198
  testStatuses.push(testStatus)
@@ -2160,8 +2283,10 @@ class CypressPlugin {
2160
2283
  })
2161
2284
  }
2162
2285
  // ATR: set TEST_HAS_FAILED_ALL_RETRIES when all auto test retries were exhausted and every attempt failed
2286
+ const dynamicAtrRetryCount = this.getDynamicAtrRetryCountForTest(testSuite, testId)
2287
+ const atrRetryCount = this.isDynamicAtrEnabled ? dynamicAtrRetryCount : this.flakyTestRetriesCount
2163
2288
  if (this.isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry &&
2164
- this.flakyTestRetriesCount > 0 && testStatuses.length === this.flakyTestRetriesCount + 1 &&
2289
+ atrRetryCount > 0 && testStatuses.length === atrRetryCount + 1 &&
2165
2290
  testStatuses.every(status => status === 'fail')) {
2166
2291
  this.activeTestSpan.setTag(TEST_HAS_FAILED_ALL_RETRIES, 'true')
2167
2292
  }
@@ -2201,6 +2326,7 @@ class CypressPlugin {
2201
2326
  }
2202
2327
 
2203
2328
  const finishedTest = {
2329
+ testId,
2204
2330
  testName,
2205
2331
  testStatus,
2206
2332
  finishTime: this._now(),
@@ -2227,7 +2353,7 @@ class CypressPlugin {
2227
2353
  })
2228
2354
  this.activeTestSpan = null
2229
2355
 
2230
- return null
2356
+ return dynamicAtrRetryCount === undefined ? null : { dynamicAtrRetryCount }
2231
2357
  },
2232
2358
  'dd:addTags': (tags) => {
2233
2359
  if (this.activeTestSpan) {