dd-trace 6.11.0 → 6.13.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.
- package/LICENSE-3rdparty.csv +5 -0
- package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/index.d.ts +143 -25
- package/package.json +26 -25
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/cucumber.js +4 -4
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +28 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/jest.js +140 -18
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +7 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +5 -2
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright-reporter.js +149 -1
- package/packages/datadog-instrumentations/src/playwright.js +67 -53
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +13 -12
- package/packages/datadog-instrumentations/src/vitest-worker.js +11 -0
- package/packages/datadog-instrumentations/src/webdriverio.js +26 -0
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +13 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -15
- package/packages/dd-trace/src/aiguard/evaluation.js +94 -20
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +21 -9
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/reporter.js +2 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
- package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
- package/packages/dd-trace/src/carrier.js +107 -48
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +51 -26
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
- package/packages/dd-trace/src/ci-visibility/final-flush.js +20 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +196 -29
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +4 -2
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -0
- package/packages/dd-trace/src/config/index.js +2 -1
- package/packages/dd-trace/src/config/supported-configurations.json +27 -0
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -10
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -3
- package/packages/dd-trace/src/debugger/index.js +4 -2
- package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
- package/packages/dd-trace/src/dogstatsd.js +45 -18
- package/packages/dd-trace/src/encode/agentless-json.js +5 -4
- package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
- package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
- package/packages/dd-trace/src/evp_proxy/path.js +22 -0
- package/packages/dd-trace/src/exporters/agent/index.js +22 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/request.js +5 -2
- package/packages/dd-trace/src/exporters/common/writer.js +24 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +102 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +10 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +301 -14
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +440 -112
- package/packages/dd-trace/src/llmobs/experiments/index.js +68 -44
- package/packages/dd-trace/src/llmobs/experiments/noop.js +103 -12
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +81 -4
- package/packages/dd-trace/src/llmobs/index.js +80 -17
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/writers/base.js +60 -24
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
- package/packages/dd-trace/src/openfeature/index.js +7 -4
- package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
- package/packages/dd-trace/src/openfeature/writers/exposures.js +70 -15
- package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +4 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +3 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +429 -352
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +74 -21
- package/packages/dd-trace/src/plugins/util/web.js +2 -65
- package/packages/dd-trace/src/profiling/oom.js +4 -3
- package/packages/dd-trace/src/profiling/profiler.js +7 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/proxy.js +36 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -6
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +55 -0
- package/packages/dd-trace/src/serverless/vercel.js +122 -0
- package/packages/dd-trace/src/serverless.js +37 -20
- package/packages/dd-trace/src/span_stats.js +40 -3
- package/packages/dd-trace/src/telemetry/metrics.js +62 -8
- package/packages/dd-trace/src/telemetry/send-data.js +1 -0
- package/packages/dd-trace/src/tracer.js +22 -0
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/https-proxy-agent/LICENSE +22 -0
- package/vendor/dist/https-proxy-agent/index.js +4 -0
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/proxy-from-env/LICENSE +20 -0
- package/vendor/dist/proxy-from-env/index.js +1 -0
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
|
|
17
17
|
TEST_SUITE_EXECUTION_ID,
|
|
18
18
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
19
|
+
const { publishWithCompletion } = require('./helpers/channel')
|
|
19
20
|
const { addHook, channel, tracingChannel } = require('./helpers/instrument')
|
|
20
21
|
const {
|
|
21
22
|
CONFIGURATION_REQUEST,
|
|
@@ -38,6 +39,7 @@ const testSuiteStartCh = channel('ci:mocha:test-suite:start')
|
|
|
38
39
|
const testSuiteFinishCh = channel('ci:mocha:test-suite:finish')
|
|
39
40
|
const knownTestsCh = channel('ci:mocha:known-tests')
|
|
40
41
|
const libraryConfigurationCh = channel('ci:mocha:library-configuration')
|
|
42
|
+
const logSubmissionFlushCh = channel('ci:log-submission:flush')
|
|
41
43
|
const modifiedFilesCh = channel('ci:mocha:modified-files')
|
|
42
44
|
const testManagementTestsCh = channel('ci:mocha:test-management-tests')
|
|
43
45
|
const workerConfigurationCh = channel('ci:mocha:worker:configuration')
|
|
@@ -46,6 +48,7 @@ const workerReportTelemetryCh = channel('ci:mocha:worker-report:telemetry')
|
|
|
46
48
|
const workerReportTraceCh = channel('ci:mocha:worker-report:trace')
|
|
47
49
|
|
|
48
50
|
const jasmineAdapterInitCh = tracingChannel('orchestrion:@wdio/jasmine-framework:JasmineAdapter_init')
|
|
51
|
+
const baseReporterWaitForSyncCh = tracingChannel('orchestrion:@wdio/runner:BaseReporter_waitForSync')
|
|
49
52
|
const launcherStartInstanceCh = tracingChannel('orchestrion:@wdio/cli:Launcher_startInstance')
|
|
50
53
|
const localRunnerRunCh = tracingChannel('orchestrion:@wdio/local-runner:LocalRunner_run')
|
|
51
54
|
const localRunnerShutdownCh = tracingChannel('orchestrion:@wdio/local-runner:LocalRunner_shutdown')
|
|
@@ -1000,6 +1003,29 @@ function finishCoordinator (state, error, onDone) {
|
|
|
1000
1003
|
})
|
|
1001
1004
|
}
|
|
1002
1005
|
|
|
1006
|
+
/**
|
|
1007
|
+
* Delays WebdriverIO worker exit until pending log-submission requests settle.
|
|
1008
|
+
*
|
|
1009
|
+
* @param {{
|
|
1010
|
+
* resolveCallback?: (onDone: () => void) => void,
|
|
1011
|
+
* rejectCallback?: (onDone: () => void) => void
|
|
1012
|
+
* }} context
|
|
1013
|
+
* @returns {void}
|
|
1014
|
+
*/
|
|
1015
|
+
function waitForLogSubmissionAtWorkerExit (context) {
|
|
1016
|
+
if (!isWebdriverioWorker || !logSubmissionFlushCh.hasSubscribers) {
|
|
1017
|
+
return
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
const waitForLogs = onDone => publishWithCompletion(logSubmissionFlushCh, {}, onDone)
|
|
1021
|
+
context.resolveCallback = waitForLogs
|
|
1022
|
+
context.rejectCallback = waitForLogs
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
baseReporterWaitForSyncCh.asyncEnd.subscribe(
|
|
1026
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForLogSubmissionAtWorkerExit)
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1003
1029
|
// dc-polyfill supports partial tracing-channel subscribers, unlike the Node.js type definition.
|
|
1004
1030
|
// @ts-expect-error
|
|
1005
1031
|
jasmineAdapterInitCh.subscribe({
|
|
@@ -12,12 +12,15 @@ const patched = new WeakSet()
|
|
|
12
12
|
const configureCh = channel('ci:log-submission:winston:configure')
|
|
13
13
|
const addTransport = channel('ci:log-submission:winston:add-transport')
|
|
14
14
|
|
|
15
|
-
addHook({ name: 'winston',
|
|
15
|
+
addHook({ name: 'winston', versions: ['>=3'] }, winston => {
|
|
16
16
|
if (configureCh.hasSubscribers) {
|
|
17
|
-
configureCh.publish(
|
|
17
|
+
configureCh.publish({
|
|
18
|
+
createJsonFormat: winston.format.json,
|
|
19
|
+
StreamTransport: winston.transports.Stream,
|
|
20
|
+
})
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
return
|
|
23
|
+
return winston
|
|
21
24
|
})
|
|
22
25
|
|
|
23
26
|
addHook({ name: 'winston', file: 'lib/winston/logger.js', versions: ['>=3'] }, Logger => {
|
|
@@ -8,8 +8,8 @@ const tags = require('../../../ext/tags')
|
|
|
8
8
|
const formats = require('../../../ext/formats')
|
|
9
9
|
const HTTP_HEADERS = formats.HTTP_HEADERS
|
|
10
10
|
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
|
|
11
|
+
const { getClientStatusValidator } = require('../../dd-trace/src/plugins/util/status-validator')
|
|
11
12
|
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
|
|
12
|
-
const log = require('../../dd-trace/src/log')
|
|
13
13
|
const { stripQueryAndFragment } = require('../../dd-trace/src/util')
|
|
14
14
|
const { CLIENT_PORT_KEY, COMPONENT, ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')
|
|
15
15
|
|
|
@@ -167,7 +167,7 @@ function addRequestHeaders (req, span, config) {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
function normalizeClientConfig (config) {
|
|
170
|
-
const validateStatus =
|
|
170
|
+
const validateStatus = getClientStatusValidator(config)
|
|
171
171
|
const filter = getFilter(config)
|
|
172
172
|
const propagationFilter = getFilter({ blocklist: config.propagationBlocklist })
|
|
173
173
|
const headers = getHeaders(config)
|
|
@@ -183,19 +183,6 @@ function normalizeClientConfig (config) {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
function is400ErrorCode (code) {
|
|
187
|
-
return code < 400 || code >= 500
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function getStatusValidator (config) {
|
|
191
|
-
if (typeof config.validateStatus === 'function') {
|
|
192
|
-
return config.validateStatus
|
|
193
|
-
} else if (config.hasOwnProperty('validateStatus')) {
|
|
194
|
-
log.error('Expected `validateStatus` to be a function.')
|
|
195
|
-
}
|
|
196
|
-
return is400ErrorCode
|
|
197
|
-
}
|
|
198
|
-
|
|
199
186
|
function getFilter (config) {
|
|
200
187
|
config = { ...config, blocklist: config.blocklist || [] }
|
|
201
188
|
|
|
@@ -4,12 +4,12 @@ const URL = require('url').URL
|
|
|
4
4
|
|
|
5
5
|
const { storage } = require('../../datadog-core')
|
|
6
6
|
const ClientPlugin = require('../../dd-trace/src/plugins/client')
|
|
7
|
-
const log = require('../../dd-trace/src/log')
|
|
8
7
|
const tags = require('../../../ext/tags')
|
|
9
8
|
const kinds = require('../../../ext/kinds')
|
|
10
9
|
const formats = require('../../../ext/formats')
|
|
11
10
|
const { COMPONENT, CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
12
11
|
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
|
|
12
|
+
const { getClientStatusValidator } = require('../../dd-trace/src/plugins/util/status-validator')
|
|
13
13
|
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
|
|
14
14
|
|
|
15
15
|
const HTTP_HEADERS = formats.HTTP_HEADERS
|
|
@@ -165,21 +165,8 @@ function hasAmazonSignature (headers, path) {
|
|
|
165
165
|
return false
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
function is400ErrorCode (code) {
|
|
169
|
-
return code < 400 || code >= 500
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function getStatusValidator (config) {
|
|
173
|
-
if (typeof config.validateStatus === 'function') {
|
|
174
|
-
return config.validateStatus
|
|
175
|
-
} else if (config.hasOwnProperty('validateStatus')) {
|
|
176
|
-
log.error('Expected `validateStatus` to be a function.')
|
|
177
|
-
}
|
|
178
|
-
return is400ErrorCode
|
|
179
|
-
}
|
|
180
|
-
|
|
181
168
|
function normalizeConfig (config) {
|
|
182
|
-
const validateStatus =
|
|
169
|
+
const validateStatus = getClientStatusValidator(config)
|
|
183
170
|
const filter = getFilter(config)
|
|
184
171
|
const headers = getHeaders(config)
|
|
185
172
|
|
|
@@ -63,6 +63,7 @@ class OpenAIAgentsIntegration {
|
|
|
63
63
|
#tracer
|
|
64
64
|
#config
|
|
65
65
|
#enabled = false
|
|
66
|
+
#llmobsEnabled = true
|
|
66
67
|
#service
|
|
67
68
|
/**
|
|
68
69
|
* LLMObs is gated independently of APM tracing: when DD_LLMOBS_ENABLED is
|
|
@@ -95,10 +96,11 @@ class OpenAIAgentsIntegration {
|
|
|
95
96
|
/**
|
|
96
97
|
* Apply plugin lifecycle configuration.
|
|
97
98
|
*
|
|
98
|
-
* @param {{ enabled?: boolean, service?: string }} [config]
|
|
99
|
+
* @param {{ enabled?: boolean, llmobs?: boolean, service?: string }} [config]
|
|
99
100
|
*/
|
|
100
101
|
configure (config) {
|
|
101
102
|
this.#enabled = !!config?.enabled
|
|
103
|
+
this.#llmobsEnabled = config?.llmobs !== false
|
|
102
104
|
this.#service = config?.service
|
|
103
105
|
}
|
|
104
106
|
|
|
@@ -210,15 +212,17 @@ class OpenAIAgentsIntegration {
|
|
|
210
212
|
completionRequested: false,
|
|
211
213
|
})
|
|
212
214
|
|
|
213
|
-
this.#
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
215
|
+
if (this.#isLLMObsEnabled()) {
|
|
216
|
+
this.#tagger.registerLLMObsSpan(ddSpan, {
|
|
217
|
+
kind: 'workflow',
|
|
218
|
+
name,
|
|
219
|
+
integration: COMPONENT,
|
|
220
|
+
parent: llmobsParentStore?.span,
|
|
221
|
+
sessionId: oaiTrace.groupId || undefined,
|
|
222
|
+
})
|
|
223
|
+
if (LLMObsTagger.tagMap.has(ddSpan)) {
|
|
224
|
+
llmobsStorage.enterWith({ ...llmobsParentStore, span: ddSpan })
|
|
225
|
+
}
|
|
222
226
|
}
|
|
223
227
|
}
|
|
224
228
|
|
|
@@ -680,7 +684,7 @@ class OpenAIAgentsIntegration {
|
|
|
680
684
|
* @returns {boolean}
|
|
681
685
|
*/
|
|
682
686
|
#isLLMObsEnabled () {
|
|
683
|
-
return !!this.#config.llmobs?.DD_LLMOBS_ENABLED
|
|
687
|
+
return this.#llmobsEnabled && !!this.#config.llmobs?.DD_LLMOBS_ENABLED
|
|
684
688
|
}
|
|
685
689
|
|
|
686
690
|
/**
|
|
@@ -523,7 +523,15 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
523
523
|
|
|
524
524
|
finishAllTraceSpans(span)
|
|
525
525
|
if (this._tracerConfig.DD_PLAYWRIGHT_WORKER) {
|
|
526
|
-
|
|
526
|
+
try {
|
|
527
|
+
this.tracer._exporter.flush(onDone)
|
|
528
|
+
} catch (error) {
|
|
529
|
+
// A synchronous flush failure must still release the worker, otherwise it hangs.
|
|
530
|
+
// Log rather than rethrow: an exception from this diagnostics-channel subscriber
|
|
531
|
+
// would surface as an uncaught error in the worker and crash the user's test run.
|
|
532
|
+
onDone?.()
|
|
533
|
+
log.error('Error flushing traces at Playwright worker shutdown', error)
|
|
534
|
+
}
|
|
527
535
|
}
|
|
528
536
|
})
|
|
529
537
|
|
|
@@ -534,6 +542,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
534
542
|
testSourceLine,
|
|
535
543
|
browserName,
|
|
536
544
|
isNew,
|
|
545
|
+
isAttemptToFix,
|
|
537
546
|
isDisabled,
|
|
538
547
|
isModified,
|
|
539
548
|
isQuarantined,
|
|
@@ -559,6 +568,9 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
559
568
|
if (isNew) {
|
|
560
569
|
span.setTag(TEST_IS_NEW, 'true')
|
|
561
570
|
}
|
|
571
|
+
if (isAttemptToFix) {
|
|
572
|
+
span.setTag(TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX, 'true')
|
|
573
|
+
}
|
|
562
574
|
if (isDisabled) {
|
|
563
575
|
span.setTag(TEST_MANAGEMENT_IS_DISABLED, 'true')
|
|
564
576
|
}
|
|
@@ -5,7 +5,7 @@ const { storage } = require('../../datadog-core')
|
|
|
5
5
|
const tags = require('../../../ext/tags')
|
|
6
6
|
const formats = require('../../../ext/formats')
|
|
7
7
|
const HTTP_HEADERS = formats.HTTP_HEADERS
|
|
8
|
-
const
|
|
8
|
+
const { getClientStatusValidator } = require('../../dd-trace/src/plugins/util/status-validator')
|
|
9
9
|
const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
|
|
10
10
|
const { stripQueryAndFragment } = require('../../dd-trace/src/util')
|
|
11
11
|
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
@@ -302,7 +302,7 @@ function normalizeHeaders (headers) {
|
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
function normalizeConfig (config) {
|
|
305
|
-
const validateStatus =
|
|
305
|
+
const validateStatus = getClientStatusValidator(config)
|
|
306
306
|
const hooks = getHooks(config)
|
|
307
307
|
|
|
308
308
|
return {
|
|
@@ -312,19 +312,6 @@ function normalizeConfig (config) {
|
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
function getStatusValidator (config) {
|
|
316
|
-
if (typeof config.validateStatus === 'function') {
|
|
317
|
-
return config.validateStatus
|
|
318
|
-
} else if (Object.hasOwn(config, 'validateStatus')) {
|
|
319
|
-
log.error('Expected `validateStatus` to be a function.')
|
|
320
|
-
}
|
|
321
|
-
return defaultValidateStatus
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function defaultValidateStatus (code) {
|
|
325
|
-
return code < 400 || code >= 500
|
|
326
|
-
}
|
|
327
|
-
|
|
328
315
|
function getHooks (config) {
|
|
329
316
|
const request = config.hooks?.request ?? noop
|
|
330
317
|
|
|
@@ -8,11 +8,14 @@ const { keepTrace } = require('../priority_sampler')
|
|
|
8
8
|
const { extractIp } = require('../plugins/util/ip_extractor')
|
|
9
9
|
const { AI_GUARD } = require('../standalone/product')
|
|
10
10
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
11
|
+
const { normalizeRedactionReplacements, redactMessages } = require('./redaction')
|
|
11
12
|
const TAGS = require('./tags')
|
|
12
13
|
|
|
13
14
|
const ALLOW = 'ALLOW'
|
|
14
15
|
|
|
16
|
+
/** @typedef {import('../../../../index').aiguard.ContentPart} ContentPart */
|
|
15
17
|
/** @typedef {import('../../../../index').aiguard.Message} Message */
|
|
18
|
+
/** @typedef {import('../../../../index').aiguard.RedactionReplacement} RedactionReplacement */
|
|
16
19
|
/** @typedef {import('../opentracing/span')} Span */
|
|
17
20
|
|
|
18
21
|
/**
|
|
@@ -24,19 +27,22 @@ const ALLOW = 'ALLOW'
|
|
|
24
27
|
* @property {Record<string, unknown>} tagProbabilities
|
|
25
28
|
* @property {boolean} hasTagProbabilities
|
|
26
29
|
* @property {boolean} blockingEnabled
|
|
30
|
+
* @property {unknown} redactionReplacements
|
|
27
31
|
*/
|
|
28
32
|
|
|
29
33
|
/**
|
|
30
34
|
* @typedef {object} EvaluationOutcome
|
|
31
35
|
* @property {{ action: string, reason: string|undefined, tags: Array<unknown>,
|
|
32
|
-
* tagProbabilities: Record<string, unknown>, sds: Array<unknown
|
|
36
|
+
* tagProbabilities: Record<string, unknown>, sds: Array<unknown>, messages: Message[],
|
|
37
|
+
* redactionReplacements: RedactionReplacement[] }} result
|
|
33
38
|
* @property {boolean} shouldBlock
|
|
34
39
|
* @property {boolean} hasTagProbabilities
|
|
40
|
+
* @property {{ enabled: boolean, applied: boolean, failures: number }} redaction
|
|
35
41
|
*/
|
|
36
42
|
|
|
37
43
|
/**
|
|
38
44
|
* @typedef {object} EvaluationMetaStruct
|
|
39
|
-
* @property {Message[]} messages
|
|
45
|
+
* @property {Message[]} [messages]
|
|
40
46
|
* @property {Array<unknown>} [attack_categories]
|
|
41
47
|
* @property {Array<unknown>} [sds]
|
|
42
48
|
* @property {Record<string, unknown>} [tag_probs]
|
|
@@ -45,6 +51,7 @@ const ALLOW = 'ALLOW'
|
|
|
45
51
|
/**
|
|
46
52
|
* @typedef {object} EvaluationReport
|
|
47
53
|
* @property {Span} span
|
|
54
|
+
* @property {Message[]} messages
|
|
48
55
|
* @property {EvaluationMetaStruct} metaStruct
|
|
49
56
|
* @property {{ source: string, integration: string }} telemetryTags
|
|
50
57
|
*/
|
|
@@ -79,6 +86,7 @@ function parseEvaluationResponse (body) {
|
|
|
79
86
|
tagProbabilities: isRecord(attributes.tag_probs) ? attributes.tag_probs : {},
|
|
80
87
|
hasTagProbabilities: isRecord(attributes.tag_probs),
|
|
81
88
|
blockingEnabled: attributes.is_blocking_enabled === true,
|
|
89
|
+
redactionReplacements: attributes.redaction_replacements,
|
|
82
90
|
}
|
|
83
91
|
}
|
|
84
92
|
|
|
@@ -96,11 +104,16 @@ function shouldBlockEvaluation (block, evaluation) {
|
|
|
96
104
|
/**
|
|
97
105
|
* Applies local evaluation policy and creates the result returned to the caller.
|
|
98
106
|
*
|
|
107
|
+
* @param {Message[]} messages
|
|
99
108
|
* @param {EvaluationResponse} evaluation
|
|
100
|
-
* @param {boolean}
|
|
109
|
+
* @param {{ block: boolean, redactionEnabled: boolean }} options
|
|
101
110
|
* @returns {EvaluationOutcome}
|
|
102
111
|
*/
|
|
103
|
-
function createEvaluationOutcome (evaluation,
|
|
112
|
+
function createEvaluationOutcome (messages, evaluation, options) {
|
|
113
|
+
const redaction = options.redactionEnabled
|
|
114
|
+
? redactMessages(messages, evaluation.redactionReplacements)
|
|
115
|
+
: { messages, redacted: false, failures: 0 }
|
|
116
|
+
|
|
104
117
|
return {
|
|
105
118
|
result: {
|
|
106
119
|
action: evaluation.action,
|
|
@@ -108,9 +121,16 @@ function createEvaluationOutcome (evaluation, block) {
|
|
|
108
121
|
tags: evaluation.tags,
|
|
109
122
|
tagProbabilities: evaluation.tagProbabilities,
|
|
110
123
|
sds: evaluation.sdsFindings,
|
|
124
|
+
messages: redaction.messages,
|
|
125
|
+
redactionReplacements: normalizeRedactionReplacements(evaluation.redactionReplacements),
|
|
111
126
|
},
|
|
112
|
-
shouldBlock: shouldBlockEvaluation(block, evaluation),
|
|
127
|
+
shouldBlock: shouldBlockEvaluation(options.block, evaluation),
|
|
113
128
|
hasTagProbabilities: evaluation.hasTagProbabilities,
|
|
129
|
+
redaction: {
|
|
130
|
+
enabled: options.redactionEnabled,
|
|
131
|
+
applied: redaction.redacted,
|
|
132
|
+
failures: redaction.failures,
|
|
133
|
+
},
|
|
114
134
|
}
|
|
115
135
|
}
|
|
116
136
|
|
|
@@ -130,6 +150,7 @@ class EvaluationReporter {
|
|
|
130
150
|
#config
|
|
131
151
|
#maxMessagesLength
|
|
132
152
|
#maxContentSize
|
|
153
|
+
#redactionEnabled
|
|
133
154
|
|
|
134
155
|
/**
|
|
135
156
|
* @param {import('../config/config-base')} config
|
|
@@ -138,6 +159,7 @@ class EvaluationReporter {
|
|
|
138
159
|
this.#config = config
|
|
139
160
|
this.#maxMessagesLength = config.experimental.aiguard.maxMessagesLength
|
|
140
161
|
this.#maxContentSize = config.experimental.aiguard.maxContentSize
|
|
162
|
+
this.#redactionEnabled = config.experimental.aiguard.redactionEnabled
|
|
141
163
|
}
|
|
142
164
|
|
|
143
165
|
/**
|
|
@@ -150,19 +172,18 @@ class EvaluationReporter {
|
|
|
150
172
|
*/
|
|
151
173
|
start (span, messages, options) {
|
|
152
174
|
const telemetryTags = { source: options.source, integration: options.integration }
|
|
153
|
-
const
|
|
175
|
+
const evaluatedMessages = this.#redactionEnabled ? clone(messages) : messages
|
|
176
|
+
const last = evaluatedMessages.at(-1)
|
|
154
177
|
const target = this.#isToolCall(last) ? 'tool' : 'prompt'
|
|
155
178
|
span.setTag(TAGS.TARGET_TAG_KEY, target)
|
|
156
179
|
if (target === 'tool') {
|
|
157
|
-
const name = this.#getToolName(last,
|
|
180
|
+
const name = this.#getToolName(last, evaluatedMessages)
|
|
158
181
|
if (name) {
|
|
159
182
|
span.setTag(TAGS.TOOL_NAME_TAG_KEY, name)
|
|
160
183
|
}
|
|
161
184
|
}
|
|
162
185
|
|
|
163
|
-
const metaStruct = {
|
|
164
|
-
messages: this.#buildMessagesForMetaStruct(messages, telemetryTags),
|
|
165
|
-
}
|
|
186
|
+
const metaStruct = {}
|
|
166
187
|
span.meta_struct = {
|
|
167
188
|
[TAGS.META_STRUCT_KEY]: metaStruct,
|
|
168
189
|
}
|
|
@@ -178,6 +199,7 @@ class EvaluationReporter {
|
|
|
178
199
|
|
|
179
200
|
return {
|
|
180
201
|
span,
|
|
202
|
+
messages: evaluatedMessages,
|
|
181
203
|
metaStruct,
|
|
182
204
|
telemetryTags,
|
|
183
205
|
}
|
|
@@ -191,6 +213,7 @@ class EvaluationReporter {
|
|
|
191
213
|
* @returns {void}
|
|
192
214
|
*/
|
|
193
215
|
fail (report, errorType) {
|
|
216
|
+
report.metaStruct.messages = this.#buildMessagesForMetaStruct(report.messages, report.telemetryTags)
|
|
194
217
|
aiguardMetrics.count(TAGS.TELEMETRY_REQUESTS, { error: true, ...report.telemetryTags }).inc(1)
|
|
195
218
|
aiguardMetrics.count(TAGS.TELEMETRY_ERROR, { type: errorType, ...report.telemetryTags }).inc(1)
|
|
196
219
|
}
|
|
@@ -203,18 +226,38 @@ class EvaluationReporter {
|
|
|
203
226
|
* @returns {void}
|
|
204
227
|
*/
|
|
205
228
|
finish (report, outcome) {
|
|
206
|
-
const { result, shouldBlock } = outcome
|
|
229
|
+
const { result, redaction, shouldBlock } = outcome
|
|
207
230
|
|
|
208
231
|
if (result.tags.length > 0) report.metaStruct.attack_categories = result.tags
|
|
209
232
|
if (result.sds.length > 0) report.metaStruct.sds = result.sds
|
|
210
233
|
if (outcome.hasTagProbabilities) report.metaStruct.tag_probs = result.tagProbabilities
|
|
211
234
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
235
|
+
if (redaction.enabled) {
|
|
236
|
+
report.span.setTag(TAGS.REDACTED_TAG_KEY, redaction.applied ? 'true' : 'false')
|
|
237
|
+
if (redaction.failures > 0) {
|
|
238
|
+
aiguardMetrics.count(TAGS.TELEMETRY_ERROR, {
|
|
239
|
+
type: TAGS.ERROR_TYPE_REDACTION,
|
|
240
|
+
...report.telemetryTags,
|
|
241
|
+
}).inc(redaction.failures)
|
|
242
|
+
}
|
|
217
243
|
}
|
|
244
|
+
|
|
245
|
+
report.metaStruct.messages = this.#buildMessagesForMetaStruct(result.messages, report.telemetryTags)
|
|
246
|
+
|
|
247
|
+
const requestTelemetryTags = redaction.enabled
|
|
248
|
+
? {
|
|
249
|
+
action: result.action,
|
|
250
|
+
error: false,
|
|
251
|
+
block: shouldBlock,
|
|
252
|
+
redacted: redaction.applied,
|
|
253
|
+
...report.telemetryTags,
|
|
254
|
+
}
|
|
255
|
+
: {
|
|
256
|
+
action: result.action,
|
|
257
|
+
error: false,
|
|
258
|
+
block: shouldBlock,
|
|
259
|
+
...report.telemetryTags,
|
|
260
|
+
}
|
|
218
261
|
aiguardMetrics.count(TAGS.TELEMETRY_REQUESTS, requestTelemetryTags).inc(1)
|
|
219
262
|
|
|
220
263
|
report.span.setTag(TAGS.ACTION_TAG_KEY, result.action)
|
|
@@ -242,10 +285,7 @@ class EvaluationReporter {
|
|
|
242
285
|
let contentTruncated = false
|
|
243
286
|
for (let i = messages.length - size; i < messages.length; i++) {
|
|
244
287
|
const message = clone(messages[i])
|
|
245
|
-
if (
|
|
246
|
-
contentTruncated = true
|
|
247
|
-
message.content = message.content.slice(0, this.#maxContentSize)
|
|
248
|
-
}
|
|
288
|
+
if (this.#truncateMessageContent(message)) contentTruncated = true
|
|
249
289
|
result.push(message)
|
|
250
290
|
}
|
|
251
291
|
if (contentTruncated) {
|
|
@@ -254,6 +294,40 @@ class EvaluationReporter {
|
|
|
254
294
|
return result
|
|
255
295
|
}
|
|
256
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Truncates text in a cloned message to one shared content-size limit.
|
|
299
|
+
*
|
|
300
|
+
* @param {{ content?: string|ContentPart[] }} message
|
|
301
|
+
* @returns {boolean}
|
|
302
|
+
*/
|
|
303
|
+
#truncateMessageContent (message) {
|
|
304
|
+
const { content } = message
|
|
305
|
+
if (typeof content === 'string') {
|
|
306
|
+
if (content.length <= this.#maxContentSize) return false
|
|
307
|
+
|
|
308
|
+
message.content = content.slice(0, this.#maxContentSize)
|
|
309
|
+
return true
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (!Array.isArray(content)) return false
|
|
313
|
+
|
|
314
|
+
let remainingContentSize = this.#maxContentSize
|
|
315
|
+
let truncated = false
|
|
316
|
+
for (const part of content) {
|
|
317
|
+
const text = part?.text
|
|
318
|
+
if (typeof text !== 'string') continue
|
|
319
|
+
|
|
320
|
+
if (text.length > remainingContentSize) {
|
|
321
|
+
part.text = text.slice(0, remainingContentSize)
|
|
322
|
+
truncated = true
|
|
323
|
+
remainingContentSize = 0
|
|
324
|
+
} else {
|
|
325
|
+
remainingContentSize -= text.length
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return truncated
|
|
329
|
+
}
|
|
330
|
+
|
|
257
331
|
/**
|
|
258
332
|
* Returns whether a message represents a tool call or tool output.
|
|
259
333
|
*
|
|
@@ -74,18 +74,17 @@ function onStreamAfter (ctx) {
|
|
|
74
74
|
*/
|
|
75
75
|
function getStreamContent (chunks) {
|
|
76
76
|
const toolCalls = []
|
|
77
|
-
|
|
77
|
+
let text = ''
|
|
78
78
|
|
|
79
79
|
for (const chunk of chunks) {
|
|
80
80
|
if (chunk?.type === 'tool-call') {
|
|
81
81
|
toolCalls.push(chunk)
|
|
82
82
|
} else if (chunk?.type === 'text-delta') {
|
|
83
|
-
|
|
83
|
+
text += chunk.delta ?? chunk.textDelta ?? ''
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
if (toolCalls.length) return toolCalls
|
|
88
|
-
const text = textParts.join('')
|
|
89
88
|
return text ? [{ type: 'text', text }] : []
|
|
90
89
|
}
|
|
91
90
|
|
|
@@ -269,27 +269,39 @@ function convertServerToolResultContent (content) {
|
|
|
269
269
|
return content.error_message ? `${content.error_code}: ${content.error_message}` : content.error_code
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
|
|
272
|
+
let lines
|
|
273
273
|
if (Array.isArray(content)) {
|
|
274
274
|
for (const item of content) {
|
|
275
275
|
if (!item || typeof item !== 'object') continue
|
|
276
276
|
// text blocks (MCP / generic), then web-search title + url.
|
|
277
|
-
if (typeof item.text === 'string')
|
|
278
|
-
|
|
279
|
-
|
|
277
|
+
if (typeof item.text === 'string') {
|
|
278
|
+
lines = lines === undefined ? item.text : `${lines}\n${item.text}`
|
|
279
|
+
}
|
|
280
|
+
if (typeof item.title === 'string') {
|
|
281
|
+
lines = lines === undefined ? item.title : `${lines}\n${item.title}`
|
|
282
|
+
}
|
|
283
|
+
if (typeof item.url === 'string') {
|
|
284
|
+
lines = lines === undefined ? item.url : `${lines}\n${item.url}`
|
|
285
|
+
}
|
|
280
286
|
}
|
|
281
287
|
} else {
|
|
282
|
-
if (typeof content.stdout === 'string' && content.stdout) lines
|
|
283
|
-
if (typeof content.stderr === 'string' && content.stderr)
|
|
284
|
-
|
|
288
|
+
if (typeof content.stdout === 'string' && content.stdout) lines = content.stdout
|
|
289
|
+
if (typeof content.stderr === 'string' && content.stderr) {
|
|
290
|
+
lines = lines === undefined ? content.stderr : `${lines}\n${content.stderr}`
|
|
291
|
+
}
|
|
292
|
+
if (typeof content.content === 'string' && content.content) {
|
|
293
|
+
lines = lines === undefined ? content.content : `${lines}\n${content.content}`
|
|
294
|
+
}
|
|
285
295
|
if (Array.isArray(content.lines)) {
|
|
286
296
|
for (const line of content.lines) {
|
|
287
|
-
if (typeof line === 'string')
|
|
297
|
+
if (typeof line === 'string') {
|
|
298
|
+
lines = lines === undefined ? line : `${lines}\n${line}`
|
|
299
|
+
}
|
|
288
300
|
}
|
|
289
301
|
}
|
|
290
302
|
}
|
|
291
303
|
|
|
292
|
-
return lines
|
|
304
|
+
return lines || '[tool result]'
|
|
293
305
|
}
|
|
294
306
|
|
|
295
307
|
/**
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
class NoopAIGuard {
|
|
4
4
|
evaluate (messages, opts) {
|
|
5
|
-
return Promise.resolve({
|
|
5
|
+
return Promise.resolve({
|
|
6
|
+
action: 'ALLOW',
|
|
7
|
+
reason: 'AI Guard is not enabled',
|
|
8
|
+
tags: [],
|
|
9
|
+
tagProbabilities: {},
|
|
10
|
+
sds: [],
|
|
11
|
+
messages,
|
|
12
|
+
redactionReplacements: [],
|
|
13
|
+
})
|
|
6
14
|
}
|
|
7
15
|
}
|
|
8
16
|
|