dd-trace 5.131.1 → 5.132.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 (43) hide show
  1. package/index.d.ts +91 -3
  2. package/package.json +2 -2
  3. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +57 -4
  4. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
  5. package/packages/datadog-instrumentations/src/jest.js +119 -1
  6. package/packages/datadog-instrumentations/src/mocha/utils.js +124 -36
  7. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +61 -34
  8. package/packages/datadog-instrumentations/src/mocha/worker.js +6 -3
  9. package/packages/datadog-instrumentations/src/playwright.js +3 -0
  10. package/packages/datadog-instrumentations/src/vitest-main.js +0 -2
  11. package/packages/datadog-instrumentations/src/webdriverio.js +115 -15
  12. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -0
  13. package/packages/datadog-plugin-mocha/src/index.js +216 -29
  14. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +449 -0
  15. package/packages/dd-trace/src/debugger/constants.js +1 -0
  16. package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -3
  17. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +17 -12
  18. package/packages/dd-trace/src/debugger/devtools_client/send.js +6 -2
  19. package/packages/dd-trace/src/debugger/devtools_client/worker-error.js +21 -0
  20. package/packages/dd-trace/src/debugger/index.js +24 -2
  21. package/packages/dd-trace/src/debugger/probe_sampler.js +108 -11
  22. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  23. package/packages/dd-trace/src/exporters/common/url.js +1 -1
  24. package/packages/dd-trace/src/id.js +1 -4
  25. package/packages/dd-trace/src/llmobs/agent-manifest.js +296 -0
  26. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  27. package/packages/dd-trace/src/llmobs/sdk.js +23 -6
  28. package/packages/dd-trace/src/llmobs/span_processor.js +17 -6
  29. package/packages/dd-trace/src/llmobs/tagger.js +54 -1
  30. package/packages/dd-trace/src/llmobs/writers/base.js +3 -1
  31. package/packages/dd-trace/src/llmobs/writers/util.js +3 -1
  32. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +30 -1
  33. package/packages/dd-trace/src/openfeature/configuration_source.js +17 -2
  34. package/packages/dd-trace/src/openfeature/debug-logging-hook.js +25 -0
  35. package/packages/dd-trace/src/openfeature/flagging_provider.js +29 -0
  36. package/packages/dd-trace/src/openfeature/remote_config.js +9 -0
  37. package/packages/dd-trace/src/openfeature/writers/util.js +43 -4
  38. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +1 -1
  39. package/packages/dd-trace/src/otel-thread-ctx.js +2 -2
  40. package/packages/dd-trace/src/profiling/profilers/wall.js +4 -10
  41. package/packages/dd-trace/src/proxy.js +2 -0
  42. package/packages/dd-trace/src/serverless.js +5 -0
  43. package/packages/dd-trace/src/storage-channels.js +112 -46
