dd-trace 6.12.0 → 6.14.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 (251) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
  8. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  9. package/ci/test-optimization-validation/package-check.js +1 -1
  10. package/ci/test-optimization-validation/report-writer.js +7 -5
  11. package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
  12. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  13. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  14. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  15. package/index.d.ts +127 -26
  16. package/package.json +22 -19
  17. package/packages/datadog-esbuild/index.js +3 -1
  18. package/packages/datadog-instrumentations/src/bunyan.js +4 -2
  19. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  20. package/packages/datadog-instrumentations/src/fs.js +24 -20
  21. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  22. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  23. package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
  24. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  25. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  26. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  34. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
  35. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
  36. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
  37. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  38. package/packages/datadog-instrumentations/src/jest.js +165 -38
  39. package/packages/datadog-instrumentations/src/knex.js +76 -0
  40. package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
  41. package/packages/datadog-instrumentations/src/mariadb.js +13 -0
  42. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  43. package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
  44. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  45. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  46. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  47. package/packages/datadog-instrumentations/src/net.js +14 -17
  48. package/packages/datadog-instrumentations/src/next.js +212 -62
  49. package/packages/datadog-instrumentations/src/openai.js +42 -40
  50. package/packages/datadog-instrumentations/src/pg.js +15 -0
  51. package/packages/datadog-instrumentations/src/pino.js +15 -4
  52. package/packages/datadog-instrumentations/src/playwright.js +137 -27
  53. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  54. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  55. package/packages/datadog-instrumentations/src/undici.js +53 -4
  56. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
  57. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  58. package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
  59. package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
  60. package/packages/datadog-instrumentations/src/winston.js +6 -3
  61. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  62. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  63. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  64. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  65. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  66. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  67. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  68. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  69. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  70. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  71. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  72. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  73. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  74. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  75. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  76. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  77. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  78. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  79. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  80. package/packages/datadog-plugin-http/src/client.js +2 -15
  81. package/packages/datadog-plugin-http2/src/client.js +2 -15
  82. package/packages/datadog-plugin-jest/src/index.js +0 -6
  83. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  84. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  85. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  86. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  87. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  88. package/packages/datadog-plugin-next/src/index.js +63 -29
  89. package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
  90. package/packages/datadog-plugin-playwright/src/index.js +176 -28
  91. package/packages/datadog-plugin-undici/src/index.js +29 -56
  92. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  93. package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
  94. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
  95. package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
  96. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  97. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  98. package/packages/dd-trace/src/aiguard/noop.js +9 -1
  99. package/packages/dd-trace/src/aiguard/redaction.js +183 -0
  100. package/packages/dd-trace/src/aiguard/sdk.js +7 -2
  101. package/packages/dd-trace/src/aiguard/tags.js +2 -0
  102. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  103. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  104. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  107. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  108. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
  109. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  110. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
  111. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  112. package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
  113. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  114. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  115. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  116. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  117. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
  118. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  119. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
  122. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  124. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  125. package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
  126. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  127. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  128. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  129. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
  130. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  131. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  132. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  133. package/packages/dd-trace/src/config/index.js +75 -14
  134. package/packages/dd-trace/src/config/parsers.js +11 -1
  135. package/packages/dd-trace/src/config/stable.js +3 -3
  136. package/packages/dd-trace/src/config/supported-configurations.json +47 -2
  137. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  138. package/packages/dd-trace/src/debugger/config.js +12 -1
  139. package/packages/dd-trace/src/debugger/constants.js +1 -0
  140. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  141. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
  142. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
  143. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  144. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  145. package/packages/dd-trace/src/debugger/index.js +23 -3
  146. package/packages/dd-trace/src/dogstatsd.js +532 -71
  147. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  148. package/packages/dd-trace/src/encode/0.4.js +5 -5
  149. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  150. package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
  151. package/packages/dd-trace/src/exporters/agent/index.js +24 -3
  152. package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
  153. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  154. package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
  155. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  156. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  157. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  158. package/packages/dd-trace/src/exporters/common/request.js +31 -11
  159. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  160. package/packages/dd-trace/src/exporters/common/writer.js +31 -7
  161. package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
  162. package/packages/dd-trace/src/external-logger/src/index.js +4 -3
  163. package/packages/dd-trace/src/flush.js +105 -0
  164. package/packages/dd-trace/src/id.js +14 -0
  165. package/packages/dd-trace/src/index.js +5 -2
  166. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
  167. package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
  168. package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
  169. package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
  170. package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
  171. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  172. package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
  173. package/packages/dd-trace/src/llmobs/index.js +111 -48
  174. package/packages/dd-trace/src/llmobs/noop.js +2 -1
  175. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  176. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  177. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
  178. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  180. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
  181. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
  182. package/packages/dd-trace/src/llmobs/util.js +0 -33
  183. package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
  184. package/packages/dd-trace/src/log/index.js +7 -1
  185. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
  186. package/packages/dd-trace/src/noop/span.js +5 -0
  187. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  188. package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
  189. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  190. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
  191. package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
  192. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
  193. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  194. package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
  195. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
  196. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
  199. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
  200. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  201. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  202. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  203. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  204. package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
  205. package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
  206. package/packages/dd-trace/src/opentracing/span.js +28 -0
  207. package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
  208. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  209. package/packages/dd-trace/src/plugin_manager.js +2 -0
  210. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  211. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  212. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  213. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  214. package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
  215. package/packages/dd-trace/src/plugins/util/test.js +157 -41
  216. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  217. package/packages/dd-trace/src/plugins/util/web.js +6 -74
  218. package/packages/dd-trace/src/profiling/config.js +1 -0
  219. package/packages/dd-trace/src/profiling/oom.js +11 -5
  220. package/packages/dd-trace/src/profiling/profiler.js +30 -13
  221. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  222. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  223. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
  224. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  225. package/packages/dd-trace/src/proxy.js +108 -10
  226. package/packages/dd-trace/src/remote_config/index.js +319 -101
  227. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  228. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  229. package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
  230. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
  231. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
  232. package/packages/dd-trace/src/sampling_rule.js +36 -9
  233. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
  234. package/packages/dd-trace/src/serverless/vercel.js +155 -0
  235. package/packages/dd-trace/src/serverless.js +45 -21
  236. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  237. package/packages/dd-trace/src/span_stats.js +64 -31
  238. package/packages/dd-trace/src/startup-log.js +3 -2
  239. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  240. package/packages/dd-trace/src/telemetry/send-data.js +18 -16
  241. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  242. package/packages/dd-trace/src/tracer.js +26 -5
  243. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  244. package/packages/dd-trace/src/web-tags-cache.js +67 -13
  245. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  246. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  247. package/vendor/dist/protobufjs/index.js +1 -1
  248. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  249. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  250. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
  251. package/packages/dd-trace/src/encode/agentless-json.js +0 -209
