dd-trace 5.116.0 → 5.118.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 (98) hide show
  1. package/ci/diagnose.js +162 -21
  2. package/ci/init.js +0 -1
  3. package/ci/runbook.md +187 -180
  4. package/ci/test-optimization-validation/approval-artifacts.js +3 -1
  5. package/ci/test-optimization-validation/approval.js +52 -15
  6. package/ci/test-optimization-validation/ci-command-candidate.js +26 -32
  7. package/ci/test-optimization-validation/ci-discovery.js +1 -1
  8. package/ci/test-optimization-validation/ci-package-scripts.js +141 -0
  9. package/ci/test-optimization-validation/ci-remediation.js +112 -39
  10. package/ci/test-optimization-validation/cli.js +516 -681
  11. package/ci/test-optimization-validation/command-blocker.js +188 -33
  12. package/ci/test-optimization-validation/command-output-policy.js +3 -26
  13. package/ci/test-optimization-validation/command-runner.js +189 -245
  14. package/ci/test-optimization-validation/environment.js +90 -0
  15. package/ci/test-optimization-validation/executable.js +159 -388
  16. package/ci/test-optimization-validation/framework-adapters/cucumber.js +119 -0
  17. package/ci/test-optimization-validation/framework-adapters/cypress.js +107 -0
  18. package/ci/test-optimization-validation/framework-adapters/playwright.js +181 -0
  19. package/ci/test-optimization-validation/generated-files.js +75 -13
  20. package/ci/test-optimization-validation/generated-test-contract.js +283 -0
  21. package/ci/test-optimization-validation/generated-verifier.js +49 -16
  22. package/ci/test-optimization-validation/literal-environment.js +57 -0
  23. package/ci/test-optimization-validation/manifest-loader.js +2 -22
  24. package/ci/test-optimization-validation/manifest-scaffold.js +816 -515
  25. package/ci/test-optimization-validation/manifest-schema.js +494 -702
  26. package/ci/test-optimization-validation/offline-fixtures.js +4 -1
  27. package/ci/test-optimization-validation/plan-writer.js +243 -984
  28. package/ci/test-optimization-validation/preflight-runner.js +49 -55
  29. package/ci/test-optimization-validation/redaction.js +2 -1
  30. package/ci/test-optimization-validation/report-writer.js +391 -1357
  31. package/ci/test-optimization-validation/result-semantics.js +86 -0
  32. package/ci/test-optimization-validation/runner-command.js +249 -0
  33. package/ci/test-optimization-validation/runner-contract.js +664 -0
  34. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +29 -3
  35. package/ci/test-optimization-validation/scenarios/basic-reporting.js +240 -543
  36. package/ci/test-optimization-validation/scenarios/ci-wiring.js +450 -670
  37. package/ci/test-optimization-validation/scenarios/early-flake-detection.js +4 -3
  38. package/ci/test-optimization-validation/scenarios/helpers.js +67 -9
  39. package/ci/test-optimization-validation/scenarios/test-management.js +4 -3
  40. package/ci/test-optimization-validation/source-text.js +87 -0
  41. package/ci/test-optimization-validation/test-output.js +12 -22
  42. package/ext/tags.d.ts +1 -0
  43. package/index.d.ts +7 -0
  44. package/package.json +3 -3
  45. package/packages/datadog-instrumentations/src/cucumber.js +11 -12
  46. package/packages/datadog-instrumentations/src/helpers/channel.js +74 -0
  47. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  48. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  49. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/openai-agents.js +31 -0
  50. package/packages/datadog-instrumentations/src/jest.js +18 -17
  51. package/packages/datadog-instrumentations/src/mocha/main.js +79 -30
  52. package/packages/datadog-instrumentations/src/openai-agents.js +159 -0
  53. package/packages/datadog-instrumentations/src/playwright.js +3 -24
  54. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1 -1
  55. package/packages/datadog-instrumentations/src/vitest-main.js +14 -26
  56. package/packages/datadog-instrumentations/src/vitest-util.js +0 -7
  57. package/packages/datadog-instrumentations/src/vitest-worker.js +5 -8
  58. package/packages/datadog-plugin-cucumber/src/index.js +2 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +2 -1
  60. package/packages/datadog-plugin-openai-agents/src/index.js +74 -0
  61. package/packages/datadog-plugin-openai-agents/src/integration.js +503 -0
  62. package/packages/datadog-plugin-openai-agents/src/processor.js +108 -0
  63. package/packages/datadog-plugin-openai-agents/src/util.js +60 -0
  64. package/packages/datadog-plugin-playwright/src/index.js +4 -1
  65. package/packages/datadog-plugin-vitest/src/index.js +5 -5
  66. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +48 -49
  67. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +161 -33
  68. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +6 -4
  69. package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
  70. package/packages/dd-trace/src/config/supported-configurations.json +7 -0
  71. package/packages/dd-trace/src/encode/span-stats.js +7 -1
  72. package/packages/dd-trace/src/exporter.js +16 -9
  73. package/packages/dd-trace/src/llmobs/index.js +9 -1
  74. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +104 -22
  75. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +5 -9
  76. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +20 -0
  77. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +321 -0
  78. package/packages/dd-trace/src/llmobs/sdk.js +2 -1
  79. package/packages/dd-trace/src/llmobs/span_processor.js +7 -2
  80. package/packages/dd-trace/src/llmobs/tagger.js +23 -3
  81. package/packages/dd-trace/src/llmobs/util.js +56 -3
  82. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +3 -6
  83. package/packages/dd-trace/src/openfeature/configuration_source.js +5 -8
  84. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +5 -1
  85. package/packages/dd-trace/src/plugins/ci_plugin.js +9 -3
  86. package/packages/dd-trace/src/plugins/index.js +1 -0
  87. package/packages/dd-trace/src/plugins/outbound.js +4 -1
  88. package/packages/dd-trace/src/profiling/profilers/space.js +1 -1
  89. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  90. package/packages/dd-trace/src/span_processor.js +1 -1
  91. package/packages/dd-trace/src/span_stats.js +22 -4
  92. package/ci/test-optimization-validation/command-suitability.js +0 -471
  93. package/ci/test-optimization-validation/init-probe-preload.js +0 -163
  94. package/ci/test-optimization-validation/init-probe.js +0 -246
  95. package/ci/test-optimization-validation/late-initialization.js +0 -63
  96. package/ci/test-optimization-validation/local-command.js +0 -163
  97. package/ci/test-optimization-validation/setup-runner.js +0 -97
  98. package/ci/test-optimization-validation-manifest.schema.json +0 -1
