dd-trace 5.117.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 (83) 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 +2 -2
  45. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  46. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  47. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/openai-agents.js +31 -0
  48. package/packages/datadog-instrumentations/src/openai-agents.js +159 -0
  49. package/packages/datadog-plugin-openai-agents/src/index.js +74 -0
  50. package/packages/datadog-plugin-openai-agents/src/integration.js +503 -0
  51. package/packages/datadog-plugin-openai-agents/src/processor.js +108 -0
  52. package/packages/datadog-plugin-openai-agents/src/util.js +60 -0
  53. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +48 -49
  54. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +161 -33
  55. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +6 -4
  56. package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
  57. package/packages/dd-trace/src/config/supported-configurations.json +7 -0
  58. package/packages/dd-trace/src/encode/span-stats.js +7 -1
  59. package/packages/dd-trace/src/exporter.js +16 -9
  60. package/packages/dd-trace/src/llmobs/index.js +9 -1
  61. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +104 -22
  62. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +5 -9
  63. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +20 -0
  64. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +321 -0
  65. package/packages/dd-trace/src/llmobs/sdk.js +2 -1
  66. package/packages/dd-trace/src/llmobs/span_processor.js +7 -2
  67. package/packages/dd-trace/src/llmobs/tagger.js +23 -3
  68. package/packages/dd-trace/src/llmobs/util.js +56 -3
  69. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +3 -6
  70. package/packages/dd-trace/src/openfeature/configuration_source.js +5 -8
  71. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +5 -1
  72. package/packages/dd-trace/src/plugins/ci_plugin.js +9 -3
  73. package/packages/dd-trace/src/plugins/index.js +1 -0
  74. package/packages/dd-trace/src/plugins/outbound.js +4 -1
  75. package/packages/dd-trace/src/span_processor.js +1 -1
  76. package/packages/dd-trace/src/span_stats.js +22 -4
  77. package/ci/test-optimization-validation/command-suitability.js +0 -471
  78. package/ci/test-optimization-validation/init-probe-preload.js +0 -163
  79. package/ci/test-optimization-validation/init-probe.js +0 -246
  80. package/ci/test-optimization-validation/late-initialization.js +0 -63
  81. package/ci/test-optimization-validation/local-command.js +0 -163
  82. package/ci/test-optimization-validation/setup-runner.js +0 -97
  83. package/ci/test-optimization-validation-manifest.schema.json +0 -1