@@ -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 log = require('../../dd-trace/src/log')
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')
@@ -16,6 +16,8 @@ const {
16
16
  HTTP_RESPONSE_HEADERS,
17
17
  } = tags
18
18
 
19
+ const UPGRADE_CHANNEL = 'apm:undici:request:upgrade'
20
+
19
21
  // WeakMap to store span context for native undici request objects
20
22
  const requestContexts = new WeakMap()
21
23
 
@@ -33,6 +35,7 @@ class UndiciPlugin extends HttpClientPlugin {
33
35
  this.addSub('undici:request:headers', this.#onNativeRequestHeaders.bind(this))
34
36
  this.addSub('undici:request:trailers', this.#onNativeRequestTrailers.bind(this))
35
37
  this.addSub('undici:request:error', this.#onNativeRequestError.bind(this))
38
+ this.addSub(UPGRADE_CHANNEL, this.#onNativeRequestUpgrade.bind(this))
36
39
  }
37
40
 
38
41
  // ===========================================
@@ -112,30 +115,15 @@ class UndiciPlugin extends HttpClientPlugin {
112
115
  // Store span context for request for later retrieval
113
116
  requestContexts.set(request, {
114
117
  span,
115
- store,
116
- uri,
117
118
  method,
118
119
  })
119
-
120
- // Enter the span context
121
- storage('legacy').enterWith({ ...store, span })
122
120
  }
123
121
 
124
122
  #onNativeRequestBodySent ({ request }) {
125
123
  const ctx = requestContexts.get(request)
126
124
  if (!ctx || ctx.method !== 'CONNECT') return
127
125
 
128
- const { span, store } = ctx
129
-
130
- this.config.hooks.request(span, null, null)
131
-
132
- span.finish()
133
-
134
- requestContexts.delete(request)
135
-
136
- if (store) {
137
- storage('legacy').enterWith(store)
138
- }
126
+ this.#finishNativeRequest(request)
139
127
  }
140
128
 
141
129
  #onNativeRequestHeaders ({ request, response }) {
@@ -160,50 +148,48 @@ class UndiciPlugin extends HttpClientPlugin {
160
148
  }
161
149
 
162
150
  #onNativeRequestTrailers ({ request }) {
163
- const ctx = requestContexts.get(request)
164
- if (!ctx) return
165
-
166
- const { span, store } = ctx
167
-
168
- // Call the request hook if configured
169
- this.config.hooks.request(span, null, null)
170
-
171
- // Finish the span
172
- span.finish()
151
+ this.#finishNativeRequest(request)
152
+ }
173
153
 
174
- // Clean up
175
- requestContexts.delete(request)
154
+ #onNativeRequestError ({ request, error }) {
155
+ this.#finishNativeRequest(request, error)
156
+ }
176
157
 
177
- // Restore parent store
178
- if (store) {
179
- storage('legacy').enterWith(store)
180
- }
158
+ /**
159
+ * @param {{ error?: Error, headers: unknown, request: object, statusCode: number }} context
160
+ */
161
+ #onNativeRequestUpgrade ({ error, headers, request, statusCode }) {
162
+ this.#onNativeRequestHeaders({ request, response: { headers, statusCode } })
163
+ this.#finishNativeRequest(request, error)
181
164
  }