@@ -46,6 +46,10 @@ const {
46
46
  addCoverageBackfillUntestedFiles,
47
47
  getCoverageBackfillFiles,
48
48
  } = require('./jest/coverage-backfill')
49
+ const {
50
+ getChannelPromise,
51
+ publishWithCompletion,
52
+ } = require('./helpers/channel')
49
53
  const { addHook, channel } = require('./helpers/instrument')
50
54
 
51
55
  const testSessionStartCh = channel('ci:jest:session:start')
@@ -1971,6 +1975,9 @@ function shouldFinishBailTestSession (globalConfig, results) {
1971
1975
  return !!globalConfig?.bail && getNumBailFailures(results) >= globalConfig.bail
1972
1976
  }
1973
1977
 
1978
+ /**
1979
+ * @param {Record<string, unknown> & { onDone?: () => void }} payload
1980
+ */
1974
1981
  async function waitForTestSessionFinish (payload) {
1975
1982
  if (!testSessionFinishCh.hasSubscribers || hasFinishedTestSession) return
1976
1983
 
@@ -1978,8 +1985,9 @@ async function waitForTestSessionFinish (payload) {
1978
1985
 
1979
1986
  let timeoutId
1980
1987
 
1988
+ let onDone
1981
1989
  const flushPromise = new Promise((resolve) => {
1982
- payload.onDone = () => {
1990
+ onDone = () => {
1983
1991
  clearTimeout(timeoutId)
1984
1992
  resolve()
1985
1993
  }
@@ -1992,7 +2000,7 @@ async function waitForTestSessionFinish (payload) {
1992
2000
  timeoutId.unref?.()
1993
2001
  })
1994
2002
 
1995
- testSessionFinishCh.publish(payload)
2003
+ publishWithCompletion(testSessionFinishCh, payload, onDone)
1996
2004
 
1997
2005
  const waitingResult = await Promise.race([flushPromise, timeoutPromise])
1998
2006
 
@@ -2157,12 +2165,6 @@ function getWrappedScheduleTests (scheduleTests, frameworkVersion) {
2157
2165
  }
2158
2166
  }
2159
2167
 
2160
- function getChannelPromise (channelToPublishTo, payload = {}) {
2161
- return new Promise(resolve => {
2162
- channelToPublishTo.publish({ ...payload, onDone: resolve })
2163
- })
2164
- }
2165
-
2166
2168
  function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
2167
2169
  const SearchSource = searchSourcePackage.default ?? searchSourcePackage
2168
2170
 
@@ -2517,9 +2519,7 @@ function getCliWrapper (isNewJestVersion) {
2517
2519
 
2518
2520
  if (codeCoverageReportCh.hasSubscribers) {
2519
2521
  const rootDir = result.globalConfig?.rootDir || process.cwd()
2520
- await new Promise((resolve) => {
2521
- codeCoverageReportCh.publish({ rootDir, onDone: resolve })
2522
- })
2522
+ await getChannelPromise(codeCoverageReportCh, { rootDir })
2523
2523
  }
2524
2524
 
2525
2525
  logSessionSummary(ignoredFailuresSummary, getAttemptToFixExecutionsFromJestResults(result))
@@ -2537,6 +2537,10 @@ function shouldWaitForTestSuiteFinish (environment) {
2537
2537
  return isJestWorker && environment.globalConfig?.workerIdleMemoryLimit !== undefined
2538
2538
  }
2539
2539
 
2540
+ /**
2541
+ * @param {Record<string, unknown>} payload
2542
+ * @param {boolean} waitForFinish
2543
+ */
2540
2544
  function publishTestSuiteFinish (payload, waitForFinish) {
2541
2545
  if (!testSuiteFinishCh.hasSubscribers) return
2542
2546
 
@@ -2545,12 +2549,9 @@ function publishTestSuiteFinish (payload, waitForFinish) {
2545
2549
  return
2546
2550
  }
2547
2551
 
2548
- return new Promise(resolve => {
2549
- testSuiteFinishCh.publish({
2550
- ...payload,
2551
- waitForFinish,
2552
- onDone: resolve,
2553
- })
2552
+ return getChannelPromise(testSuiteFinishCh, {
2553
+ ...payload,
2554
+ waitForFinish,
2554
2555
  })
2555
2556
  }
2556
2557
 
@@ -3,6 +3,11 @@
3
3
  const { createCoverageMap } = require('../../../../vendor/dist/istanbul-lib-coverage')
4
4
  const satisfies = require('../../../../vendor/dist/semifies')
5
5
  const { DD_MAJOR } = require('../../../../version')
6
+ const {
7
+ getRunStoresPromise,
8
+ publishWithCompletion,
9
+ runStoresWithCompletion,
10
+ } = require('../helpers/channel')
6
11
  const { addHook, channel } = require('../helpers/instrument')
7
12
  const shimmer = require('../../../datadog-shimmer')
8
13
  const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
@@ -240,6 +245,41 @@ function resetSuiteSkippingRunState () {
240
245
  writeCoverageBackfillToCache({})
241
246
  }
242
247
 
248
+ /**
249
+ * @param {((failures: number) => void) | undefined} callback
250
+ * @returns {{ onRunDone: (failures: number) => void, onFlushDone: () => void }}
251
+ */
252
+ function getRunCompletionCallbacks (callback) {
253
+ let failures
254
+ let hasRunFinished = false
255
+ let hasFlushFinished = false
256
+ let hasCompleted = false
257
+ const onDone = callback || (() => {})
258
+
259
+ const completeIfReady = () => {
260
+ if (hasCompleted || !hasRunFinished || !hasFlushFinished) return
261
+
262
+ hasCompleted = true
263
+ onDone(failures)
264
+ }
265
+
266
+ return {
267
+ onRunDone: (runFailures) => {
268
+ if (hasRunFinished) return
269
+
270
+ failures = runFailures
271
+ hasRunFinished = true
272
+ completeIfReady()
273
+ },
274
+ onFlushDone: () => {
275
+ if (hasFlushFinished) return
276
+
277
+ hasFlushFinished = true
278
+ completeIfReady()
279
+ },
280
+ }
281
+ }
282
+
243
283
  function getOnStartHandler (frameworkVersion) {
244
284
  return function () {
245
285
  const processArgv = process.argv.slice(2).join(' ')
@@ -251,7 +291,12 @@ function getOnStartHandler (frameworkVersion) {
251
291
  }
252
292
  }
253
293
 
254
- function getOnEndHandler (isParallel) {
294
+ /**
295
+ * @param {boolean} isParallel
296
+ * @param {() => void} onDone
297
+ * @returns {() => void}
298
+ */
299
+ function getOnEndHandler (isParallel, onDone) {
255
300
  return function () {
256
301
  let status = 'pass'
257
302
  let error
@@ -337,7 +382,7 @@ function getOnEndHandler (isParallel) {
337
382
  global.__coverage__ = fromCoverageMapToCoverage(originalCoverageMap)
338
383
  }
339
384
 
340
- testSessionFinishCh.publish({
385
+ publishWithCompletion(testSessionFinishCh, {
341
386
  status,
342
387
  isSuitesSkipped,
343
388
  testCodeCoverageLinesTotal,
@@ -350,19 +395,13 @@ function getOnEndHandler (isParallel) {
350
395
  isEarlyFlakeDetectionFaulty: config.isEarlyFlakeDetectionFaulty,
351
396
  isTestManagementEnabled: config.isTestManagementTestsEnabled,
352
397
  isParallel,
353
- })
398
+ }, onDone)
354
399
 
355
400
  logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
356
401
  loggedAttemptToFixTests.clear()
357
402
  }
358
403
  }
359
404
 
360
- function getRunStoresPromise (channelToPublishTo, ctx) {
361
- return new Promise(resolve => {
362
- channelToPublishTo.runStores({ ...ctx, onDone: resolve }, () => {})
363
- })
364
- }
365
-
366
405
  function applyKnownTestsResponse ({ err, knownTests }) {
367
406
  if (err) {
368
407
  config.knownTests = []
@@ -395,13 +434,14 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
395
434
  let skippableSuitesResponse
396
435
  resetSuiteSkippingRunState()
397
436
 
398
- const onReceivedSkippableSuites = ({
399
- err,
400
- skippableSuites,
401
- itrCorrelationId: responseItrCorrelationId,
402
- skippableSuitesCoverage: responseSkippableSuitesCoverage,
403
- }) => {
404
- if (err) {
437
+ const onReceivedSkippableSuites = (response) => {
438
+ const {
439
+ err,
440
+ skippableSuites,
441
+ itrCorrelationId: responseItrCorrelationId,
442
+ skippableSuitesCoverage: responseSkippableSuitesCoverage,
443
+ } = response || {}
444
+ if (!response || err) {
405
445
  suitesToSkip = []
406
446
  skippableSuitesCoverage = {}
407
447
  } else {
@@ -443,12 +483,12 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
443
483
  return
444
484
  }
445
485
 
446
- ctx.onDone = onReceivedSkippableSuites
447
- skippableSuitesCh.runStores(ctx, () => {})
486
+ runStoresWithCompletion(skippableSuitesCh, ctx, onReceivedSkippableSuites)
448
487
  }
449
488
 
450
- const onReceivedImpactedTests = ({ err, modifiedFiles: receivedModifiedFiles }) => {
451
- if (err) {
489
+ const onReceivedImpactedTests = (response) => {
490
+ const { err, modifiedFiles: receivedModifiedFiles } = response || {}
491
+ if (!response || err) {
452
492
  config.modifiedFiles = []
453
493
  config.isImpactedTestsEnabled = false
454
494
  } else {
@@ -465,8 +505,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
465
505
 
466
506
  const continueAfterTestRequests = () => {
467
507
  if (config.isImpactedTestsEnabled) {
468
- ctx.onDone = onReceivedImpactedTests
469
- modifiedFilesCh.runStores(ctx, () => {})
508
+ runStoresWithCompletion(modifiedFilesCh, ctx, onReceivedImpactedTests)
470
509
  } else if (config.isSuitesSkippingEnabled) {
471
510
  requestSkippableSuites()
472
511
  } else {
@@ -476,8 +515,14 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
476
515
  }
477
516
  }
478
517
 
479
- const onReceivedConfiguration = ({ err, isTestDynamicInstrumentationEnabled, libraryConfig, repositoryRoot }) => {
480
- if (err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
518
+ const onReceivedConfiguration = (response) => {
519
+ const {
520
+ err,
521
+ isTestDynamicInstrumentationEnabled,
522
+ libraryConfig,
523
+ repositoryRoot,
524
+ } = response || {}
525
+ if (!response || err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
481
526
  return mochaGlobalRunCh.runStores(ctx, () => {
482
527
  onFinishRequest()
483
528
  })
@@ -526,9 +571,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
526
571
  })
527
572
  }
528
573
 
529
- ctx.onDone = onReceivedConfiguration
530
-
531
- libraryConfigurationCh.runStores(ctx, () => {})
574
+ runStoresWithCompletion(libraryConfigurationCh, ctx, onReceivedConfiguration)
532
575
  }
533
576
 
534
577
  // In this hook we delay the execution with options.delay to grab library configuration,
@@ -641,6 +684,9 @@ addHook({
641
684
  return run.apply(this, args)
642
685
  }
643
686
 
687
+ const { onRunDone, onFlushDone } = getRunCompletionCallbacks(args[0])
688
+ args[0] = onRunDone
689
+
644
690
  const { suitesByTestFile, numSuitesByTestFile } = getSuitesByTestFile(this.suite)
645
691
  // Root-level tests (direct children of root, no describe wrapper) keyed by file.
646
692
  // Populated during the root 'suite' event so the normal finish path can include them
@@ -752,7 +798,7 @@ addHook({
752
798
  finishRootSuiteForFile(test.file)
753
799
  }
754
800
 
755
- const onEnd = getOnEndHandler(false)
801
+ const onEnd = getOnEndHandler(false, onFlushDone)
756
802
 
757
803
  this.once('start', getOnStartHandler(frameworkVersion))
758
804
 
@@ -1041,8 +1087,11 @@ addHook({
1041
1087
  return run.apply(this, arguments)
1042
1088
  }
1043
1089
 
1090
+ const { onRunDone, onFlushDone } = getRunCompletionCallbacks(cb)
1091
+ arguments[0] = onRunDone
1092
+
1044
1093
  this.once('start', getOnStartHandler(frameworkVersion))
1045
- this.once('end', getOnEndHandler(true))
1094
+ this.once('end', getOnEndHandler(true, onFlushDone))
1046
1095
 
1047
1096
  // Populate unskippable suites before config is fetched (matches serial mode at Mocha.prototype.run)
1048
1097
  for (const filePath of files) {
@@ -1082,7 +1131,7 @@ addHook({
1082
1131
  skippedSuites = skippedFiles
1083
1132
  skippedSuitesCoverage = getSkippedSuitesCoverageForRun()
1084
1133
  writeCoverageBackfillToCache(skippedSuitesCoverage, getCoverageRootDir())
1085
- run.apply(this, [cb, { files: filteredFiles }])
1134
+ run.apply(this, [onRunDone, { files: filteredFiles }])
1086
1135
  } else {
1087
1136
  run.apply(this, arguments)
1088
1137
  }
@@ -0,0 +1,159 @@
1
+ 'use strict'
2
+
3
+ const { channel, tracingChannel } = require('dc-polyfill')
4
+ const shimmer = require('../../datadog-shimmer')
5
+ const { addHook, getHooks } = require('./helpers/instrument')
6
+
7
+ // `WeakSet` keyed by module exports — replaces the underscored
8
+ // `mod._datadogPatched` flag while keeping dedupe semantics. Mods are kept
9
+ // alive by `require.cache` anyway, so this doesn't add lifetime to anything.
10
+ const patchedMods = new WeakSet()
11
+ const modelBaseURLs = new WeakMap()
12
+ const chatCompletionsModelConstructorCh =
13
+ tracingChannel('orchestrion:@openai/agents-openai:OpenAIChatCompletionsModel_constructor')
14
+
15
+ /**
16
+ * Capture the client base URL before the SDK stores the client in a #private field.
17
+ *
18
+ * @param {{ arguments?: [{ baseURL?: string }], self?: object }} ctx
19
+ */
20
+ function captureChatCompletionsModelBaseURL (ctx) {
21
+ const baseURL = ctx.arguments?.[0]?.baseURL
22
+ if (ctx.self && typeof baseURL === 'string') modelBaseURLs.set(ctx.self, baseURL)
23
+ }
24
+
25
+ chatCompletionsModelConstructorCh.end.subscribe(captureChatCompletionsModelBaseURL)
26
+
27
+ for (const hook of getHooks('@openai/agents-openai')) {
28
+ addHook(hook, moduleExports => moduleExports)
29
+ }
30
+
31
+ // Plugin subscribes to this and registers its TracingProcessor when
32
+ // `@openai/agents` loads. Publishing from here keeps this file free of
33
+ // any cross-package import from the plugin.
34
+ const agentsCoreLoadedCh = channel('apm:openai-agents:agents-core:loaded')
35
+
36
+ // Plugin uses addBind on this channel so that legacyStorage.run(store, fn) wraps
37
+ // the model call — including async iterator advancement for streaming responses.
38
+ // This ensures the active dd-trace span is visible to the openai plugin when it
39
+ // creates its openai.request span, correctly parenting it under the agent span.
40
+ const modelStartCh = channel('apm:openai-agents:model:start')
41
+
42
+ // Tool.invoke runs inside agents-core's public function-span context. Bind the
43
+ // corresponding dd-trace tool span around that public invocation boundary so
44
+ // spans created by user tool code inherit it.
45
+ const toolStartCh = channel('apm:openai-agents:tool:start')
46
+
47
+ // Reference to the loaded @openai/agents module, captured in the first hook
48
+ // so that wrapResponseMethod can call getCurrentSpan() without an additional
49
+ // require (and without triggering n/no-missing-require on agents-core internals).
50
+ let agentsMod
51
+
52
+ // @openai/agents >=0.8.0 moved addTraceProcessor / getCurrentSpan out of the
53
+ // top-level re-exports. The new public surface uses getGlobalTraceProvider():
54
+ // provider.registerProcessor(processor) (replaces addTraceProcessor)
55
+ // provider.getCurrentSpan() (replaces getCurrentSpan)
56
+ // Both APIs are tried so this file works across the full supported version range.
57
+ // The plugin subscriber (index.js) handles processor registration via the channel.
58
+ function getCurrentSpan () {
59
+ if (typeof agentsMod?.getCurrentSpan === 'function') {
60
+ return agentsMod.getCurrentSpan()
61
+ }
62
+ if (typeof agentsMod?.getGlobalTraceProvider === 'function') {
63
+ return agentsMod.getGlobalTraceProvider().getCurrentSpan()
64
+ }
65
+ }
66
+
67
+ function getCurrentSpanId () {
68
+ return getCurrentSpan()?.spanId
69
+ }
70
+
71
+ addHook({ name: '@openai/agents', versions: ['>=0.7.0'] }, (mod) => {
72
+ if (patchedMods.has(mod)) return mod
73
+ if (typeof mod?.addTraceProcessor !== 'function' && typeof mod?.getGlobalTraceProvider !== 'function') return mod
74
+ patchedMods.add(mod)
75
+ agentsMod = mod
76
+ if (typeof mod.tool === 'function') {
77
+ shimmer.wrap(mod, 'tool', wrapToolFactory, { replaceGetter: true })
78
+ }
79
+ agentsCoreLoadedCh.publish({ mod })
80
+ return mod
81
+ })
82
+
83
+ function wrapToolFactory (original) {
84
+ return function (...args) {
85
+ const tool = original.apply(this, args)
86
+ if (typeof tool?.invoke === 'function') {
87
+ shimmer.wrap(tool, 'invoke', wrapToolInvoke)
88
+ }
89
+ return tool
90
+ }
91
+ }
92
+
93
+ function wrapToolInvoke (original) {
94
+ return function (...args) {
95
+ const agentsCoreSpan = getCurrentSpan()
96
+ return toolStartCh.runStores({ agentsCoreSpan }, () => original.apply(this, args))
97
+ }
98
+ }
99
+
100
+ function wrapResponseMethod (original) {
101
+ return function (...args) {
102
+ const agentsCoreSpanId = getCurrentSpanId()
103
+ const baseURL = getClientBaseURL(this)
104
+ return modelStartCh.runStores({ agentsCoreSpanId, baseURL }, () => original.apply(this, args))
105
+ }
106
+ }
107
+
108
+ function wrapStreamedResponseMethod (original) {
109
+ return function (...args) {
110
+ const agentsCoreSpanId = getCurrentSpanId()
111
+ const baseURL = getClientBaseURL(this)
112
+ const context = { agentsCoreSpanId, baseURL }
113
+ const iterator = modelStartCh.runStores(context, () => original.apply(this, args))
114
+ return wrapAsyncIterator(iterator, context)
115
+ }
116
+ }
117
+
118
+ function getClientBaseURL (model) {
119
+ return model?.client?.baseURL ?? model?._client?.baseURL ?? modelBaseURLs.get(model)
120
+ }
121
+
122
+ function wrapAsyncIterator (iterator, context) {
123
+ if (!iterator || typeof iterator !== 'object') return iterator
124
+
125
+ return {
126
+ next () {
127
+ return modelStartCh.runStores(context, () => iterator.next.apply(iterator, arguments))
128
+ },
129
+ throw () {
130
+ if (typeof iterator.throw !== 'function') return Promise.reject(arguments[0])
131
+ return modelStartCh.runStores(context, () => iterator.throw.apply(iterator, arguments))
132
+ },
133
+ return () {
134
+ if (typeof iterator.return !== 'function') return Promise.resolve({ done: true, value: arguments[0] })
135
+ return modelStartCh.runStores(context, () => iterator.return.apply(iterator, arguments))
136
+ },
137
+ [Symbol.asyncIterator] () {
138
+ return this
139
+ },
140
+ }
141
+ }
142
+
143
+ addHook({ name: '@openai/agents-openai', versions: ['>=0.7.0'] }, (mod) => {
144
+ if (patchedMods.has(mod)) return mod
145
+ const responseProto = mod?.OpenAIResponsesModel?.prototype
146
+ const chatCompletionsProto = mod?.OpenAIChatCompletionsModel?.prototype
147
+ if (!responseProto && !chatCompletionsProto) return mod
148
+
149
+ patchedMods.add(mod)
150
+ for (const proto of [responseProto, chatCompletionsProto]) {
151
+ if (typeof proto?.getResponse === 'function') {
152
+ shimmer.wrap(proto, 'getResponse', wrapResponseMethod)
153
+ }
154
+ if (typeof proto?.getStreamedResponse === 'function') {
155
+ shimmer.wrap(proto, 'getStreamedResponse', wrapStreamedResponseMethod)
156
+ }
157
+ }
158
+ return mod
159
+ })
@@ -29,6 +29,7 @@ const {
29
29
  RUM_TEST_EXECUTION_ID_COOKIE_NAME: RUM_COOKIE_NAME,
30
30
  } = require('../../dd-trace/src/ci-visibility/rum')
31
31
  const { DD_MAJOR } = require('../../../version')
32
+ const { getChannelPromise } = require('./helpers/channel')
32
33
  const { addHook, channel, tracingChannel } = require('./helpers/instrument')
33
34
 
34
35
  const testStartCh = channel('ci:playwright:test:start')
@@ -614,12 +615,6 @@ function getTestByTestId (dispatcher, testId) {
614
615
  }
615
616
  }
616
617
 
617
- function getChannelPromise (channelToPublishTo, params) {
618
- return new Promise(resolve => {
619
- channelToPublishTo.publish({ onDone: resolve, ...params })
620
- })
621
- }
622
-
623
618
  // Inspired by https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/reporters/base.ts#L293
624
619
  // We can't use test.outcome() directly because it's set on follow up handlers:
625
620
  // our `testEndHandler` is called before the outcome is set.
@@ -1206,8 +1201,6 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
1206
1201
  function runAllTestsWrapper (runAllTests, playwrightVersion) {
1207
1202
  // Config parameter is only available from >=1.55.0
1208
1203
  return async function (config) {
1209
- let onDone
1210
-
1211
1204
  rootDir = getRootDir(this, config)
1212
1205
  const projects = getProjectsFromRunner(this, config)
1213
1206
  const isFailureScreenshotEnabled = isFailureScreenshotCaptureEnabled(projects)
@@ -1379,17 +1372,12 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1379
1372
  logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
1380
1373
  loggedAttemptToFixTests.clear()
1381
1374
 
1382
- const flushWait = new Promise(resolve => {
1383
- onDone = resolve
1384
- })
1385
- testSessionFinishCh.publish({
1375
+ await getChannelPromise(testSessionFinishCh, {
1386
1376
  status: preventedToFail ? 'pass' : STATUS_TO_TEST_STATUS[sessionStatus],
1387
1377
  isEarlyFlakeDetectionEnabled,
1388
1378
  isEarlyFlakeDetectionFaulty,
1389
1379
  isTestManagementTestsEnabled,
1390
- onDone,
1391
1380
  })
1392
- await flushWait
1393
1381
 
1394
1382
  startedSuites = []
1395
1383
  remainingTestsByFile = {}
@@ -2112,12 +2100,6 @@ function instrumentWorkerMainMethods (workerMain) {
2112
2100
  annotationTags = parseAnnotations(annotations)
2113
2101
  }
2114
2102
 
2115
- let onDone
2116
-
2117
- const flushPromise = new Promise(resolve => {
2118
- onDone = resolve
2119
- })
2120
-
2121
2103
  // Wait for the properties to be received, but do not block the worker forever if IPC fails.
2122
2104
  const ddPropertiesTimeoutPromise = new Promise(resolve => {
2123
2105
  const ddPropertiesTimeout = realSetTimeout(() => {
@@ -2143,7 +2125,7 @@ function instrumentWorkerMainMethods (workerMain) {
2143
2125
  testStatus: STATUS_TO_TEST_STATUS[status],
2144
2126
  })
2145
2127
 
2146
- testFinishCh.publish({
2128
+ await getChannelPromise(testFinishCh, {
2147
2129
  testStatus: STATUS_TO_TEST_STATUS[status],
2148
2130
  steps: steps.filter(step => step.testId === testId),
2149
2131
  error,
@@ -2161,14 +2143,11 @@ function instrumentWorkerMainMethods (workerMain) {
2161
2143
  hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
2162
2144
  isAtrRetry: test._ddIsAtrRetry,
2163
2145
  isModified: test._ddIsModified,
2164
- onDone,
2165
2146
  finalStatus,
2166
2147
  earlyFlakeAbortReason: test._ddEarlyFlakeAbortReason,
2167
2148
  ...testCtx.currentStore,
2168
2149
  })
2169
2150
 
2170
- await flushPromise
2171
-
2172
2151
  return res
2173
2152
  })
2174
2153
 
@@ -419,7 +419,7 @@ function createMainProcessReporter (reporterState) {
419
419
  testSuiteFinishCh.publish({
420
420
  status: getDatadogStatus(testSuiteResult),
421
421
  deferFlush: true,
422
- onFinish: noop,
422
+ onDone: noop,
423
423
  ...testSuiteCtx.currentStore,
424
424
  })
425
425
  testSuiteContexts.delete(testModuleId)
@@ -16,6 +16,7 @@ const {
16
16
  getTestSuitePath,
17
17
  isModifiedTest,
18
18
  } = require('../../dd-trace/src/plugins/util/test')
19
+ const { getChannelPromise } = require('./helpers/channel')
19
20
  const { addHook } = require('./helpers/instrument')
20
21
  const noWorkerInit = require('./vitest-main-no-worker-init')
21
22
  const {
@@ -38,7 +39,6 @@ const {
38
39
  workerReportLogsCh,
39
40
  codeCoverageReportCh,
40
41
  findExportByName,
41
- getChannelPromise,
42
42
  getTypeTasks,
43
43
  getWorkspaceProject,
44
44
  setProvidedContext,
@@ -433,7 +433,8 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
433
433
  err,
434
434
  libraryConfig,
435
435
  requestErrorTags: receivedRequestErrorTags = {},
436
- } = await getChannelPromise(libraryConfigurationCh, frameworkVersion, {
436
+ } = await getChannelPromise(libraryConfigurationCh, {
437
+ frameworkVersion,
437
438
  isVitestNoWorkerInitActive: shouldInstallNoWorkerInit,
438
439
  })
439
440
  requestErrorTags = receivedRequestErrorTags
@@ -450,7 +451,7 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
450
451
  resetMainProcessProvidedContext(ctx)
451
452
 
452
453
  if (testSessionConfigurationCh.hasSubscribers) {
453
- testSessionConfiguration = await getChannelPromise(testSessionConfigurationCh, frameworkVersion)
454
+ testSessionConfiguration = await getChannelPromise(testSessionConfigurationCh, { frameworkVersion }) || {}
454
455
  const {
455
456
  testSessionId,
456
457
  testModuleId,
@@ -494,7 +495,7 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
494
495
 
495
496
  if (isKnownTestsEnabled) {
496
497
  const currentKnownTestsResponse = knownTestsResponse || await getChannelPromise(knownTestsCh)
497
- if (currentKnownTestsResponse.err) {
498
+ if (!currentKnownTestsResponse || currentKnownTestsResponse.err) {
498
499
  isEarlyFlakeDetectionEnabled = false
499
500
  } else {
500
501
  knownTests = currentKnownTestsResponse.knownTests
@@ -538,12 +539,13 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
538
539
  }
539
540
 
540
541
  if (isTestManagementTestsEnabled) {
541
- const { err, testManagementTests: receivedTestManagementTests } =
542
+ const testManagementResponse =
542
543
  testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
543
- if (err) {
544
+ if (!testManagementResponse || testManagementResponse.err) {
544
545
  isTestManagementTestsEnabled = false
545
546
  log.error('Could not get test management tests.')
546
547
  } else {
548
+ const { testManagementTests: receivedTestManagementTests } = testManagementResponse
547
549
  testManagementTests = receivedTestManagementTests
548
550
  testManagementTestsBySuite = getTestManagementTestsBySuite(receivedTestManagementTests)
549
551
  shouldSendTestProperties = true
@@ -558,8 +560,7 @@ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, s
558
560
 
559
561
  if (isImpactedTestsEnabled) {
560
562
  const modifiedFilesResponse = await getChannelPromise(modifiedFilesCh)
561
- const { err } = modifiedFilesResponse
562
- if (err) {
563
+ if (!modifiedFilesResponse || modifiedFilesResponse.err) {
563
564
  log.error('Could not get modified tests.')
564
565
  } else {
565
566
  modifiedFiles = modifiedFilesResponse.modifiedFiles
@@ -812,18 +813,13 @@ function getFinishWrapper (exitOrClose) {
812
813
  return exitOrClose.apply(this, arguments)
813
814
  }
814
815
 
815
- let onFinish
816
-
817
- const flushPromise = new Promise(resolve => {
818
- onFinish = resolve
819
- })
820
816
  const failedSuites = this.state.getFailedFilepaths()
821
817
  let error
822
818
  if (failedSuites.length) {
823
819
  error = new Error(`Test suites failed: ${failedSuites.length}.`)
824
820
  }
825
821
 
826
- testSessionFinishCh.publish({
822
+ const flushPromise = getChannelPromise(testSessionFinishCh, {
827
823
  status: getSessionStatus(this.state),
828
824
  testCodeCoverageLinesTotal,
829
825
  error,
@@ -833,7 +829,6 @@ function getFinishWrapper (exitOrClose) {
833
829
  requestErrorTags,
834
830
  vitestPool,
835
831
  isVitestNoWorkerInitActive,
836
- onFinish,
837
832
  })
838
833
 
839
834
  logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
@@ -842,9 +837,7 @@ function getFinishWrapper (exitOrClose) {
842
837
 
843
838
  // If coverage was generated, publish coverage report channel for upload
844
839
  if (coverageRootDir && codeCoverageReportCh.hasSubscribers) {
845
- await new Promise((resolve) => {
846
- codeCoverageReportCh.publish({ rootDir: coverageRootDir, onDone: resolve })
847
- })
840
+ await getChannelPromise(codeCoverageReportCh, { rootDir: coverageRootDir })
848
841
  }
849
842
 
850
843
  return exitOrClose.apply(this, arguments)
@@ -1213,16 +1206,11 @@ async function reportTypecheckFile (file, sessionConfiguration, frameworkVersion
1213
1206
  testSuiteErrorCh.runStores(testSuiteCtx, () => {})
1214
1207
  }
1215
1208
 
1216
- let onFinish
1217
- const onFinishPromise = new Promise(resolve => {
1218
- onFinish = resolve
1219
- })
1220
- testSuiteFinishCh.publish({
1209
+ await getChannelPromise(testSuiteFinishCh, {
1210
+ frameworkVersion,
1221
1211
  status: getTypecheckTaskStatus(file),
1222
- onFinish,
1223
1212
  ...testSuiteCtx.currentStore,
1224
1213
  })
1225
- await onFinishPromise
1226
1214
  }
1227
1215
 
1228
1216
  async function reportTypecheckResults (result, frameworkVersion, ctx, typechecker) {
@@ -1234,7 +1222,7 @@ async function reportTypecheckResults (result, frameworkVersion, ctx, typechecke
1234
1222
  }
1235
1223
  const providedContext = getMainProcessProvidedContext(ctx)
1236
1224
  const sessionConfiguration = testSessionConfigurationCh.hasSubscribers
1237
- ? await getChannelPromise(testSessionConfigurationCh, frameworkVersion)
1225
+ ? await getChannelPromise(testSessionConfigurationCh, { frameworkVersion }) || {}
1238
1226
  : {}
1239
1227
 
1240
1228
  await Promise.all(result.files.map(file => reportTypecheckFile(