@@ -0,0 +1,86 @@
1
+ 'use strict'
2
+
3
+ function annotateResults (results) {
4
+ return results.map(result => ({
5
+ ...result,
6
+ conclusion: result.evidence?.conclusion || getConclusion(result),
7
+ domain: result.evidence?.domain || getDomain(result),
8
+ evidenceStrength: result.evidence?.evidenceStrength || getEvidenceStrength(result),
9
+ }))
10
+ }
11
+
12
+ function getExecutionStatus (results) {
13
+ if (results.some(isValidatorError)) return 'validator_error'
14
+
15
+ const conclusionResults = results.filter(result => {
16
+ const runLevelDomains = ['execution_environment', 'local_runtime', 'project_setup']
17
+ return result.scenario !== 'all' || runLevelDomains.includes(result.domain)
18
+ })
19
+ const allIncomplete = conclusionResults.length > 0 && conclusionResults.every(result => {
20
+ return result.domain === 'execution_environment' || ['not_checked', 'incomplete'].includes(result.conclusion)
21
+ })
22
+ if (!allIncomplete) return 'completed'
23
+ if (conclusionResults.some(result => ['execution_environment', 'local_runtime'].includes(result.domain))) {
24
+ return 'blocked'
25
+ }
26
+ if (conclusionResults.some(result => result.domain === 'project_setup')) return 'project_setup_required'
27
+ return 'completed'
28
+ }
29
+
30
+ function getValidatorExitCode (results, executionStatus) {
31
+ if (executionStatus === 'validator_error') return 3
32
+ if (results.some(result => result.status === 'fail' && result.evidenceStrength.startsWith('confirmed_'))) return 1
33
+ const incomplete = results.some(result => {
34
+ return ['configured_propagation_unverified', 'incomplete', 'not_checked'].includes(result.conclusion)
35
+ })
36
+ if (executionStatus !== 'completed' || incomplete || !results.some(result => result.scenario !== 'all')) return 2
37
+ return 0
38
+ }
39
+
40
+ function getValidationCoverage (results) {
41
+ return results.some(result => result.conclusion === 'not_checked' ||
42
+ result.evidence?.validationIncomplete ||
43
+ result.evidence?.manifestIncomplete)
44
+ ? 'partial'
45
+ : 'complete'
46
+ }
47
+
48
+ function getConclusion (result) {
49
+ if (result.evidence?.validationIncomplete || result.evidence?.manifestIncomplete) return 'incomplete'
50
+ if (result.status === 'pass') return 'confirmed_working'
51
+ if (result.status === 'fail') {
52
+ return result.scenario === 'ci-wiring' ? 'confirmed_misconfigured' : 'confirmed_not_working'
53
+ }
54
+ if (result.status === 'skip') {
55
+ return result.evidence?.featureEligibility?.eligible === false ? 'not_checked' : 'not_eligible'
56
+ }
57
+ return 'incomplete'
58
+ }
59
+
60
+ function getDomain (result) {
61
+ if (result.evidence?.blockedByProjectSetup) return 'project_setup'
62
+ if (result.evidence?.localRuntimeBlocked) return 'local_runtime'
63
+ if (result.evidence?.blockedByExecutionEnvironment) return 'execution_environment'
64
+ if (result.scenario === 'ci-wiring') return 'ci_configuration'
65
+ if (result.evidence?.staticDiagnosis) return 'project_setup'
66
+ if (result.status === 'blocked') return 'execution_environment'
67
+ if (result.evidence?.validatorAdapterUnavailable || result.evidence?.manifestIncomplete ||
68
+ result.frameworkId === 'validator' || result.frameworkId === 'validation-cleanup') return 'validator_adapter'
69
+ return 'test_optimization'
70
+ }
71
+
72
+ function getEvidenceStrength (result) {
73
+ if (result.status === 'pass' || result.status === 'fail') {
74
+ return result.evidence?.staticDiagnosis ? 'confirmed_static' : 'confirmed_runtime'
75
+ }
76
+ if (result.status === 'blocked') return 'confirmed_runtime'
77
+ return result.evidence?.staticDiagnosis ? 'inferred_static' : 'unknown'
78
+ }
79
+
80
+ function isValidatorError (result) {
81
+ return result.evidence?.validationOrchestrationFailed === true ||
82
+ result.frameworkId === 'validator' ||
83
+ result.frameworkId === 'validation-cleanup'
84
+ }
85
+
86
+ module.exports = { annotateResults, getExecutionStatus, getValidationCoverage, getValidatorExitCode }
@@ -0,0 +1,249 @@
1
+ 'use strict'
2
+
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
+
6
+ const cucumber = require('./framework-adapters/cucumber')
7
+ const cypress = require('./framework-adapters/cypress')
8
+ const playwright = require('./framework-adapters/playwright')
9
+ const { inheritApprovedExecutable } = require('./executable-approval')
10
+
11
+ const DEFAULT_TIMEOUT_MS = 180_000
12
+ const BROWSER_TIMEOUT_MS = 300_000
13
+ const GENERATED_SCENARIO_BY_FEATURE = {
14
+ atr: 'atr-fail-once',
15
+ efd: 'basic-pass',
16
+ 'test-management': 'test-management-target',
17
+ }
18
+
19
+ /**
20
+ * Builds the validator-owned direct command for an existing representative test.
21
+ *
22
+ * @param {object} framework normalized framework manifest entry
23
+ * @returns {object} structured direct-runner command
24
+ */
25
+ function getBasicCommand (framework) {
26
+ return buildCommand(framework, framework.validation.testFile, false)
27
+ }
28
+
29
+ /**
30
+ * Builds the validator-owned direct command for a generated scenario.
31
+ *
32
+ * @param {object} framework normalized framework manifest entry
33
+ * @param {object} scenario generated scenario
34
+ * @returns {object} structured direct-runner command
35
+ */
36
+ function getGeneratedCommand (framework, scenario) {
37
+ return buildCommand(framework, scenario.testIdentities[0].file, true)
38
+ }
39
+
40
+ /**
41
+ * Returns every command the approval permits for one framework.
42
+ *
43
+ * @param {object} framework normalized framework manifest entry
44
+ * @param {string|null} [requestedScenario] selected validator scenario
45
+ * @returns {Array<[string, object]>} labeled direct-runner commands
46
+ */
47
+ function getFrameworkCommands (framework, requestedScenario = null) {
48
+ if (framework.status !== 'runnable') return []
49
+ if (requestedScenario === 'ci-wiring') return []
50
+
51
+ const commands = [['basic-reporting', getBasicCommand(framework)]]
52
+ for (const scenario of framework.generatedTestStrategy?.scenarios || []) {
53
+ if (!shouldIncludeGeneratedScenario(scenario.id, requestedScenario)) continue
54
+ commands.push([`generated:${scenario.id}`, getGeneratedCommand(framework, scenario)])
55
+ }
56
+ return commands
57
+ }
58
+
59
+ /**
60
+ * Returns every command covered by a validation manifest.
61
+ *
62
+ * @param {object} manifest normalized validation manifest
63
+ * @param {string|null} [requestedScenario] selected validator scenario
64
+ * @returns {Array<[string, object]>} labeled direct-runner commands
65
+ */
66
+ function getManifestCommands (manifest, requestedScenario = null) {
67
+ const commands = []
68
+ for (const framework of manifest.frameworks || []) {
69
+ for (const [label, command] of getFrameworkCommands(framework, requestedScenario)) {
70
+ commands.push([`${framework.id}:${label}`, command])
71
+ }
72
+ }
73
+ return commands
74
+ }
75
+
76
+ /**
77
+ * Returns existing project files that affect direct validation.
78
+ *
79
+ * @param {object} manifest normalized validation manifest
80
+ * @param {object} [options] file selection options
81
+ * @param {boolean} [options.includeLocal] include local runner inputs
82
+ * @returns {string[]} sorted unique absolute paths
83
+ */
84
+ function getManifestInputFiles (manifest, { includeLocal = true } = {}) {
85
+ const files = new Set()
86
+ for (const framework of manifest.frameworks || []) {
87
+ addExistingFile(files, framework.ciWiring?.configFile)
88
+ if (!includeLocal) {
89
+ addExistingFile(files, framework.project?.packageJson)
90
+ continue
91
+ }
92
+ if (framework.status !== 'runnable') continue
93
+ addExistingFile(files, framework.validation?.runner)
94
+ addExistingFile(files, framework.validation?.testFile)
95
+ addExistingFile(files, framework.project?.packageJson)
96
+ for (const filename of framework.project?.configFiles || []) addExistingFile(files, filename)
97
+ }
98
+ return [...files].sort()
99
+ }
100
+
101
+ /**
102
+ * Returns whether a generated scenario can execute for the selected feature.
103
+ *
104
+ * @param {string} scenarioId generated scenario id
105
+ * @param {string|null} requestedScenario selected validator scenario
106
+ * @returns {boolean} whether to include the command
107
+ */
108
+ function shouldIncludeGeneratedScenario (scenarioId, requestedScenario) {
109
+ if (!requestedScenario) return true
110
+ return GENERATED_SCENARIO_BY_FEATURE[requestedScenario] === scenarioId
111
+ }
112
+
113
+ /**
114
+ * Builds one direct Node.js runner invocation.
115
+ *
116
+ * @param {object} framework normalized framework manifest entry
117
+ * @param {string} testFile selected existing or generated test file
118
+ * @param {boolean} generated whether this is a validator-owned generated test
119
+ * @returns {object} structured command
120
+ */
121
+ function buildCommand (framework, testFile, generated) {
122
+ const { runner, requiredEnvVars = [], timeoutMs } = framework.validation
123
+ const outputPaths = framework.framework === 'playwright'
124
+ ? [playwright.getOutputPath(testFile)]
125
+ : []
126
+
127
+ return inheritApprovedExecutable(framework.validation, {
128
+ argv: [process.execPath, runner, ...getRunnerArgs(framework, testFile, generated)],
129
+ cwd: framework.project.root,
130
+ description: `${formatFrameworkName(framework.framework)} ${generated ? 'generated' : 'representative'} test`,
131
+ env: getRunnerEnv(framework),
132
+ outputPaths,
133
+ requiredEnvVars,
134
+ timeoutMs: timeoutMs || (framework.browserRequired ? BROWSER_TIMEOUT_MS : DEFAULT_TIMEOUT_MS),
135
+ usesShell: false,
136
+ })
137
+ }
138
+
139
+ /**
140
+ * Returns adapter-owned arguments for one exact test file.
141
+ *
142
+ * @param {object} framework normalized framework manifest entry
143
+ * @param {string} testFile selected existing or generated test file
144
+ * @param {boolean} generated whether this is a validator-owned generated test
145
+ * @returns {string[]} runner arguments
146
+ */
147
+ function getRunnerArgs (framework, testFile, generated) {
148
+ const name = framework.framework
149
+ const configuration = framework.validation.runnerArgs || []
150
+ if (name === 'cucumber') {
151
+ if (!generated) return [...configuration, ...cucumber.getFocusedTestArgs(testFile)]
152
+ return [
153
+ ...configuration,
154
+ ...cucumber.getGeneratedTestArgs(
155
+ testFile,
156
+ cucumber.getGeneratedStepsPath(framework.generatedTestStrategy.testDirectory)
157
+ ),
158
+ ]
159
+ }
160
+ if (name === 'cypress') {
161
+ if (generated) return cypress.getGeneratedTestArgs(testFile, configuration)
162
+ return [
163
+ 'run',
164
+ ...configuration,
165
+ ...cypress.getFocusedTestArgs(testFile),
166
+ '--config',
167
+ 'video=false,screenshotOnRunFailure=false',
168
+ ]
169
+ }
170
+ if (name === 'playwright') {
171
+ if (generated) {
172
+ return playwright.getGeneratedTestArgs(
173
+ testFile,
174
+ playwright.getGeneratedConfigPath(framework.generatedTestStrategy.testDirectory)
175
+ )
176
+ }
177
+ return ['test', ...configuration, ...playwright.getFocusedTestArgs(testFile)]
178
+ }
179
+ if (name === 'jest') {
180
+ const generatedOverrides = []
181
+ if (generated && configuration.some(argument => {
182
+ return argument === '--detectLeaks' || argument === '--detectLeaks=true'
183
+ })) {
184
+ generatedOverrides.push('--detectLeaks=false')
185
+ }
186
+ return [
187
+ ...configuration,
188
+ '--runTestsByPath',
189
+ testFile,
190
+ '--runInBand',
191
+ '--silent',
192
+ '--no-watchman',
193
+ ...generatedOverrides,
194
+ ]
195
+ }
196
+ if (name === 'mocha') {
197
+ return [...configuration, '--no-config', '--no-package', '--no-opts', '--reporter', 'spec', testFile]
198
+ }
199
+ if (name === 'vitest') {
200
+ const needsGlobals = generated && framework.generatedTestStrategy.moduleSystem === 'commonjs'
201
+ return ['run', ...configuration, testFile, ...(needsGlobals ? ['--globals'] : [])]
202
+ }
203
+ throw new Error(`No direct-runner adapter is available for ${name}.`)
204
+ }
205
+
206
+ /**
207
+ * Returns small deterministic environment adjustments owned by an adapter.
208
+ *
209
+ * @param {object} framework framework entry
210
+ * @returns {Record<string, string>} command environment
211
+ */
212
+ function getRunnerEnv (framework) {
213
+ return {
214
+ ...(framework.validation.environment || {}),
215
+ ...(framework.framework === 'cucumber' ? { CUCUMBER_PUBLISH_QUIET: 'true' } : {}),
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Adds an existing regular file to an approval input set.
221
+ *
222
+ * @param {Set<string>} files collected files
223
+ * @param {string|undefined} filename candidate file
224
+ * @returns {void}
225
+ */
226
+ function addExistingFile (files, filename) {
227
+ if (typeof filename !== 'string') return
228
+ try {
229
+ if (fs.statSync(filename).isFile()) files.add(path.resolve(filename))
230
+ } catch {}
231
+ }
232
+
233
+ /**
234
+ * Formats a framework name for command descriptions.
235
+ *
236
+ * @param {string} framework framework name
237
+ * @returns {string} display name
238
+ */
239
+ function formatFrameworkName (framework) {
240
+ return framework.charAt(0).toUpperCase() + framework.slice(1)
241
+ }
242
+
243
+ module.exports = {
244
+ getBasicCommand,
245
+ getFrameworkCommands,
246
+ getGeneratedCommand,
247
+ getManifestCommands,
248
+ getManifestInputFiles,
249
+ }