182
165
 
183
- #onNativeRequestError ({ request, error }) {
166
+ /**
167
+ * @param {object} request
168
+ * @param {Error} [error]
169
+ */
170
+ #finishNativeRequest (request, error) {
184
171
  const ctx = requestContexts.get(request)
185
172
  if (!ctx) return
186
173
 
187
- const { span, store } = ctx
174
+ const { span } = ctx
188
175
 
189
176
  // Don't record AbortError as an error - it's user-initiated cancellation
190
177
  if (error && error.name !== 'AbortError') {
191
178
  span.setTag('error', error)
192
179
  }
193
180
 
194
- // Call the request hook if configured
195
- this.config.hooks.request(span, null, null)
181
+ const requestHook = this.config.hooks.request
182
+ if (requestHook !== noop) {
183
+ this.tracer.scope().activate(span, () => {
184
+ requestHook(span, null, null)
185
+ })
186
+ }
196
187
 
197
188
  // Finish the span
198
189
  span.finish()
199
190
 
200
191
  // Clean up
201
192
  requestContexts.delete(request)
202
-
203
- // Restore parent store
204
- if (store) {
205
- storage('legacy').enterWith(store)
206
- }
207
193
  }
208
194
 
209
195
  // ===========================================
@@ -302,7 +288,7 @@ function normalizeHeaders (headers) {
302
288
  }
303
289
 