package/index.d.ts CHANGED
@@ -4063,7 +4063,8 @@ declare namespace tracer {
4063
4063
 
4064
4064
  /**
4065
4065
  * Sets inputs, outputs, tags, metadata, and metrics as provided for a given LLM Observability span.
4066
- * Note that with the exception of tags, this method will override any existing values for the provided fields.
4066
+ * Note that with the exception of tags and agent, this method will override any existing values for the provided fields.
4067
+ * Agent fields update earlier agent annotations one top-level field at a time.
4067
4068
  *
4068
4069
  * For example:
4069
4070
  * ```javascript
@@ -4079,7 +4080,7 @@ declare namespace tracer {
4079
4080
  * ```
4080
4081
  *
4081
4082
  * @param span The span to annotate (defaults to the current LLM Observability span if not provided)
4082
- * @param options An object containing the inputs, outputs, tags, metadata, and metrics to set on the span.
4083
+ * @param options An object containing the inputs, outputs, tags, metadata, metrics, and agent to set on the span.
4083
4084
  */
4084
4085
  annotate (options: llmobs.AnnotationOptions): void
4085
4086
  annotate (span: tracer.Span | undefined, options: llmobs.AnnotationOptions): void
@@ -5006,6 +5007,81 @@ declare namespace tracer {
5006
5007
  }
5007
5008
 
5008
5009
 
5010
+ interface AgentToolParameter {
5011
+ type?: string,
5012
+ required?: boolean
5013
+ }
5014
+
5015
+ /**
5016
+ * A plain JSON Schema object with `type: 'object'`, which is how it is told apart from a parameter map.
5017
+ * Schema-library objects (for example Zod schemas) must be converted first, for example with
5018
+ * `z.toJSONSchema()`, or they are dropped.
5019
+ */
5020
+ interface AgentToolJsonSchema {
5021
+ type: 'object',
5022
+ properties: { [param: string]: { type?: string, [key: string]: unknown } },
5023
+ required?: string[]
5024
+ }
5025
+
5026
+ interface AgentTool {
5027
+ name: string,
5028
+ description?: string,
5029
+ /**
5030
+ * Parameters as `{ [param]: { type, required } }`, or a JSON Schema object, which is flattened to the
5031
+ * same shape.
5032
+ */
5033
+ parameters?: { [param: string]: AgentToolParameter } | AgentToolJsonSchema
5034
+ }
5035
+
5036
+ /**
5037
+ * Inference parameters reported in an agent manifest. Keys are accepted in snake_case or camelCase and
5038
+ * reported in snake_case. Other keys are dropped, since provider-specific settings can carry secrets.
5039
+ */
5040
+ interface AgentModelSettings {
5041
+ frequency_penalty?: number,
5042
+ frequencyPenalty?: number,
5043
+ logit_bias?: { [token: string]: number },
5044
+ logitBias?: { [token: string]: number },
5045
+ logprobs?: boolean,
5046
+ max_tokens?: number,
5047
+ maxTokens?: number,
5048
+ parallel_tool_calls?: boolean,
5049
+ parallelToolCalls?: boolean,
5050
+ presence_penalty?: number,
5051
+ presencePenalty?: number,
5052
+ seed?: number,
5053
+ stop_sequences?: string[],
5054
+ stopSequences?: string[],
5055
+ temperature?: number,
5056
+ timeout?: number,
5057
+ tool_choice?: string,
5058
+ toolChoice?: string,
5059
+ top_k?: number,
5060
+ topK?: number,
5061
+ top_logprobs?: number,
5062
+ topLogprobs?: number,
5063
+ top_p?: number,
5064
+ topP?: number
5065
+ }
5066
+
5067
+ /**
5068
+ * Declares the agent an `agent` span represents, reported as the agent's manifest. Only applies to
5069
+ * `agent` spans. Unreportable values are dropped with a warning, and unset values (`undefined`, `null`,
5070
+ * `''`, `[]`) leave what an earlier annotation declared in place.
5071
+ */
5072
+ interface Agent {
5073
+ /** The agent's name. Defaults to the agent span's name. */
5074
+ name?: string,
5075
+ /** The system instructions the agent runs with. */
5076
+ instructions?: string,
5077
+ /** The model the agent is configured to call. */
5078
+ model?: string,
5079
+ /** Inference parameters. Replaces the settings an earlier annotation declared as a whole. */
5080
+ modelSettings?: AgentModelSettings,
5081
+ /** The tools the agent can call. Replaces tools declared by an earlier annotation. */
5082
+ tools?: AgentTool[]
5083
+ }
5084
+
5009
5085
  /**
5010
5086
  * Annotation options for LLM Observability spans.
5011
5087
  */
@@ -5057,7 +5133,12 @@ declare namespace tracer {
5057
5133
  * A list of ToolDefinition object that represents the tools available to the LLM for this span
5058
5134
  * Each definition requires a `name` and optionally accepts `description`, `schema`, and `version`.
5059
5135
  * */
5060
- toolDefinitions?: ToolDefinition[]
5136
+ toolDefinitions?: ToolDefinition[],
5137
+
5138
+ /**
5139
+ * Declares the agent this span represents. Only used on `agent` spans.
5140
+ */
5141
+ agent?: Agent
5061
5142
  }
5062
5143
 
5063
5144
  interface AnnotationContextOptions {
@@ -5082,6 +5163,13 @@ declare namespace tracer {
5082
5163
  * A Prompt object that represents the prompt used for an LLM call. Only used on `llm` spans.
5083
5164
  */
5084
5165
  prompt?: Prompt,
5166
+
5167
+ /**
5168
+ * Declares the agent running in this context, read when the context is entered. The manifest is
5169
+ * reported on every `agent` span in the context, nested ones included. Use `annotate` on a nested agent
5170
+ * span to declare its own agent.
5171
+ */
5172
+ agent?: Agent,
5085
5173
  }
5086
5174
 
5087
5175
  interface RoutingContextOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.131.1",
3
+ "version": "5.132.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -193,7 +193,7 @@
193
193
  "@datadog/native-appsec": "11.0.3",
194
194
  "@datadog/native-iast-taint-tracking": "4.2.1",
195
195
  "@datadog/native-metrics": "4.0.0",
196
- "@datadog/pprof": "5.19.0",
196
+ "@datadog/pprof": "5.20.0",
197
197
  "@datadog/wasm-js-rewriter": "5.0.4",
198
198
  "@opentelemetry/api": ">=1.0.0 <1.10.0",
199
199
  "@opentelemetry/api-logs": "<1.0.0",
@@ -80,6 +80,19 @@ module.exports = [
80
80
  },
81
81
  channelName: 'Runner_run',
82
82
  },
83
+ {
84
+ module: {
85
+ name: '@wdio/runner',
86
+ versionRange: '>=9.0.0',
87
+ filePath: 'build/index.js',
88
+ },
89
+ functionQuery: {
90
+ className: 'Runner',
91
+ methodName: '_startSession',
92
+ kind: 'Async',
93
+ },
94
+ channelName: 'Runner_startSession',
95
+ },
83
96
  {
84
97
  module: {
85
98
  name: '@wdio/runner',
@@ -130,6 +143,33 @@ module.exports = [
130
143
  channelName: 'BaseReporter_waitForSync',
131
144
  transform: 'waitForAsyncEnd',
132
145
  },
146
+ {
147
+ module: {
148
+ name: '@wdio/mocha-framework',
149
+ versionRange: '>=9.0.0',
150
+ filePath: 'build/index.js',
151
+ },
152
+ functionQuery: {
153
+ className: 'MochaAdapter',
154
+ methodName: 'init',
155
+ kind: 'Async',
156
+ },
157
+ channelName: 'MochaAdapter_init',
158
+ },
159
+ {
160
+ module: {
161
+ name: '@wdio/mocha-framework',
162
+ versionRange: '>=9.0.0',
163
+ filePath: 'build/index.js',
164
+ },
165
+ astQuery: 'VariableDeclarator[id.name="MochaAdapter"] > ClassExpression > ClassBody > ' +
166
+ 'MethodDefinition[key.name="init"] ReturnStatement > ' +
167
+ 'CallExpression[callee.object.name="promise"][callee.property.name="then"], ' +
168
+ 'ClassDeclaration[id.name="MochaAdapter"] > ClassBody > MethodDefinition[key.name="init"] ReturnStatement > ' +
169
+ 'CallExpression[callee.object.name="promise"][callee.property.name="then"]',
170
+ channelName: 'MochaAdapter_init',
171
+ transform: 'waitForAsyncEnd',
172
+ },
133
173
  {
134
174
  module: {
135
175
  name: '@wdio/jasmine-framework',
@@ -495,7 +535,7 @@ module.exports = [
495
535
  {
496
536
  module: {
497
537
  name: 'jasmine-core',
498
- versionRange: '>=5.0.0 <6.0.0',
538
+ versionRange: '>=5.0.0 <7.0.0',
499
539
  filePath: 'lib/jasmine-core/jasmine.js',
500
540
  },
501
541
  astQuery: 'AssignmentExpression[left.object.object.name="Spec"]' +
@@ -508,7 +548,7 @@ module.exports = [
508
548
  {
509
549
  module: {
510
550
  name: 'jasmine-core',
511
- versionRange: '>=5.0.0 <6.0.0',
551
+ versionRange: '>=5.0.0 <7.0.0',
512
552
  filePath: 'lib/jasmine-core/jasmine.js',
513
553
  },
514
554
  astQuery: 'AssignmentExpression[left.object.object.name="Spec"]' +
@@ -521,7 +561,20 @@ module.exports = [
521
561
  {
522
562
  module: {
523
563
  name: 'jasmine-core',
524
- versionRange: '>=5.0.0 <6.0.0',
564
+ versionRange: '>=5.0.0 <7.0.0',
565
+ filePath: 'lib/jasmine-core/jasmine.js',
566
+ },
567
+ functionQuery: {
568
+ className: 'Spec',
569
+ methodName: 'status',
570
+ kind: 'Sync',
571
+ },
572
+ channelName: 'Spec_attemptDone',
573
+ },
574
+ {
575
+ module: {
576
+ name: 'jasmine-core',
577
+ versionRange: '>=5.0.0 <7.0.0',
525
578
  filePath: 'lib/jasmine-core/jasmine.js',
526
579
  },
527
580
  functionQuery: {
@@ -534,7 +587,7 @@ module.exports = [
534
587
  {
535
588
  module: {
536
589
  name: 'jasmine-core',
537
- versionRange: '>=5.0.0 <6.0.0',
590
+ versionRange: '>=5.0.0 <7.0.0',
538
591
  filePath: 'lib/jasmine-core/jasmine.js',
539
592
  },
540
593
  functionQuery: {
@@ -45,6 +45,7 @@
45
45
  "@wdio/config/build/node/index.js": "@wdio/config",
46
46
  "@wdio/jasmine-framework/build/index.js": "@wdio/jasmine-framework",
47
47
  "@wdio/local-runner/build/index.js": "@wdio/local-runner",
48
+ "@wdio/mocha-framework/build/index.js": "@wdio/mocha-framework",
48
49
  "@wdio/runner/build/index.js": "@wdio/runner",
49
50
  "@wdio/utils/build/index.js": "@wdio/utils",
50
51
  "ai/dist/index.js": "ai",
@@ -10,6 +10,7 @@ const { performance } = require('node:perf_hooks')
10
10
  const path = require('path')
11
11
  const satisfies = require('../../../vendor/dist/semifies')
12
12
  const { DD_MAJOR } = require('../../../version')
13
+ const { storage } = require('../../datadog-core')
13
14
  const shimmer = require('../../datadog-shimmer')
14
15
  const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
15
16
  const log = require('../../dd-trace/src/log')
@@ -88,6 +89,8 @@ const testSkippedCh = channel('ci:jest:test:skip')
88
89
  const testFinishCh = channel('ci:jest:test:finish')
89
90
  const testErrCh = channel('ci:jest:test:err')
90
91
  const testFnCh = channel('ci:jest:test:fn')
92
+ const snapshotStorage = storage('ci:jest:snapshots')
93
+ let activeSnapshotTrackers = 0
91
94
  const testSuiteHookFnCh = channel('ci:jest:test-suite:hook:fn')
92
95
  const consoleLogSubmissionCh = channel('ci:log-submission:console')
93
96
  const logSubmissionFlushCh = channel('ci:log-submission:flush')
@@ -666,7 +669,12 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
666
669
  #discardedEfdRetryTests
667
670
  #earlyFlakeDetectionRetryPolicy
668
671
  #efdRetryGatesByName
672
+ #expect
669
673
  #pendingPre30ConcurrentTests
674
+ /** @type {(() => void)|undefined} */
675
+ #restoreSnapshotState
676
+ /** @type {Set<string>|undefined} */
677
+ #snapshotTestNames
670
678
 
671
679
  constructor (config, context) {
672
680
  super(config, context)
@@ -1445,6 +1453,98 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
1445
1453
  }
1446
1454
  }
1447
1455
 
1456
+ getSnapshotState () {
1457
+ return this.#expect?.getState?.()?.snapshotState
1458
+ }
1459
+
1460
+ /**
1461
+ * Attributes snapshot increments to the executing test when serial and concurrent bodies can overlap.
1462
+ * Both Jest and image snapshot matchers write this shared counter directly.
1463
+ */
1464
+ #trackQuarantinedSnapshots () {
1465
+ if (!this.hasConcurrentTests || !this.testManagementTestsForThisSuite?.quarantined?.length) return
1466
+
1467
+ const snapshotState = this.getSnapshotState()
1468
+ const descriptor = snapshotState && Object.getOwnPropertyDescriptor(snapshotState, 'unmatched')
1469
+ if (!descriptor?.configurable || typeof descriptor.value !== 'number') return
1470
+
1471
+ let unmatched = descriptor.value
1472
+ try {
1473
+ Object.defineProperty(snapshotState, 'unmatched', {
1474
+ configurable: true,
1475
+ enumerable: descriptor.enumerable,
1476
+ get () { return unmatched },
1477
+ /** @param {number} value */
1478
+ set (value) {
1479
+ const ctx = /** @type {{ unmatchedSnapshots?: number }|undefined} */ (snapshotStorage.getStore())
1480
+ if (ctx?.unmatchedSnapshots !== undefined && value > unmatched) {
1481
+ ctx.unmatchedSnapshots += value - unmatched
1482
+ }
1483
+ unmatched = value
1484
+ },
1485
+ })
1486
+ } catch (error) {
1487
+ log.error('Could not track quarantined Jest snapshots: %s', error)
1488
+ return
1489
+ }
1490
+
1491
+ if (activeSnapshotTrackers++ === 0) testFnCh.bindStore(snapshotStorage)
1492
+ this.#restoreSnapshotState = () => {
1493
+ try {
1494
+ Object.defineProperty(snapshotState, 'unmatched', { ...descriptor, value: unmatched })
1495
+ } catch (error) {
1496
+ log.error('Could not restore Jest snapshot state: %s', error)
1497
+ } finally {
1498
+ if (--activeSnapshotTrackers === 0) testFnCh.unbindStore(snapshotStorage)
1499
+ }
1500
+ }
1501
+ }
1502
+
1503
+ /**
1504
+ * Keeps the snapshot results of a quarantined test from failing the run.
1505
+ *
1506
+ * Jest fails the run on unmatched or obsolete snapshots even when no test failed.
1507
+ *
1508
+ * @param {object} test
1509
+ */
1510
+ ignoreQuarantinedSnapshotResults (test) {
1511
+ const ctx = testContexts.get(test)
1512
+ const unmatchedBeforeTest = ctx?.unmatchedSnapshotsBeforeTest
1513
+ const unmatchedSnapshots = ctx?.unmatchedSnapshots
1514
+ // Jest retries the attempts that still have errors and resets their snapshot results itself.
1515
+ if ((unmatchedBeforeTest === undefined && unmatchedSnapshots === undefined) || test.errors?.length) return
1516
+
1517
+ const snapshotState = this.getSnapshotState()
1518
+ if (!snapshotState) return
1519
+
1520
+ if (unmatchedSnapshots !== undefined) {
1521
+ snapshotState.unmatched -= unmatchedSnapshots
1522
+ ctx.unmatchedSnapshots = 0
1523
+ } else if (snapshotState.unmatched > unmatchedBeforeTest) {
1524
+ snapshotState.unmatched = unmatchedBeforeTest
1525
+ }
1526
+ // Jest's name-based marker can consume other tests' keys, so delete only keys owned by this test.
1527
+ const keys = snapshotState._uncheckedKeys
1528
+ const testNames = this.#snapshotTestNames
1529
+ if (!keys?.delete || !testNames) return
1530
+ const testName = getRawJestTestName(test)
1531
+ const hintPrefix = `${testName}: `
1532
+ for (const key of keys) {
1533
+ const snapshotName = key.slice(0, key.lastIndexOf(' '))
1534
+ if (snapshotName !== testName && !snapshotName.startsWith(hintPrefix)) continue
1535
+ let belongsToAnotherTest = false
1536
+ // Snapshot names do not identify their owner when another registered name matches the same key.
1537
+ for (const name of testNames) {
1538
+ if (name !== testName &&
1539
+ (snapshotName === name || snapshotName.startsWith(`${name}: `))) {
1540
+ belongsToAnotherTest = true
1541
+ break
1542
+ }
1543
+ }
1544
+ if (!belongsToAnotherTest) keys.delete(key)
1545
+ }
1546
+ }
1547
+
1448
1548
  // This function returns an array if the known tests are valid and null otherwise.
1449
1549
  getKnownTestsForSuite (suiteKnownTests) {
1450
1550
  // `suiteKnownTests` is `this.testEnvironmentOptions._ddKnownTests`,
@@ -1819,6 +1919,11 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
1819
1919
  if (event.concurrent) {
1820
1920
  this.hasConcurrentTests = true
1821
1921
  }
1922
+ const registeredTest = getTestEntries(state.currentDescribeBlock)?.at(-1)
1923
+ if (registeredTest && this.testManagementTestsForThisSuite?.quarantined?.length) {
1924
+ this.#snapshotTestNames ??= new Set()
1925
+ this.#snapshotTestNames.add(getRawJestTestName(registeredTest))
1926
+ }
1822
1927
  if (event.failing) {
1823
1928
  return
1824
1929
  }
@@ -1827,7 +1932,6 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
1827
1932
  const concurrentTestContexts = this.concurrentTestContexts.get(testFullName)
1828
1933
  const concurrentTestState = this.concurrentTestStates.get(event.fn) ||
1829
1934
  concurrentTestContexts?.at(-1)?.concurrentTestState
1830
- const registeredTest = getTestEntries(state.currentDescribeBlock)?.at(-1)
1831
1935
  if (concurrentTestState && registeredTest?.fn === event.fn) {
1832
1936
  testContexts.set(registeredTest, concurrentTestState.ctx)
1833
1937
  }
@@ -1925,10 +2029,12 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
1925
2029
  }
1926
2030
 
1927
2031
  if (event.name === 'run_start') {
2032
+ this.#trackQuarantinedSnapshots()
1928
2033
  const registerResultHandler = dynamicAtrResultHandlerRegistrations.get(this)
1929
2034
  dynamicAtrResultHandlerRegistrations.delete(this)
1930
2035
  registerResultHandler?.()
1931
2036
  } else if (event.name === 'setup') {
2037
+ this.#expect = event.runtimeGlobals?.expect ?? this.global.expect
1932
2038
  this.wrapConcurrentTest(state)
1933
2039
  this.bindTestEach(this.global.test)
1934
2040
  }
@@ -2044,6 +2150,13 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
2044
2150
  ctx.concurrentTestState = concurrentTestState
2045
2151
  concurrentTestState.ctx = ctx
2046
2152
  }
2153
+ if (isQuarantined && !isAttemptToFix && !event.test.concurrent && !ctx.concurrentTestState) {
2154
+ if (this.#restoreSnapshotState) {
2155
+ ctx.unmatchedSnapshots = 0
2156
+ } else if (!this.hasConcurrentTests) {
2157
+ ctx.unmatchedSnapshotsBeforeTest = this.getSnapshotState()?.unmatched
2158
+ }
2159
+ }
2047
2160
  testContexts.set(event.test, ctx)
2048
2161
 
2049
2162
  if (isAttemptToFix) {
@@ -2282,6 +2395,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
2282
2395
  event.test.errors = []
2283
2396
  }
2284
2397
  }
2398
+ this.ignoreQuarantinedSnapshotResults(event.test)
2285
2399
 
2286
2400
  // Quarantine must consume terminal failures before we save errors for Jest's final result.
2287
2401
  if (dynamicAtrCount !== undefined && failedAllTests && event.test.errors?.length) {
@@ -2359,6 +2473,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
2359
2473
  ctx.currentStore = undefined
2360
2474
  }
2361
2475
  } else if (event.name === 'run_finish') {
2476
+ this.#restoreSnapshotState?.()
2477
+ this.#restoreSnapshotState = undefined
2362
2478
  this.removeDiscardedEfdRetries()
2363
2479
  for (const [test, errors] of atrSuppressedErrors) {
2364
2480
  // Do not restore errors for non-ATF quarantined tests — they should stay suppressed
@@ -2528,6 +2644,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
2528
2644
  }
2529
2645
 
2530
2646
  teardown () {
2647
+ this.#restoreSnapshotState?.()
2648
+ this.#restoreSnapshotState = undefined
2531
2649
  if (this._globalProxy?.propertyToValue) {
2532
2650
  for (const [key] of this._globalProxy.propertyToValue) {
2533
2651
  if (typeof key === 'string' && key.startsWith('_dd')) {