dd-trace 6.7.0 → 6.9.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 (272) hide show
  1. package/ci/diagnose.js +27 -25
  2. package/ci/init.js +6 -6
  3. package/ci/runbook.md +114 -20
  4. package/ci/test-optimization-validation/approval-artifacts.js +36 -3
  5. package/ci/test-optimization-validation/approval.js +78 -22
  6. package/ci/test-optimization-validation/blocker-category.js +24 -0
  7. package/ci/test-optimization-validation/ci-discovery.js +23 -17
  8. package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
  9. package/ci/test-optimization-validation/ci-remediation.js +1 -1
  10. package/ci/test-optimization-validation/cli.js +280 -52
  11. package/ci/test-optimization-validation/command-blocker.js +159 -20
  12. package/ci/test-optimization-validation/command-output-policy.js +14 -8
  13. package/ci/test-optimization-validation/command-runner.js +9 -7
  14. package/ci/test-optimization-validation/environment.js +4 -2
  15. package/ci/test-optimization-validation/executable.js +15 -13
  16. package/ci/test-optimization-validation/execution-lock.js +70 -0
  17. package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
  18. package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
  19. package/ci/test-optimization-validation/framework-adapters/playwright.js +5 -5
  20. package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
  21. package/ci/test-optimization-validation/generated-files.js +47 -37
  22. package/ci/test-optimization-validation/generated-test-contract.js +9 -7
  23. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  24. package/ci/test-optimization-validation/literal-glob.js +52 -0
  25. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  26. package/ci/test-optimization-validation/manifest-scaffold.js +301 -52
  27. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  28. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  29. package/ci/test-optimization-validation/offline-output.js +6 -6
  30. package/ci/test-optimization-validation/package-check.js +94 -0
  31. package/ci/test-optimization-validation/plan-writer.js +183 -10
  32. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  33. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  34. package/ci/test-optimization-validation/redaction.js +18 -27
  35. package/ci/test-optimization-validation/report-writer.js +182 -48
  36. package/ci/test-optimization-validation/result-semantics.js +20 -3
  37. package/ci/test-optimization-validation/runner-command.js +41 -21
  38. package/ci/test-optimization-validation/runner-contract.js +79 -43
  39. package/ci/test-optimization-validation/safe-files.js +4 -4
  40. package/ci/test-optimization-validation/scenarios/basic-reporting.js +60 -6
  41. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  42. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  43. package/ci/test-optimization-validation/scenarios/test-management.js +2 -2
  44. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  45. package/ci/test-optimization-validation/test-output.js +2 -1
  46. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  47. package/ci/vitest-no-worker-init-setup.mjs +377 -84
  48. package/index.d.ts +175 -14
  49. package/init.js +1 -17
  50. package/initialize.mjs +11 -0
  51. package/loader-hook.mjs +6 -4
  52. package/package.json +12 -4
  53. package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
  54. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  55. package/packages/datadog-esbuild/index.js +4 -2
  56. package/packages/datadog-esbuild/src/utils.js +17 -2
  57. package/packages/datadog-instrumentations/src/ai.js +61 -34
  58. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  59. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +85 -9
  60. package/packages/datadog-instrumentations/src/cucumber.js +131 -51
  61. package/packages/datadog-instrumentations/src/dns.js +21 -2
  62. package/packages/datadog-instrumentations/src/electron.js +1 -0
  63. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  64. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  65. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  66. package/packages/datadog-instrumentations/src/helpers/extract-package-and-module-path.js +10 -0
  67. package/packages/datadog-instrumentations/src/helpers/hook.js +19 -2
  68. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  69. package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
  70. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  71. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  72. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +175 -0
  73. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  74. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  75. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
  76. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
  77. package/packages/datadog-instrumentations/src/helpers/router-helper.js +38 -7
  78. package/packages/datadog-instrumentations/src/helpers/shared-utils.js +39 -0
  79. package/packages/datadog-instrumentations/src/http/client.js +1 -3
  80. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  81. package/packages/datadog-instrumentations/src/jest.js +986 -246
  82. package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
  83. package/packages/datadog-instrumentations/src/mocha/main.js +109 -51
  84. package/packages/datadog-instrumentations/src/mocha/utils.js +120 -32
  85. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +44 -0
  86. package/packages/datadog-instrumentations/src/mocha/worker.js +441 -43
  87. package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
  88. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  89. package/packages/datadog-instrumentations/src/next.js +155 -23
  90. package/packages/datadog-instrumentations/src/openai.js +14 -0
  91. package/packages/datadog-instrumentations/src/pg.js +180 -6
  92. package/packages/datadog-instrumentations/src/playwright.js +176 -81
  93. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  94. package/packages/datadog-instrumentations/src/promise.js +3 -3
  95. package/packages/datadog-instrumentations/src/rhea.js +1 -1
  96. package/packages/datadog-instrumentations/src/router.js +195 -19
  97. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +567 -65
  98. package/packages/datadog-instrumentations/src/vitest-main.js +438 -54
  99. package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
  100. package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
  101. package/packages/datadog-instrumentations/src/webdriverio.js +1090 -0
  102. package/packages/datadog-instrumentations/src/when.js +3 -3
  103. package/packages/datadog-instrumentations/src/ws.js +5 -1
  104. package/packages/datadog-plugin-aerospike/src/index.js +1 -3
  105. package/packages/datadog-plugin-amqplib/src/client.js +1 -1
  106. package/packages/datadog-plugin-amqplib/src/producer.js +1 -1
  107. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  108. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
  109. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +2 -2
  110. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -3
  111. package/packages/datadog-plugin-azure-service-bus/src/producer.js +5 -3
  112. package/packages/datadog-plugin-bullmq/src/producer.js +3 -3
  113. package/packages/datadog-plugin-child_process/src/index.js +3 -3
  114. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -11
  115. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  116. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
  117. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
  118. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  119. package/packages/datadog-plugin-electron/src/net.js +10 -4
  120. package/packages/datadog-plugin-fastify/src/tracing.js +1 -1
  121. package/packages/datadog-plugin-fs/src/index.js +0 -4
  122. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +14 -7
  123. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +19 -10
  124. package/packages/datadog-plugin-graphql/src/execute.js +1 -0
  125. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -1
  126. package/packages/datadog-plugin-http/src/client.js +3 -2
  127. package/packages/datadog-plugin-http2/src/client.js +1 -1
  128. package/packages/datadog-plugin-jest/src/index.js +3 -21
  129. package/packages/datadog-plugin-jest/src/util.js +4 -1
  130. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
  131. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  132. package/packages/datadog-plugin-mocha/src/index.js +342 -6
  133. package/packages/datadog-plugin-next/src/index.js +57 -9
  134. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  135. package/packages/datadog-plugin-pg/src/index.js +65 -1
  136. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  137. package/packages/datadog-plugin-prisma/src/index.js +5 -1
  138. package/packages/datadog-plugin-rhea/src/consumer.js +16 -13
  139. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  140. package/packages/datadog-plugin-undici/src/index.js +2 -1
  141. package/packages/datadog-plugin-vitest/src/index.js +179 -18
  142. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  143. package/packages/datadog-webpack/index.js +2 -2
  144. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  145. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  146. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
  147. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  148. package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
  149. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  150. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  151. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  152. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
  153. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +1 -1
  154. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  155. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  156. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -3
  157. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
  158. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
  159. package/packages/dd-trace/src/appsec/index.js +48 -476
  160. package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -1
  161. package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
  162. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  163. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  164. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +7 -6
  165. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -15
  166. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +19 -1
  167. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +25 -1
  168. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/webdriverio.js +26 -0
  169. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -1
  170. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  171. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  172. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -3
  173. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  174. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +2 -2
  175. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  176. package/packages/dd-trace/src/config/defaults.js +14 -6
  177. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -2
  178. package/packages/dd-trace/src/config/helper.js +2 -0
  179. package/packages/dd-trace/src/config/index.js +31 -5
  180. package/packages/dd-trace/src/config/parsers.js +19 -1
  181. package/packages/dd-trace/src/config/remote_config.js +1 -1
  182. package/packages/dd-trace/src/config/supported-configurations.json +13 -6
  183. package/packages/dd-trace/src/datastreams/encoding.js +3 -4
  184. package/packages/dd-trace/src/debugger/devtools_client/log.js +2 -1
  185. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +1 -4
  186. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -2
  187. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
  188. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +16 -2
  189. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  190. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  191. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  192. package/packages/dd-trace/src/exporters/common/url.js +3 -5
  193. package/packages/dd-trace/src/exporters/electron/index.js +4 -1
  194. package/packages/dd-trace/src/external-logger/src/index.js +1 -2
  195. package/packages/dd-trace/src/flare/index.js +2 -2
  196. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  197. package/packages/dd-trace/src/guardrails/log.js +1 -10
  198. package/packages/dd-trace/src/id.js +1 -1
  199. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
  200. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  201. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -2
  202. package/packages/dd-trace/src/llmobs/experiments/dataset.js +66 -14
  203. package/packages/dd-trace/src/llmobs/experiments/experiment.js +289 -95
  204. package/packages/dd-trace/src/llmobs/experiments/index.js +66 -19
  205. package/packages/dd-trace/src/llmobs/experiments/noop.js +95 -10
  206. package/packages/dd-trace/src/llmobs/experiments/result.js +13 -2
  207. package/packages/dd-trace/src/llmobs/experiments/util.js +220 -0
  208. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  209. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  210. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  211. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
  212. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  213. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +1 -1
  214. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  215. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  216. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  217. package/packages/dd-trace/src/llmobs/sdk.js +152 -78
  218. package/packages/dd-trace/src/llmobs/span_processor.js +9 -7
  219. package/packages/dd-trace/src/llmobs/tagger.js +1 -2
  220. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  221. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  222. package/packages/dd-trace/src/llmobs/writers/spans.js +12 -6
  223. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  224. package/packages/dd-trace/src/log/channels.js +1 -9
  225. package/packages/dd-trace/src/log/levels.js +12 -0
  226. package/packages/dd-trace/src/noop/proxy.js +2 -2
  227. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  228. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  229. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  230. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  231. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  232. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
  233. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  234. package/packages/dd-trace/src/opentracing/span.js +1 -1
  235. package/packages/dd-trace/src/opentracing/span_context.js +1 -1
  236. package/packages/dd-trace/src/plugins/ci_plugin.js +124 -23
  237. package/packages/dd-trace/src/plugins/index.js +2 -0
  238. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  239. package/packages/dd-trace/src/plugins/tracing.js +1 -0
  240. package/packages/dd-trace/src/plugins/util/ci.js +13 -6
  241. package/packages/dd-trace/src/plugins/util/git.js +7 -5
  242. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +3 -3
  243. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  244. package/packages/dd-trace/src/plugins/util/llm.js +2 -2
  245. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  246. package/packages/dd-trace/src/plugins/util/tags.js +2 -0
  247. package/packages/dd-trace/src/plugins/util/test.js +356 -86
  248. package/packages/dd-trace/src/plugins/util/url.js +5 -5
  249. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
  250. package/packages/dd-trace/src/plugins/util/web.js +24 -2
  251. package/packages/dd-trace/src/priority_sampler.js +6 -3
  252. package/packages/dd-trace/src/profiler.js +1 -1
  253. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  254. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  255. package/packages/dd-trace/src/profiling/profilers/poisson.js +1 -1
  256. package/packages/dd-trace/src/profiling/profilers/shared.js +2 -2
  257. package/packages/dd-trace/src/proxy.js +1 -0
  258. package/packages/dd-trace/src/ritm.js +10 -20
  259. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  260. package/packages/dd-trace/src/sampling_rule.js +4 -2
  261. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  262. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  263. package/packages/dd-trace/src/startup-log.js +3 -0
  264. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  265. package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -1
  266. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  267. package/packages/dd-trace/src/util.js +16 -0
  268. package/vendor/dist/@apm-js-collab/code-transformer/index.js +12 -12
  269. package/vendor/dist/pprof-format/index.js +1 -1
  270. package/vendor/dist/source-map/index.js +1 -1
  271. package/version.js +10 -8
  272. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -13,6 +13,66 @@ const LOCAL_LIFECYCLE_LOOKAHEAD = 4