304
290
  function normalizeConfig (config) {
305
- const validateStatus = getStatusValidator(config)
291
+ const validateStatus = getClientStatusValidator(config)
306
292
  const hooks = getHooks(config)
307
293
 
308
294
  return {
@@ -312,19 +298,6 @@ function normalizeConfig (config) {
312
298
  }
313
299
  }
314
300
 
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
301
  function getHooks (config) {
329
302
  const request = config.hooks?.request ?? noop
330
303
 
@@ -361,13 +361,13 @@ class VitestPlugin extends CiPlugin {
361
361
  [TEST_SOURCE_START]: testStartLine || 1,
362
362
  [TEST_STATUS]: 'skip',
363
363
  [TEST_FINAL_STATUS]: 'skip',
364
- ...(isAttemptToFix ? { [TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX]: 'true' } : {}),
365
- ...(isDisabled ? { [TEST_MANAGEMENT_IS_DISABLED]: 'true' } : {}),
366
- ...(isQuarantined ? { [TEST_MANAGEMENT_IS_QUARANTINED]: 'true' } : {}),
367
- ...(isNew ? { [TEST_IS_NEW]: 'true' } : {}),
368
- ...(isRumActive ? { [TEST_IS_RUM_ACTIVE]: 'true' } : {}),
369
- ...(isTestFrameworkWorker ? { [TEST_IS_TEST_FRAMEWORK_WORKER]: 'true' } : {}),
370
364
  }
365
+ if (isAttemptToFix) extraTags[TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX] = 'true'
366
+ if (isDisabled) extraTags[TEST_MANAGEMENT_IS_DISABLED] = 'true'
367
+ if (isQuarantined) extraTags[TEST_MANAGEMENT_IS_QUARANTINED] = 'true'
368
+ if (isNew) extraTags[TEST_IS_NEW] = 'true'
369
+ if (isRumActive) extraTags[TEST_IS_RUM_ACTIVE] = 'true'
370
+ if (isTestFrameworkWorker) extraTags[TEST_IS_TEST_FRAMEWORK_WORKER] = 'true'
371
371
  setBrowserTags(extraTags, {
372
372
  browserDriver,
373
373
  browserName,
@@ -523,7 +523,6 @@ class VitestPlugin extends CiPlugin {
523
523
  this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: coverageLibrary })
524
524
  this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeFiles.length)
525
525
  }
526
- this.tracer._exporter.deferTestSuiteSpan?.(testSuiteSpan)
527
526
  testSuiteSpan.finish()
528
527
  finishAllTraceSpans(testSuiteSpan)
529
528
  }
@@ -567,7 +566,6 @@ class VitestPlugin extends CiPlugin {
567
566
  this.addSub('ci:vitest:session:finish', ({
568
567
  status,
569
568
  error,
570
- isTestSessionFinalizationError,
571
569
  testCodeCoverageLinesTotal,
572
570
  isEarlyFlakeDetectionEnabled,
573
571
  isEarlyFlakeDetectionFaulty,
@@ -590,9 +588,6 @@ class VitestPlugin extends CiPlugin {
590
588
  this.testSessionSpan.setTag(TEST_STATUS, status)
591
589
  this.testModuleSpan.setTag(TEST_STATUS, status)
592
590
  if (error) {
593
- if (isTestSessionFinalizationError) {
594
- this.tracer._exporter.setDeferredTestSuiteError?.(error)
595
- }
596
591
  this.testModuleSpan.setTag('error', error)
597
592
  this.testSessionSpan.setTag('error', error)
598
593
  }
@@ -622,7 +617,6 @@ class VitestPlugin extends CiPlugin {
622
617
  if (vitestPool) {
623
618
  this.testSessionSpan.setTag(VITEST_POOL, vitestPool)
624
619
  }
625
- this.tracer._exporter.exportDeferredTestSuiteSpans?.()
626
620
  this.testModuleSpan.finish()
627
621
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'module')
628
622
  this.testSessionSpan.finish()
@@ -1,18 +1,22 @@
1
1
  'use strict'
2
2
 
3
3
  const { HTTP_CLIENT_IP, HTTP_USERAGENT, NETWORK_CLIENT_IP } = require('../../../../ext/tags')
4
- const clone = require('../../../../vendor/dist/rfdc')({ proto: false, circles: false })
4
+ const createRfdc = require('../../../../vendor/dist/rfdc')
5
+ const clone = createRfdc({ proto: false, circles: false })
5
6
  const { USER_ID, USER_SESSION_ID } = require('../appsec/addresses')
6
7
  const { getActiveRequest } = require('../appsec/store')
7
8
  const { keepTrace } = require('../priority_sampler')
8
9
  const { extractIp } = require('../plugins/util/ip_extractor')
9
10
  const { AI_GUARD } = require('../standalone/product')
10
11
  const telemetryMetrics = require('../telemetry/metrics')
12
+ const { normalizeRedactionReplacements, redactMessages } = require('./redaction')
11
13
  const TAGS = require('./tags')
12
14
 
13
15
  const ALLOW = 'ALLOW'
14
16
 
17
+ /** @typedef {import('../../../../index').aiguard.ContentPart} ContentPart */
15
18
  /** @typedef {import('../../../../index').aiguard.Message} Message */
19
+ /** @typedef {import('../../../../index').aiguard.RedactionReplacement} RedactionReplacement */
16
20
  /** @typedef {import('../opentracing/span')} Span */
17
21
 
18
22
  /**
@@ -24,19 +28,22 @@ const ALLOW = 'ALLOW'
24
28
  * @property {Record<string, unknown>} tagProbabilities
25
29
  * @property {boolean} hasTagProbabilities
26
30
  * @property {boolean} blockingEnabled
31
+ * @property {unknown} redactionReplacements
27
32
  */
28
33
 
29
34
  /**
30
35
  * @typedef {object} EvaluationOutcome
31
36
  * @property {{ action: string, reason: string|undefined, tags: Array<unknown>,
32
- * tagProbabilities: Record<string, unknown>, sds: Array<unknown> }} result
37
+ * tagProbabilities: Record<string, unknown>, sds: Array<unknown>, messages: Message[],
38
+ * redactionReplacements: RedactionReplacement[] }} result
33
39
  * @property {boolean} shouldBlock
34
40
  * @property {boolean} hasTagProbabilities
41
+ * @property {{ enabled: boolean, applied: boolean, failures: number }} redaction
35
42
  */
36
43
 
37
44
  /**
38
45
  * @typedef {object} EvaluationMetaStruct
39
- * @property {Message[]} messages
46
+ * @property {Message[]} [messages]
40
47
  * @property {Array<unknown>} [attack_categories]
41
48
  * @property {Array<unknown>} [sds]
42
49
  * @property {Record<string, unknown>} [tag_probs]
@@ -45,6 +52,7 @@ const ALLOW = 'ALLOW'
45
52
  /**
46
53
  * @typedef {object} EvaluationReport
47
54
  * @property {Span} span
55
+ * @property {Message[]} messages
48
56
  * @property {EvaluationMetaStruct} metaStruct
49
57
  * @property {{ source: string, integration: string }} telemetryTags
50
58
  */
@@ -79,6 +87,7 @@ function parseEvaluationResponse (body) {
79
87
  tagProbabilities: isRecord(attributes.tag_probs) ? attributes.tag_probs : {},
80
88
  hasTagProbabilities: isRecord(attributes.tag_probs),
81
89
  blockingEnabled: attributes.is_blocking_enabled === true,
90
+ redactionReplacements: attributes.redaction_replacements,
82
91
  }
83
92
  }
84
93
 
@@ -96,11 +105,16 @@ function shouldBlockEvaluation (block, evaluation) {
96
105
  /**
97
106
  * Applies local evaluation policy and creates the result returned to the caller.
98
107
  *
108
+ * @param {Message[]} messages
99
109
  * @param {EvaluationResponse} evaluation
100
- * @param {boolean} block
110
+ * @param {{ block: boolean, redactionEnabled: boolean }} options
101
111
  * @returns {EvaluationOutcome}
102
112
  */
103
- function createEvaluationOutcome (evaluation, block) {
113
+ function createEvaluationOutcome (messages, evaluation, options) {
114
+ const redaction = options.redactionEnabled
115
+ ? redactMessages(messages, evaluation.redactionReplacements)
116
+ : { messages, redacted: false, failures: 0 }
117
+
104
118
  return {
105
119
  result: {
106
120
  action: evaluation.action,
@@ -108,9 +122,16 @@ function createEvaluationOutcome (evaluation, block) {
108
122
  tags: evaluation.tags,
109
123
  tagProbabilities: evaluation.tagProbabilities,
110
124
  sds: evaluation.sdsFindings,
125
+ messages: redaction.messages,
126
+ redactionReplacements: normalizeRedactionReplacements(evaluation.redactionReplacements),
111
127
  },
112
- shouldBlock: shouldBlockEvaluation(block, evaluation),
128
+ shouldBlock: shouldBlockEvaluation(options.block, evaluation),
113
129
  hasTagProbabilities: evaluation.hasTagProbabilities,
130
+ redaction: {
131
+ enabled: options.redactionEnabled,
132
+ applied: redaction.redacted,
133
+ failures: redaction.failures,
134
+ },
114
135
  }
115
136
  }
116
137
 
@@ -130,6 +151,7 @@ class EvaluationReporter {
130
151
  #config
131
152
  #maxMessagesLength
132
153
  #maxContentSize
154
+ #redactionEnabled
133
155
 
134
156
  /**
135
157
  * @param {import('../config/config-base')} config
@@ -138,6 +160,7 @@ class EvaluationReporter {
138
160
  this.#config = config
139
161
  this.#maxMessagesLength = config.experimental.aiguard.maxMessagesLength
140
162
  this.#maxContentSize = config.experimental.aiguard.maxContentSize
163
+ this.#redactionEnabled = config.experimental.aiguard.redactionEnabled
141
164
  }
142
165
 
143
166
  /**
@@ -150,19 +173,18 @@ class EvaluationReporter {
150
173
  */
151
174
  start (span, messages, options) {
152
175
  const telemetryTags = { source: options.source, integration: options.integration }
153
- const last = messages.at(-1)
176
+ const evaluatedMessages = this.#redactionEnabled ? clone(messages) : messages
177
+ const last = evaluatedMessages.at(-1)
154
178
  const target = this.#isToolCall(last) ? 'tool' : 'prompt'
155
179
  span.setTag(TAGS.TARGET_TAG_KEY, target)
156
180
  if (target === 'tool') {
157
- const name = this.#getToolName(last, messages)
181
+ const name = this.#getToolName(last, evaluatedMessages)
158
182
  if (name) {
159
183
  span.setTag(TAGS.TOOL_NAME_TAG_KEY, name)
160
184
  }
161
185
  }
162
186
 
163
- const metaStruct = {
164
- messages: this.#buildMessagesForMetaStruct(messages, telemetryTags),
165
- }
187
+ const metaStruct = {}
166
188
  span.meta_struct = {
167
189
  [TAGS.META_STRUCT_KEY]: metaStruct,
168
190
  }
@@ -178,6 +200,7 @@ class EvaluationReporter {
178
200
 
179
201
  return {
180
202
  span,
203
+ messages: evaluatedMessages,
181
204
  metaStruct,
182
205
  telemetryTags,
183
206
  }
@@ -191,6 +214,7 @@ class EvaluationReporter {
191
214
  * @returns {void}
192
215
  */
193
216
  fail (report, errorType) {
217
+ report.metaStruct.messages = this.#buildMessagesForMetaStruct(report.messages, report.telemetryTags)
194
218
  aiguardMetrics.count(TAGS.TELEMETRY_REQUESTS, { error: true, ...report.telemetryTags }).inc(1)
195
219
  aiguardMetrics.count(TAGS.TELEMETRY_ERROR, { type: errorType, ...report.telemetryTags }).inc(1)
196
220
  }
@@ -203,18 +227,38 @@ class EvaluationReporter {
203
227
  * @returns {void}
204
228
  */
205
229
  finish (report, outcome) {
206
- const { result, shouldBlock } = outcome
230
+ const { result, redaction, shouldBlock } = outcome
207
231
 
208
232
  if (result.tags.length > 0) report.metaStruct.attack_categories = result.tags
209
233
  if (result.sds.length > 0) report.metaStruct.sds = result.sds
210
234
  if (outcome.hasTagProbabilities) report.metaStruct.tag_probs = result.tagProbabilities
211
235
 
212
- const requestTelemetryTags = {
213
- action: result.action,
214
- error: false,
215
- block: shouldBlock,
216
- ...report.telemetryTags,
236
+ if (redaction.enabled) {
237
+ report.span.setTag(TAGS.REDACTED_TAG_KEY, redaction.applied ? 'true' : 'false')
238
+ if (redaction.failures > 0) {
239
+ aiguardMetrics.count(TAGS.TELEMETRY_ERROR, {
240
+ type: TAGS.ERROR_TYPE_REDACTION,
241
+ ...report.telemetryTags,
242
+ }).inc(redaction.failures)
243
+ }
217
244
  }
245
+
246
+ report.metaStruct.messages = this.#buildMessagesForMetaStruct(result.messages, report.telemetryTags)
247
+
248
+ const requestTelemetryTags = redaction.enabled
249
+ ? {
250
+ action: result.action,
251
+ error: false,
252
+ block: shouldBlock,
253
+ redacted: redaction.applied,
254
+ ...report.telemetryTags,
255
+ }
256
+ : {
257
+ action: result.action,
258
+ error: false,
259
+ block: shouldBlock,
260
+ ...report.telemetryTags,
261
+ }
218
262
  aiguardMetrics.count(TAGS.TELEMETRY_REQUESTS, requestTelemetryTags).inc(1)
219
263
 
220
264
  report.span.setTag(TAGS.ACTION_TAG_KEY, result.action)
@@ -242,10 +286,7 @@ class EvaluationReporter {
242
286
  let contentTruncated = false
243
287
  for (let i = messages.length - size; i < messages.length; i++) {
244
288
  const message = clone(messages[i])
245
- if (message.content?.length > this.#maxContentSize) {
246
- contentTruncated = true
247
- message.content = message.content.slice(0, this.#maxContentSize)
248
- }
289
+ if (this.#truncateMessageContent(message)) contentTruncated = true
249
290
  result.push(message)
250
291
  }
251
292
  if (contentTruncated) {
@@ -254,6 +295,40 @@ class EvaluationReporter {
254
295
  return result
255
296
  }
256
297
 
298
+ /**
299
+ * Truncates text in a cloned message to one shared content-size limit.
300
+ *
301
+ * @param {{ content?: string|ContentPart[] }} message
302
+ * @returns {boolean}
303
+ */
304
+ #truncateMessageContent (message) {
305
+ const { content } = message
306
+ if (typeof content === 'string') {
307
+ if (content.length <= this.#maxContentSize) return false
308
+
309
+ message.content = content.slice(0, this.#maxContentSize)
310
+ return true
311
+ }
312
+
313
+ if (!Array.isArray(content)) return false
314
+
315
+ let remainingContentSize = this.#maxContentSize
316
+ let truncated = false
317
+ for (const part of content) {
318
+ const text = part?.text
319
+ if (typeof text !== 'string') continue
320
+
321
+ if (text.length > remainingContentSize) {
322
+ part.text = text.slice(0, remainingContentSize)
323
+ truncated = true
324
+ remainingContentSize = 0
325
+ } else {
326
+ remainingContentSize -= text.length
327
+ }
328
+ }
329
+ return truncated
330
+ }
331
+
257
332
  /**
258
333
  * Returns whether a message represents a tool call or tool output.
259
334
  *
@@ -74,18 +74,17 @@ function onStreamAfter (ctx) {
74
74
  */
75
75
  function getStreamContent (chunks) {
76
76
  const toolCalls = []
77
- const textParts = []
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
- textParts.push(chunk.textDelta)
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
 
@@ -208,7 +208,14 @@ function walkContentBlocks (blocks) {
208
208
  function partsToContent (parts, hasImages) {
209
209
  if (!parts.length) return
210
210
  if (hasImages) return parts
211
- return parts.map(p => p.text).join('\n')
211
+ let content = ''
212
+ let isFirstPart = true
213
+ for (const part of parts) {
214
+ if (!isFirstPart) content += '\n'
215
+ content += part.text
216
+ isFirstPart = false
217
+ }
218
+ return content
212
219
  }
213
220
 
214
221
  /**
@@ -269,27 +276,39 @@ function convertServerToolResultContent (content) {
269
276
  return content.error_message ? `${content.error_code}: ${content.error_message}` : content.error_code
270
277
  }
271
278
 
272
- const lines = []
279
+ let lines
273
280
  if (Array.isArray(content)) {
274
281
  for (const item of content) {
275
282
  if (!item || typeof item !== 'object') continue
276
283
  // text blocks (MCP / generic), then web-search title + url.
277
- if (typeof item.text === 'string') lines.push(item.text)
278
- if (typeof item.title === 'string') lines.push(item.title)
279
- if (typeof item.url === 'string') lines.push(item.url)
284
+ if (typeof item.text === 'string') {
285
+ lines = lines === undefined ? item.text : `${lines}\n${item.text}`
286
+ }
287
+ if (typeof item.title === 'string') {
288
+ lines = lines === undefined ? item.title : `${lines}\n${item.title}`
289
+ }
290
+ if (typeof item.url === 'string') {
291
+ lines = lines === undefined ? item.url : `${lines}\n${item.url}`
292
+ }
280
293
  }
281
294
  } else {
282
- if (typeof content.stdout === 'string' && content.stdout) lines.push(content.stdout)
283
- if (typeof content.stderr === 'string' && content.stderr) lines.push(content.stderr)
284
- if (typeof content.content === 'string' && content.content) lines.push(content.content)
295
+ if (typeof content.stdout === 'string' && content.stdout) lines = content.stdout
296
+ if (typeof content.stderr === 'string' && content.stderr) {
297
+ lines = lines === undefined ? content.stderr : `${lines}\n${content.stderr}`
298
+ }
299
+ if (typeof content.content === 'string' && content.content) {
300
+ lines = lines === undefined ? content.content : `${lines}\n${content.content}`
301
+ }
285
302
  if (Array.isArray(content.lines)) {
286
303
  for (const line of content.lines) {
287
- if (typeof line === 'string') lines.push(line)
304
+ if (typeof line === 'string') {
305
+ lines = lines === undefined ? line : `${lines}\n${line}`
306
+ }
288
307
  }
289
308
  }
290
309
  }
291
310
 
292
- return lines.join('\n') || '[tool result]'
311
+ return lines || '[tool result]'
293
312
  }
294
313
 
295
314
  /**
@@ -322,7 +322,14 @@ function openAIResponseContentToMessageContent (content) {
322
322
 
323
323
  if (!parts.length) return
324
324
  if (hasImages) return parts
325
- return parts.map(part => part.text).join('\n')
325
+ let textContent = ''
326
+ let isFirstPart = true
327
+ for (const part of parts) {
328
+ if (!isFirstPart) textContent += '\n'
329
+ textContent += part.text
330
+ isFirstPart = false
331
+ }
332
+ return textContent
326
333
  }
327
334
 
328
335
  /**
@@ -62,19 +62,26 @@ function convertVercelPromptToMessages (prompt) {
62
62
  if (hasImages) {
63
63
  messages.push({ role: 'user', content: contentParts })
64
64
  } else {
65
- messages.push({ role: 'user', content: contentParts.map(p => p.text).join('\n') })
65
+ let content = ''
66
+ let isFirstPart = true
67
+ for (const part of contentParts) {
68
+ if (!isFirstPart) content += '\n'
69
+ content += part.text
70
+ isFirstPart = false
71
+ }
72
+ messages.push({ role: 'user', content })
66
73
  }
67
74
  break
68
75
  }
69
76
 
70
77
  case 'assistant': {
71
- const textParts = []
78
+ let text
72
79
  const toolCalls = []
73
80
  if (!Array.isArray(msg.content)) break
74
81
 
75
82
  for (const part of msg.content) {
76
83
  if (part.type === 'text') {
77
- textParts.push(part.text)
84
+ text = text === undefined ? part.text : `${text}\n${part.text}`
78
85
  } else if (part.type === 'tool-call') {
79
86
  toolCalls.push({
80
87
  id: part.toolCallId,
@@ -88,8 +95,8 @@ function convertVercelPromptToMessages (prompt) {
88
95
 
89
96
  if (toolCalls.length > 0) {
90
97
  messages.push({ role: 'assistant', tool_calls: toolCalls })
91
- } else if (textParts.length > 0) {
92
- messages.push({ role: 'assistant', content: textParts.join('\n') })
98
+ } else if (text !== undefined) {
99
+ messages.push({ role: 'assistant', content: text })
93
100
  }
94
101
  break
95
102
  }
@@ -161,7 +168,14 @@ function buildTextOutputMessages (inputMessages, text) {
161
168
  function buildOutputMessages (inputMessages, content) {
162
169
  const toolCalls = content.filter(c => c.type === 'tool-call')
163
170
  if (toolCalls.length) return buildToolCallOutputMessages(inputMessages, toolCalls)
164
- const text = content.filter(c => c.type === 'text').map(c => c.text).join('\n')
171
+ let text = ''
172
+ let isFirstTextPart = true
173
+ for (const part of content) {
174
+ if (part.type !== 'text') continue
175
+ if (!isFirstTextPart) text += '\n'
176
+ text += part.text
177
+ isFirstTextPart = false
178
+ }
165
179
  if (text) return buildTextOutputMessages(inputMessages, text)
166
180
  return []
167
181
  }
@@ -2,7 +2,15 @@
2
2
 
3
3
  class NoopAIGuard {
4
4
  evaluate (messages, opts) {
5
- return Promise.resolve({ action: 'ALLOW', reason: 'AI Guard is not enabled', tags: [], sds: [] })
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