13
13
 
14
14
  const chunkEmitTimes = new WeakMap()
15
15
 
16
+ /**
17
+ * @typedef {{ session_id?: string } & Record<string, unknown>} Chunk
18
+ */
19
+
20
+ /**
21
+ * @typedef {{
22
+ * id: string,
23
+ * name?: string,
24
+ * input?: unknown,
25
+ * output?: unknown,
26
+ * error?: unknown,
27
+ * isInterrupt?: boolean,
28
+ * sessionId?: string,
29
+ * hookStartTime?: number,
30
+ * hookFinishTime?: number,
31
+ * }} ToolState
32
+ */
33
+
34
+ /**
35
+ * @typedef {{ taskStartedChunk?: Chunk, toolResultIndex?: number }} ToolLifecycle
36
+ */
37
+
38
+ /**
39
+ * @typedef {(startIndex: number, toolUseId: string) => ToolLifecycle} GetLifecycle
40
+ */
41
+
42
+ /**
43
+ * @typedef {{
44
+ * tools: Map<string, ToolState>,
45
+ * subagents: Map<string, Record<string, unknown>>,
46
+ * prompt?: string,
47
+ * model?: string,
48
+ * resume?: string,
49
+ * maxTurns?: number,
50
+ * permissionMode?: string,
51
+ * sessionId?: string,
52
+ * source?: string,
53
+ * cwd?: string,
54
+ * transcriptPath?: string,
55
+ * agentType?: string,
56
+ * endReason?: string,
57
+ * stopReason?: string,
58
+ * lastAssistantMessage?: string,
59
+ * }} SessionContext
60
+ */
61
+
62
+ /**
63
+ * @typedef {{
64
+ * stepIndex: number,
65
+ * startTime?: number,
66
+ * finishTime?: number,
67
+ * parentToolUseId?: string,
68
+ * sessionId?: string,
69
+ * chunks?: Chunk[],
70
+ * llmStartIdx?: number,
71
+ * llmEndIdx?: number,
72
+ * toolOutputs?: unknown[],
73
+ * }} StepContext
74
+ */
75
+
16
76
  function hasDownstreamSubscribers () {
17
77
  return queryChannel.asyncEnd.hasSubscribers ||
18
78
  queryChannel.error.hasSubscribers ||
@@ -54,7 +114,7 @@ function buildTracerHooks (sessionCtx) {
54
114
  sessionCtx.cwd = input.cwd
55
115
  sessionCtx.transcriptPath = input.transcript_path
56
116
  sessionCtx.agentType = input.agent_type
57
- sessionCtx.permissionMode = sessionCtx.permissionMode || input.permission_mode
117
+ sessionCtx.permissionMode ||= input.permission_mode
58
118
  return {}
59
119
  }
60
120
 
@@ -64,8 +124,8 @@ function buildTracerHooks (sessionCtx) {
64
124
  }
65
125
 
66
126
  function onUserPromptSubmit (input) {
67
- sessionCtx.sessionId = sessionCtx.sessionId || input.session_id
68
- sessionCtx.prompt = sessionCtx.prompt || input.prompt
127
+ sessionCtx.sessionId ||= input.session_id
128
+ sessionCtx.prompt ||= input.prompt
69
129
  return {}
70
130
  }
71
131
 
@@ -254,7 +314,7 @@ function createStreamLookup (chunks) {
254
314
  scanLocalLifecycle(chunks, startIndex, toolUseId, localLifecycle)
255
315
  if (localLifecycle.toolResultIndex !== undefined) return localLifecycle
256
316
 
257
- streamIndex = streamIndex || buildStreamIndex(chunks)
317
+ streamIndex ||= buildStreamIndex(chunks)
258
318
  const indexedLifecycle = streamIndex.get(toolUseId)
259
319
  return {
260
320
  taskStartedChunk: localLifecycle.taskStartedChunk || indexedLifecycle?.taskStartedChunk,
@@ -287,10 +347,12 @@ function getToolResultContent (chunk, toolUseId) {
287
347
  }
288
348
 
289
349
  /**
290
- *
291
- * @param {Array<Record<string, unknown>>} chunks
350
+ * @param {Chunk[]} chunks
292
351
  * @param {number} startIndex
293
352
  * @param {string} toolUseId
353
+ * @param {SessionContext | null | undefined} sessionCtx
354
+ * @param {GetLifecycle} getLifecycle
355
+ * @param {ToolLifecycle} lifecycle
294
356
  * @returns {number} index in chunks where the next step should start iteration
295
357
  */
296
358
  function processTool (chunks, startIndex, toolUseId, sessionCtx, getLifecycle, lifecycle) {
@@ -342,9 +404,14 @@ function processTool (chunks, startIndex, toolUseId, sessionCtx, getLifecycle, l
342
404
  }
343
405
 
344
406
  /**
345
- *
346
- * @param {Array<Record<string, unknown>>} chunks
407
+ * @param {Chunk[]} chunks
347
408
  * @param {number} startIndex
409
+ * @param {number | undefined} stepStartTime
410
+ * @param {string | null | undefined} parentToolUseId
411
+ * @param {string | undefined} initialPrompt
412
+ * @param {StepContext | null | undefined} stepCtx
413
+ * @param {SessionContext | null | undefined} sessionCtx
414
+ * @param {GetLifecycle} getLifecycle
348
415
  * @returns {number} index in chunks where the next step should start iteration
349
416
  */
350
417
  function processStep (
@@ -447,7 +514,16 @@ function processStep (
447
514
  }
448
515
 
449
516
  /**
450
- * @param {Array<Record<string, unknown>>} chunks
517
+ * @param {Chunk[]} chunks
518
+ * @param {{
519
+ * sessionCtx?: SessionContext,
520
+ * session_id?: string,
521
+ * cwd?: string,
522
+ * permissionMode?: string,
523
+ * output?: unknown,
524
+ * arguments?: Array<{ prompt?: string }>,
525
+ * runInContext?: (fn: () => number) => number,
526
+ * }} agentCtx
451
527
  */
452
528
  function processChunks (chunks, agentCtx) {
453
529
  let chunkIndex = 0
@@ -6,6 +6,12 @@ const { createCoverageMap } = require('../../../vendor/dist/istanbul-lib-coverag
6
6
  const shimmer = require('../../datadog-shimmer')
7
7
  const log = require('../../dd-trace/src/log')
8
8
  const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
9
+ const { getSegment } = require('../../dd-trace/src/util')
10
+ const {
11
+ EMPTY_EFD_RETRY_POLICY,
12
+ getEfdRetryCountForDuration,
13
+ hasEfdRetries,
14
+ } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
9
15
  const {
10
16
  getCoveredFilesFromCoverage,
11
17
  getExecutableFilesFromCoverage,
@@ -15,15 +21,16 @@ const {
15
21
  getTestSuitePath,
16
22
  getRelativeCoverageFiles,
17
23
  CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
24
+ CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE,
18
25
  getIsFaultyEarlyFlakeDetection,
19
- getEfdRetryCount,
20
- getMaxEfdRetryCount,
21
26
  applySkippedCoverageToCoverage,
22
27
  getTestCoverageLinesPercentage,
28
+ recordTestManagementExecution,
23
29
  recordAttemptToFixExecution,
24
- collectAttemptToFixExecutionsFromTraces,
30
+ collectTestOptimizationSummariesFromTraces,
25
31
  logAttemptToFixTestExecution,
26
32
  logTestOptimizationSummary,
33
+ TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE,
27
34
  getTestOptimizationRequestResults,
28
35
  } = require('../../dd-trace/src/plugins/util/test')
29
36
  const { writeCoverageBackfillToCache } = require('../../dd-trace/src/ci-visibility/test-optimization-cache')
@@ -56,12 +63,25 @@ const modifiedFilesCh = channel('ci:cucumber:modified-files')
56
63
  const isModifiedCh = channel('ci:cucumber:is-modified-test')
57
64
 
58
65
  const workerReportTraceCh = channel('ci:cucumber:worker-report:trace')
66
+ const workerReportTelemetryCh = channel('ci:cucumber:worker-report:telemetry')
59
67
 
60
68
  const itrSkippedSuitesCh = channel('ci:cucumber:itr:skipped-suites')
61
69
 
62
70
  const getCodeCoverageCh = channel('ci:nyc:get-coverage')
63
71
 
64
72
  const DD_EFD_RETRY_COUNT_MESSAGE = '_ddEfdRetryCount'
73
+ const CUCUMBER_RETRY_NAME_SUFFIX = / ?\(attempt \d+(?:, retried)?\) ?$/
74
+
75
+ /**
76
+ * Removes Cucumber's generated retry suffix without changing literal scenario names.
77
+ *
78
+ * @param {string} testName
79
+ * @param {boolean} isRetry
80
+ * @returns {string}
81
+ */
82
+ function getCucumberTestName (testName, isRetry) {
83
+ return isRetry ? testName.replace(CUCUMBER_RETRY_NAME_SUFFIX, '') : testName
84
+ }
65
85
 
66
86
  const isMarkedAsUnskippable = (pickle) => {
67
87
  return pickle.tags.some(tag => tag.name === '@datadog:unskippable')
@@ -105,8 +125,7 @@ let isItrEnabled = false
105
125
  let isSuitesSkippingEnabled = false
106
126
  let isCoverageReportUploadEnabled = false
107
127
  let isEarlyFlakeDetectionEnabled = false
108
- let earlyFlakeDetectionNumRetries = 0
109
- let earlyFlakeDetectionSlowTestRetries = {}
128
+ let earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
110
129
  let earlyFlakeDetectionFaultyThreshold = 0
111
130
  let isEarlyFlakeDetectionFaulty = false
112
131
  let isFlakyTestRetriesEnabled = false
@@ -120,8 +139,16 @@ let numTestRetries = 0
120
139
  let knownTests = {}
121
140
  let skippedSuites = []
122
141
  let isSuitesSkipped = false
142
+ let areAllSuitesSkipped = false
123
143
  let repositoryRoot
124
144
 
145
+ /**
146
+ * @returns {boolean}
147
+ */
148
+ function shouldRunEarlyFlakeDetection () {
149
+ return isEarlyFlakeDetectionEnabled && hasEfdRetries(earlyFlakeDetectionRetryPolicy)
150
+ }
151
+
125
152
  function isValidKnownTests (receivedKnownTests) {
126
153
  return !!receivedKnownTests.cucumber
127
154
  }
@@ -165,6 +192,7 @@ function resetSuiteSkippingRunState () {
165
192
  skippedSuitesCoverage = {}
166
193
  skippedSuites = []
167
194
  isSuitesSkipped = false
195
+ areAllSuitesSkipped = false
168
196
  repositoryRoot = undefined
169
197
  writeCoverageBackfillToCache({})
170
198
  }
@@ -179,11 +207,6 @@ function getSuiteStatusFromTestStatuses (testStatuses) {
179
207
  return 'pass'
180
208
  }
181
209
 
182
- function getConfiguredEfdRetryCount () {
183
- const maxSlowTestRetryCount = getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
184
- return maxSlowTestRetryCount ?? earlyFlakeDetectionNumRetries
185
- }
186
-
187
210
  function publishWorkerEfdRetryCount (pickle, retryCount) {
188
211
  const message = {
189
212
  [DD_EFD_RETRY_COUNT_MESSAGE]: {
@@ -220,15 +243,13 @@ function configureParallelWorkerWorldParameters (options) {
220
243
  options.worldParameters._ddIsKnownTestsEnabled = true
221
244
  options.worldParameters._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
222
245
  options.worldParameters._ddKnownTests = knownTests
223
- options.worldParameters._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
224
- options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries
246
+ options.worldParameters._ddEarlyFlakeDetectionRetryPolicy = earlyFlakeDetectionRetryPolicy
225
247
  } else {
226
248
  isEarlyFlakeDetectionEnabled = false
227
249
  isKnownTestsEnabled = false
228
250
  options.worldParameters._ddIsEarlyFlakeDetectionEnabled = false
229
251
  options.worldParameters._ddIsKnownTestsEnabled = false
230
- options.worldParameters._ddEarlyFlakeDetectionNumRetries = 0
231
- options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = {}
252
+ options.worldParameters._ddEarlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
232
253
  }
233
254
 
234
255
  if (isImpactedTestsEnabled) {
@@ -260,10 +281,9 @@ function readParallelWorkerWorldParameters (options) {
260
281
  }
261
282
  }
262
283
  isEarlyFlakeDetectionEnabled = !!worldParameters._ddIsEarlyFlakeDetectionEnabled
263
- if (isEarlyFlakeDetectionEnabled) {
264
- earlyFlakeDetectionNumRetries = worldParameters._ddEarlyFlakeDetectionNumRetries
265
- earlyFlakeDetectionSlowTestRetries = worldParameters._ddEarlyFlakeDetectionSlowTestRetries ?? {}
266
- }
284
+ earlyFlakeDetectionRetryPolicy = isEarlyFlakeDetectionEnabled
285
+ ? worldParameters._ddEarlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
286
+ : EMPTY_EFD_RETRY_POLICY
267
287
  isImpactedTestsEnabled = !!worldParameters._ddImpactedTestsEnabled
268
288
  if (isImpactedTestsEnabled) {
269
289
  modifiedFiles = worldParameters._ddModifiedFiles
@@ -281,10 +301,14 @@ function handleDdWorkerMessage (message) {
281
301
  if (Array.isArray(message)) {
282
302
  const [messageCode, payload] = message
283
303
  if (messageCode === CUCUMBER_WORKER_TRACE_PAYLOAD_CODE) {
284
- collectAttemptToFixExecutionsFromTraces(payload, attemptToFixExecutions)
304
+ collectTestOptimizationSummariesFromTraces(payload, { attemptToFixExecutions })
285
305
  workerReportTraceCh.publish(payload)
286
306
  return true
287
307
  }
308
+ if (messageCode === CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE) {
309
+ workerReportTelemetryCh.publish(payload)
310
+ return true
311
+ }
288
312
  }
289
313
 
290
314
  if (message?.[DD_EFD_RETRY_COUNT_MESSAGE]) {
@@ -333,8 +357,10 @@ function maybeStartParallelSuite (pickle) {
333
357
  })
334
358
  }
335
359
 
336
- function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
337
- const { status } = getStatusFromResultLatest(worstTestStepResult)
360
+ function handleParallelTestCaseFinished (pickle, worstTestStepResult, usesNumericStatus = false) {
361
+ const { status } = usesNumericStatus
362
+ ? getStatusFromResult(worstTestStepResult)
363
+ : getStatusFromResultLatest(worstTestStepResult)
338
364
  let isNew = false
339
365
 
340
366
  if (isKnownTestsEnabled) {
@@ -342,9 +368,9 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
342
368
  }
343
369
 
344
370
  const testFileAbsolutePath = pickle.uri
345
- const finished = pickleResultByFile[testFileAbsolutePath] || (pickleResultByFile[testFileAbsolutePath] = [])
371
+ const finished = (pickleResultByFile[testFileAbsolutePath] ||= [])
346
372
 
347
- if (isEarlyFlakeDetectionEnabled && isNew) {
373
+ if (shouldRunEarlyFlakeDetection() && isNew) {
348
374
  const testFullname = `${pickle.uri}:${pickle.name}`
349
375
  let testStatuses = newTestsByTestFullname.get(testFullname)
350
376
  if (testStatuses) {
@@ -357,7 +383,7 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
357
383
  if (efdRetryCount === undefined) {
358
384
  efdRetryCount = status === 'skip'
359
385
  ? 0
360
- : getConfiguredEfdRetryCount()
386
+ : earlyFlakeDetectionRetryPolicy.schedulingRetryCount
361
387
  efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
362
388
  if (efdRetryCount === 0 && status !== 'skip') {
363
389
  efdSlowAbortedPickleIds.add(pickle.id)
@@ -555,8 +581,7 @@ function getErrorFromCucumberResult (cucumberResult) {
555
581
  return
556
582
  }
557
583
 
558
- const [message] = cucumberResult.message.split('\n')
559
- const error = new Error(message)
584
+ const error = new Error(getSegment(cucumberResult.message, '\n', 0))
560
585
  if (cucumberResult.exception) {
561
586
  error.type = cucumberResult.exception.type
562
587
  }
@@ -696,7 +721,7 @@ function publishRetriedAttempt (runner, state) {
696
721
 
697
722
  // ATR: record this attempt as failed so when run().finally runs (after retry) we have all statuses
698
723
  if (isFlakyTestRetriesEnabled) {
699
- const nameForKey = runner.pickle.name.replace(/\s*\(attempt \d+(?:, retried)?\)\s*$/, '')
724
+ const nameForKey = getCucumberTestName(runner.pickle.name, currentAttempt > 0)
700
725
  const atrKey = `${runner.pickle.uri}:${nameForKey}`
701
726
  if (atrStatusesByScenarioKey.has(atrKey)) {
702
727
  atrStatusesByScenarioKey.get(atrKey).push('fail')
@@ -793,7 +818,7 @@ function wrapRun (pl, isLatestVersion, version) {
793
818
  testFnCh.runStores(ctx, () => {
794
819
  promise = run.apply(this, args)
795
820
  })
796
- promise.finally(async () => {
821
+ const finalize = async () => {
797
822
  if (!canAwaitRetries) {
798
823
  this.eventBroadcaster.removeListener('envelope', onEnvelope)
799
824
  }
@@ -802,6 +827,7 @@ function wrapRun (pl, isLatestVersion, version) {
802
827
  const { status, skipReason } = isLatestVersion
803
828
  ? getStatusFromResultLatest(result)
804
829
  : getStatusFromResult(result)
830
+ const testName = getCucumberTestName(this.pickle.name, state.numAttempt > 0)
805
831
 
806
832
  if (lastStatusByPickleId.has(this.pickle.id)) {
807
833
  lastStatusByPickleId.get(this.pickle.id).push(status)
@@ -821,7 +847,7 @@ function wrapRun (pl, isLatestVersion, version) {
821
847
 
822
848
  if (isTestManagementTestsEnabled) {
823
849
  const testSuitePath = getTestSuitePath(testFileAbsolutePath, process.cwd())
824
- const testProperties = getTestProperties(testSuitePath, this.pickle.name)
850
+ const testProperties = getTestProperties(testSuitePath, testName)
825
851
  const numRetries = numRetriesByPickleId.get(this.pickle.id)
826
852
  isAttemptToFix = testProperties.attemptToFix
827
853
  isAttemptToFixRetry = isAttemptToFix && numRetries > 0
@@ -857,20 +883,24 @@ function wrapRun (pl, isLatestVersion, version) {
857
883
  }
858
884
 
859
885
  if (
860
- isEarlyFlakeDetectionEnabled &&
886
+ shouldRunEarlyFlakeDetection() &&
861
887
  status !== 'skip' &&
862
888
  (isNew || isModified) &&
863
889
  !isEfdRetry &&
864
890
  !efdRetryCountByPickleId.has(this.pickle.id)
865
891
  ) {
866
- const retryCount = getEfdRetryCount(performance.now() - executionStart, earlyFlakeDetectionSlowTestRetries)
892
+ const retryCount = getEfdRetryCountForDuration(
893
+ performance.now() - executionStart,
894
+ earlyFlakeDetectionRetryPolicy
895
+ )
867
896
  efdRetryCountByPickleId.set(this.pickle.id, retryCount)
868
897
  if (retryCount === 0) {
869
898
  efdSlowAbortedPickleIds.add(this.pickle.id)
870
899
  }
871
900
  }
872
901
 
873
- const efdRetryCount = efdRetryCountByPickleId.get(this.pickle.id) ?? earlyFlakeDetectionNumRetries
902
+ const efdRetryCount = efdRetryCountByPickleId.get(this.pickle.id) ??
903
+ earlyFlakeDetectionRetryPolicy.schedulingRetryCount
874
904
 
875
905
  // Check if all EFD retries failed
876
906
  if (isEfdRetry && (isNew || isModified)) {
@@ -889,8 +919,7 @@ function wrapRun (pl, isLatestVersion, version) {
889
919
  // ATR: accumulate statuses by stable scenario key (uri:name) so retries are grouped.
890
920
  // Cucumber appends " (attempt N)" or " (attempt N, retried)" to the scenario name; normalize for keying.
891
921
  if (isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry && numTestRetries > 0) {
892
- const nameForKey = this.pickle.name.replace(/\s*\(attempt \d+(?:, retried)?\)\s*$/, '')
893
- const atrKey = `${this.pickle.uri}:${nameForKey}`
922
+ const atrKey = `${this.pickle.uri}:${testName}`
894
923
  if (atrStatusesByScenarioKey.has(atrKey)) {
895
924
  atrStatusesByScenarioKey.get(atrKey).push(status)
896
925
  } else {
@@ -909,10 +938,21 @@ function wrapRun (pl, isLatestVersion, version) {
909
938
 
910
939
  const error = getErrorFromCucumberResult(result)
911
940
 
941
+ if (!testStartPayload.isParallel) {
942
+ recordTestManagementExecution({
943
+ testSuite: testSuitePath,
944
+ testName,
945
+ status,
946
+ isAttemptToFix,
947
+ isDisabled,
948
+ isQuarantined,
949
+ })
950
+ }
951
+
912
952
  if (isAttemptToFix) {
913
953
  recordAttemptToFixExecution(attemptToFixExecutions, {
914
954
  testSuite: testSuitePath,
915
- testName: this.pickle.name,
955
+ testName,
916
956
  status,
917
957
  isDisabled,
918
958
  isQuarantined,
@@ -953,7 +993,8 @@ function wrapRun (pl, isLatestVersion, version) {
953
993
  error,
954
994
  isNew,
955
995
  isEfdRetry,
956
- isFlakyRetry: state.numAttempt > 0,
996
+ isFlakyRetry: state.numAttempt > 0 && isFlakyTestRetriesEnabled,
997
+ isExternalRetry: state.numAttempt > 0 && !isFlakyTestRetriesEnabled,
957
998
  isAttemptToFix,
958
999
  isAttemptToFixRetry,
959
1000
  hasFailedAllRetries,
@@ -966,6 +1007,9 @@ function wrapRun (pl, isLatestVersion, version) {
966
1007
  ...attemptCtx.currentStore,
967
1008
  finalStatus,
968
1009
  })
1010
+ }
1011
+ promise.then(finalize, finalize).catch(error => {
1012
+ log.error('Cucumber test finalization error', error)
969
1013
  })
970
1014
  return promise
971
1015
  } catch (err) {
@@ -1056,8 +1100,8 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
1056
1100
  repositoryRoot = configurationResponse.repositoryRoot
1057
1101
  isItrEnabled = configurationResponse.libraryConfig?.isItrEnabled
1058
1102
  isEarlyFlakeDetectionEnabled = configurationResponse.libraryConfig?.isEarlyFlakeDetectionEnabled
1059
- earlyFlakeDetectionNumRetries = configurationResponse.libraryConfig?.earlyFlakeDetectionNumRetries
1060
- earlyFlakeDetectionSlowTestRetries = configurationResponse.libraryConfig?.earlyFlakeDetectionSlowTestRetries ?? {}
1103
+ earlyFlakeDetectionRetryPolicy = configurationResponse.libraryConfig?.earlyFlakeDetectionRetryPolicy ??
1104
+ EMPTY_EFD_RETRY_POLICY
1061
1105
  earlyFlakeDetectionFaultyThreshold = configurationResponse.libraryConfig?.earlyFlakeDetectionFaultyThreshold
1062
1106
  isSuitesSkippingEnabled = isItrEnabled && configurationResponse.libraryConfig?.isSuitesSkippingEnabled
1063
1107
  isCoverageReportUploadEnabled = configurationResponse.libraryConfig?.isCoverageReportUploadEnabled
@@ -1110,6 +1154,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
1110
1154
  const oldPickles = isCoordinator ? this.sourcedPickles : this.pickleIds
1111
1155
 
1112
1156
  isSuitesSkipped = picklesToRun.length !== oldPickles.length
1157
+ areAllSuitesSkipped = oldPickles.length > 0 && picklesToRun.length === 0
1113
1158
 
1114
1159
  log.debug('%s out of %s suites are going to run.', picklesToRun.length, oldPickles.length)
1115
1160
 
@@ -1226,7 +1271,10 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
1226
1271
  isParallel,
1227
1272
  })
1228
1273
 
1229
- logTestOptimizationSummary({ attemptToFixExecutions })
1274
+ logTestOptimizationSummary({
1275
+ attemptToFixExecutions,
1276
+ extraSections: areAllSuitesSkipped ? [TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE] : [],
1277
+ })
1230
1278
  loggedAttemptToFixTests.clear()
1231
1279
  eventDataCollector = null
1232
1280
  await flushPromise
@@ -1314,7 +1362,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1314
1362
  }
1315
1363
  }
1316
1364
  const originalRetry = this.options.retry
1317
- const isManagedRetry = isAttemptToFix || (isEarlyFlakeDetectionEnabled && (isNew || isModified))
1365
+ const isManagedRetry = isAttemptToFix || (shouldRunEarlyFlakeDetection() && (isNew || isModified))
1318
1366
  if (isManagedRetry) {
1319
1367
  this.options.retry = 0
1320
1368
  }
@@ -1339,10 +1387,10 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1339
1387
  }
1340
1388
 
1341
1389
  // If it's a new test and it hasn't been skipped, we run it again
1342
- if (isEarlyFlakeDetectionEnabled && lastTestStatus !== 'skip' && (isNew || isModified)) {
1390
+ if (shouldRunEarlyFlakeDetection() && lastTestStatus !== 'skip' && (isNew || isModified)) {
1343
1391
  let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
1344
1392
  if (efdRetryCount === undefined) {
1345
- efdRetryCount = getEfdRetryCount(firstExecutionDurationMs, earlyFlakeDetectionSlowTestRetries)
1393
+ efdRetryCount = getEfdRetryCountForDuration(firstExecutionDurationMs, earlyFlakeDetectionRetryPolicy)
1346
1394
  efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
1347
1395
  if (efdRetryCount === 0) {
1348
1396
  efdSlowAbortedPickleIds.add(pickle.id)
@@ -1361,7 +1409,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1361
1409
  let shouldBePassedByEFD = false
1362
1410
  let shouldBePassedByTestManagement = false
1363
1411
  let shouldBeFailedByAttemptToFix = false
1364
- if ((isNew || isModified) && isEarlyFlakeDetectionEnabled) {
1412
+ if ((isNew || isModified) && shouldRunEarlyFlakeDetection()) {
1365
1413
  /**
1366
1414
  * If Early Flake Detection (EFD) is enabled the logic is as follows:
1367
1415
  * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
@@ -1419,7 +1467,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1419
1467
 
1420
1468
  this.options.retry = originalRetry
1421
1469
 
1422
- if (isNewerCucumberVersion && isEarlyFlakeDetectionEnabled && (isNew || isModified)) {
1470
+ if (isNewerCucumberVersion && shouldRunEarlyFlakeDetection() && (isNew || isModified)) {
1423
1471
  return shouldBePassedByEFD
1424
1472
  }
1425
1473
 
@@ -1448,7 +1496,7 @@ function patchCucumberWorkerRunTestCase (runtimeExecutorPackage, isWorker) {
1448
1496
  )
1449
1497
  }
1450
1498
 
1451
- function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion) {
1499
+ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion, usesNumericStatus = false) {
1452
1500
  return function (worker, message) {
1453
1501
  if (!testSuiteFinishCh.hasSubscribers) {
1454
1502
  return parseWorkerMessageFunction.apply(this, arguments)
@@ -1499,13 +1547,31 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
1499
1547
  pickle = testCase.pickle
1500
1548
  }
1501
1549
 
1502
- handleParallelTestCaseFinished(pickle, worstTestStepResult)
1550
+ handleParallelTestCaseFinished(pickle, worstTestStepResult, usesNumericStatus)
1503
1551
  }
1504
1552
 
1505
1553
  return parseWorkerResponse
1506
1554
  }
1507
1555
  }
1508
1556
 
1557
+ /**
1558
+ * Adapts Cucumber 7's callback-based parallel coordinator to the Promise contract used by getWrappedStart.
1559
+ *
1560
+ * @param {Function} run
1561
+ * @param {string} frameworkVersion
1562
+ * @returns {Function}
1563
+ */
1564
+ function getWrappedCoordinatorRun (run, frameworkVersion) {
1565
+ const runAsPromise = function (numberOfWorkers) {
1566
+ return new Promise(resolve => run.call(this, numberOfWorkers, resolve))
1567
+ }
1568
+ const wrappedStart = getWrappedStart(runAsPromise, frameworkVersion, true)
1569
+
1570
+ return function (numberOfWorkers, done) {
1571
+ return wrappedStart.call(this, numberOfWorkers).then(done)
1572
+ }
1573
+ }
1574
+
1509
1575
  module.exports.patchCucumberWorkerRunTestCase = patchCucumberWorkerRunTestCase
1510
1576
 
1511
1577
  // Test start / finish for older versions. The only hook executed in workers when in parallel mode
@@ -1553,19 +1619,33 @@ addHook({
1553
1619
  return runtimePackage
1554
1620
  })
1555
1621
 
1556
- // Only executed in parallel mode.
1557
- // `getWrappedStart` generates session start and finish events
1622
+ // Only executed in parallel mode in Cucumber 7 through 10.
1623
+ // `getWrappedCoordinatorRun` or `getWrappedStart` generates session start and finish events
1558
1624
  // `getWrappedParseWorkerMessage` generates suite start and finish events
1625
+ // Shimmer is required because the coordinator must be changed before it starts workers and exposes no lifecycle hook.
1559
1626
  addHook({
1560
1627
  name: '@cucumber/cucumber',
1561
- versions: ['>=8.0.0 <11.0.0'],
1628
+ versions: ['>=7.0.0 <11.0.0'],
1562
1629
  file: 'lib/runtime/parallel/coordinator.js',
1563
1630
  }, (coordinatorPackage, frameworkVersion) => {
1564
- shimmer.wrap(coordinatorPackage.default.prototype, 'start', start => getWrappedStart(start, frameworkVersion, true))
1631
+ const isCucumber7 = satisfies(frameworkVersion, '<8.0.0')
1632
+ if (isCucumber7) {
1633
+ shimmer.wrap(
1634
+ coordinatorPackage.default.prototype,
1635
+ 'run',
1636
+ run => getWrappedCoordinatorRun(run, frameworkVersion)
1637
+ )
1638
+ } else {
1639
+ shimmer.wrap(
1640
+ coordinatorPackage.default.prototype,
1641
+ 'start',
1642
+ start => getWrappedStart(start, frameworkVersion, true)
1643
+ )
1644
+ }
1565
1645
  shimmer.wrap(
1566
1646
  coordinatorPackage.default.prototype,
1567
1647
  'parseWorkerMessage',
1568
- parseWorkerMessage => getWrappedParseWorkerMessage(parseWorkerMessage)
1648
+ parseWorkerMessage => getWrappedParseWorkerMessage(parseWorkerMessage, false, isCucumber7)
1569
1649
  )
1570
1650
  return coordinatorPackage
1571
1651
  })
@@ -9,6 +9,7 @@ const rrtypes = {
9
9
  resolveAny: 'ANY',
10
10
  resolve4: 'A',
11
11
  resolve6: 'AAAA',
12
+ resolveCaa: 'CAA',
12
13
  resolveCname: 'CNAME',
13
14
  resolveMx: 'MX',
14
15
  resolveNs: 'NS',
@@ -17,6 +18,7 @@ const rrtypes = {
17
18
  resolvePtr: 'PTR',
18
19
  resolveNaptr: 'NAPTR',
19
20
  resolveSoa: 'SOA',
21
+ resolveTlsa: 'TLSA',
20
22
  }
21
23
 
22
24
  // `dns.promises` and `require('dns/promises')` resolve to the same exports object. Both
@@ -81,7 +83,7 @@ function buildCallbackArgsContext (rrtype) {
81
83
  const captured = [...args]
82
84
  captured.pop() // remove the callback
83
85
  if (rrtype) {
84
- captured.push(rrtype)
86
+ insertRrtype(captured, rrtype)
85
87
  }
86
88
  return { args: captured }
87
89
  }
@@ -91,8 +93,25 @@ function buildPromiseArgsContext (rrtype) {
91
93
  return function (_, args) {
92
94
  const captured = [...args]
93
95
  if (rrtype) {
94
- captured.push(rrtype)
96
+ insertRrtype(captured, rrtype)
95
97
  }
96
98
  return { args: captured }
97
99
  }
98
100
  }
101
+
102
+ /**
103
+ * @param {unknown[]} captured
104
+ * @param {string} rrtype
105
+ */
106
+ function insertRrtype (captured, rrtype) {
107
+ if (captured.length === 0) {
108
+ captured.push(rrtype)
109
+ return
110
+ }
111
+
112
+ captured.push(rrtype)
113
+ for (let index = captured.length - 1; index > 1; index--) {
114
+ captured[index] = captured[index - 1]
115
+ }
116
+ captured[1] = rrtype
117
+ }
@@ -149,6 +149,7 @@ function wrapBrowserWindow (electron) {
149
149
 
150
150
  // BrowserWindow doesn't support subclassing because it's all native code
151
151
  // so we return an instance of it instead of the subclass.
152
+ // eslint-disable-next-line no-constructor-return
152
153
  return win
153
154
  }
154
155
  }
@@ -25,6 +25,8 @@ const bodyPublished = new WeakSet()
25
25
  let lastPublishedError
26
26
  let lastPublishedReq
27
27
 
28
+ /** @typedef {{ length: number, [index: number]: unknown } & Iterable<unknown>} ArgumentsLike */
29
+
28
30
  function wrapFastify (fastify, hasParsingEvents) {
29
31
  if (typeof fastify !== 'function') return fastify
30
32
 
@@ -81,7 +83,7 @@ function wrapAddHook (addHook) {
81
83
  * @param {string} name Lifecycle phase the hook was registered against.
82
84
  * @param {Function} fn User-supplied hook.
83
85
  * @param {unknown} thisArg `this` Fastify passes to the hook.
84
- * @param {ArrayLike<unknown>} args Fastify's positional args; the dispatcher always
86
+ * @param {ArgumentsLike} args Fastify's positional args; the dispatcher always
85
87
  * places `done` as the trailing positional (see fastify/lib/hooks.js hookIterator,
86
88
  * onSendHookRunner, preParsingHookRunner, onRequestAbortHookRunner).
87
89
  */