observability-toolkit 1.8.5 → 2.1.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 (1168) hide show
  1. package/README.md +167 -281
  2. package/dist/__tests__/find-constant-dedup.test.d.ts +11 -0
  3. package/dist/__tests__/find-constant-dedup.test.d.ts.map +1 -0
  4. package/dist/__tests__/find-constant-dedup.test.js +132 -0
  5. package/dist/__tests__/find-constant-dedup.test.js.map +1 -0
  6. package/dist/backends/backend-schemas.d.ts +309 -0
  7. package/dist/backends/backend-schemas.d.ts.map +1 -0
  8. package/dist/backends/backend-schemas.js +215 -0
  9. package/dist/backends/backend-schemas.js.map +1 -0
  10. package/dist/backends/cloud.d.ts +46 -0
  11. package/dist/backends/cloud.d.ts.map +1 -0
  12. package/dist/backends/cloud.js +520 -0
  13. package/dist/backends/cloud.js.map +1 -0
  14. package/dist/backends/cloud.test.d.ts +2 -0
  15. package/dist/backends/cloud.test.d.ts.map +1 -0
  16. package/dist/backends/cloud.test.js +436 -0
  17. package/dist/backends/cloud.test.js.map +1 -0
  18. package/dist/backends/index.d.ts +672 -236
  19. package/dist/backends/index.d.ts.map +1 -1
  20. package/dist/backends/index.js +334 -0
  21. package/dist/backends/index.js.map +1 -1
  22. package/dist/backends/index.test.js +606 -31
  23. package/dist/backends/index.test.js.map +1 -1
  24. package/dist/backends/local-jsonl-boolean-search.test.js +8 -7
  25. package/dist/backends/local-jsonl-boolean-search.test.js.map +1 -1
  26. package/dist/backends/local-jsonl-cache.test.js +33 -31
  27. package/dist/backends/local-jsonl-cache.test.js.map +1 -1
  28. package/dist/backends/local-jsonl-circuit-breaker.test.js +9 -7
  29. package/dist/backends/local-jsonl-circuit-breaker.test.js.map +1 -1
  30. package/dist/backends/local-jsonl-export.test.js +73 -58
  31. package/dist/backends/local-jsonl-export.test.js.map +1 -1
  32. package/dist/backends/local-jsonl-index.test.js +52 -50
  33. package/dist/backends/local-jsonl-index.test.js.map +1 -1
  34. package/dist/backends/local-jsonl-logs.test.js +47 -31
  35. package/dist/backends/local-jsonl-logs.test.js.map +1 -1
  36. package/dist/backends/local-jsonl-metrics.test.js +85 -82
  37. package/dist/backends/local-jsonl-metrics.test.js.map +1 -1
  38. package/dist/backends/local-jsonl-otlp-unwrap.test.d.ts +2 -0
  39. package/dist/backends/local-jsonl-otlp-unwrap.test.d.ts.map +1 -0
  40. package/dist/backends/local-jsonl-otlp-unwrap.test.js +602 -0
  41. package/dist/backends/local-jsonl-otlp-unwrap.test.js.map +1 -0
  42. package/dist/backends/local-jsonl-traces.test.js +161 -147
  43. package/dist/backends/local-jsonl-traces.test.js.map +1 -1
  44. package/dist/backends/local-jsonl.d.ts +64 -5
  45. package/dist/backends/local-jsonl.d.ts.map +1 -1
  46. package/dist/backends/local-jsonl.js +1821 -612
  47. package/dist/backends/local-jsonl.js.map +1 -1
  48. package/dist/backends/shared.d.ts +9 -0
  49. package/dist/backends/shared.d.ts.map +1 -0
  50. package/dist/backends/shared.js +9 -0
  51. package/dist/backends/shared.js.map +1 -0
  52. package/dist/generated/opentelemetry/proto/collector/logs/v1/logs_service_pb.d.ts +40 -0
  53. package/dist/generated/opentelemetry/proto/collector/logs/v1/logs_service_pb.d.ts.map +1 -0
  54. package/dist/generated/opentelemetry/proto/collector/logs/v1/logs_service_pb.js +27 -0
  55. package/dist/generated/opentelemetry/proto/collector/logs/v1/logs_service_pb.js.map +1 -0
  56. package/dist/generated/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.d.ts +106 -0
  57. package/dist/generated/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.d.ts.map +1 -0
  58. package/dist/generated/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.js +43 -0
  59. package/dist/generated/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.js.map +1 -0
  60. package/dist/generated/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb.d.ts +111 -0
  61. package/dist/generated/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb.d.ts.map +1 -0
  62. package/dist/generated/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb.js +42 -0
  63. package/dist/generated/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb.js.map +1 -0
  64. package/dist/generated/opentelemetry/proto/collector/trace/v1/trace_service_pb.d.ts +106 -0
  65. package/dist/generated/opentelemetry/proto/collector/trace/v1/trace_service_pb.d.ts.map +1 -0
  66. package/dist/generated/opentelemetry/proto/collector/trace/v1/trace_service_pb.js +43 -0
  67. package/dist/generated/opentelemetry/proto/collector/trace/v1/trace_service_pb.js.map +1 -0
  68. package/dist/generated/opentelemetry/proto/common/v1/common_pb.d.ts +243 -0
  69. package/dist/generated/opentelemetry/proto/common/v1/common_pb.d.ts.map +1 -0
  70. package/dist/generated/opentelemetry/proto/common/v1/common_pb.js +49 -0
  71. package/dist/generated/opentelemetry/proto/common/v1/common_pb.js.map +1 -0
  72. package/dist/generated/opentelemetry/proto/logs/v1/logs_pb.d.ts +90 -0
  73. package/dist/generated/opentelemetry/proto/logs/v1/logs_pb.d.ts.map +1 -0
  74. package/dist/generated/opentelemetry/proto/logs/v1/logs_pb.js +66 -0
  75. package/dist/generated/opentelemetry/proto/logs/v1/logs_pb.js.map +1 -0
  76. package/dist/generated/opentelemetry/proto/metrics/v1/metrics_pb.d.ts +1134 -0
  77. package/dist/generated/opentelemetry/proto/metrics/v1/metrics_pb.d.ts.map +1 -0
  78. package/dist/generated/opentelemetry/proto/metrics/v1/metrics_pb.js +223 -0
  79. package/dist/generated/opentelemetry/proto/metrics/v1/metrics_pb.js.map +1 -0
  80. package/dist/generated/opentelemetry/proto/profiles/v1development/profiles_pb.d.ts +678 -0
  81. package/dist/generated/opentelemetry/proto/profiles/v1development/profiles_pb.d.ts.map +1 -0
  82. package/dist/generated/opentelemetry/proto/profiles/v1development/profiles_pb.js +107 -0
  83. package/dist/generated/opentelemetry/proto/profiles/v1development/profiles_pb.js.map +1 -0
  84. package/dist/generated/opentelemetry/proto/resource/v1/resource_pb.d.ts +46 -0
  85. package/dist/generated/opentelemetry/proto/resource/v1/resource_pb.d.ts.map +1 -0
  86. package/dist/generated/opentelemetry/proto/resource/v1/resource_pb.js +25 -0
  87. package/dist/generated/opentelemetry/proto/resource/v1/resource_pb.js.map +1 -0
  88. package/dist/generated/opentelemetry/proto/trace/v1/trace_pb.d.ts +569 -0
  89. package/dist/generated/opentelemetry/proto/trace/v1/trace_pb.d.ts.map +1 -0
  90. package/dist/generated/opentelemetry/proto/trace/v1/trace_pb.js +195 -0
  91. package/dist/generated/opentelemetry/proto/trace/v1/trace_pb.js.map +1 -0
  92. package/dist/lib/agent-judge/agent-as-judge.d.ts +157 -0
  93. package/dist/lib/agent-judge/agent-as-judge.d.ts.map +1 -0
  94. package/dist/lib/agent-judge/agent-as-judge.js +137 -0
  95. package/dist/lib/agent-judge/agent-as-judge.js.map +1 -0
  96. package/dist/lib/agent-judge/agent-as-judge.test.d.ts +5 -0
  97. package/dist/lib/agent-judge/agent-as-judge.test.d.ts.map +1 -0
  98. package/dist/lib/agent-judge/agent-as-judge.test.js +839 -0
  99. package/dist/lib/agent-judge/agent-as-judge.test.js.map +1 -0
  100. package/dist/lib/agent-judge/agent-eval-metrics.d.ts +293 -0
  101. package/dist/lib/agent-judge/agent-eval-metrics.d.ts.map +1 -0
  102. package/dist/lib/agent-judge/agent-eval-metrics.js +715 -0
  103. package/dist/lib/agent-judge/agent-eval-metrics.js.map +1 -0
  104. package/dist/lib/agent-judge/agent-eval-metrics.test.d.ts +5 -0
  105. package/dist/lib/agent-judge/agent-eval-metrics.test.d.ts.map +1 -0
  106. package/dist/lib/agent-judge/agent-eval-metrics.test.js +676 -0
  107. package/dist/lib/agent-judge/agent-eval-metrics.test.js.map +1 -0
  108. package/dist/lib/agent-judge/agent-judge-classes.d.ts +95 -0
  109. package/dist/lib/agent-judge/agent-judge-classes.d.ts.map +1 -0
  110. package/dist/lib/agent-judge/agent-judge-classes.js +222 -0
  111. package/dist/lib/agent-judge/agent-judge-classes.js.map +1 -0
  112. package/dist/lib/agent-judge/agent-judge-classes.test.d.ts +6 -0
  113. package/dist/lib/agent-judge/agent-judge-classes.test.d.ts.map +1 -0
  114. package/dist/lib/agent-judge/agent-judge-classes.test.js +271 -0
  115. package/dist/lib/agent-judge/agent-judge-classes.test.js.map +1 -0
  116. package/dist/lib/agent-judge/agent-judge-consensus.d.ts +58 -0
  117. package/dist/lib/agent-judge/agent-judge-consensus.d.ts.map +1 -0
  118. package/dist/lib/agent-judge/agent-judge-consensus.js +149 -0
  119. package/dist/lib/agent-judge/agent-judge-consensus.js.map +1 -0
  120. package/dist/lib/agent-judge/agent-judge-consensus.test.d.ts +2 -0
  121. package/dist/lib/agent-judge/agent-judge-consensus.test.d.ts.map +1 -0
  122. package/dist/lib/agent-judge/agent-judge-consensus.test.js +170 -0
  123. package/dist/lib/agent-judge/agent-judge-consensus.test.js.map +1 -0
  124. package/dist/lib/agent-judge/agent-judge-verification.d.ts +89 -0
  125. package/dist/lib/agent-judge/agent-judge-verification.d.ts.map +1 -0
  126. package/dist/lib/agent-judge/agent-judge-verification.js +235 -0
  127. package/dist/lib/agent-judge/agent-judge-verification.js.map +1 -0
  128. package/dist/lib/agent-judge/agent-judge-verification.test.d.ts +5 -0
  129. package/dist/lib/agent-judge/agent-judge-verification.test.d.ts.map +1 -0
  130. package/dist/lib/agent-judge/agent-judge-verification.test.js +399 -0
  131. package/dist/lib/agent-judge/agent-judge-verification.test.js.map +1 -0
  132. package/dist/lib/audit/agent-auditor-scoring.d.ts +167 -0
  133. package/dist/lib/audit/agent-auditor-scoring.d.ts.map +1 -0
  134. package/dist/lib/audit/agent-auditor-scoring.js +338 -0
  135. package/dist/lib/audit/agent-auditor-scoring.js.map +1 -0
  136. package/dist/lib/audit/agent-auditor-scoring.test.d.ts +2 -0
  137. package/dist/lib/audit/agent-auditor-scoring.test.d.ts.map +1 -0
  138. package/dist/lib/audit/agent-auditor-scoring.test.js +576 -0
  139. package/dist/lib/audit/agent-auditor-scoring.test.js.map +1 -0
  140. package/dist/lib/audit/audit-record.d.ts +139 -0
  141. package/dist/lib/audit/audit-record.d.ts.map +1 -0
  142. package/dist/lib/audit/audit-record.js +288 -0
  143. package/dist/lib/audit/audit-record.js.map +1 -0
  144. package/dist/lib/audit/audit-record.test.d.ts +5 -0
  145. package/dist/lib/audit/audit-record.test.d.ts.map +1 -0
  146. package/dist/lib/audit/audit-record.test.js +258 -0
  147. package/dist/lib/audit/audit-record.test.js.map +1 -0
  148. package/dist/lib/audit/audit-scoring-constants.d.ts +57 -0
  149. package/dist/lib/audit/audit-scoring-constants.d.ts.map +1 -0
  150. package/dist/lib/audit/audit-scoring-constants.js +59 -0
  151. package/dist/lib/audit/audit-scoring-constants.js.map +1 -0
  152. package/dist/lib/audit/compliance-report.d.ts +125 -0
  153. package/dist/lib/audit/compliance-report.d.ts.map +1 -0
  154. package/dist/lib/audit/compliance-report.js +205 -0
  155. package/dist/lib/audit/compliance-report.js.map +1 -0
  156. package/dist/lib/audit/compliance-report.test.d.ts +5 -0
  157. package/dist/lib/audit/compliance-report.test.d.ts.map +1 -0
  158. package/dist/lib/audit/compliance-report.test.js +290 -0
  159. package/dist/lib/audit/compliance-report.test.js.map +1 -0
  160. package/dist/lib/audit/retention-guard.d.ts +41 -0
  161. package/dist/lib/audit/retention-guard.d.ts.map +1 -0
  162. package/dist/lib/audit/retention-guard.js +103 -0
  163. package/dist/lib/audit/retention-guard.js.map +1 -0
  164. package/dist/lib/audit/retention-guard.test.d.ts +5 -0
  165. package/dist/lib/audit/retention-guard.test.d.ts.map +1 -0
  166. package/dist/lib/audit/retention-guard.test.js +109 -0
  167. package/dist/lib/audit/retention-guard.test.js.map +1 -0
  168. package/dist/lib/audit/skill-auditor-scoring.d.ts +69 -0
  169. package/dist/lib/audit/skill-auditor-scoring.d.ts.map +1 -0
  170. package/dist/lib/audit/skill-auditor-scoring.js +149 -0
  171. package/dist/lib/audit/skill-auditor-scoring.js.map +1 -0
  172. package/dist/lib/audit/skill-auditor-scoring.test.d.ts +2 -0
  173. package/dist/lib/audit/skill-auditor-scoring.test.d.ts.map +1 -0
  174. package/dist/lib/audit/skill-auditor-scoring.test.js +369 -0
  175. package/dist/lib/audit/skill-auditor-scoring.test.js.map +1 -0
  176. package/dist/lib/audit/verification-events.d.ts +119 -0
  177. package/dist/lib/audit/verification-events.d.ts.map +1 -0
  178. package/dist/lib/audit/verification-events.js +175 -0
  179. package/dist/lib/audit/verification-events.js.map +1 -0
  180. package/dist/lib/audit/verification-events.test.d.ts +5 -0
  181. package/dist/lib/audit/verification-events.test.d.ts.map +1 -0
  182. package/dist/lib/audit/verification-events.test.js +197 -0
  183. package/dist/lib/audit/verification-events.test.js.map +1 -0
  184. package/dist/lib/core/constants-models.d.ts +90 -0
  185. package/dist/lib/core/constants-models.d.ts.map +1 -0
  186. package/dist/lib/core/constants-models.js +208 -0
  187. package/dist/lib/core/constants-models.js.map +1 -0
  188. package/dist/lib/core/constants-otel.d.ts +68 -0
  189. package/dist/lib/core/constants-otel.d.ts.map +1 -0
  190. package/dist/lib/core/constants-otel.js +128 -0
  191. package/dist/lib/core/constants-otel.js.map +1 -0
  192. package/dist/lib/core/constants-symlink.test.d.ts.map +1 -0
  193. package/dist/lib/{constants-symlink.test.js → core/constants-symlink.test.js} +25 -24
  194. package/dist/lib/core/constants-symlink.test.js.map +1 -0
  195. package/dist/lib/core/constants-telemetry.d.ts +21 -0
  196. package/dist/lib/core/constants-telemetry.d.ts.map +1 -0
  197. package/dist/lib/core/constants-telemetry.js +162 -0
  198. package/dist/lib/core/constants-telemetry.js.map +1 -0
  199. package/dist/lib/core/constants.d.ts +152 -0
  200. package/dist/lib/core/constants.d.ts.map +1 -0
  201. package/dist/lib/core/constants.js +223 -0
  202. package/dist/lib/core/constants.js.map +1 -0
  203. package/dist/lib/core/constants.test.d.ts.map +1 -0
  204. package/dist/lib/{constants.test.js → core/constants.test.js} +198 -82
  205. package/dist/lib/core/constants.test.js.map +1 -0
  206. package/dist/lib/core/doc-sync.test.d.ts +9 -0
  207. package/dist/lib/core/doc-sync.test.d.ts.map +1 -0
  208. package/dist/lib/core/doc-sync.test.js +159 -0
  209. package/dist/lib/core/doc-sync.test.js.map +1 -0
  210. package/dist/lib/core/edge-cases.test.d.ts.map +1 -0
  211. package/dist/lib/{edge-cases.test.js → core/edge-cases.test.js} +76 -73
  212. package/dist/lib/core/edge-cases.test.js.map +1 -0
  213. package/dist/lib/{file-utils.d.ts → core/file-utils.d.ts} +63 -8
  214. package/dist/lib/core/file-utils.d.ts.map +1 -0
  215. package/dist/lib/{file-utils.js → core/file-utils.js} +186 -93
  216. package/dist/lib/core/file-utils.js.map +1 -0
  217. package/dist/lib/core/file-utils.test-constants.d.ts +38 -0
  218. package/dist/lib/core/file-utils.test-constants.d.ts.map +1 -0
  219. package/dist/lib/core/file-utils.test-constants.js +40 -0
  220. package/dist/lib/core/file-utils.test-constants.js.map +1 -0
  221. package/dist/lib/core/file-utils.test.d.ts.map +1 -0
  222. package/dist/lib/{file-utils.test.js → core/file-utils.test.js} +240 -214
  223. package/dist/lib/core/file-utils.test.js.map +1 -0
  224. package/dist/lib/{input-validator.d.ts → core/input-validator.d.ts} +30 -20
  225. package/dist/lib/core/input-validator.d.ts.map +1 -0
  226. package/dist/lib/core/input-validator.fuzz.test.d.ts.map +1 -0
  227. package/dist/lib/{input-validator.fuzz.test.js → core/input-validator.fuzz.test.js} +41 -29
  228. package/dist/lib/core/input-validator.fuzz.test.js.map +1 -0
  229. package/dist/lib/{input-validator.js → core/input-validator.js} +83 -39
  230. package/dist/lib/core/input-validator.js.map +1 -0
  231. package/dist/lib/core/input-validator.test.d.ts.map +1 -0
  232. package/dist/lib/{input-validator.test.js → core/input-validator.test.js} +95 -45
  233. package/dist/lib/core/input-validator.test.js.map +1 -0
  234. package/dist/lib/{logger.d.ts → core/logger.d.ts} +4 -18
  235. package/dist/lib/core/logger.d.ts.map +1 -0
  236. package/dist/lib/core/logger.js +104 -0
  237. package/dist/lib/core/logger.js.map +1 -0
  238. package/dist/lib/core/logger.test.d.ts.map +1 -0
  239. package/dist/lib/core/logger.test.js.map +1 -0
  240. package/dist/lib/core/schema-types.d.ts +37 -0
  241. package/dist/lib/core/schema-types.d.ts.map +1 -0
  242. package/dist/lib/core/schema-types.js +29 -0
  243. package/dist/lib/core/schema-types.js.map +1 -0
  244. package/dist/lib/{server-utils.d.ts → core/server-utils.d.ts} +11 -1
  245. package/dist/lib/core/server-utils.d.ts.map +1 -0
  246. package/dist/lib/{server-utils.js → core/server-utils.js} +25 -5
  247. package/dist/lib/core/server-utils.js.map +1 -0
  248. package/dist/lib/core/shared-schemas.d.ts +301 -0
  249. package/dist/lib/core/shared-schemas.d.ts.map +1 -0
  250. package/dist/lib/core/shared-schemas.js +222 -0
  251. package/dist/lib/core/shared-schemas.js.map +1 -0
  252. package/dist/lib/core/shared-schemas.test.d.ts.map +1 -0
  253. package/dist/lib/{shared-schemas.test.js → core/shared-schemas.test.js} +48 -18
  254. package/dist/lib/core/shared-schemas.test.js.map +1 -0
  255. package/dist/lib/core/units.d.ts +67 -0
  256. package/dist/lib/core/units.d.ts.map +1 -0
  257. package/dist/lib/core/units.js +88 -0
  258. package/dist/lib/core/units.js.map +1 -0
  259. package/dist/lib/cost/cost-estimation.d.ts +264 -0
  260. package/dist/lib/cost/cost-estimation.d.ts.map +1 -0
  261. package/dist/lib/cost/cost-estimation.js +541 -0
  262. package/dist/lib/cost/cost-estimation.js.map +1 -0
  263. package/dist/lib/cost/cost-estimation.test.d.ts +5 -0
  264. package/dist/lib/cost/cost-estimation.test.d.ts.map +1 -0
  265. package/dist/lib/cost/cost-estimation.test.js +701 -0
  266. package/dist/lib/cost/cost-estimation.test.js.map +1 -0
  267. package/dist/lib/cost/pricing-cache.d.ts +59 -0
  268. package/dist/lib/cost/pricing-cache.d.ts.map +1 -0
  269. package/dist/lib/cost/pricing-cache.js +120 -0
  270. package/dist/lib/cost/pricing-cache.js.map +1 -0
  271. package/dist/lib/cost/pricing-cache.test.d.ts +5 -0
  272. package/dist/lib/cost/pricing-cache.test.d.ts.map +1 -0
  273. package/dist/lib/cost/pricing-cache.test.js +176 -0
  274. package/dist/lib/cost/pricing-cache.test.js.map +1 -0
  275. package/dist/lib/dashboard-file-utils.d.ts +35 -0
  276. package/dist/lib/dashboard-file-utils.d.ts.map +1 -0
  277. package/dist/lib/dashboard-file-utils.js +94 -0
  278. package/dist/lib/dashboard-file-utils.js.map +1 -0
  279. package/dist/lib/{error-sanitizer.d.ts → errors/error-sanitizer.d.ts} +5 -0
  280. package/dist/lib/errors/error-sanitizer.d.ts.map +1 -0
  281. package/dist/lib/{error-sanitizer.js → errors/error-sanitizer.js} +8 -6
  282. package/dist/lib/errors/error-sanitizer.js.map +1 -0
  283. package/dist/lib/errors/error-sanitizer.test.d.ts.map +1 -0
  284. package/dist/lib/{error-sanitizer.test.js → errors/error-sanitizer.test.js} +17 -11
  285. package/dist/lib/errors/error-sanitizer.test.js.map +1 -0
  286. package/dist/lib/{error-types.d.ts → errors/error-types.d.ts} +5 -0
  287. package/dist/lib/errors/error-types.d.ts.map +1 -0
  288. package/dist/lib/{error-types.js → errors/error-types.js} +34 -1
  289. package/dist/lib/errors/error-types.js.map +1 -0
  290. package/dist/lib/errors/error-types.test.d.ts.map +1 -0
  291. package/dist/lib/{error-types.test.js → errors/error-types.test.js} +51 -1
  292. package/dist/lib/errors/error-types.test.js.map +1 -0
  293. package/dist/lib/errors/query-sanitizer.d.ts.map +1 -0
  294. package/dist/lib/{query-sanitizer.js → errors/query-sanitizer.js} +9 -1
  295. package/dist/lib/errors/query-sanitizer.js.map +1 -0
  296. package/dist/lib/errors/query-sanitizer.test.d.ts.map +1 -0
  297. package/dist/lib/{query-sanitizer.test.js → errors/query-sanitizer.test.js} +9 -6
  298. package/dist/lib/errors/query-sanitizer.test.js.map +1 -0
  299. package/dist/lib/exports/confident-export.d.ts +105 -0
  300. package/dist/lib/exports/confident-export.d.ts.map +1 -0
  301. package/dist/lib/exports/confident-export.js +385 -0
  302. package/dist/lib/exports/confident-export.js.map +1 -0
  303. package/dist/lib/exports/confident-export.test.d.ts +7 -0
  304. package/dist/lib/exports/confident-export.test.d.ts.map +1 -0
  305. package/dist/lib/exports/confident-export.test.js +848 -0
  306. package/dist/lib/exports/confident-export.test.js.map +1 -0
  307. package/dist/lib/exports/datadog-export.d.ts +200 -0
  308. package/dist/lib/exports/datadog-export.d.ts.map +1 -0
  309. package/dist/lib/exports/datadog-export.js +488 -0
  310. package/dist/lib/exports/datadog-export.js.map +1 -0
  311. package/dist/lib/exports/datadog-export.test.d.ts +2 -0
  312. package/dist/lib/exports/datadog-export.test.d.ts.map +1 -0
  313. package/dist/lib/exports/datadog-export.test.js +890 -0
  314. package/dist/lib/exports/datadog-export.test.js.map +1 -0
  315. package/dist/lib/exports/export-config-schemas.d.ts +67 -0
  316. package/dist/lib/exports/export-config-schemas.d.ts.map +1 -0
  317. package/dist/lib/exports/export-config-schemas.js +120 -0
  318. package/dist/lib/exports/export-config-schemas.js.map +1 -0
  319. package/dist/lib/exports/export-config-schemas.test.d.ts +8 -0
  320. package/dist/lib/exports/export-config-schemas.test.d.ts.map +1 -0
  321. package/dist/lib/exports/export-config-schemas.test.js +503 -0
  322. package/dist/lib/exports/export-config-schemas.test.js.map +1 -0
  323. package/dist/lib/exports/export-utils.d.ts +127 -0
  324. package/dist/lib/exports/export-utils.d.ts.map +1 -0
  325. package/dist/lib/exports/export-utils.js +303 -0
  326. package/dist/lib/exports/export-utils.js.map +1 -0
  327. package/dist/lib/exports/export-utils.test.d.ts +5 -0
  328. package/dist/lib/exports/export-utils.test.d.ts.map +1 -0
  329. package/dist/lib/exports/export-utils.test.js +344 -0
  330. package/dist/lib/exports/export-utils.test.js.map +1 -0
  331. package/dist/lib/exports/langfuse-export.d.ts +129 -0
  332. package/dist/lib/exports/langfuse-export.d.ts.map +1 -0
  333. package/dist/lib/exports/langfuse-export.js +370 -0
  334. package/dist/lib/exports/langfuse-export.js.map +1 -0
  335. package/dist/lib/exports/langfuse-export.test.d.ts +7 -0
  336. package/dist/lib/exports/langfuse-export.test.d.ts.map +1 -0
  337. package/dist/lib/exports/langfuse-export.test.js +1020 -0
  338. package/dist/lib/exports/langfuse-export.test.js.map +1 -0
  339. package/dist/lib/{otlp-export.d.ts → exports/otlp-export.d.ts} +3 -2
  340. package/dist/lib/exports/otlp-export.d.ts.map +1 -0
  341. package/dist/lib/{otlp-export.js → exports/otlp-export.js} +51 -36
  342. package/dist/lib/exports/otlp-export.js.map +1 -0
  343. package/dist/lib/exports/otlp-format-converter.d.ts +70 -0
  344. package/dist/lib/exports/otlp-format-converter.d.ts.map +1 -0
  345. package/dist/lib/exports/otlp-format-converter.js +401 -0
  346. package/dist/lib/exports/otlp-format-converter.js.map +1 -0
  347. package/dist/lib/exports/otlp-proto-encode.d.ts +53 -0
  348. package/dist/lib/exports/otlp-proto-encode.d.ts.map +1 -0
  349. package/dist/lib/exports/otlp-proto-encode.js +165 -0
  350. package/dist/lib/exports/otlp-proto-encode.js.map +1 -0
  351. package/dist/lib/exports/otlp-proto-encode.test.d.ts +7 -0
  352. package/dist/lib/exports/otlp-proto-encode.test.d.ts.map +1 -0
  353. package/dist/lib/exports/otlp-proto-encode.test.js +997 -0
  354. package/dist/lib/exports/otlp-proto-encode.test.js.map +1 -0
  355. package/dist/lib/exports/phoenix-export.d.ts +119 -0
  356. package/dist/lib/exports/phoenix-export.d.ts.map +1 -0
  357. package/dist/lib/exports/phoenix-export.js +448 -0
  358. package/dist/lib/exports/phoenix-export.js.map +1 -0
  359. package/dist/lib/exports/phoenix-export.test.d.ts +11 -0
  360. package/dist/lib/exports/phoenix-export.test.d.ts.map +1 -0
  361. package/dist/lib/exports/phoenix-export.test.js +816 -0
  362. package/dist/lib/exports/phoenix-export.test.js.map +1 -0
  363. package/dist/lib/index.d.ts +16 -0
  364. package/dist/lib/index.d.ts.map +1 -0
  365. package/dist/lib/index.js +31 -0
  366. package/dist/lib/index.js.map +1 -0
  367. package/dist/lib/judge/evaluation-hooks-schemas.d.ts +186 -0
  368. package/dist/lib/judge/evaluation-hooks-schemas.d.ts.map +1 -0
  369. package/dist/lib/judge/evaluation-hooks-schemas.js +125 -0
  370. package/dist/lib/judge/evaluation-hooks-schemas.js.map +1 -0
  371. package/dist/lib/judge/evaluation-hooks.d.ts +88 -0
  372. package/dist/lib/judge/evaluation-hooks.d.ts.map +1 -0
  373. package/dist/lib/judge/evaluation-hooks.js +658 -0
  374. package/dist/lib/judge/evaluation-hooks.js.map +1 -0
  375. package/dist/lib/judge/evaluation-hooks.test.d.ts +8 -0
  376. package/dist/lib/judge/evaluation-hooks.test.d.ts.map +1 -0
  377. package/dist/lib/judge/evaluation-hooks.test.js +934 -0
  378. package/dist/lib/judge/evaluation-hooks.test.js.map +1 -0
  379. package/dist/lib/judge/llm-as-judge.d.ts +138 -0
  380. package/dist/lib/judge/llm-as-judge.d.ts.map +1 -0
  381. package/dist/lib/judge/llm-as-judge.js +103 -0
  382. package/dist/lib/judge/llm-as-judge.js.map +1 -0
  383. package/dist/lib/judge/llm-as-judge.test.d.ts +2 -0
  384. package/dist/lib/judge/llm-as-judge.test.d.ts.map +1 -0
  385. package/dist/lib/judge/llm-as-judge.test.js +2179 -0
  386. package/dist/lib/judge/llm-as-judge.test.js.map +1 -0
  387. package/dist/lib/judge/llm-judge-bias.d.ts +44 -0
  388. package/dist/lib/judge/llm-judge-bias.d.ts.map +1 -0
  389. package/dist/lib/judge/llm-judge-bias.js +130 -0
  390. package/dist/lib/judge/llm-judge-bias.js.map +1 -0
  391. package/dist/lib/judge/llm-judge-bias.test.d.ts +2 -0
  392. package/dist/lib/judge/llm-judge-bias.test.d.ts.map +1 -0
  393. package/dist/lib/judge/llm-judge-bias.test.js +380 -0
  394. package/dist/lib/judge/llm-judge-bias.test.js.map +1 -0
  395. package/dist/lib/judge/llm-judge-code.d.ts +99 -0
  396. package/dist/lib/judge/llm-judge-code.d.ts.map +1 -0
  397. package/dist/lib/judge/llm-judge-code.js +261 -0
  398. package/dist/lib/judge/llm-judge-code.js.map +1 -0
  399. package/dist/lib/judge/llm-judge-code.test.d.ts +2 -0
  400. package/dist/lib/judge/llm-judge-code.test.d.ts.map +1 -0
  401. package/dist/lib/judge/llm-judge-code.test.js +981 -0
  402. package/dist/lib/judge/llm-judge-code.test.js.map +1 -0
  403. package/dist/lib/judge/llm-judge-config.d.ts +241 -0
  404. package/dist/lib/judge/llm-judge-config.d.ts.map +1 -0
  405. package/dist/lib/judge/llm-judge-config.js +390 -0
  406. package/dist/lib/judge/llm-judge-config.js.map +1 -0
  407. package/dist/lib/judge/llm-judge-config.test.d.ts +5 -0
  408. package/dist/lib/judge/llm-judge-config.test.d.ts.map +1 -0
  409. package/dist/lib/judge/llm-judge-config.test.js +392 -0
  410. package/dist/lib/judge/llm-judge-config.test.js.map +1 -0
  411. package/dist/lib/judge/llm-judge-constants.d.ts +111 -0
  412. package/dist/lib/judge/llm-judge-constants.d.ts.map +1 -0
  413. package/dist/lib/judge/llm-judge-constants.js +150 -0
  414. package/dist/lib/judge/llm-judge-constants.js.map +1 -0
  415. package/dist/lib/judge/llm-judge-dag.d.ts +57 -0
  416. package/dist/lib/judge/llm-judge-dag.d.ts.map +1 -0
  417. package/dist/lib/judge/llm-judge-dag.js +217 -0
  418. package/dist/lib/judge/llm-judge-dag.js.map +1 -0
  419. package/dist/lib/judge/llm-judge-dag.test.d.ts +8 -0
  420. package/dist/lib/judge/llm-judge-dag.test.d.ts.map +1 -0
  421. package/dist/lib/judge/llm-judge-dag.test.js +973 -0
  422. package/dist/lib/judge/llm-judge-dag.test.js.map +1 -0
  423. package/dist/lib/judge/llm-judge-domain.d.ts +42 -0
  424. package/dist/lib/judge/llm-judge-domain.d.ts.map +1 -0
  425. package/dist/lib/judge/llm-judge-domain.js +167 -0
  426. package/dist/lib/judge/llm-judge-domain.js.map +1 -0
  427. package/dist/lib/judge/llm-judge-domain.test.d.ts +6 -0
  428. package/dist/lib/judge/llm-judge-domain.test.d.ts.map +1 -0
  429. package/dist/lib/judge/llm-judge-domain.test.js +337 -0
  430. package/dist/lib/judge/llm-judge-domain.test.js.map +1 -0
  431. package/dist/lib/judge/llm-judge-geval.d.ts +42 -0
  432. package/dist/lib/judge/llm-judge-geval.d.ts.map +1 -0
  433. package/dist/lib/judge/llm-judge-geval.js +213 -0
  434. package/dist/lib/judge/llm-judge-geval.js.map +1 -0
  435. package/dist/lib/judge/llm-judge-geval.test.d.ts +2 -0
  436. package/dist/lib/judge/llm-judge-geval.test.d.ts.map +1 -0
  437. package/dist/lib/judge/llm-judge-geval.test.js +556 -0
  438. package/dist/lib/judge/llm-judge-geval.test.js.map +1 -0
  439. package/dist/lib/judge/llm-judge-otel.test.d.ts +9 -0
  440. package/dist/lib/judge/llm-judge-otel.test.d.ts.map +1 -0
  441. package/dist/lib/judge/llm-judge-otel.test.js +91 -0
  442. package/dist/lib/judge/llm-judge-otel.test.js.map +1 -0
  443. package/dist/lib/judge/llm-judge-qag.d.ts +38 -0
  444. package/dist/lib/judge/llm-judge-qag.d.ts.map +1 -0
  445. package/dist/lib/judge/llm-judge-qag.js +205 -0
  446. package/dist/lib/judge/llm-judge-qag.js.map +1 -0
  447. package/dist/lib/judge/llm-judge-qag.test.d.ts +2 -0
  448. package/dist/lib/judge/llm-judge-qag.test.d.ts.map +1 -0
  449. package/dist/lib/judge/llm-judge-qag.test.js +386 -0
  450. package/dist/lib/judge/llm-judge-qag.test.js.map +1 -0
  451. package/dist/lib/judge/llm-judge-resilience.d.ts +74 -0
  452. package/dist/lib/judge/llm-judge-resilience.d.ts.map +1 -0
  453. package/dist/lib/judge/llm-judge-resilience.js +146 -0
  454. package/dist/lib/judge/llm-judge-resilience.js.map +1 -0
  455. package/dist/lib/judge/llm-judge-resilience.test.d.ts +2 -0
  456. package/dist/lib/judge/llm-judge-resilience.test.d.ts.map +1 -0
  457. package/dist/lib/judge/llm-judge-resilience.test.js +353 -0
  458. package/dist/lib/judge/llm-judge-resilience.test.js.map +1 -0
  459. package/dist/lib/judge/llm-judge-security.d.ts +106 -0
  460. package/dist/lib/judge/llm-judge-security.d.ts.map +1 -0
  461. package/dist/lib/judge/llm-judge-security.js +314 -0
  462. package/dist/lib/judge/llm-judge-security.js.map +1 -0
  463. package/dist/lib/judge/llm-judge-security.test.d.ts +2 -0
  464. package/dist/lib/judge/llm-judge-security.test.d.ts.map +1 -0
  465. package/dist/lib/judge/llm-judge-security.test.js +1011 -0
  466. package/dist/lib/judge/llm-judge-security.test.js.map +1 -0
  467. package/dist/lib/observability/context-accumulator.d.ts +32 -0
  468. package/dist/lib/observability/context-accumulator.d.ts.map +1 -0
  469. package/dist/lib/observability/context-accumulator.js +87 -0
  470. package/dist/lib/observability/context-accumulator.js.map +1 -0
  471. package/dist/lib/observability/evaluation-events.d.ts +35 -0
  472. package/dist/lib/observability/evaluation-events.d.ts.map +1 -0
  473. package/dist/lib/observability/evaluation-events.js +90 -0
  474. package/dist/lib/observability/evaluation-events.js.map +1 -0
  475. package/dist/lib/observability/file-span-exporter.d.ts +17 -0
  476. package/dist/lib/observability/file-span-exporter.d.ts.map +1 -0
  477. package/dist/lib/observability/file-span-exporter.js +49 -0
  478. package/dist/lib/observability/file-span-exporter.js.map +1 -0
  479. package/dist/lib/observability/histogram-bucket-constants.d.ts +25 -0
  480. package/dist/lib/observability/histogram-bucket-constants.d.ts.map +1 -0
  481. package/dist/lib/observability/histogram-bucket-constants.js +60 -0
  482. package/dist/lib/observability/histogram-bucket-constants.js.map +1 -0
  483. package/dist/lib/observability/histogram.d.ts +112 -0
  484. package/dist/lib/observability/histogram.d.ts.map +1 -0
  485. package/dist/lib/observability/histogram.js +170 -0
  486. package/dist/lib/observability/histogram.js.map +1 -0
  487. package/dist/lib/observability/histogram.test.d.ts +5 -0
  488. package/dist/lib/observability/histogram.test.d.ts.map +1 -0
  489. package/dist/lib/observability/histogram.test.js +385 -0
  490. package/dist/lib/observability/histogram.test.js.map +1 -0
  491. package/dist/lib/observability/indexer.d.ts +114 -0
  492. package/dist/lib/observability/indexer.d.ts.map +1 -0
  493. package/dist/lib/{indexer.js → observability/indexer.js} +65 -16
  494. package/dist/lib/observability/indexer.js.map +1 -0
  495. package/dist/lib/observability/indexer.test.d.ts.map +1 -0
  496. package/dist/lib/{indexer.test.js → observability/indexer.test.js} +94 -77
  497. package/dist/lib/observability/indexer.test.js.map +1 -0
  498. package/dist/lib/observability/instrumentation-eval.test.d.ts +5 -0
  499. package/dist/lib/observability/instrumentation-eval.test.d.ts.map +1 -0
  500. package/dist/lib/observability/instrumentation-eval.test.js +63 -0
  501. package/dist/lib/observability/instrumentation-eval.test.js.map +1 -0
  502. package/dist/lib/observability/instrumentation-init-errors.test.d.ts +13 -0
  503. package/dist/lib/observability/instrumentation-init-errors.test.d.ts.map +1 -0
  504. package/dist/lib/observability/instrumentation-init-errors.test.js +194 -0
  505. package/dist/lib/observability/instrumentation-init-errors.test.js.map +1 -0
  506. package/dist/lib/observability/instrumentation-retry-timeout.test.d.ts +15 -0
  507. package/dist/lib/observability/instrumentation-retry-timeout.test.d.ts.map +1 -0
  508. package/dist/lib/observability/instrumentation-retry-timeout.test.js +188 -0
  509. package/dist/lib/observability/instrumentation-retry-timeout.test.js.map +1 -0
  510. package/dist/lib/observability/instrumentation-set-otel.test.d.ts +5 -0
  511. package/dist/lib/observability/instrumentation-set-otel.test.d.ts.map +1 -0
  512. package/dist/lib/observability/instrumentation-set-otel.test.js +59 -0
  513. package/dist/lib/observability/instrumentation-set-otel.test.js.map +1 -0
  514. package/dist/lib/observability/instrumentation.d.ts +158 -0
  515. package/dist/lib/observability/instrumentation.d.ts.map +1 -0
  516. package/dist/lib/observability/instrumentation.integration.test.d.ts +2 -0
  517. package/dist/lib/observability/instrumentation.integration.test.d.ts.map +1 -0
  518. package/dist/lib/observability/instrumentation.integration.test.js +590 -0
  519. package/dist/lib/observability/instrumentation.integration.test.js.map +1 -0
  520. package/dist/lib/observability/instrumentation.js +512 -0
  521. package/dist/lib/observability/instrumentation.js.map +1 -0
  522. package/dist/lib/observability/instrumentation.test.d.ts +2 -0
  523. package/dist/lib/observability/instrumentation.test.d.ts.map +1 -0
  524. package/dist/lib/observability/instrumentation.test.js +822 -0
  525. package/dist/lib/observability/instrumentation.test.js.map +1 -0
  526. package/dist/lib/observability/mcp-semconv-constants.d.ts +98 -0
  527. package/dist/lib/observability/mcp-semconv-constants.d.ts.map +1 -0
  528. package/dist/lib/observability/mcp-semconv-constants.js +102 -0
  529. package/dist/lib/observability/mcp-semconv-constants.js.map +1 -0
  530. package/dist/lib/observability/mcp-semconv.d.ts +37 -0
  531. package/dist/lib/observability/mcp-semconv.d.ts.map +1 -0
  532. package/dist/lib/observability/mcp-semconv.js +87 -0
  533. package/dist/lib/observability/mcp-semconv.js.map +1 -0
  534. package/dist/lib/observability/mcp-semconv.test.d.ts +2 -0
  535. package/dist/lib/observability/mcp-semconv.test.d.ts.map +1 -0
  536. package/dist/lib/observability/mcp-semconv.test.js +168 -0
  537. package/dist/lib/observability/mcp-semconv.test.js.map +1 -0
  538. package/dist/lib/observability/metrics.d.ts +100 -0
  539. package/dist/lib/observability/metrics.d.ts.map +1 -0
  540. package/dist/lib/observability/metrics.js +429 -0
  541. package/dist/lib/observability/metrics.js.map +1 -0
  542. package/dist/lib/observability/metrics.test.d.ts +5 -0
  543. package/dist/lib/observability/metrics.test.d.ts.map +1 -0
  544. package/dist/lib/observability/metrics.test.js +191 -0
  545. package/dist/lib/observability/metrics.test.js.map +1 -0
  546. package/dist/lib/observability/observability-test-constants.d.ts +34 -0
  547. package/dist/lib/observability/observability-test-constants.d.ts.map +1 -0
  548. package/dist/lib/observability/observability-test-constants.js +55 -0
  549. package/dist/lib/observability/observability-test-constants.js.map +1 -0
  550. package/dist/lib/observability/opentelemetry-resources.test.d.ts +2 -0
  551. package/dist/lib/observability/opentelemetry-resources.test.d.ts.map +1 -0
  552. package/dist/lib/observability/opentelemetry-resources.test.js +19 -0
  553. package/dist/lib/observability/opentelemetry-resources.test.js.map +1 -0
  554. package/dist/lib/observability/parse-stats.d.ts +119 -0
  555. package/dist/lib/observability/parse-stats.d.ts.map +1 -0
  556. package/dist/lib/observability/parse-stats.js +207 -0
  557. package/dist/lib/observability/parse-stats.js.map +1 -0
  558. package/dist/lib/observability/parse-stats.test.d.ts +5 -0
  559. package/dist/lib/observability/parse-stats.test.d.ts.map +1 -0
  560. package/dist/lib/observability/parse-stats.test.js +287 -0
  561. package/dist/lib/observability/parse-stats.test.js.map +1 -0
  562. package/dist/lib/observability/render-trace-tree.d.ts +31 -0
  563. package/dist/lib/observability/render-trace-tree.d.ts.map +1 -0
  564. package/dist/lib/observability/render-trace-tree.js +95 -0
  565. package/dist/lib/observability/render-trace-tree.js.map +1 -0
  566. package/dist/lib/observability/render-trace-tree.test.d.ts +5 -0
  567. package/dist/lib/observability/render-trace-tree.test.d.ts.map +1 -0
  568. package/dist/lib/observability/render-trace-tree.test.js +97 -0
  569. package/dist/lib/observability/render-trace-tree.test.js.map +1 -0
  570. package/dist/lib/observability/span-attributes.d.ts +27 -0
  571. package/dist/lib/observability/span-attributes.d.ts.map +1 -0
  572. package/dist/lib/observability/span-attributes.js +85 -0
  573. package/dist/lib/observability/span-attributes.js.map +1 -0
  574. package/dist/lib/observability/trace-anomaly-detector.d.ts +23 -0
  575. package/dist/lib/observability/trace-anomaly-detector.d.ts.map +1 -0
  576. package/dist/lib/observability/trace-anomaly-detector.js +211 -0
  577. package/dist/lib/observability/trace-anomaly-detector.js.map +1 -0
  578. package/dist/lib/observability/trace-anomaly-detector.test.d.ts +5 -0
  579. package/dist/lib/observability/trace-anomaly-detector.test.d.ts.map +1 -0
  580. package/dist/lib/observability/trace-anomaly-detector.test.js +224 -0
  581. package/dist/lib/observability/trace-anomaly-detector.test.js.map +1 -0
  582. package/dist/lib/observability/trace-anomaly-schemas.d.ts +189 -0
  583. package/dist/lib/observability/trace-anomaly-schemas.d.ts.map +1 -0
  584. package/dist/lib/observability/trace-anomaly-schemas.js +167 -0
  585. package/dist/lib/observability/trace-anomaly-schemas.js.map +1 -0
  586. package/dist/lib/privacy/content-redaction.d.ts +141 -0
  587. package/dist/lib/privacy/content-redaction.d.ts.map +1 -0
  588. package/dist/lib/privacy/content-redaction.js +210 -0
  589. package/dist/lib/privacy/content-redaction.js.map +1 -0
  590. package/dist/lib/privacy/content-redaction.test.d.ts +2 -0
  591. package/dist/lib/privacy/content-redaction.test.d.ts.map +1 -0
  592. package/dist/lib/privacy/content-redaction.test.js +302 -0
  593. package/dist/lib/privacy/content-redaction.test.js.map +1 -0
  594. package/dist/lib/quality/bucket-utils.d.ts +17 -0
  595. package/dist/lib/quality/bucket-utils.d.ts.map +1 -0
  596. package/dist/lib/quality/bucket-utils.js +31 -0
  597. package/dist/lib/quality/bucket-utils.js.map +1 -0
  598. package/dist/lib/quality/bucket-utils.test.d.ts +2 -0
  599. package/dist/lib/quality/bucket-utils.test.d.ts.map +1 -0
  600. package/dist/lib/quality/bucket-utils.test.js +42 -0
  601. package/dist/lib/quality/bucket-utils.test.js.map +1 -0
  602. package/dist/lib/quality/qfe-backtest-detail.test.d.ts +5 -0
  603. package/dist/lib/quality/qfe-backtest-detail.test.d.ts.map +1 -0
  604. package/dist/lib/quality/qfe-backtest-detail.test.js +179 -0
  605. package/dist/lib/quality/qfe-backtest-detail.test.js.map +1 -0
  606. package/dist/lib/quality/qfe-calibration-paths.test.d.ts +5 -0
  607. package/dist/lib/quality/qfe-calibration-paths.test.d.ts.map +1 -0
  608. package/dist/lib/quality/qfe-calibration-paths.test.js +203 -0
  609. package/dist/lib/quality/qfe-calibration-paths.test.js.map +1 -0
  610. package/dist/lib/quality/qfe-correlation-helpers.test.d.ts +6 -0
  611. package/dist/lib/quality/qfe-correlation-helpers.test.d.ts.map +1 -0
  612. package/dist/lib/quality/qfe-correlation-helpers.test.js +143 -0
  613. package/dist/lib/quality/qfe-correlation-helpers.test.js.map +1 -0
  614. package/dist/lib/quality/qfe-cqi-paths.test.d.ts +6 -0
  615. package/dist/lib/quality/qfe-cqi-paths.test.d.ts.map +1 -0
  616. package/dist/lib/quality/qfe-cqi-paths.test.js +231 -0
  617. package/dist/lib/quality/qfe-cqi-paths.test.js.map +1 -0
  618. package/dist/lib/quality/qfe-critic-internals.test.d.ts +6 -0
  619. package/dist/lib/quality/qfe-critic-internals.test.d.ts.map +1 -0
  620. package/dist/lib/quality/qfe-critic-internals.test.js +191 -0
  621. package/dist/lib/quality/qfe-critic-internals.test.js.map +1 -0
  622. package/dist/lib/quality/qfe-derived-paths.test.d.ts +2 -0
  623. package/dist/lib/quality/qfe-derived-paths.test.d.ts.map +1 -0
  624. package/dist/lib/quality/qfe-derived-paths.test.js +372 -0
  625. package/dist/lib/quality/qfe-derived-paths.test.js.map +1 -0
  626. package/dist/lib/quality/qfe-dynamics-paths.test.d.ts +8 -0
  627. package/dist/lib/quality/qfe-dynamics-paths.test.d.ts.map +1 -0
  628. package/dist/lib/quality/qfe-dynamics-paths.test.js +223 -0
  629. package/dist/lib/quality/qfe-dynamics-paths.test.js.map +1 -0
  630. package/dist/lib/quality/qfe-granger-internals.test.d.ts +6 -0
  631. package/dist/lib/quality/qfe-granger-internals.test.d.ts.map +1 -0
  632. package/dist/lib/quality/qfe-granger-internals.test.js +158 -0
  633. package/dist/lib/quality/qfe-granger-internals.test.js.map +1 -0
  634. package/dist/lib/quality/qfe-label-normalize.test.d.ts +7 -0
  635. package/dist/lib/quality/qfe-label-normalize.test.d.ts.map +1 -0
  636. package/dist/lib/quality/qfe-label-normalize.test.js +332 -0
  637. package/dist/lib/quality/qfe-label-normalize.test.js.map +1 -0
  638. package/dist/lib/quality/qfe-ordinal-edge.test.d.ts +6 -0
  639. package/dist/lib/quality/qfe-ordinal-edge.test.d.ts.map +1 -0
  640. package/dist/lib/quality/qfe-ordinal-edge.test.js +98 -0
  641. package/dist/lib/quality/qfe-ordinal-edge.test.js.map +1 -0
  642. package/dist/lib/quality/qfe-roles-detail.test.d.ts +5 -0
  643. package/dist/lib/quality/qfe-roles-detail.test.d.ts.map +1 -0
  644. package/dist/lib/quality/qfe-roles-detail.test.js +115 -0
  645. package/dist/lib/quality/qfe-roles-detail.test.js.map +1 -0
  646. package/dist/lib/quality/qfe-rolling-detail.test.d.ts +7 -0
  647. package/dist/lib/quality/qfe-rolling-detail.test.d.ts.map +1 -0
  648. package/dist/lib/quality/qfe-rolling-detail.test.js +249 -0
  649. package/dist/lib/quality/qfe-rolling-detail.test.js.map +1 -0
  650. package/dist/lib/quality/qfe-stats-internals.test.d.ts +7 -0
  651. package/dist/lib/quality/qfe-stats-internals.test.d.ts.map +1 -0
  652. package/dist/lib/quality/qfe-stats-internals.test.js +143 -0
  653. package/dist/lib/quality/qfe-stats-internals.test.js.map +1 -0
  654. package/dist/lib/quality/qfe-streaming.test.d.ts +5 -0
  655. package/dist/lib/quality/qfe-streaming.test.d.ts.map +1 -0
  656. package/dist/lib/quality/qfe-streaming.test.js +239 -0
  657. package/dist/lib/quality/qfe-streaming.test.js.map +1 -0
  658. package/dist/lib/quality/qfe-sweep-detail.test.d.ts +6 -0
  659. package/dist/lib/quality/qfe-sweep-detail.test.d.ts.map +1 -0
  660. package/dist/lib/quality/qfe-sweep-detail.test.js +291 -0
  661. package/dist/lib/quality/qfe-sweep-detail.test.js.map +1 -0
  662. package/dist/lib/quality/quality-alerts.d.ts +23 -0
  663. package/dist/lib/quality/quality-alerts.d.ts.map +1 -0
  664. package/dist/lib/quality/quality-alerts.js +89 -0
  665. package/dist/lib/quality/quality-alerts.js.map +1 -0
  666. package/dist/lib/quality/quality-alerts.test.d.ts +2 -0
  667. package/dist/lib/quality/quality-alerts.test.d.ts.map +1 -0
  668. package/dist/lib/quality/quality-alerts.test.js +86 -0
  669. package/dist/lib/quality/quality-alerts.test.js.map +1 -0
  670. package/dist/lib/quality/quality-constants.d.ts +294 -0
  671. package/dist/lib/quality/quality-constants.d.ts.map +1 -0
  672. package/dist/lib/quality/quality-constants.js +335 -0
  673. package/dist/lib/quality/quality-constants.js.map +1 -0
  674. package/dist/lib/quality/quality-feature-engineering.d.ts +1071 -0
  675. package/dist/lib/quality/quality-feature-engineering.d.ts.map +1 -0
  676. package/dist/lib/quality/quality-feature-engineering.js +2076 -0
  677. package/dist/lib/quality/quality-feature-engineering.js.map +1 -0
  678. package/dist/lib/quality/quality-feature-engineering.test.d.ts +5 -0
  679. package/dist/lib/quality/quality-feature-engineering.test.d.ts.map +1 -0
  680. package/dist/lib/quality/quality-feature-engineering.test.js +2908 -0
  681. package/dist/lib/quality/quality-feature-engineering.test.js.map +1 -0
  682. package/dist/lib/quality/quality-metrics.d.ts +943 -0
  683. package/dist/lib/quality/quality-metrics.d.ts.map +1 -0
  684. package/dist/lib/quality/quality-metrics.js +1151 -0
  685. package/dist/lib/quality/quality-metrics.js.map +1 -0
  686. package/dist/lib/quality/quality-metrics.test.d.ts +5 -0
  687. package/dist/lib/quality/quality-metrics.test.d.ts.map +1 -0
  688. package/dist/lib/quality/quality-metrics.test.js +2766 -0
  689. package/dist/lib/quality/quality-metrics.test.js.map +1 -0
  690. package/dist/lib/quality/quality-multi-agent.d.ts +106 -0
  691. package/dist/lib/quality/quality-multi-agent.d.ts.map +1 -0
  692. package/dist/lib/quality/quality-multi-agent.js +124 -0
  693. package/dist/lib/quality/quality-multi-agent.js.map +1 -0
  694. package/dist/lib/quality/quality-multi-agent.test.d.ts +6 -0
  695. package/dist/lib/quality/quality-multi-agent.test.d.ts.map +1 -0
  696. package/dist/lib/quality/quality-multi-agent.test.js +163 -0
  697. package/dist/lib/quality/quality-multi-agent.test.js.map +1 -0
  698. package/dist/lib/quality/quality-sla.d.ts +35 -0
  699. package/dist/lib/quality/quality-sla.d.ts.map +1 -0
  700. package/dist/lib/quality/quality-sla.js +62 -0
  701. package/dist/lib/quality/quality-sla.js.map +1 -0
  702. package/dist/lib/quality/quality-sla.test.d.ts +5 -0
  703. package/dist/lib/quality/quality-sla.test.d.ts.map +1 -0
  704. package/dist/lib/quality/quality-sla.test.js +144 -0
  705. package/dist/lib/quality/quality-sla.test.js.map +1 -0
  706. package/dist/lib/quality/quality-test-constants.d.ts +23 -0
  707. package/dist/lib/quality/quality-test-constants.d.ts.map +1 -0
  708. package/dist/lib/quality/quality-test-constants.js +25 -0
  709. package/dist/lib/quality/quality-test-constants.js.map +1 -0
  710. package/dist/lib/quality/quality-trends.d.ts +101 -0
  711. package/dist/lib/quality/quality-trends.d.ts.map +1 -0
  712. package/dist/lib/quality/quality-trends.js +299 -0
  713. package/dist/lib/quality/quality-trends.js.map +1 -0
  714. package/dist/lib/quality/quality-trends.test.d.ts +6 -0
  715. package/dist/lib/quality/quality-trends.test.d.ts.map +1 -0
  716. package/dist/lib/quality/quality-trends.test.js +377 -0
  717. package/dist/lib/quality/quality-trends.test.js.map +1 -0
  718. package/dist/lib/quality/quality-views.d.ts +966 -0
  719. package/dist/lib/quality/quality-views.d.ts.map +1 -0
  720. package/dist/lib/quality/quality-views.js +367 -0
  721. package/dist/lib/quality/quality-views.js.map +1 -0
  722. package/dist/lib/quality/quality-views.test.d.ts +6 -0
  723. package/dist/lib/quality/quality-views.test.d.ts.map +1 -0
  724. package/dist/lib/quality/quality-views.test.js +262 -0
  725. package/dist/lib/quality/quality-views.test.js.map +1 -0
  726. package/dist/lib/quality/quality-visualization.d.ts +112 -0
  727. package/dist/lib/quality/quality-visualization.d.ts.map +1 -0
  728. package/dist/lib/quality/quality-visualization.js +136 -0
  729. package/dist/lib/quality/quality-visualization.js.map +1 -0
  730. package/dist/lib/quality/quality-visualization.test.d.ts +5 -0
  731. package/dist/lib/quality/quality-visualization.test.d.ts.map +1 -0
  732. package/dist/lib/quality/quality-visualization.test.js +189 -0
  733. package/dist/lib/quality/quality-visualization.test.js.map +1 -0
  734. package/dist/lib/resilience/cache.d.ts +56 -0
  735. package/dist/lib/resilience/cache.d.ts.map +1 -0
  736. package/dist/lib/resilience/cache.js +96 -0
  737. package/dist/lib/resilience/cache.js.map +1 -0
  738. package/dist/lib/resilience/cache.test.d.ts.map +1 -0
  739. package/dist/lib/{cache.test.js → resilience/cache.test.js} +21 -20
  740. package/dist/lib/resilience/cache.test.js.map +1 -0
  741. package/dist/lib/resilience/circuit-breaker.d.ts +147 -0
  742. package/dist/lib/resilience/circuit-breaker.d.ts.map +1 -0
  743. package/dist/lib/resilience/circuit-breaker.js +251 -0
  744. package/dist/lib/resilience/circuit-breaker.js.map +1 -0
  745. package/dist/lib/resilience/circuit-breaker.test.d.ts.map +1 -0
  746. package/dist/lib/{circuit-breaker.test.js → resilience/circuit-breaker.test.js} +29 -26
  747. package/dist/lib/resilience/circuit-breaker.test.js.map +1 -0
  748. package/dist/lib/{toon-encoder.d.ts → resilience/toon-encoder.d.ts} +6 -1
  749. package/dist/lib/resilience/toon-encoder.d.ts.map +1 -0
  750. package/dist/lib/{toon-encoder.js → resilience/toon-encoder.js} +7 -2
  751. package/dist/lib/resilience/toon-encoder.js.map +1 -0
  752. package/dist/lib/resilience/toon-encoder.test.d.ts.map +1 -0
  753. package/dist/lib/{toon-encoder.test.js → resilience/toon-encoder.test.js} +7 -6
  754. package/dist/lib/resilience/toon-encoder.test.js.map +1 -0
  755. package/dist/lib/testing/mock-llm-builder.d.ts +139 -0
  756. package/dist/lib/testing/mock-llm-builder.d.ts.map +1 -0
  757. package/dist/lib/testing/mock-llm-builder.js +254 -0
  758. package/dist/lib/testing/mock-llm-builder.js.map +1 -0
  759. package/dist/lib/testing/mock-llm-builder.test.d.ts +5 -0
  760. package/dist/lib/testing/mock-llm-builder.test.d.ts.map +1 -0
  761. package/dist/lib/testing/mock-llm-builder.test.js +304 -0
  762. package/dist/lib/testing/mock-llm-builder.test.js.map +1 -0
  763. package/dist/lib/validation/api-schemas.d.ts +705 -0
  764. package/dist/lib/validation/api-schemas.d.ts.map +1 -0
  765. package/dist/lib/validation/api-schemas.js +351 -0
  766. package/dist/lib/validation/api-schemas.js.map +1 -0
  767. package/dist/lib/validation/api-schemas.test.d.ts +5 -0
  768. package/dist/lib/validation/api-schemas.test.d.ts.map +1 -0
  769. package/dist/lib/validation/api-schemas.test.js +427 -0
  770. package/dist/lib/validation/api-schemas.test.js.map +1 -0
  771. package/dist/lib/validation/dashboard-schemas.d.ts +203 -0
  772. package/dist/lib/validation/dashboard-schemas.d.ts.map +1 -0
  773. package/dist/lib/validation/dashboard-schemas.js +186 -0
  774. package/dist/lib/validation/dashboard-schemas.js.map +1 -0
  775. package/dist/lib/validation/dashboard-schemas.test.d.ts +5 -0
  776. package/dist/lib/validation/dashboard-schemas.test.d.ts.map +1 -0
  777. package/dist/lib/validation/dashboard-schemas.test.js +353 -0
  778. package/dist/lib/validation/dashboard-schemas.test.js.map +1 -0
  779. package/dist/server.d.ts +7 -1
  780. package/dist/server.d.ts.map +1 -1
  781. package/dist/server.js +172 -102
  782. package/dist/server.js.map +1 -1
  783. package/dist/server.test.js +102 -95
  784. package/dist/server.test.js.map +1 -1
  785. package/dist/test-helpers/assertions.d.ts +6 -0
  786. package/dist/test-helpers/assertions.d.ts.map +1 -0
  787. package/dist/test-helpers/assertions.js +11 -0
  788. package/dist/test-helpers/assertions.js.map +1 -0
  789. package/dist/test-helpers/env-utils.d.ts +0 -64
  790. package/dist/test-helpers/env-utils.d.ts.map +1 -1
  791. package/dist/test-helpers/env-utils.js +0 -100
  792. package/dist/test-helpers/env-utils.js.map +1 -1
  793. package/dist/test-helpers/fuzz-generators.d.ts.map +1 -1
  794. package/dist/test-helpers/fuzz-generators.js +62 -22
  795. package/dist/test-helpers/fuzz-generators.js.map +1 -1
  796. package/dist/test-helpers/index.d.ts +3 -2
  797. package/dist/test-helpers/index.d.ts.map +1 -1
  798. package/dist/test-helpers/index.js +4 -2
  799. package/dist/test-helpers/index.js.map +1 -1
  800. package/dist/test-helpers/memfs-utils.test.js +81 -76
  801. package/dist/test-helpers/memfs-utils.test.js.map +1 -1
  802. package/dist/test-helpers/mock-backends.d.ts +19 -17
  803. package/dist/test-helpers/mock-backends.d.ts.map +1 -1
  804. package/dist/test-helpers/mock-backends.js +16 -4
  805. package/dist/test-helpers/mock-backends.js.map +1 -1
  806. package/dist/test-helpers/mock-backends.test.js +43 -112
  807. package/dist/test-helpers/mock-backends.test.js.map +1 -1
  808. package/dist/test-helpers/race-condition-helpers.d.ts.map +1 -1
  809. package/dist/test-helpers/race-condition-helpers.js +3 -2
  810. package/dist/test-helpers/race-condition-helpers.js.map +1 -1
  811. package/dist/test-helpers/schema-validators.d.ts +2 -2
  812. package/dist/test-helpers/schema-validators.d.ts.map +1 -1
  813. package/dist/test-helpers/schema-validators.js +35 -31
  814. package/dist/test-helpers/schema-validators.js.map +1 -1
  815. package/dist/test-helpers/test-constants.d.ts +74 -0
  816. package/dist/test-helpers/test-constants.d.ts.map +1 -0
  817. package/dist/test-helpers/test-constants.js +78 -0
  818. package/dist/test-helpers/test-constants.js.map +1 -0
  819. package/dist/test-helpers/test-data-builders.d.ts +25 -7
  820. package/dist/test-helpers/test-data-builders.d.ts.map +1 -1
  821. package/dist/test-helpers/test-data-builders.js +32 -9
  822. package/dist/test-helpers/test-data-builders.js.map +1 -1
  823. package/dist/test-helpers/test-data-builders.test.js +116 -107
  824. package/dist/test-helpers/test-data-builders.test.js.map +1 -1
  825. package/dist/test-helpers/tool-validators.d.ts +1 -1
  826. package/dist/test-helpers/tool-validators.d.ts.map +1 -1
  827. package/dist/test-helpers/tool-validators.js +10 -10
  828. package/dist/test-helpers/tool-validators.js.map +1 -1
  829. package/dist/tools/audit-trail.d.ts +170 -0
  830. package/dist/tools/audit-trail.d.ts.map +1 -0
  831. package/dist/tools/audit-trail.js +109 -0
  832. package/dist/tools/audit-trail.js.map +1 -0
  833. package/dist/tools/audit-trail.test.d.ts +5 -0
  834. package/dist/tools/audit-trail.test.d.ts.map +1 -0
  835. package/dist/tools/audit-trail.test.js +122 -0
  836. package/dist/tools/audit-trail.test.js.map +1 -0
  837. package/dist/tools/context-stats.d.ts +6 -20
  838. package/dist/tools/context-stats.d.ts.map +1 -1
  839. package/dist/tools/context-stats.js +106 -90
  840. package/dist/tools/context-stats.js.map +1 -1
  841. package/dist/tools/context-stats.test.js +109 -60
  842. package/dist/tools/context-stats.test.js.map +1 -1
  843. package/dist/tools/detect-trace-anomalies.d.ts +123 -0
  844. package/dist/tools/detect-trace-anomalies.d.ts.map +1 -0
  845. package/dist/tools/detect-trace-anomalies.js +66 -0
  846. package/dist/tools/detect-trace-anomalies.js.map +1 -0
  847. package/dist/tools/estimate-cost.d.ts +77 -0
  848. package/dist/tools/estimate-cost.d.ts.map +1 -0
  849. package/dist/tools/estimate-cost.js +104 -0
  850. package/dist/tools/estimate-cost.js.map +1 -0
  851. package/dist/tools/estimate-cost.test.d.ts +5 -0
  852. package/dist/tools/estimate-cost.test.d.ts.map +1 -0
  853. package/dist/tools/estimate-cost.test.js +343 -0
  854. package/dist/tools/estimate-cost.test.js.map +1 -0
  855. package/dist/tools/export-base.d.ts +77 -0
  856. package/dist/tools/export-base.d.ts.map +1 -0
  857. package/dist/tools/export-base.js +150 -0
  858. package/dist/tools/export-base.js.map +1 -0
  859. package/dist/tools/export-base.test.d.ts +18 -0
  860. package/dist/tools/export-base.test.d.ts.map +1 -0
  861. package/dist/tools/export-base.test.js +220 -0
  862. package/dist/tools/export-base.test.js.map +1 -0
  863. package/dist/tools/export-confident.d.ts +149 -0
  864. package/dist/tools/export-confident.d.ts.map +1 -0
  865. package/dist/tools/export-confident.js +36 -0
  866. package/dist/tools/export-confident.js.map +1 -0
  867. package/dist/tools/export-confident.test.d.ts +7 -0
  868. package/dist/tools/export-confident.test.d.ts.map +1 -0
  869. package/dist/tools/export-confident.test.js +336 -0
  870. package/dist/tools/export-confident.test.js.map +1 -0
  871. package/dist/tools/export-datadog.d.ts +121 -0
  872. package/dist/tools/export-datadog.d.ts.map +1 -0
  873. package/dist/tools/export-datadog.js +158 -0
  874. package/dist/tools/export-datadog.js.map +1 -0
  875. package/dist/tools/export-datadog.test.d.ts +8 -0
  876. package/dist/tools/export-datadog.test.d.ts.map +1 -0
  877. package/dist/tools/export-datadog.test.js +376 -0
  878. package/dist/tools/export-datadog.test.js.map +1 -0
  879. package/dist/tools/export-jaeger.d.ts +100 -0
  880. package/dist/tools/export-jaeger.d.ts.map +1 -0
  881. package/dist/tools/export-jaeger.js +154 -0
  882. package/dist/tools/export-jaeger.js.map +1 -0
  883. package/dist/tools/export-jaeger.test.d.ts +2 -0
  884. package/dist/tools/export-jaeger.test.d.ts.map +1 -0
  885. package/dist/tools/export-jaeger.test.js +113 -0
  886. package/dist/tools/export-jaeger.test.js.map +1 -0
  887. package/dist/tools/export-langfuse.d.ts +135 -0
  888. package/dist/tools/export-langfuse.d.ts.map +1 -0
  889. package/dist/tools/export-langfuse.js +33 -0
  890. package/dist/tools/export-langfuse.js.map +1 -0
  891. package/dist/tools/export-langfuse.test.d.ts +7 -0
  892. package/dist/tools/export-langfuse.test.d.ts.map +1 -0
  893. package/dist/tools/export-langfuse.test.js +292 -0
  894. package/dist/tools/export-langfuse.test.js.map +1 -0
  895. package/dist/tools/export-phoenix.d.ts +170 -0
  896. package/dist/tools/export-phoenix.d.ts.map +1 -0
  897. package/dist/tools/export-phoenix.js +47 -0
  898. package/dist/tools/export-phoenix.js.map +1 -0
  899. package/dist/tools/export-phoenix.test.d.ts +7 -0
  900. package/dist/tools/export-phoenix.test.d.ts.map +1 -0
  901. package/dist/tools/export-phoenix.test.js +317 -0
  902. package/dist/tools/export-phoenix.test.js.map +1 -0
  903. package/dist/tools/get-trace-url.d.ts +2 -10
  904. package/dist/tools/get-trace-url.d.ts.map +1 -1
  905. package/dist/tools/get-trace-url.js +5 -8
  906. package/dist/tools/get-trace-url.js.map +1 -1
  907. package/dist/tools/get-trace-url.test.js +81 -399
  908. package/dist/tools/get-trace-url.test.js.map +1 -1
  909. package/dist/tools/hallucination-detection.d.ts +203 -0
  910. package/dist/tools/hallucination-detection.d.ts.map +1 -0
  911. package/dist/tools/hallucination-detection.js +189 -0
  912. package/dist/tools/hallucination-detection.js.map +1 -0
  913. package/dist/tools/hallucination-detection.test.d.ts +5 -0
  914. package/dist/tools/hallucination-detection.test.d.ts.map +1 -0
  915. package/dist/tools/hallucination-detection.test.js +529 -0
  916. package/dist/tools/hallucination-detection.test.js.map +1 -0
  917. package/dist/tools/health-check.d.ts +35 -16
  918. package/dist/tools/health-check.d.ts.map +1 -1
  919. package/dist/tools/health-check.js +101 -85
  920. package/dist/tools/health-check.js.map +1 -1
  921. package/dist/tools/health-check.test.js +72 -165
  922. package/dist/tools/health-check.test.js.map +1 -1
  923. package/dist/tools/index.d.ts +19 -0
  924. package/dist/tools/index.d.ts.map +1 -1
  925. package/dist/tools/index.js +19 -0
  926. package/dist/tools/index.js.map +1 -1
  927. package/dist/tools/ingest-constants.d.ts +8 -0
  928. package/dist/tools/ingest-constants.d.ts.map +1 -0
  929. package/dist/tools/ingest-constants.js +8 -0
  930. package/dist/tools/ingest-constants.js.map +1 -0
  931. package/dist/tools/ingest-spans.d.ts +45 -0
  932. package/dist/tools/ingest-spans.d.ts.map +1 -0
  933. package/dist/tools/ingest-spans.js +129 -0
  934. package/dist/tools/ingest-spans.js.map +1 -0
  935. package/dist/tools/ingest-spans.test.d.ts +5 -0
  936. package/dist/tools/ingest-spans.test.d.ts.map +1 -0
  937. package/dist/tools/ingest-spans.test.js +250 -0
  938. package/dist/tools/ingest-spans.test.js.map +1 -0
  939. package/dist/tools/ingest-traces.d.ts +76 -0
  940. package/dist/tools/ingest-traces.d.ts.map +1 -0
  941. package/dist/tools/ingest-traces.js +164 -0
  942. package/dist/tools/ingest-traces.js.map +1 -0
  943. package/dist/tools/ingest-traces.test.d.ts +5 -0
  944. package/dist/tools/ingest-traces.test.d.ts.map +1 -0
  945. package/dist/tools/ingest-traces.test.js +483 -0
  946. package/dist/tools/ingest-traces.test.js.map +1 -0
  947. package/dist/tools/inject-evaluations.d.ts +254 -0
  948. package/dist/tools/inject-evaluations.d.ts.map +1 -0
  949. package/dist/tools/inject-evaluations.js +133 -0
  950. package/dist/tools/inject-evaluations.js.map +1 -0
  951. package/dist/tools/inject-evaluations.test.d.ts +5 -0
  952. package/dist/tools/inject-evaluations.test.d.ts.map +1 -0
  953. package/dist/tools/inject-evaluations.test.js +371 -0
  954. package/dist/tools/inject-evaluations.test.js.map +1 -0
  955. package/dist/tools/manage-datasets.d.ts +850 -0
  956. package/dist/tools/manage-datasets.d.ts.map +1 -0
  957. package/dist/tools/manage-datasets.js +139 -0
  958. package/dist/tools/manage-datasets.js.map +1 -0
  959. package/dist/tools/manage-datasets.test.d.ts +5 -0
  960. package/dist/tools/manage-datasets.test.d.ts.map +1 -0
  961. package/dist/tools/manage-datasets.test.js +430 -0
  962. package/dist/tools/manage-datasets.test.js.map +1 -0
  963. package/dist/tools/multi-agent-coordination.d.ts +178 -0
  964. package/dist/tools/multi-agent-coordination.d.ts.map +1 -0
  965. package/dist/tools/multi-agent-coordination.js +270 -0
  966. package/dist/tools/multi-agent-coordination.js.map +1 -0
  967. package/dist/tools/multi-agent-coordination.test.d.ts +5 -0
  968. package/dist/tools/multi-agent-coordination.test.d.ts.map +1 -0
  969. package/dist/tools/multi-agent-coordination.test.js +530 -0
  970. package/dist/tools/multi-agent-coordination.test.js.map +1 -0
  971. package/dist/tools/query-evaluations.d.ts +154 -91
  972. package/dist/tools/query-evaluations.d.ts.map +1 -1
  973. package/dist/tools/query-evaluations.js +206 -169
  974. package/dist/tools/query-evaluations.js.map +1 -1
  975. package/dist/tools/query-evaluations.test.js +386 -391
  976. package/dist/tools/query-evaluations.test.js.map +1 -1
  977. package/dist/tools/query-llm-events.d.ts +100 -75
  978. package/dist/tools/query-llm-events.d.ts.map +1 -1
  979. package/dist/tools/query-llm-events.js +106 -80
  980. package/dist/tools/query-llm-events.js.map +1 -1
  981. package/dist/tools/query-llm-events.test.js +183 -346
  982. package/dist/tools/query-llm-events.test.js.map +1 -1
  983. package/dist/tools/query-logs.d.ts +45 -58
  984. package/dist/tools/query-logs.d.ts.map +1 -1
  985. package/dist/tools/query-logs.js +54 -101
  986. package/dist/tools/query-logs.js.map +1 -1
  987. package/dist/tools/query-logs.test.js +118 -314
  988. package/dist/tools/query-logs.test.js.map +1 -1
  989. package/dist/tools/query-metric-histograms.d.ts +112 -0
  990. package/dist/tools/query-metric-histograms.d.ts.map +1 -0
  991. package/dist/tools/query-metric-histograms.js +69 -0
  992. package/dist/tools/query-metric-histograms.js.map +1 -0
  993. package/dist/tools/query-metric-histograms.test.d.ts +5 -0
  994. package/dist/tools/query-metric-histograms.test.d.ts.map +1 -0
  995. package/dist/tools/query-metric-histograms.test.js +209 -0
  996. package/dist/tools/query-metric-histograms.test.js.map +1 -0
  997. package/dist/tools/query-metrics.d.ts +159 -60
  998. package/dist/tools/query-metrics.d.ts.map +1 -1
  999. package/dist/tools/query-metrics.js +133 -111
  1000. package/dist/tools/query-metrics.js.map +1 -1
  1001. package/dist/tools/query-metrics.test.js +314 -389
  1002. package/dist/tools/query-metrics.test.js.map +1 -1
  1003. package/dist/tools/query-regressions.d.ts +76 -0
  1004. package/dist/tools/query-regressions.d.ts.map +1 -0
  1005. package/dist/tools/query-regressions.js +122 -0
  1006. package/dist/tools/query-regressions.js.map +1 -0
  1007. package/dist/tools/query-regressions.test.d.ts +8 -0
  1008. package/dist/tools/query-regressions.test.d.ts.map +1 -0
  1009. package/dist/tools/query-regressions.test.js +129 -0
  1010. package/dist/tools/query-regressions.test.js.map +1 -0
  1011. package/dist/tools/query-traces.d.ts +103 -71
  1012. package/dist/tools/query-traces.d.ts.map +1 -1
  1013. package/dist/tools/query-traces.js +75 -106
  1014. package/dist/tools/query-traces.js.map +1 -1
  1015. package/dist/tools/query-traces.test.js +140 -846
  1016. package/dist/tools/query-traces.test.js.map +1 -1
  1017. package/dist/tools/query-verifications.d.ts +123 -0
  1018. package/dist/tools/query-verifications.d.ts.map +1 -0
  1019. package/dist/tools/query-verifications.js +102 -0
  1020. package/dist/tools/query-verifications.js.map +1 -0
  1021. package/dist/tools/query-verifications.test.d.ts +5 -0
  1022. package/dist/tools/query-verifications.test.d.ts.map +1 -0
  1023. package/dist/tools/query-verifications.test.js +163 -0
  1024. package/dist/tools/query-verifications.test.js.map +1 -0
  1025. package/dist/tools/routing-telemetry.d.ts +168 -0
  1026. package/dist/tools/routing-telemetry.d.ts.map +1 -0
  1027. package/dist/tools/routing-telemetry.js +267 -0
  1028. package/dist/tools/routing-telemetry.js.map +1 -0
  1029. package/dist/tools/routing-telemetry.test.d.ts +5 -0
  1030. package/dist/tools/routing-telemetry.test.d.ts.map +1 -0
  1031. package/dist/tools/routing-telemetry.test.js +747 -0
  1032. package/dist/tools/routing-telemetry.test.js.map +1 -0
  1033. package/dist/tools/setup-claudeignore.d.ts +4 -32
  1034. package/dist/tools/setup-claudeignore.d.ts.map +1 -1
  1035. package/dist/tools/setup-claudeignore.js +18 -22
  1036. package/dist/tools/setup-claudeignore.js.map +1 -1
  1037. package/dist/tools/setup-claudeignore.test.js +50 -49
  1038. package/dist/tools/setup-claudeignore.test.js.map +1 -1
  1039. package/dist/tools/token-budget.d.ts +170 -0
  1040. package/dist/tools/token-budget.d.ts.map +1 -0
  1041. package/dist/tools/token-budget.js +219 -0
  1042. package/dist/tools/token-budget.js.map +1 -0
  1043. package/dist/tools/token-budget.test.d.ts +5 -0
  1044. package/dist/tools/token-budget.test.d.ts.map +1 -0
  1045. package/dist/tools/token-budget.test.js +293 -0
  1046. package/dist/tools/token-budget.test.js.map +1 -0
  1047. package/package.json +76 -6
  1048. package/dist/backends/local-jsonl.test.d.ts +0 -2
  1049. package/dist/backends/local-jsonl.test.d.ts.map +0 -1
  1050. package/dist/backends/local-jsonl.test.js +0 -4651
  1051. package/dist/backends/local-jsonl.test.js.map +0 -1
  1052. package/dist/backends/signoz-api-circuit-breaker.test.d.ts +0 -6
  1053. package/dist/backends/signoz-api-circuit-breaker.test.d.ts.map +0 -1
  1054. package/dist/backends/signoz-api-circuit-breaker.test.js +0 -548
  1055. package/dist/backends/signoz-api-circuit-breaker.test.js.map +0 -1
  1056. package/dist/backends/signoz-api-rate-limiter.test.d.ts +0 -6
  1057. package/dist/backends/signoz-api-rate-limiter.test.d.ts.map +0 -1
  1058. package/dist/backends/signoz-api-rate-limiter.test.js +0 -389
  1059. package/dist/backends/signoz-api-rate-limiter.test.js.map +0 -1
  1060. package/dist/backends/signoz-api-ssrf.test.d.ts +0 -6
  1061. package/dist/backends/signoz-api-ssrf.test.d.ts.map +0 -1
  1062. package/dist/backends/signoz-api-ssrf.test.js +0 -216
  1063. package/dist/backends/signoz-api-ssrf.test.js.map +0 -1
  1064. package/dist/backends/signoz-api-test-helpers.d.ts +0 -80
  1065. package/dist/backends/signoz-api-test-helpers.d.ts.map +0 -1
  1066. package/dist/backends/signoz-api-test-helpers.js +0 -79
  1067. package/dist/backends/signoz-api-test-helpers.js.map +0 -1
  1068. package/dist/backends/signoz-api.d.ts +0 -95
  1069. package/dist/backends/signoz-api.d.ts.map +0 -1
  1070. package/dist/backends/signoz-api.integration.test.d.ts +0 -8
  1071. package/dist/backends/signoz-api.integration.test.d.ts.map +0 -1
  1072. package/dist/backends/signoz-api.integration.test.js +0 -137
  1073. package/dist/backends/signoz-api.integration.test.js.map +0 -1
  1074. package/dist/backends/signoz-api.js +0 -1016
  1075. package/dist/backends/signoz-api.js.map +0 -1
  1076. package/dist/backends/signoz-api.test.d.ts +0 -11
  1077. package/dist/backends/signoz-api.test.d.ts.map +0 -1
  1078. package/dist/backends/signoz-api.test.js +0 -831
  1079. package/dist/backends/signoz-api.test.js.map +0 -1
  1080. package/dist/lib/cache.d.ts +0 -77
  1081. package/dist/lib/cache.d.ts.map +0 -1
  1082. package/dist/lib/cache.js +0 -119
  1083. package/dist/lib/cache.js.map +0 -1
  1084. package/dist/lib/cache.test.d.ts.map +0 -1
  1085. package/dist/lib/cache.test.js.map +0 -1
  1086. package/dist/lib/circuit-breaker.d.ts +0 -83
  1087. package/dist/lib/circuit-breaker.d.ts.map +0 -1
  1088. package/dist/lib/circuit-breaker.js +0 -125
  1089. package/dist/lib/circuit-breaker.js.map +0 -1
  1090. package/dist/lib/circuit-breaker.test.d.ts.map +0 -1
  1091. package/dist/lib/circuit-breaker.test.js.map +0 -1
  1092. package/dist/lib/constants-symlink.test.d.ts.map +0 -1
  1093. package/dist/lib/constants-symlink.test.js.map +0 -1
  1094. package/dist/lib/constants.d.ts +0 -108
  1095. package/dist/lib/constants.d.ts.map +0 -1
  1096. package/dist/lib/constants.js +0 -350
  1097. package/dist/lib/constants.js.map +0 -1
  1098. package/dist/lib/constants.test.d.ts.map +0 -1
  1099. package/dist/lib/constants.test.js.map +0 -1
  1100. package/dist/lib/edge-cases.test.d.ts.map +0 -1
  1101. package/dist/lib/edge-cases.test.js.map +0 -1
  1102. package/dist/lib/error-sanitizer.d.ts.map +0 -1
  1103. package/dist/lib/error-sanitizer.js.map +0 -1
  1104. package/dist/lib/error-sanitizer.test.d.ts.map +0 -1
  1105. package/dist/lib/error-sanitizer.test.js.map +0 -1
  1106. package/dist/lib/error-types.d.ts.map +0 -1
  1107. package/dist/lib/error-types.js.map +0 -1
  1108. package/dist/lib/error-types.test.d.ts.map +0 -1
  1109. package/dist/lib/error-types.test.js.map +0 -1
  1110. package/dist/lib/file-utils.d.ts.map +0 -1
  1111. package/dist/lib/file-utils.js.map +0 -1
  1112. package/dist/lib/file-utils.test.d.ts.map +0 -1
  1113. package/dist/lib/file-utils.test.js.map +0 -1
  1114. package/dist/lib/indexer.d.ts +0 -96
  1115. package/dist/lib/indexer.d.ts.map +0 -1
  1116. package/dist/lib/indexer.js.map +0 -1
  1117. package/dist/lib/indexer.test.d.ts.map +0 -1
  1118. package/dist/lib/indexer.test.js.map +0 -1
  1119. package/dist/lib/input-validator.d.ts.map +0 -1
  1120. package/dist/lib/input-validator.fuzz.test.d.ts.map +0 -1
  1121. package/dist/lib/input-validator.fuzz.test.js.map +0 -1
  1122. package/dist/lib/input-validator.js.map +0 -1
  1123. package/dist/lib/input-validator.test.d.ts.map +0 -1
  1124. package/dist/lib/input-validator.test.js.map +0 -1
  1125. package/dist/lib/logger.d.ts.map +0 -1
  1126. package/dist/lib/logger.js +0 -81
  1127. package/dist/lib/logger.js.map +0 -1
  1128. package/dist/lib/logger.test.d.ts.map +0 -1
  1129. package/dist/lib/logger.test.js.map +0 -1
  1130. package/dist/lib/otlp-export.d.ts.map +0 -1
  1131. package/dist/lib/otlp-export.js.map +0 -1
  1132. package/dist/lib/query-sanitizer.d.ts.map +0 -1
  1133. package/dist/lib/query-sanitizer.js.map +0 -1
  1134. package/dist/lib/query-sanitizer.test.d.ts.map +0 -1
  1135. package/dist/lib/query-sanitizer.test.js.map +0 -1
  1136. package/dist/lib/server-utils.d.ts.map +0 -1
  1137. package/dist/lib/server-utils.js.map +0 -1
  1138. package/dist/lib/shared-schemas.d.ts +0 -81
  1139. package/dist/lib/shared-schemas.d.ts.map +0 -1
  1140. package/dist/lib/shared-schemas.js +0 -80
  1141. package/dist/lib/shared-schemas.js.map +0 -1
  1142. package/dist/lib/shared-schemas.test.d.ts.map +0 -1
  1143. package/dist/lib/shared-schemas.test.js.map +0 -1
  1144. package/dist/lib/toon-encoder.d.ts.map +0 -1
  1145. package/dist/lib/toon-encoder.js.map +0 -1
  1146. package/dist/lib/toon-encoder.test.d.ts.map +0 -1
  1147. package/dist/lib/toon-encoder.test.js.map +0 -1
  1148. package/dist/tools/signoz.integration.test.d.ts +0 -8
  1149. package/dist/tools/signoz.integration.test.d.ts.map +0 -1
  1150. package/dist/tools/signoz.integration.test.js +0 -141
  1151. package/dist/tools/signoz.integration.test.js.map +0 -1
  1152. /package/dist/lib/{constants-symlink.test.d.ts → core/constants-symlink.test.d.ts} +0 -0
  1153. /package/dist/lib/{constants.test.d.ts → core/constants.test.d.ts} +0 -0
  1154. /package/dist/lib/{edge-cases.test.d.ts → core/edge-cases.test.d.ts} +0 -0
  1155. /package/dist/lib/{file-utils.test.d.ts → core/file-utils.test.d.ts} +0 -0
  1156. /package/dist/lib/{input-validator.fuzz.test.d.ts → core/input-validator.fuzz.test.d.ts} +0 -0
  1157. /package/dist/lib/{input-validator.test.d.ts → core/input-validator.test.d.ts} +0 -0
  1158. /package/dist/lib/{logger.test.d.ts → core/logger.test.d.ts} +0 -0
  1159. /package/dist/lib/{logger.test.js → core/logger.test.js} +0 -0
  1160. /package/dist/lib/{shared-schemas.test.d.ts → core/shared-schemas.test.d.ts} +0 -0
  1161. /package/dist/lib/{error-sanitizer.test.d.ts → errors/error-sanitizer.test.d.ts} +0 -0
  1162. /package/dist/lib/{error-types.test.d.ts → errors/error-types.test.d.ts} +0 -0
  1163. /package/dist/lib/{query-sanitizer.d.ts → errors/query-sanitizer.d.ts} +0 -0
  1164. /package/dist/lib/{query-sanitizer.test.d.ts → errors/query-sanitizer.test.d.ts} +0 -0
  1165. /package/dist/lib/{indexer.test.d.ts → observability/indexer.test.d.ts} +0 -0
  1166. /package/dist/lib/{cache.test.d.ts → resilience/cache.test.d.ts} +0 -0
  1167. /package/dist/lib/{circuit-breaker.test.d.ts → resilience/circuit-breaker.test.d.ts} +0 -0
  1168. /package/dist/lib/{toon-encoder.test.d.ts → resilience/toon-encoder.test.d.ts} +0 -0
@@ -1,24 +1,38 @@
1
1
  /**
2
2
  * Local JSONL backend for reading telemetry data from ~/.claude/telemetry/
3
3
  *
4
- * The local telemetry files use a flat JSONL format where each line is a complete
5
- * span or log record, not the batched OpenTelemetry export format.
4
+ * Supports two JSONL line formats:
5
+ * - **Flat**: one span/log/metric per line with embedded resource (original hooks format)
6
+ * - **OTLP envelope**: one ResourceSpans/ResourceLogs/ResourceMetrics envelope per line
7
+ * (as produced by the L5 batch migration script or future OTLP writers)
8
+ *
9
+ * Format detection is automatic via the unwrap adapters (A4 items 1-6).
6
10
  */
7
11
  import { join } from 'path';
8
- import { GENAI_EVALUATION_ATTRIBUTES, } from './index.js';
9
- import { convertToOTLPTraces, convertToOTLPLogs, convertToOTLPMetrics, } from '../lib/otlp-export.js';
10
- import { TELEMETRY_DIR, getTelemetryDirectories, getSpanKind, getStatusCodeName } from '../lib/constants.js';
11
- import { listFiles, streamJsonl, parseDateFromFilename, getDateString, paginateResults, hasReachedLimit, } from '../lib/file-utils.js';
12
- import { QueryCache, makeCacheKey } from '../lib/cache.js';
13
- import { getIndexPath, readIndex, isIndexStale, queryIndex, readLinesByNumber, } from '../lib/indexer.js';
14
- import { sanitizePath } from '../lib/error-sanitizer.js';
15
- import { CircuitBreaker } from '../lib/circuit-breaker.js';
12
+ import { datasetRecordSchema, datasetTraceRecordSchema, datasetRunRecordSchema, rawEvaluatorTypeSchema, } from '../lib/validation/api-schemas.js';
13
+ import { homedir } from 'os';
14
+ import { GENAI_EVALUATION_ATTRIBUTES, GENAI_RESPONSE_ATTRIBUTES, GENAI_AGENT_ATTRIBUTES, AGENT_JUDGE_ATTRIBUTES, AGGREGATION_TEMPORALITY, } from './index.js';
15
+ import { convertToOTLPTraces, convertToOTLPLogs, convertToOTLPMetrics, } from '../lib/exports/otlp-export.js';
16
+ import { DEFAULT_QUERY_RESULT_LIMIT, DATASET_TRACE_TRUNCATION_LIMIT, MAX_STEP_SCORES, MAX_TOOL_VERIFICATIONS, HOOKS_TELEMETRY_DIR, WORST_FILES_LIMIT, getOtelLogSeverityNumber, getSpanKind, getStatusCodeName, getTelemetryDirectories, normalizeOtelLogSeverity } from '../lib/core/constants.js';
17
+ import { ONE_MILLION, NANOSECONDS_PER_SECOND, NANOSECONDS_PER_SECOND_BIGINT, TIME_MS, PERCENTILE, PERCENT_MULTIPLIER, } from '../lib/core/units.js';
18
+ import { listFiles, streamJsonl, parseDateFromFilename, getDateString, paginateResults, hasReachedLimit, } from '../lib/core/file-utils.js';
19
+ import { QueryCache, makeCacheKey, CACHE_TTL_MS, DEFAULT_CACHE_SIZE } from '../lib/resilience/cache.js';
20
+ import { recordQueryDuration, recordCacheHit } from '../lib/observability/metrics.js';
21
+ import { Histogram } from '../lib/observability/histogram.js';
22
+ import { getIndexPath, readIndex, isIndexStale, queryIndex, readLinesByNumber, } from '../lib/observability/indexer.js';
23
+ import { sanitizePath } from '../lib/errors/error-sanitizer.js';
24
+ import { CircuitBreaker } from '../lib/resilience/circuit-breaker.js';
25
+ import { withSpan } from '../lib/observability/instrumentation.js';
16
26
  import { existsSync } from 'fs';
27
+ import { appendFile, mkdir, readFile, writeFile, rename } from 'fs/promises';
28
+ import { randomUUID } from 'crypto';
29
+ import { ParseStatsTracker } from '../lib/observability/parse-stats.js';
30
+ import { createLogger } from '../lib/core/logger.js';
31
+ const queryLog = createLogger('query');
32
+ const configLog = createLogger('config');
17
33
  function startTiming() {
18
34
  const start = performance.now();
19
- return {
20
- end: () => performance.now() - start
21
- };
35
+ return { end: () => performance.now() - start };
22
36
  }
23
37
  const SLOW_QUERY_THRESHOLD_MS = 500;
24
38
  /**
@@ -29,29 +43,39 @@ const regexCache = new Map();
29
43
  const MAX_CACHED_REGEX_PATTERNS = 100;
30
44
  /**
31
45
  * Get a cached regex or compile and cache a new one.
46
+ * Accepts an optional pre-compiled RegExp to seed the cache on first miss (CR-L3).
32
47
  * Returns null for invalid patterns (logs warning).
33
48
  */
34
- function getCachedRegex(pattern) {
49
+ function getCachedRegex(pattern, precompiled) {
35
50
  const cached = regexCache.get(pattern);
36
51
  if (cached) {
52
+ // Move to end of Map for proper LRU ordering
53
+ regexCache.delete(pattern);
54
+ regexCache.set(pattern, cached);
37
55
  return cached;
38
56
  }
39
- try {
40
- const regex = new RegExp(pattern);
41
- // LRU eviction: remove oldest entry if at capacity
42
- if (regexCache.size >= MAX_CACHED_REGEX_PATTERNS) {
43
- const firstKey = regexCache.keys().next().value;
44
- if (firstKey !== undefined) {
45
- regexCache.delete(firstKey);
46
- }
57
+ // Use pre-compiled regex if provided (avoids double-compilation when validator returns RegExp)
58
+ const regex = precompiled ?? (() => {
59
+ try {
60
+ return new RegExp(pattern);
47
61
  }
48
- regexCache.set(pattern, regex);
49
- return regex;
50
- }
51
- catch {
52
- console.warn(`Invalid regex pattern: ${pattern}`);
62
+ catch {
63
+ const truncatedPattern = pattern.length > PERCENTILE.P50 ? pattern.slice(0, PERCENTILE.P50) + '...' : pattern;
64
+ configLog.warn(`Invalid regex pattern: ${truncatedPattern.replace(/[\n\r]/g, ' ')}`);
65
+ return null;
66
+ }
67
+ })();
68
+ if (!regex)
53
69
  return null;
70
+ // LRU eviction: remove oldest entry if at capacity
71
+ if (regexCache.size >= MAX_CACHED_REGEX_PATTERNS) {
72
+ const firstKey = regexCache.keys().next().value;
73
+ if (firstKey !== undefined) {
74
+ regexCache.delete(firstKey);
75
+ }
54
76
  }
77
+ regexCache.set(pattern, regex);
78
+ return regex;
55
79
  }
56
80
  /**
57
81
  * Insert item into a sorted array, maintaining sort order and max size.
@@ -132,11 +156,11 @@ function parseTimeBucket(bucket) {
132
156
  const unit = match[2];
133
157
  switch (unit) {
134
158
  case 'm':
135
- return value * 60 * 1000; // minutes to ms
159
+ return value * TIME_MS.MINUTE; // minutes to ms
136
160
  case 'h':
137
- return value * 60 * 60 * 1000; // hours to ms
161
+ return value * TIME_MS.HOUR; // hours to ms
138
162
  case 'd':
139
- return value * 24 * 60 * 60 * 1000; // days to ms
163
+ return value * TIME_MS.DAY; // days to ms
140
164
  default:
141
165
  return null;
142
166
  }
@@ -148,6 +172,325 @@ function floorToBucket(timestamp, bucketMs) {
148
172
  const ts = new Date(timestamp).getTime();
149
173
  return Math.floor(ts / bucketMs) * bucketMs;
150
174
  }
175
+ // ---------------------------------------------------------------------------
176
+ // OTLP envelope unwrap adapters (A4 items 1-6)
177
+ //
178
+ // When the L5 batch migration script converts flat JSONL to OTLP format,
179
+ // each JSONL line becomes an OTLP envelope wrapping one or more records.
180
+ // These adapters detect envelope lines and unwrap them to Flat* shapes
181
+ // so the existing normalize* functions can process them unchanged.
182
+ // ---------------------------------------------------------------------------
183
+ /** Reserved keys that must not be set on plain objects to prevent prototype pollution */
184
+ const RESERVED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
185
+ /** Convert OTLP KeyValue array to flat attributes record (null-prototype to prevent pollution) */
186
+ function fromOTLPAttributes(kvs) {
187
+ if (!kvs)
188
+ return Object.create(null);
189
+ const out = Object.create(null);
190
+ for (const kv of kvs) {
191
+ if (RESERVED_KEYS.has(kv.key))
192
+ continue;
193
+ out[kv.key] = fromOTLPValue(kv.value);
194
+ }
195
+ return out;
196
+ }
197
+ /** Convert a single OTLP AnyValue to a JS primitive/object */
198
+ function fromOTLPValue(v) {
199
+ if (!v)
200
+ return undefined;
201
+ if (v.stringValue !== undefined)
202
+ return v.stringValue;
203
+ if (v.intValue !== undefined) {
204
+ const n = Number(v.intValue);
205
+ return Number.isFinite(n) ? n : undefined;
206
+ }
207
+ if (v.doubleValue !== undefined)
208
+ return v.doubleValue;
209
+ if (v.boolValue !== undefined)
210
+ return v.boolValue;
211
+ if (v.arrayValue)
212
+ return v.arrayValue.values.map(fromOTLPValue);
213
+ if (v.kvlistValue)
214
+ return fromOTLPAttributes(v.kvlistValue.values);
215
+ return undefined;
216
+ }
217
+ /** Convert OTLP nanosecond string to [seconds, nanoseconds] tuple. Returns undefined on invalid input. */
218
+ function nanoStrToTuple(ns) {
219
+ if (!ns)
220
+ return undefined;
221
+ try {
222
+ const n = BigInt(ns);
223
+ return [
224
+ Number(n / NANOSECONDS_PER_SECOND_BIGINT),
225
+ Number(n % NANOSECONDS_PER_SECOND_BIGINT),
226
+ ];
227
+ }
228
+ catch {
229
+ return undefined;
230
+ }
231
+ }
232
+ /** Convert OTLP nanosecond string to epoch milliseconds. Returns undefined on invalid input. */
233
+ function nanoStrToMs(ns) {
234
+ if (!ns)
235
+ return undefined;
236
+ try {
237
+ return Number(BigInt(ns) / BigInt(ONE_MILLION));
238
+ }
239
+ catch {
240
+ return undefined;
241
+ }
242
+ }
243
+ /** Convert OTLP nanosecond string to ISO-8601 timestamp. Returns undefined on invalid input. */
244
+ function nanoStrToISO(ns) {
245
+ const ms = nanoStrToMs(ns);
246
+ return ms !== undefined ? new Date(ms).toISOString() : undefined;
247
+ }
248
+ /** Extract numeric value from OTLP data point (asDouble preferred, asInt fallback with precision guard). */
249
+ function dataPointValue(dp) {
250
+ if (dp.asDouble !== undefined) {
251
+ const d = Number(dp.asDouble);
252
+ return Number.isFinite(d) ? d : 0;
253
+ }
254
+ const n = Number(dp.asInt ?? '0');
255
+ return Number.isFinite(n) ? n : 0;
256
+ }
257
+ /**
258
+ * Extract service.name and service.version from OTLP resource attributes.
259
+ * Only these two fields are mapped; all other resource attributes (host.name,
260
+ * process.pid, telemetry.sdk.*, etc.) are intentionally dropped because flat
261
+ * records carry them in span-level attributes and the normalize* functions
262
+ * already handle that shape.
263
+ */
264
+ function extractResource(attrs) {
265
+ if (!attrs)
266
+ return undefined;
267
+ let serviceName;
268
+ let serviceVersion;
269
+ for (const kv of attrs) {
270
+ if (kv.key === 'service.name' && kv.value?.stringValue)
271
+ serviceName = kv.value.stringValue;
272
+ if (kv.key === 'service.version' && kv.value?.stringValue)
273
+ serviceVersion = kv.value.stringValue;
274
+ if (serviceName && serviceVersion)
275
+ break;
276
+ }
277
+ return (serviceName || serviceVersion) ? { serviceName, serviceVersion } : undefined;
278
+ }
279
+ /**
280
+ * Detect and unwrap an OTLP trace envelope line to FlatSpan[].
281
+ * Returns null if the record is not an OTLP envelope (i.e., it's a flat span).
282
+ */
283
+ function unwrapOTLPSpans(record) {
284
+ if (!record || typeof record !== 'object' || !('resourceSpans' in record))
285
+ return null;
286
+ const envelope = record;
287
+ const results = [];
288
+ for (const rs of envelope.resourceSpans) {
289
+ const resource = extractResource(rs.resource?.attributes);
290
+ for (const ss of rs.scopeSpans ?? []) {
291
+ const instrumentationScope = ss.scope
292
+ ? { name: ss.scope.name, version: ss.scope.version }
293
+ : undefined;
294
+ for (const span of ss.spans ?? []) {
295
+ const attrs = fromOTLPAttributes(span.attributes);
296
+ // Merge resource attributes into span attributes (service.name, etc.)
297
+ if (resource?.serviceName)
298
+ attrs['service.name'] = resource.serviceName;
299
+ if (resource?.serviceVersion)
300
+ attrs['service.version'] = resource.serviceVersion;
301
+ results.push({
302
+ traceId: span.traceId,
303
+ spanId: span.spanId,
304
+ parentSpanId: span.parentSpanId,
305
+ name: span.name,
306
+ kind: span.kind,
307
+ startTime: nanoStrToTuple(span.startTimeUnixNano),
308
+ endTime: nanoStrToTuple(span.endTimeUnixNano),
309
+ status: span.status,
310
+ attributes: attrs,
311
+ events: span.events?.map(e => ({
312
+ name: e.name,
313
+ timestamp: nanoStrToMs(e.timeUnixNano),
314
+ attributes: fromOTLPAttributes(e.attributes),
315
+ })),
316
+ links: span.links?.map(l => ({
317
+ context: { traceId: l.traceId, spanId: l.spanId },
318
+ attributes: fromOTLPAttributes(l.attributes),
319
+ })),
320
+ resource,
321
+ instrumentationScope,
322
+ });
323
+ }
324
+ }
325
+ }
326
+ return results;
327
+ }
328
+ /**
329
+ * Detect and unwrap an OTLP log envelope line to FlatLog[].
330
+ * Returns null if the record is not an OTLP envelope.
331
+ */
332
+ function unwrapOTLPLogs(record) {
333
+ if (!record || typeof record !== 'object' || !('resourceLogs' in record))
334
+ return null;
335
+ const envelope = record;
336
+ const results = [];
337
+ for (const rl of envelope.resourceLogs) {
338
+ const resource = extractResource(rl.resource?.attributes);
339
+ for (const sl of rl.scopeLogs ?? []) {
340
+ const instrumentationScope = sl.scope
341
+ ? { name: sl.scope.name, version: sl.scope.version }
342
+ : undefined;
343
+ for (const log of sl.logRecords ?? []) {
344
+ const attrs = fromOTLPAttributes(log.attributes);
345
+ const bodyValue = log.body;
346
+ results.push({
347
+ timestamp: nanoStrToISO(log.timeUnixNano) ?? '',
348
+ severityText: log.severityText,
349
+ body: bodyValue?.stringValue ?? '',
350
+ traceId: log.traceId,
351
+ spanId: log.spanId,
352
+ attributes: attrs,
353
+ resource: resource ? { serviceName: resource.serviceName } : undefined,
354
+ instrumentationScope,
355
+ });
356
+ }
357
+ }
358
+ }
359
+ return results;
360
+ }
361
+ /**
362
+ * Detect and unwrap an OTLP metric envelope line to FlatMetric[].
363
+ * Returns null if the record is not an OTLP envelope.
364
+ */
365
+ function unwrapOTLPMetrics(record) {
366
+ if (!record || typeof record !== 'object' || !('resourceMetrics' in record))
367
+ return null;
368
+ const envelope = record;
369
+ const results = [];
370
+ for (const rm of envelope.resourceMetrics) {
371
+ const resource = extractResource(rm.resource?.attributes);
372
+ for (const sm of rm.scopeMetrics ?? []) {
373
+ for (const metric of sm.metrics ?? []) {
374
+ const name = metric.name;
375
+ const unit = metric.unit;
376
+ // Determine metric type and extract data points
377
+ const gauge = metric.gauge;
378
+ const sum = metric.sum;
379
+ const histogram = metric.histogram;
380
+ if (histogram?.dataPoints) {
381
+ for (const dp of histogram.dataPoints) {
382
+ const attrs = fromOTLPAttributes(dp.attributes);
383
+ const bucketCounts = dp.bucketCounts ?? [];
384
+ const explicitBounds = dp.explicitBounds ?? [];
385
+ // Convert OTLP per-bucket counts + explicitBounds to cumulative {le, count}
386
+ let cumulative = 0;
387
+ const buckets = [];
388
+ for (let i = 0; i < bucketCounts.length; i++) {
389
+ cumulative += Number(bucketCounts[i]);
390
+ buckets.push({ le: i < explicitBounds.length ? explicitBounds[i] : Infinity, count: cumulative });
391
+ }
392
+ const histSum = dp.sum ?? 0;
393
+ results.push({
394
+ timestamp: nanoStrToISO(dp.timeUnixNano) ?? '',
395
+ name,
396
+ value: histSum,
397
+ type: 'histogram',
398
+ unit,
399
+ attributes: attrs,
400
+ resource: resource ? { serviceName: resource.serviceName } : undefined,
401
+ histogram: { buckets, sum: histSum, count: Number(dp.count ?? 0) },
402
+ aggregationTemporality: histogram.aggregationTemporality,
403
+ });
404
+ }
405
+ }
406
+ else if (sum?.dataPoints) {
407
+ for (const dp of sum.dataPoints) {
408
+ const attrs = fromOTLPAttributes(dp.attributes);
409
+ results.push({
410
+ timestamp: nanoStrToISO(dp.timeUnixNano) ?? '',
411
+ name,
412
+ value: dataPointValue(dp),
413
+ type: 'counter',
414
+ unit,
415
+ attributes: attrs,
416
+ resource: resource ? { serviceName: resource.serviceName } : undefined,
417
+ aggregationTemporality: sum.aggregationTemporality,
418
+ });
419
+ }
420
+ }
421
+ else if (gauge?.dataPoints) {
422
+ for (const dp of gauge.dataPoints) {
423
+ const attrs = fromOTLPAttributes(dp.attributes);
424
+ results.push({
425
+ timestamp: nanoStrToISO(dp.timeUnixNano) ?? '',
426
+ name,
427
+ value: dataPointValue(dp),
428
+ type: 'gauge',
429
+ unit,
430
+ attributes: attrs,
431
+ resource: resource ? { serviceName: resource.serviceName } : undefined,
432
+ });
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }
438
+ return results;
439
+ }
440
+ /**
441
+ * Detect and unwrap an OTLP trace envelope containing evaluation events.
442
+ * Evaluations in OTLP are span events with name "gen_ai.evaluation.result".
443
+ * Returns null if the record is not an OTLP envelope.
444
+ */
445
+ function unwrapOTLPEvaluations(record) {
446
+ if (!record || typeof record !== 'object' || !('resourceSpans' in record))
447
+ return null;
448
+ const envelope = record;
449
+ const results = [];
450
+ for (const rs of envelope.resourceSpans) {
451
+ for (const ss of rs.scopeSpans ?? []) {
452
+ for (const span of ss.spans ?? []) {
453
+ for (const event of span.events ?? []) {
454
+ if (event.name !== 'gen_ai.evaluation.result')
455
+ continue;
456
+ const attrs = fromOTLPAttributes(event.attributes);
457
+ results.push({
458
+ timestamp: nanoStrToISO(event.timeUnixNano) ?? '',
459
+ name: event.name,
460
+ attributes: attrs,
461
+ traceId: span.traceId,
462
+ spanId: span.spanId,
463
+ });
464
+ }
465
+ }
466
+ }
467
+ }
468
+ // Return null (not []) when envelope contains no evaluation events,
469
+ // so flattenEvaluationRecord falls through to the passthrough path.
470
+ // This prevents a flat record that coincidentally has a 'resourceSpans'
471
+ // key from being silently swallowed.
472
+ return results.length > 0 ? results : null;
473
+ }
474
+ /** Unwrap-or-passthrough for trace records. OTLP envelopes yield multiple spans; flat records yield one. */
475
+ function flattenSpanRecord(raw) {
476
+ const unwrapped = unwrapOTLPSpans(raw);
477
+ return unwrapped ?? [raw];
478
+ }
479
+ /** Unwrap-or-passthrough for log records */
480
+ function flattenLogRecord(raw) {
481
+ const unwrapped = unwrapOTLPLogs(raw);
482
+ return unwrapped ?? [raw];
483
+ }
484
+ /** Unwrap-or-passthrough for metric records */
485
+ function flattenMetricRecord(raw) {
486
+ const unwrapped = unwrapOTLPMetrics(raw);
487
+ return unwrapped ?? [raw];
488
+ }
489
+ /** Unwrap-or-passthrough for evaluation records */
490
+ function flattenEvaluationRecord(raw) {
491
+ const unwrapped = unwrapOTLPEvaluations(raw);
492
+ return unwrapped ?? [raw];
493
+ }
151
494
  /**
152
495
  * Extract a field from an object using dot notation path.
153
496
  * Returns undefined if the path doesn't exist or the object is not traversable.
@@ -156,7 +499,7 @@ function extractField(obj, path) {
156
499
  const parts = path.split('.');
157
500
  let current = obj;
158
501
  for (const part of parts) {
159
- if (current == null || typeof current !== 'object')
502
+ if (current === null || typeof current !== 'object')
160
503
  return undefined;
161
504
  current = current[part];
162
505
  }
@@ -201,21 +544,95 @@ function extractValidNumber(value) {
201
544
  return undefined;
202
545
  return Number.isFinite(value) ? value : undefined;
203
546
  }
204
- /** Valid evaluator types per our schema */
205
- const VALID_EVALUATOR_TYPES = ['llm', 'human', 'rule', 'classifier'];
206
547
  /**
207
- * Extract and validate evaluatorType
548
+ * Extract and validate evaluatorType using schema-based normalization (NARROW-11).
208
549
  */
209
550
  function extractValidEvaluatorType(value) {
210
551
  if (typeof value !== 'string')
211
552
  return undefined;
212
- const trimmed = value.trim().toLowerCase();
213
- return VALID_EVALUATOR_TYPES.includes(trimmed) ? trimmed : undefined;
553
+ const result = rawEvaluatorTypeSchema.safeParse(value);
554
+ return result.success ? result.data : undefined;
214
555
  }
215
556
  /**
216
557
  * Convert flat evaluation to normalized EvaluationResult
217
558
  * Includes runtime validation for data integrity (P0-2 fix)
218
559
  */
560
+ /**
561
+ * Safely extract StepScore array from raw attribute value.
562
+ * Returns undefined if not a valid array or if parsing fails.
563
+ */
564
+ function extractStepScores(value) {
565
+ if (!value)
566
+ return undefined;
567
+ // If it's a string, try to parse as JSON
568
+ let arr;
569
+ if (typeof value === 'string') {
570
+ try {
571
+ arr = JSON.parse(value);
572
+ if (!Array.isArray(arr))
573
+ return undefined;
574
+ }
575
+ catch {
576
+ return undefined;
577
+ }
578
+ }
579
+ else if (Array.isArray(value)) {
580
+ arr = value;
581
+ }
582
+ else {
583
+ return undefined;
584
+ }
585
+ // Validate each element and filter invalid entries
586
+ return arr
587
+ .filter((item) => {
588
+ if (!item || typeof item !== 'object')
589
+ return false;
590
+ const obj = item;
591
+ return ((typeof obj.step === 'string' || typeof obj.step === 'number') &&
592
+ typeof obj.score === 'number' &&
593
+ Number.isFinite(obj.score));
594
+ })
595
+ .slice(0, MAX_STEP_SCORES); // Respect MAX_STEP_SCORES limit
596
+ }
597
+ /**
598
+ * Safely extract ToolVerification array from raw attribute value.
599
+ * Returns undefined if not a valid array or if parsing fails.
600
+ */
601
+ function extractToolVerifications(value) {
602
+ if (!value)
603
+ return undefined;
604
+ // If it's a string, try to parse as JSON
605
+ let arr;
606
+ if (typeof value === 'string') {
607
+ try {
608
+ arr = JSON.parse(value);
609
+ if (!Array.isArray(arr))
610
+ return undefined;
611
+ }
612
+ catch {
613
+ return undefined;
614
+ }
615
+ }
616
+ else if (Array.isArray(value)) {
617
+ arr = value;
618
+ }
619
+ else {
620
+ return undefined;
621
+ }
622
+ // Validate each element and filter invalid entries
623
+ return arr
624
+ .filter((item) => {
625
+ if (!item || typeof item !== 'object')
626
+ return false;
627
+ const obj = item;
628
+ return (typeof obj.toolName === 'string' &&
629
+ typeof obj.toolCorrect === 'boolean' &&
630
+ typeof obj.argsCorrect === 'boolean' &&
631
+ typeof obj.score === 'number' &&
632
+ Number.isFinite(obj.score));
633
+ })
634
+ .slice(0, MAX_TOOL_VERIFICATIONS);
635
+ }
219
636
  function normalizeEvaluation(raw) {
220
637
  const attrs = raw.attributes;
221
638
  // evaluationName is required per OTel spec - must be non-empty string
@@ -235,24 +652,18 @@ function normalizeEvaluation(raw) {
235
652
  // Evaluator fields (custom extension)
236
653
  evaluator: extractValidString(attrs?.[GENAI_EVALUATION_ATTRIBUTES.EVALUATOR]),
237
654
  evaluatorType: extractValidEvaluatorType(attrs?.[GENAI_EVALUATION_ATTRIBUTES.EVALUATOR_TYPE]),
238
- responseId: extractValidString(attrs?.['gen_ai.response.id']),
655
+ responseId: extractValidString(attrs?.[GENAI_RESPONSE_ATTRIBUTES.ID]),
239
656
  traceId: raw.traceId,
240
657
  spanId: raw.spanId,
241
658
  sessionId: extractValidString(attrs?.['session.id']),
659
+ // Agent-as-Judge fields (Section 10.7)
660
+ agentId: extractValidString(attrs?.[GENAI_AGENT_ATTRIBUTES.AGENT_ID]),
661
+ agentName: extractValidString(attrs?.[GENAI_AGENT_ATTRIBUTES.AGENT_NAME]),
662
+ stepScores: extractStepScores(attrs?.[AGENT_JUDGE_ATTRIBUTES.STEP_SCORES]),
663
+ toolVerifications: extractToolVerifications(attrs?.[AGENT_JUDGE_ATTRIBUTES.TOOL_VERIFICATIONS]),
664
+ trajectoryLength: extractValidNumber(attrs?.[AGENT_JUDGE_ATTRIBUTES.TRAJECTORY_LENGTH]),
242
665
  };
243
666
  }
244
- /**
245
- * OTel-compliant severity number mapping
246
- * https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber
247
- */
248
- const SEVERITY_MAP = {
249
- TRACE: 1,
250
- DEBUG: 5,
251
- INFO: 9,
252
- WARN: 13,
253
- ERROR: 17,
254
- FATAL: 21,
255
- };
256
667
  /**
257
668
  * Convert flat span to normalized TraceSpan
258
669
  */
@@ -261,18 +672,18 @@ function normalizeSpan(raw) {
261
672
  return null;
262
673
  // Convert [seconds, nanoseconds] to nanoseconds
263
674
  const startTimeUnixNano = raw.startTime
264
- ? raw.startTime[0] * 1_000_000_000 + raw.startTime[1]
675
+ ? raw.startTime[0] * NANOSECONDS_PER_SECOND + raw.startTime[1]
265
676
  : 0;
266
677
  const endTimeUnixNano = raw.endTime
267
- ? raw.endTime[0] * 1_000_000_000 + raw.endTime[1]
678
+ ? raw.endTime[0] * NANOSECONDS_PER_SECOND + raw.endTime[1]
268
679
  : undefined;
269
680
  // Calculate duration in ms
270
681
  let durationMs;
271
682
  if (raw.duration) {
272
- durationMs = (raw.duration[0] * 1000) + (raw.duration[1] / 1_000_000);
683
+ durationMs = (raw.duration[0] * TIME_MS.SECOND) + (raw.duration[1] / ONE_MILLION);
273
684
  }
274
685
  else if (endTimeUnixNano) {
275
- durationMs = (endTimeUnixNano - startTimeUnixNano) / 1_000_000;
686
+ durationMs = (endTimeUnixNano - startTimeUnixNano) / ONE_MILLION;
276
687
  }
277
688
  // Add service name to attributes if present in resource
278
689
  const attributes = { ...raw.attributes };
@@ -324,14 +735,15 @@ function normalizeLog(raw, extractFields) {
324
735
  // Handle timestamp: either ISO string or [seconds, nanoseconds] array
325
736
  let timestamp;
326
737
  if (Array.isArray(raw.timestamp)) {
327
- const ms = raw.timestamp[0] * 1000 + Math.floor(raw.timestamp[1] / 1_000_000);
738
+ const ms = raw.timestamp[0] * TIME_MS.SECOND + Math.floor(raw.timestamp[1] / ONE_MILLION);
328
739
  timestamp = new Date(ms).toISOString();
329
740
  }
330
741
  else {
331
742
  timestamp = raw.timestamp;
332
743
  }
333
- const severity = raw.severityText || raw.severity || 'INFO';
334
- const severityNumber = SEVERITY_MAP[severity.toUpperCase()];
744
+ const rawSeverity = raw.severityText || raw.severity || 'INFO';
745
+ const severity = normalizeOtelLogSeverity(rawSeverity) ?? rawSeverity;
746
+ const severityNumber = getOtelLogSeverityNumber(severity);
335
747
  const body = raw.body || '';
336
748
  // Extract fields from JSON body if requested
337
749
  let extractedFields;
@@ -368,11 +780,11 @@ function normalizeAggregationTemporality(value) {
368
780
  }
369
781
  // Handle numeric values (OTel spec)
370
782
  switch (value) {
371
- case 1:
783
+ case AGGREGATION_TEMPORALITY.DELTA:
372
784
  return 'DELTA';
373
- case 2:
785
+ case AGGREGATION_TEMPORALITY.CUMULATIVE:
374
786
  return 'CUMULATIVE';
375
- case 0:
787
+ case AGGREGATION_TEMPORALITY.UNSPECIFIED:
376
788
  default:
377
789
  return 'UNSPECIFIED';
378
790
  }
@@ -386,7 +798,7 @@ function normalizeExemplarTimestamp(ts, metricTimestamp) {
386
798
  if (typeof ts === 'string')
387
799
  return ts;
388
800
  // Convert [seconds, nanoseconds] to ISO string
389
- const ms = ts[0] * 1000 + Math.floor(ts[1] / 1_000_000);
801
+ const ms = ts[0] * TIME_MS.SECOND + Math.floor(ts[1] / ONE_MILLION);
390
802
  return new Date(ms).toISOString();
391
803
  }
392
804
  /**
@@ -434,10 +846,12 @@ function normalizeMetric(raw) {
434
846
  };
435
847
  }
436
848
  /**
437
- * Get files within date range
849
+ * Get files within date range.
850
+ * allowedBases constrains which directories listFiles may access (CR-H2).
851
+ * Callers should pass the telemetryDir as a base so test-provided directories are allowed.
438
852
  */
439
- function getFilesInRange(dir, pattern, startDate, endDate) {
440
- const files = listFiles(dir, pattern);
853
+ function getFilesInRange(dir, pattern, startDate, endDate, allowedBases) {
854
+ const files = listFiles(dir, pattern, allowedBases);
441
855
  if (!startDate && !endDate) {
442
856
  // Default to today only
443
857
  const today = getDateString();
@@ -457,20 +871,47 @@ function getFilesInRange(dir, pattern, startDate, endDate) {
457
871
  return true;
458
872
  });
459
873
  }
874
+ function encodeCursor(payload) {
875
+ return Buffer.from(JSON.stringify(payload)).toString('base64url');
876
+ }
877
+ function decodeCursor(cursor) {
878
+ try {
879
+ return JSON.parse(Buffer.from(cursor, 'base64url').toString());
880
+ }
881
+ catch {
882
+ return null;
883
+ }
884
+ }
460
885
  export class LocalJsonlBackend {
461
886
  name = 'local-jsonl';
462
887
  telemetryDir;
463
- traceCache = new QueryCache();
464
- logCache = new QueryCache();
465
- metricCache = new QueryCache();
466
- llmEventCache = new QueryCache();
467
- evaluationCache = new QueryCache();
888
+ /** Security: directories listFiles is allowed to access (CR-H2) */
889
+ allowedBases;
890
+ traceCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'traces');
891
+ logCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'logs');
892
+ metricCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'metrics');
893
+ llmEventCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'llmEvents');
894
+ evaluationCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'evaluations');
468
895
  useIndexes;
469
896
  circuitBreaker;
897
+ parseStatsTracker;
898
+ /** Count of trace lines that expanded to multiple spans (OTLP envelopes), across both indexed and full-scan paths */
899
+ otlpTraceExpansions = 0;
900
+ /** Histograms for tracking query latency per type */
901
+ queryHistograms = {
902
+ traces: new Histogram(),
903
+ logs: new Histogram(),
904
+ metrics: new Histogram(),
905
+ llmEvents: new Histogram(),
906
+ evaluations: new Histogram(),
907
+ };
470
908
  constructor(telemetryDir, useIndexes = true) {
471
- this.telemetryDir = telemetryDir || TELEMETRY_DIR;
909
+ this.telemetryDir = telemetryDir || HOOKS_TELEMETRY_DIR;
472
910
  this.useIndexes = useIndexes;
473
911
  this.circuitBreaker = new CircuitBreaker({ name: 'local-file-io' });
912
+ this.parseStatsTracker = new ParseStatsTracker();
913
+ // Allow telemetryDir itself so custom dirs (including test temp dirs) are permitted (CR-H2)
914
+ this.allowedBases = [homedir(), process.cwd(), this.telemetryDir];
474
915
  }
475
916
  /**
476
917
  * Get circuit breaker state (for health check and testing)
@@ -506,6 +947,45 @@ export class LocalJsonlBackend {
506
947
  evaluations: this.evaluationCache.getStats(),
507
948
  };
508
949
  }
950
+ /**
951
+ * Get parse statistics for JSONL file processing.
952
+ * Returns aggregate stats including success rate and worst files.
953
+ */
954
+ getParseStats() {
955
+ return {
956
+ ...this.parseStatsTracker.getAggregateStats(),
957
+ otlpTraceExpansions: this.otlpTraceExpansions,
958
+ };
959
+ }
960
+ /**
961
+ * Reset parse statistics (useful for testing)
962
+ */
963
+ resetParseStats() {
964
+ this.parseStatsTracker.reset();
965
+ this.otlpTraceExpansions = 0;
966
+ }
967
+ /**
968
+ * Get query latency statistics per query type
969
+ */
970
+ getQueryStats() {
971
+ return {
972
+ traces: this.queryHistograms.traces.getStats(),
973
+ logs: this.queryHistograms.logs.getStats(),
974
+ metrics: this.queryHistograms.metrics.getStats(),
975
+ llmEvents: this.queryHistograms.llmEvents.getStats(),
976
+ evaluations: this.queryHistograms.evaluations.getStats(),
977
+ };
978
+ }
979
+ /**
980
+ * Reset query latency histograms (useful for testing)
981
+ */
982
+ resetQueryStats() {
983
+ this.queryHistograms.traces.reset();
984
+ this.queryHistograms.logs.reset();
985
+ this.queryHistograms.metrics.reset();
986
+ this.queryHistograms.llmEvents.reset();
987
+ this.queryHistograms.evaluations.reset();
988
+ }
509
989
  /**
510
990
  * Try to use an index for a file, returning matching line numbers or null if full scan needed
511
991
  */
@@ -521,365 +1001,474 @@ export class LocalJsonlBackend {
521
1001
  return queryIndex(index, indexOptions);
522
1002
  }
523
1003
  async queryTraces(options) {
524
- const timer = startTiming();
525
- const logTiming = () => {
526
- const durationMs = timer.end();
527
- if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
528
- console.warn(`[obs-toolkit] Slow query: queryTraces took ${durationMs.toFixed(1)}ms`);
1004
+ // P2 fix: Defer attribute allocation until span is actually created
1005
+ return withSpan('obs_toolkit.query.traces', () => ({
1006
+ 'obs_toolkit.query.type': 'traces',
1007
+ 'obs_toolkit.query.backend': 'local',
1008
+ 'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
1009
+ 'obs_toolkit.query.start_date': options.startDate,
1010
+ 'obs_toolkit.query.end_date': options.endDate,
1011
+ }), async (span) => {
1012
+ const timer = startTiming();
1013
+ const logTiming = () => {
1014
+ const durationMs = timer.end();
1015
+ recordQueryDuration('traces', durationMs, 'local');
1016
+ this.queryHistograms.traces.observe(durationMs);
1017
+ if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
1018
+ queryLog.warn(`Slow query: queryTraces took ${durationMs.toFixed(1)}ms`, { durationMs });
1019
+ }
1020
+ };
1021
+ // Check circuit breaker - fail fast if open
1022
+ if (!this.circuitBreaker.canRequest()) {
1023
+ logTiming();
1024
+ if (span)
1025
+ span.setAttribute('obs_toolkit.query.result_count', 0);
1026
+ return [];
529
1027
  }
530
- };
531
- // Check circuit breaker - fail fast if open
532
- if (!this.circuitBreaker.canRequest()) {
533
- logTiming();
534
- return [];
535
- }
536
- // Check cache first
537
- const cacheKey = makeCacheKey('traces', options);
538
- const cached = this.traceCache.get(cacheKey);
539
- if (cached) {
540
- logTiming();
541
- return cached;
542
- }
543
- const files = getFilesInRange(this.telemetryDir, /traces-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate);
544
- const results = [];
545
- const limit = options.limit || 100;
546
- const offset = options.offset || 0;
547
- // Use cached regex to avoid recompilation for frequently-used patterns
548
- const spanNameRegex = options.spanNameRegex
549
- ? getCachedRegex(options.spanNameRegex)
550
- : null;
551
- // Build index query options for indexable filters
552
- const indexOptions = {
553
- traceId: options.traceId,
554
- spanName: options.spanName,
555
- serviceName: options.serviceName,
556
- };
557
- // Helper to apply non-indexable filters to a span
558
- const applyFilters = (span) => {
559
- // Regex filter (not indexable)
560
- if (spanNameRegex && !spanNameRegex.test(span.name))
561
- return false;
562
- if (options.excludeSpanName && span.name.includes(options.excludeSpanName))
563
- return false;
564
- if (options.minDurationMs && (span.durationMs || 0) < options.minDurationMs)
565
- return false;
566
- if (options.maxDurationMs && (span.durationMs || Infinity) > options.maxDurationMs)
567
- return false;
568
- // Apply attribute filter
569
- if (options.attributeFilter) {
570
- for (const [key, value] of Object.entries(options.attributeFilter)) {
571
- if (span.attributes?.[key] !== value)
572
- return false;
1028
+ // Check cache first
1029
+ const cacheKey = makeCacheKey('traces', options);
1030
+ const cached = this.traceCache.get(cacheKey);
1031
+ if (cached) {
1032
+ logTiming();
1033
+ // M3 fix: Record cache hit for metrics and span event
1034
+ recordCacheHit('traces');
1035
+ if (span) {
1036
+ span.addEvent('cache_hit');
1037
+ span.setAttribute('obs_toolkit.query.result_count', cached.length);
573
1038
  }
1039
+ return cached;
574
1040
  }
575
- // Apply attributeExists filter
576
- if (options.attributeExists) {
577
- for (const key of options.attributeExists) {
578
- if (span.attributes?.[key] === undefined)
1041
+ const files = getFilesInRange(this.telemetryDir, /traces-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
1042
+ const results = [];
1043
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1044
+ const offset = options.offset || 0;
1045
+ // Use cached regex to avoid recompilation for frequently-used patterns
1046
+ const spanNameRegex = options.spanNameRegex
1047
+ ? getCachedRegex(options.spanNameRegex)
1048
+ : null;
1049
+ // Build index query options for indexable filters
1050
+ const indexOptions = {
1051
+ traceId: options.traceId,
1052
+ spanName: options.spanName,
1053
+ serviceName: options.serviceName,
1054
+ };
1055
+ // Helper to apply non-indexable filters to a span
1056
+ const applyFilters = (s) => {
1057
+ // Regex filter (not indexable)
1058
+ if (spanNameRegex && !spanNameRegex.test(s.name))
1059
+ return false;
1060
+ if (options.excludeSpanName && s.name.includes(options.excludeSpanName))
1061
+ return false;
1062
+ if (options.minDurationMs && (s.durationMs || 0) < options.minDurationMs)
1063
+ return false;
1064
+ if (options.maxDurationMs && (s.durationMs || Infinity) > options.maxDurationMs)
1065
+ return false;
1066
+ // Apply attribute filter
1067
+ if (options.attributeFilter) {
1068
+ for (const [key, value] of Object.entries(options.attributeFilter)) {
1069
+ if (s.attributes?.[key] !== value)
1070
+ return false;
1071
+ }
1072
+ }
1073
+ // Apply attributeExists filter
1074
+ if (options.attributeExists) {
1075
+ for (const key of options.attributeExists) {
1076
+ if (s.attributes?.[key] === undefined)
1077
+ return false;
1078
+ }
1079
+ }
1080
+ // Apply attributeNotExists filter
1081
+ if (options.attributeNotExists) {
1082
+ for (const key of options.attributeNotExists) {
1083
+ if (s.attributes?.[key] !== undefined)
1084
+ return false;
1085
+ }
1086
+ }
1087
+ // Apply numeric filter conditions
1088
+ if (options.numericFilter && options.numericFilter.length > 0) {
1089
+ if (!applyNumericFilters(s.attributes, options.numericFilter))
579
1090
  return false;
580
1091
  }
581
- }
582
- // Apply attributeNotExists filter
583
- if (options.attributeNotExists) {
584
- for (const key of options.attributeNotExists) {
585
- if (span.attributes?.[key] !== undefined)
1092
+ // OTel GenAI agent/tool filters
1093
+ if (options.agentId && s.attributes?.['gen_ai.agent.id'] !== options.agentId)
1094
+ return false;
1095
+ if (options.agentName && s.attributes?.['gen_ai.agent.name'] !== options.agentName)
1096
+ return false;
1097
+ if (options.agentDescription) {
1098
+ const desc = s.attributes?.['gen_ai.agent.description'];
1099
+ if (typeof desc !== 'string' || !desc.toLowerCase().includes(options.agentDescription.toLowerCase()))
586
1100
  return false;
587
1101
  }
588
- }
589
- // Apply numeric filter conditions
590
- if (options.numericFilter && options.numericFilter.length > 0) {
591
- if (!applyNumericFilters(span.attributes, options.numericFilter))
1102
+ if (options.agentVersion) {
1103
+ const ver = s.attributes?.['gen_ai.agent.version'];
1104
+ if (typeof ver !== 'string' || !ver.toLowerCase().includes(options.agentVersion.toLowerCase()))
1105
+ return false;
1106
+ }
1107
+ if (options.toolName && s.attributes?.['gen_ai.tool.name'] !== options.toolName)
592
1108
  return false;
593
- }
594
- // OTel GenAI agent/tool filters
595
- if (options.agentId && span.attributes?.['gen_ai.agent.id'] !== options.agentId)
596
- return false;
597
- if (options.agentName && span.attributes?.['gen_ai.agent.name'] !== options.agentName)
598
- return false;
599
- if (options.toolName && span.attributes?.['gen_ai.tool.name'] !== options.toolName)
600
- return false;
601
- if (options.toolCallId && span.attributes?.['gen_ai.tool.call.id'] !== options.toolCallId)
602
- return false;
603
- if (options.toolType && span.attributes?.['gen_ai.tool.type'] !== options.toolType)
604
- return false;
605
- if (options.operationName && span.attributes?.['gen_ai.operation.name'] !== options.operationName)
606
- return false;
607
- return true;
608
- };
609
- try {
610
- for (const file of files) {
611
- // Try to use index for pre-filtering
612
- const matchingLines = this.tryUseIndex(file, 'traces', indexOptions);
613
- if (matchingLines !== null) {
614
- // Use indexed query - read only matching lines
615
- const rawRecords = await readLinesByNumber(file, matchingLines);
616
- for (const raw of rawRecords) {
617
- const span = normalizeSpan(raw);
618
- if (!span)
619
- continue;
620
- if (!applyFilters(span))
621
- continue;
622
- results.push(span);
623
- if (hasReachedLimit(results.length, offset, limit)) {
624
- const paginated = paginateResults(results, offset, limit);
625
- this.traceCache.set(cacheKey, paginated);
626
- this.circuitBreaker.recordSuccess();
627
- logTiming();
628
- return paginated;
629
- }
630
- }
1109
+ if (options.toolCallId && s.attributes?.['gen_ai.tool.call.id'] !== options.toolCallId)
1110
+ return false;
1111
+ if (options.toolType && s.attributes?.['gen_ai.tool.type'] !== options.toolType)
1112
+ return false;
1113
+ if (options.operationName && s.attributes?.['gen_ai.operation.name'] !== options.operationName)
1114
+ return false;
1115
+ if (options.outputType) {
1116
+ const ot = s.attributes?.['gen_ai.output.type'];
1117
+ if (typeof ot !== 'string' || ot.toLowerCase() !== options.outputType.toLowerCase())
1118
+ return false;
631
1119
  }
632
- else {
633
- // Fall back to full file scan
634
- for await (const raw of streamJsonl(file)) {
635
- const span = normalizeSpan(raw);
636
- if (!span)
637
- continue;
638
- // Apply indexable filters (since no index was used)
639
- if (options.traceId && span.traceId !== options.traceId)
640
- continue;
641
- if (options.spanName && !span.name.includes(options.spanName))
642
- continue;
643
- if (options.serviceName) {
644
- const svc = span.attributes?.['service.name'];
645
- if (svc !== options.serviceName)
646
- continue;
1120
+ return true;
1121
+ };
1122
+ try {
1123
+ for (const file of files) {
1124
+ // Try to use index for pre-filtering
1125
+ const matchingLines = this.tryUseIndex(file, 'traces', indexOptions);
1126
+ if (matchingLines !== null) {
1127
+ // Use indexed query - read only matching lines
1128
+ // Note: index was built against flat records. For OTLP envelope lines,
1129
+ // multi-span envelopes may have only one traceId indexed per line.
1130
+ // The full-scan fallback path handles these correctly.
1131
+ const rawRecords = await readLinesByNumber(file, matchingLines);
1132
+ for (const rawLine of rawRecords) {
1133
+ const expanded = flattenSpanRecord(rawLine);
1134
+ if (expanded.length > 1)
1135
+ this.otlpTraceExpansions++;
1136
+ for (const raw of expanded) {
1137
+ const traceSpan = normalizeSpan(raw);
1138
+ if (!traceSpan)
1139
+ continue;
1140
+ if (!applyFilters(traceSpan))
1141
+ continue;
1142
+ results.push(traceSpan);
1143
+ if (hasReachedLimit(results.length, offset, limit)) {
1144
+ const paginated = paginateResults(results, offset, limit);
1145
+ this.traceCache.set(cacheKey, paginated);
1146
+ this.circuitBreaker.recordSuccess();
1147
+ logTiming();
1148
+ if (span)
1149
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1150
+ return paginated;
1151
+ }
1152
+ }
647
1153
  }
648
- if (!applyFilters(span))
649
- continue;
650
- results.push(span);
651
- if (hasReachedLimit(results.length, offset, limit)) {
652
- const paginated = paginateResults(results, offset, limit);
653
- this.traceCache.set(cacheKey, paginated);
654
- this.circuitBreaker.recordSuccess();
655
- logTiming();
656
- return paginated;
1154
+ }
1155
+ else {
1156
+ // Fall back to full file scan
1157
+ for await (const rawLine of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
1158
+ const expanded = flattenSpanRecord(rawLine);
1159
+ if (expanded.length > 1)
1160
+ this.otlpTraceExpansions++;
1161
+ for (const raw of expanded) {
1162
+ const traceSpan = normalizeSpan(raw);
1163
+ if (!traceSpan)
1164
+ continue;
1165
+ // Apply indexable filters (since no index was used)
1166
+ if (options.traceId && traceSpan.traceId !== options.traceId)
1167
+ continue;
1168
+ if (options.spanName && !traceSpan.name.includes(options.spanName))
1169
+ continue;
1170
+ if (options.serviceName) {
1171
+ const svc = traceSpan.attributes?.['service.name'];
1172
+ if (svc !== options.serviceName)
1173
+ continue;
1174
+ }
1175
+ if (!applyFilters(traceSpan))
1176
+ continue;
1177
+ results.push(traceSpan);
1178
+ if (hasReachedLimit(results.length, offset, limit)) {
1179
+ const paginated = paginateResults(results, offset, limit);
1180
+ this.traceCache.set(cacheKey, paginated);
1181
+ this.circuitBreaker.recordSuccess();
1182
+ logTiming();
1183
+ if (span)
1184
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1185
+ return paginated;
1186
+ }
1187
+ }
657
1188
  }
658
1189
  }
659
1190
  }
1191
+ const paginated = paginateResults(results, offset, limit);
1192
+ this.traceCache.set(cacheKey, paginated);
1193
+ this.circuitBreaker.recordSuccess();
1194
+ logTiming();
1195
+ if (span)
1196
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1197
+ return paginated;
660
1198
  }
661
- const paginated = paginateResults(results, offset, limit);
662
- this.traceCache.set(cacheKey, paginated);
663
- this.circuitBreaker.recordSuccess();
664
- logTiming();
665
- return paginated;
666
- }
667
- catch (error) {
668
- this.circuitBreaker.recordFailure();
669
- logTiming();
670
- throw error;
671
- }
1199
+ catch (error) {
1200
+ this.circuitBreaker.recordFailure();
1201
+ logTiming();
1202
+ throw error;
1203
+ }
1204
+ });
672
1205
  }
673
1206
  async queryLogs(options) {
674
- const timer = startTiming();
675
- const logTiming = () => {
676
- const durationMs = timer.end();
677
- if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
678
- console.warn(`[obs-toolkit] Slow query: queryLogs took ${durationMs.toFixed(1)}ms`);
679
- }
680
- };
681
- // Check circuit breaker - fail fast if open
682
- if (!this.circuitBreaker.canRequest()) {
683
- logTiming();
684
- return [];
685
- }
686
- // Check cache first
687
- const cacheKey = makeCacheKey('logs', options);
688
- const cached = this.logCache.get(cacheKey);
689
- if (cached) {
690
- logTiming();
691
- return cached;
692
- }
693
- const files = getFilesInRange(this.telemetryDir, /logs-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate);
694
- const results = [];
695
- const limit = options.limit || 100;
696
- const offset = options.offset || 0;
697
- // Build index query options for indexable filters
698
- const indexOptions = {
699
- traceId: options.traceId,
700
- severity: options.severity,
701
- };
702
- // Helper to apply non-indexable filters to a log
703
- const applyFilters = (log) => {
704
- if (options.search && !log.body.toLowerCase().includes(options.search.toLowerCase()))
705
- return false;
706
- // Apply boolean search with multiple terms
707
- if (options.searchTerms && options.searchTerms.length > 0) {
708
- const bodyLower = log.body.toLowerCase();
709
- const operator = options.searchOperator || 'AND';
710
- if (operator === 'AND') {
711
- const allMatch = options.searchTerms.every(term => bodyLower.includes(term.toLowerCase()));
712
- if (!allMatch)
713
- return false;
714
- }
715
- else {
716
- const anyMatch = options.searchTerms.some(term => bodyLower.includes(term.toLowerCase()));
717
- if (!anyMatch)
718
- return false;
1207
+ // P2 fix: Defer attribute allocation until span is actually created
1208
+ return withSpan('obs_toolkit.query.logs', () => ({
1209
+ 'obs_toolkit.query.type': 'logs',
1210
+ 'obs_toolkit.query.backend': 'local',
1211
+ 'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
1212
+ 'obs_toolkit.query.start_date': options.startDate,
1213
+ 'obs_toolkit.query.end_date': options.endDate,
1214
+ }), async (span) => {
1215
+ const timer = startTiming();
1216
+ const logTiming = () => {
1217
+ const durationMs = timer.end();
1218
+ recordQueryDuration('logs', durationMs, 'local');
1219
+ this.queryHistograms.logs.observe(durationMs);
1220
+ if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
1221
+ queryLog.warn(`Slow query: queryLogs took ${durationMs.toFixed(1)}ms`, { durationMs });
719
1222
  }
1223
+ };
1224
+ // Check circuit breaker - fail fast if open
1225
+ if (!this.circuitBreaker.canRequest()) {
1226
+ logTiming();
1227
+ if (span)
1228
+ span.setAttribute('obs_toolkit.query.result_count', 0);
1229
+ return [];
720
1230
  }
721
- if (options.excludeSearch && log.body.toLowerCase().includes(options.excludeSearch.toLowerCase()))
722
- return false;
723
- // Apply attributeExists filter
724
- if (options.attributeExists) {
725
- for (const key of options.attributeExists) {
726
- if (log.attributes?.[key] === undefined)
727
- return false;
1231
+ // Check cache first
1232
+ const cacheKey = makeCacheKey('logs', options);
1233
+ const cached = this.logCache.get(cacheKey);
1234
+ if (cached) {
1235
+ logTiming();
1236
+ // M3 fix: Record cache hit for metrics and span event
1237
+ recordCacheHit('logs');
1238
+ if (span) {
1239
+ span.addEvent('cache_hit');
1240
+ span.setAttribute('obs_toolkit.query.result_count', cached.length);
728
1241
  }
1242
+ return cached;
729
1243
  }
730
- // Apply attributeNotExists filter
731
- if (options.attributeNotExists) {
732
- for (const key of options.attributeNotExists) {
733
- if (log.attributes?.[key] !== undefined)
734
- return false;
1244
+ const files = getFilesInRange(this.telemetryDir, /logs-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
1245
+ const results = [];
1246
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1247
+ const offset = options.offset || 0;
1248
+ // Build index query options for indexable filters
1249
+ const indexOptions = {
1250
+ traceId: options.traceId,
1251
+ severity: options.severity,
1252
+ };
1253
+ // Helper to apply non-indexable filters to a log
1254
+ const applyFilters = (logRecord) => {
1255
+ if (options.search && !logRecord.body.toLowerCase().includes(options.search.toLowerCase()))
1256
+ return false;
1257
+ // Apply boolean search with multiple terms
1258
+ if (options.searchTerms && options.searchTerms.length > 0) {
1259
+ const bodyLower = logRecord.body.toLowerCase();
1260
+ const operator = options.searchOperator || 'AND';
1261
+ if (operator === 'AND') {
1262
+ const allMatch = options.searchTerms.every(term => bodyLower.includes(term.toLowerCase()));
1263
+ if (!allMatch)
1264
+ return false;
1265
+ }
1266
+ else {
1267
+ const anyMatch = options.searchTerms.some(term => bodyLower.includes(term.toLowerCase()));
1268
+ if (!anyMatch)
1269
+ return false;
1270
+ }
735
1271
  }
736
- }
737
- // Apply numeric filter conditions
738
- if (options.numericFilter && options.numericFilter.length > 0) {
739
- if (!applyNumericFilters(log.attributes, options.numericFilter))
1272
+ if (options.excludeSearch && logRecord.body.toLowerCase().includes(options.excludeSearch.toLowerCase()))
740
1273
  return false;
741
- }
742
- return true;
743
- };
744
- try {
745
- for (const file of files) {
746
- // Try to use index for pre-filtering
747
- const matchingLines = this.tryUseIndex(file, 'logs', indexOptions);
748
- if (matchingLines !== null) {
749
- // Use indexed query - read only matching lines
750
- const rawRecords = await readLinesByNumber(file, matchingLines);
751
- for (const raw of rawRecords) {
752
- const log = normalizeLog(raw, options.extractFields);
753
- if (!log)
754
- continue;
755
- if (!applyFilters(log))
756
- continue;
757
- results.push(log);
758
- if (hasReachedLimit(results.length, offset, limit)) {
759
- const paginated = paginateResults(results, offset, limit);
760
- this.logCache.set(cacheKey, paginated);
761
- this.circuitBreaker.recordSuccess();
762
- logTiming();
763
- return paginated;
764
- }
1274
+ // Apply attributeExists filter
1275
+ if (options.attributeExists) {
1276
+ for (const key of options.attributeExists) {
1277
+ if (logRecord.attributes?.[key] === undefined)
1278
+ return false;
1279
+ }
1280
+ }
1281
+ // Apply attributeNotExists filter
1282
+ if (options.attributeNotExists) {
1283
+ for (const key of options.attributeNotExists) {
1284
+ if (logRecord.attributes?.[key] !== undefined)
1285
+ return false;
765
1286
  }
766
1287
  }
767
- else {
768
- // Fall back to full file scan
769
- for await (const raw of streamJsonl(file)) {
770
- const log = normalizeLog(raw, options.extractFields);
771
- if (!log)
772
- continue;
773
- // Apply indexable filters (since no index was used)
774
- if (options.severity && log.severity.toUpperCase() !== options.severity.toUpperCase())
775
- continue;
776
- if (options.traceId && log.traceId !== options.traceId)
777
- continue;
778
- if (!applyFilters(log))
779
- continue;
780
- results.push(log);
781
- if (hasReachedLimit(results.length, offset, limit)) {
782
- const paginated = paginateResults(results, offset, limit);
783
- this.logCache.set(cacheKey, paginated);
784
- this.circuitBreaker.recordSuccess();
785
- logTiming();
786
- return paginated;
1288
+ // Apply numeric filter conditions
1289
+ if (options.numericFilter && options.numericFilter.length > 0) {
1290
+ if (!applyNumericFilters(logRecord.attributes, options.numericFilter))
1291
+ return false;
1292
+ }
1293
+ return true;
1294
+ };
1295
+ try {
1296
+ for (const file of files) {
1297
+ // Try to use index for pre-filtering
1298
+ const matchingLines = this.tryUseIndex(file, 'logs', indexOptions);
1299
+ if (matchingLines !== null) {
1300
+ // Use indexed query - read only matching lines
1301
+ const rawRecords = await readLinesByNumber(file, matchingLines);
1302
+ for (const rawLine of rawRecords) {
1303
+ for (const raw of flattenLogRecord(rawLine)) {
1304
+ const logRecord = normalizeLog(raw, options.extractFields);
1305
+ if (!logRecord)
1306
+ continue;
1307
+ if (!applyFilters(logRecord))
1308
+ continue;
1309
+ results.push(logRecord);
1310
+ if (hasReachedLimit(results.length, offset, limit)) {
1311
+ const paginated = paginateResults(results, offset, limit);
1312
+ this.logCache.set(cacheKey, paginated);
1313
+ this.circuitBreaker.recordSuccess();
1314
+ logTiming();
1315
+ if (span)
1316
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1317
+ return paginated;
1318
+ }
1319
+ }
1320
+ }
1321
+ }
1322
+ else {
1323
+ // Fall back to full file scan
1324
+ for await (const rawLine of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
1325
+ for (const raw of flattenLogRecord(rawLine)) {
1326
+ const logRecord = normalizeLog(raw, options.extractFields);
1327
+ if (!logRecord)
1328
+ continue;
1329
+ // Apply indexable filters (since no index was used)
1330
+ if (options.severity && logRecord.severity !== options.severity)
1331
+ continue;
1332
+ if (options.traceId && logRecord.traceId !== options.traceId)
1333
+ continue;
1334
+ if (!applyFilters(logRecord))
1335
+ continue;
1336
+ results.push(logRecord);
1337
+ if (hasReachedLimit(results.length, offset, limit)) {
1338
+ const paginated = paginateResults(results, offset, limit);
1339
+ this.logCache.set(cacheKey, paginated);
1340
+ this.circuitBreaker.recordSuccess();
1341
+ logTiming();
1342
+ if (span)
1343
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1344
+ return paginated;
1345
+ }
1346
+ }
787
1347
  }
788
1348
  }
789
1349
  }
1350
+ const paginated = paginateResults(results, offset, limit);
1351
+ this.logCache.set(cacheKey, paginated);
1352
+ this.circuitBreaker.recordSuccess();
1353
+ logTiming();
1354
+ if (span)
1355
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1356
+ return paginated;
790
1357
  }
791
- const paginated = paginateResults(results, offset, limit);
792
- this.logCache.set(cacheKey, paginated);
793
- this.circuitBreaker.recordSuccess();
794
- logTiming();
795
- return paginated;
796
- }
797
- catch (error) {
798
- this.circuitBreaker.recordFailure();
799
- logTiming();
800
- throw error;
801
- }
1358
+ catch (error) {
1359
+ this.circuitBreaker.recordFailure();
1360
+ logTiming();
1361
+ throw error;
1362
+ }
1363
+ });
802
1364
  }
803
1365
  async queryMetrics(options) {
804
- const timer = startTiming();
805
- const logTiming = () => {
806
- const durationMs = timer.end();
807
- if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
808
- console.warn(`[obs-toolkit] Slow query: queryMetrics took ${durationMs.toFixed(1)}ms`);
1366
+ // P2 fix: Defer attribute allocation until span is actually created
1367
+ return withSpan('obs_toolkit.query.metrics', () => ({
1368
+ 'obs_toolkit.query.type': 'metrics',
1369
+ 'obs_toolkit.query.backend': 'local',
1370
+ 'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
1371
+ 'obs_toolkit.query.start_date': options.startDate,
1372
+ 'obs_toolkit.query.end_date': options.endDate,
1373
+ }), async (span) => {
1374
+ const timer = startTiming();
1375
+ const logTiming = () => {
1376
+ const durationMs = timer.end();
1377
+ recordQueryDuration('metrics', durationMs, 'local');
1378
+ this.queryHistograms.metrics.observe(durationMs);
1379
+ if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
1380
+ queryLog.warn(`Slow query: queryMetrics took ${durationMs.toFixed(1)}ms`, { durationMs });
1381
+ }
1382
+ };
1383
+ // Check circuit breaker - fail fast if open
1384
+ if (!this.circuitBreaker.canRequest()) {
1385
+ logTiming();
1386
+ if (span)
1387
+ span.setAttribute('obs_toolkit.query.result_count', 0);
1388
+ return [];
809
1389
  }
810
- };
811
- // Check circuit breaker - fail fast if open
812
- if (!this.circuitBreaker.canRequest()) {
813
- logTiming();
814
- return [];
815
- }
816
- // Check cache first
817
- const cacheKey = makeCacheKey('metrics', options);
818
- const cached = this.metricCache.get(cacheKey);
819
- if (cached) {
820
- logTiming();
821
- return cached;
822
- }
823
- const files = getFilesInRange(this.telemetryDir, /metrics-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate);
824
- const results = [];
825
- const limit = options.limit || 100;
826
- const offset = options.offset || 0;
827
- // Build index query options for indexable filters
828
- const indexOptions = {
829
- metricName: options.metricName,
830
- };
831
- try {
832
- outer: for (const file of files) {
833
- // Try to use index for pre-filtering
834
- const matchingLines = this.tryUseIndex(file, 'metrics', indexOptions);
835
- if (matchingLines !== null) {
836
- // Use indexed query - read only matching lines
837
- const rawRecords = await readLinesByNumber(file, matchingLines);
838
- for (const raw of rawRecords) {
839
- const point = normalizeMetric(raw);
840
- if (!point)
841
- continue;
842
- results.push(point);
843
- if (hasReachedLimit(results.length, offset, limit)) {
844
- break outer;
1390
+ // Check cache first
1391
+ const cacheKey = makeCacheKey('metrics', options);
1392
+ const cached = this.metricCache.get(cacheKey);
1393
+ if (cached) {
1394
+ logTiming();
1395
+ // M3 fix: Record cache hit for metrics and span event
1396
+ recordCacheHit('metrics');
1397
+ if (span) {
1398
+ span.addEvent('cache_hit');
1399
+ span.setAttribute('obs_toolkit.query.result_count', cached.length);
1400
+ }
1401
+ return cached;
1402
+ }
1403
+ const files = getFilesInRange(this.telemetryDir, /metrics-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
1404
+ const results = [];
1405
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1406
+ const offset = options.offset || 0;
1407
+ // Build index query options for indexable filters
1408
+ const indexOptions = {
1409
+ metricName: options.metricName,
1410
+ };
1411
+ try {
1412
+ outer: for (const file of files) {
1413
+ // Try to use index for pre-filtering
1414
+ const matchingLines = this.tryUseIndex(file, 'metrics', indexOptions);
1415
+ if (matchingLines !== null) {
1416
+ // Use indexed query - read only matching lines
1417
+ const rawRecords = await readLinesByNumber(file, matchingLines);
1418
+ for (const rawLine of rawRecords) {
1419
+ for (const raw of flattenMetricRecord(rawLine)) {
1420
+ const point = normalizeMetric(raw);
1421
+ if (!point)
1422
+ continue;
1423
+ results.push(point);
1424
+ if (hasReachedLimit(results.length, offset, limit)) {
1425
+ break outer;
1426
+ }
1427
+ }
845
1428
  }
846
1429
  }
847
- }
848
- else {
849
- // Fall back to full file scan
850
- for await (const raw of streamJsonl(file)) {
851
- const point = normalizeMetric(raw);
852
- if (!point)
853
- continue;
854
- // Apply filters (since no index was used)
855
- if (options.metricName && !point.name.includes(options.metricName))
856
- continue;
857
- results.push(point);
858
- if (hasReachedLimit(results.length, offset, limit)) {
859
- break outer;
1430
+ else {
1431
+ // Fall back to full file scan
1432
+ for await (const rawLine of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
1433
+ for (const raw of flattenMetricRecord(rawLine)) {
1434
+ const point = normalizeMetric(raw);
1435
+ if (!point)
1436
+ continue;
1437
+ // Apply filters (since no index was used)
1438
+ if (options.metricName && !point.name.includes(options.metricName))
1439
+ continue;
1440
+ results.push(point);
1441
+ if (hasReachedLimit(results.length, offset, limit)) {
1442
+ break outer;
1443
+ }
1444
+ }
860
1445
  }
861
1446
  }
862
1447
  }
863
- }
864
- // Apply aggregation if requested
865
- if (options.aggregation && results.length > 0) {
866
- const aggregated = this.aggregate(results, options.aggregation, options.groupBy, options.timeBucket);
867
- this.metricCache.set(cacheKey, aggregated);
1448
+ // Apply aggregation if requested
1449
+ if (options.aggregation && results.length > 0) {
1450
+ const aggregated = this.aggregate(results, options.aggregation, options.groupBy, options.timeBucket);
1451
+ this.metricCache.set(cacheKey, aggregated);
1452
+ this.circuitBreaker.recordSuccess();
1453
+ logTiming();
1454
+ if (span)
1455
+ span.setAttribute('obs_toolkit.query.result_count', aggregated.length);
1456
+ return aggregated;
1457
+ }
1458
+ const paginated = paginateResults(results, offset, limit);
1459
+ this.metricCache.set(cacheKey, paginated);
868
1460
  this.circuitBreaker.recordSuccess();
869
1461
  logTiming();
870
- return aggregated;
1462
+ if (span)
1463
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1464
+ return paginated;
871
1465
  }
872
- const paginated = paginateResults(results, offset, limit);
873
- this.metricCache.set(cacheKey, paginated);
874
- this.circuitBreaker.recordSuccess();
875
- logTiming();
876
- return paginated;
877
- }
878
- catch (error) {
879
- this.circuitBreaker.recordFailure();
880
- logTiming();
881
- throw error;
882
- }
1466
+ catch (error) {
1467
+ this.circuitBreaker.recordFailure();
1468
+ logTiming();
1469
+ throw error;
1470
+ }
1471
+ });
883
1472
  }
884
1473
  aggregate(points, aggregation, groupBy, timeBucket) {
885
1474
  // Parse time bucket if provided
@@ -926,13 +1515,13 @@ export class LocalJsonlBackend {
926
1515
  value = values.length;
927
1516
  break;
928
1517
  case 'p50':
929
- value = this.calculatePercentile(values, 50);
1518
+ value = this.calculatePercentile(values, PERCENTILE.P50);
930
1519
  break;
931
1520
  case 'p95':
932
- value = this.calculatePercentile(values, 95);
1521
+ value = this.calculatePercentile(values, PERCENTILE.P95);
933
1522
  break;
934
1523
  case 'p99':
935
- value = this.calculatePercentile(values, 99);
1524
+ value = this.calculatePercentile(values, PERCENTILE.P99);
936
1525
  break;
937
1526
  case 'rate':
938
1527
  value = this.calculateRate(group);
@@ -962,7 +1551,7 @@ export class LocalJsonlBackend {
962
1551
  if (values.length === 0)
963
1552
  return 0;
964
1553
  const sorted = [...values].sort((a, b) => a - b);
965
- const index = Math.ceil((percentile / 100) * sorted.length) - 1;
1554
+ const index = Math.ceil((percentile / PERCENT_MULTIPLIER) * sorted.length) - 1;
966
1555
  return sorted[Math.max(0, index)];
967
1556
  }
968
1557
  /**
@@ -971,7 +1560,8 @@ export class LocalJsonlBackend {
971
1560
  * Edge cases: single value returns 0, same timestamp returns 0
972
1561
  */
973
1562
  calculateRate(group) {
974
- if (group.length < 2)
1563
+ const MIN_POINTS_FOR_RATE = 2;
1564
+ if (group.length < MIN_POINTS_FOR_RATE)
975
1565
  return 0;
976
1566
  // Sort by timestamp
977
1567
  const sorted = [...group].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
@@ -983,250 +1573,665 @@ export class LocalJsonlBackend {
983
1573
  // Avoid division by zero when timestamps are the same
984
1574
  if (durationMs === 0)
985
1575
  return 0;
986
- const durationSeconds = durationMs / 1000;
1576
+ const durationSeconds = durationMs / TIME_MS.SECOND;
987
1577
  return (lastPoint.value - firstPoint.value) / durationSeconds;
988
1578
  }
989
1579
  async queryLLMEvents(options) {
990
- const timer = startTiming();
991
- const logTiming = () => {
992
- const durationMs = timer.end();
993
- if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
994
- console.warn(`[obs-toolkit] Slow query: queryLLMEvents took ${durationMs.toFixed(1)}ms`);
1580
+ // P2 fix: Defer attribute allocation until span is actually created
1581
+ return withSpan('obs_toolkit.query.llm_events', () => ({
1582
+ 'obs_toolkit.query.type': 'llm_events',
1583
+ 'obs_toolkit.query.backend': 'local',
1584
+ 'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
1585
+ 'obs_toolkit.query.start_date': options.startDate,
1586
+ 'obs_toolkit.query.end_date': options.endDate,
1587
+ }), async (span) => {
1588
+ const timer = startTiming();
1589
+ const logTiming = () => {
1590
+ const durationMs = timer.end();
1591
+ recordQueryDuration('llm_events', durationMs, 'local');
1592
+ this.queryHistograms.llmEvents.observe(durationMs);
1593
+ if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
1594
+ queryLog.warn(`Slow query: queryLLMEvents took ${durationMs.toFixed(1)}ms`, { durationMs });
1595
+ }
1596
+ };
1597
+ // Check circuit breaker - fail fast if open
1598
+ if (!this.circuitBreaker.canRequest()) {
1599
+ logTiming();
1600
+ if (span)
1601
+ span.setAttribute('obs_toolkit.query.result_count', 0);
1602
+ return [];
995
1603
  }
996
- };
997
- // Check circuit breaker - fail fast if open
998
- if (!this.circuitBreaker.canRequest()) {
999
- logTiming();
1000
- return [];
1001
- }
1002
- // Check cache first
1003
- const cacheKey = makeCacheKey('llm-events', options);
1004
- const cached = this.llmEventCache.get(cacheKey);
1005
- if (cached) {
1006
- logTiming();
1007
- return cached;
1008
- }
1009
- const files = getFilesInRange(this.telemetryDir, /llm-events-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate);
1010
- const results = [];
1011
- const limit = options.limit || 100;
1012
- const offset = options.offset || 0;
1013
- // Build index query options - eventName maps to spanName in index
1014
- const indexOptions = {
1015
- spanName: options.eventName,
1016
- };
1017
- // Helper to apply non-indexable filters to an event
1018
- const applyFilters = (event) => {
1019
- if (options.model) {
1020
- const model = event.attributes?.['gen_ai.request.model'] || event.attributes?.['model'];
1021
- if (model !== options.model)
1022
- return false;
1604
+ // Check cache first
1605
+ const cacheKey = makeCacheKey('llm-events', options);
1606
+ const cached = this.llmEventCache.get(cacheKey);
1607
+ if (cached) {
1608
+ logTiming();
1609
+ // M3 fix: Record cache hit for metrics and span event
1610
+ recordCacheHit('llmEvents');
1611
+ if (span) {
1612
+ span.addEvent('cache_hit');
1613
+ span.setAttribute('obs_toolkit.query.result_count', cached.length);
1614
+ }
1615
+ return cached;
1023
1616
  }
1024
- if (options.provider) {
1025
- // OTel GenAI compliant: gen_ai.provider.name -> gen_ai.system -> provider
1026
- const provider = event.attributes?.['gen_ai.provider.name'] ||
1027
- event.attributes?.['gen_ai.system'] ||
1028
- event.attributes?.['provider'];
1029
- if (provider !== options.provider)
1030
- return false;
1617
+ const files = getFilesInRange(this.telemetryDir, /llm-events-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
1618
+ const results = [];
1619
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1620
+ const offset = options.offset || 0;
1621
+ // Build index query options - eventName maps to spanName in index
1622
+ const indexOptions = {
1623
+ spanName: options.eventName,
1624
+ };
1625
+ // Helper to apply non-indexable filters to an event
1626
+ const applyFilters = (event) => {
1627
+ if (options.model) {
1628
+ const model = event.attributes?.['gen_ai.request.model'] || event.attributes?.['model'];
1629
+ if (model !== options.model)
1630
+ return false;
1631
+ }
1632
+ if (options.provider) {
1633
+ // OTel GenAI compliant: gen_ai.provider.name -> gen_ai.system -> provider
1634
+ const provider = event.attributes?.['gen_ai.provider.name'] ||
1635
+ event.attributes?.['gen_ai.system'] ||
1636
+ event.attributes?.['provider'];
1637
+ if (provider !== options.provider)
1638
+ return false;
1639
+ }
1640
+ if (options.operationName) {
1641
+ const opName = event.attributes?.['gen_ai.operation.name'];
1642
+ if (opName !== options.operationName)
1643
+ return false;
1644
+ }
1645
+ if (options.conversationId) {
1646
+ const convId = event.attributes?.['gen_ai.conversation.id'];
1647
+ if (convId !== options.conversationId)
1648
+ return false;
1649
+ }
1650
+ if (options.search) {
1651
+ const searchLower = options.search.toLowerCase();
1652
+ const attrStr = JSON.stringify(event.attributes).toLowerCase();
1653
+ if (!attrStr.includes(searchLower) && !event.name.toLowerCase().includes(searchLower))
1654
+ return false;
1655
+ }
1656
+ return true;
1657
+ };
1658
+ try {
1659
+ for (const file of files) {
1660
+ // Try to use index for pre-filtering
1661
+ const matchingLines = this.tryUseIndex(file, 'llm-events', indexOptions);
1662
+ if (matchingLines !== null) {
1663
+ // Use indexed query - read only matching lines
1664
+ const rawRecords = await readLinesByNumber(file, matchingLines);
1665
+ for (const event of rawRecords) {
1666
+ if (!event.timestamp || !event.name)
1667
+ continue;
1668
+ if (!applyFilters(event))
1669
+ continue;
1670
+ results.push({
1671
+ timestamp: event.timestamp,
1672
+ name: event.name,
1673
+ attributes: event.attributes,
1674
+ });
1675
+ if (hasReachedLimit(results.length, offset, limit)) {
1676
+ const paginated = paginateResults(results, offset, limit);
1677
+ this.llmEventCache.set(cacheKey, paginated);
1678
+ this.circuitBreaker.recordSuccess();
1679
+ logTiming();
1680
+ if (span)
1681
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1682
+ return paginated;
1683
+ }
1684
+ }
1685
+ }
1686
+ else {
1687
+ // Fall back to full file scan
1688
+ for await (const event of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
1689
+ if (!event.timestamp || !event.name)
1690
+ continue;
1691
+ // Apply indexable filters (since no index was used)
1692
+ if (options.eventName && !event.name.includes(options.eventName))
1693
+ continue;
1694
+ if (!applyFilters(event))
1695
+ continue;
1696
+ results.push({
1697
+ timestamp: event.timestamp,
1698
+ name: event.name,
1699
+ attributes: event.attributes,
1700
+ });
1701
+ if (hasReachedLimit(results.length, offset, limit)) {
1702
+ const paginated = paginateResults(results, offset, limit);
1703
+ this.llmEventCache.set(cacheKey, paginated);
1704
+ this.circuitBreaker.recordSuccess();
1705
+ logTiming();
1706
+ if (span)
1707
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1708
+ return paginated;
1709
+ }
1710
+ }
1711
+ }
1712
+ }
1713
+ const paginated = paginateResults(results, offset, limit);
1714
+ this.llmEventCache.set(cacheKey, paginated);
1715
+ this.circuitBreaker.recordSuccess();
1716
+ logTiming();
1717
+ if (span)
1718
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1719
+ return paginated;
1031
1720
  }
1032
- if (options.operationName) {
1033
- const opName = event.attributes?.['gen_ai.operation.name'];
1034
- if (opName !== options.operationName)
1035
- return false;
1721
+ catch (error) {
1722
+ this.circuitBreaker.recordFailure();
1723
+ logTiming();
1724
+ throw error;
1036
1725
  }
1037
- if (options.conversationId) {
1038
- const convId = event.attributes?.['gen_ai.conversation.id'];
1039
- if (convId !== options.conversationId)
1040
- return false;
1726
+ });
1727
+ }
1728
+ async queryEvaluations(options) {
1729
+ // P2 fix: Defer attribute allocation until span is actually created
1730
+ return withSpan('obs_toolkit.query.evaluations', () => ({
1731
+ 'obs_toolkit.query.type': 'evaluations',
1732
+ 'obs_toolkit.query.backend': 'local',
1733
+ 'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
1734
+ 'obs_toolkit.query.start_date': options.startDate,
1735
+ 'obs_toolkit.query.end_date': options.endDate,
1736
+ }), async (span) => {
1737
+ const timer = startTiming();
1738
+ const logTiming = () => {
1739
+ const durationMs = timer.end();
1740
+ recordQueryDuration('evaluations', durationMs, 'local');
1741
+ this.queryHistograms.evaluations.observe(durationMs);
1742
+ if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
1743
+ queryLog.warn(`Slow query: queryEvaluations took ${durationMs.toFixed(1)}ms`, { durationMs });
1744
+ }
1745
+ };
1746
+ // Check circuit breaker - fail fast if open
1747
+ if (!this.circuitBreaker.canRequest()) {
1748
+ logTiming();
1749
+ if (span)
1750
+ span.setAttribute('obs_toolkit.query.result_count', 0);
1751
+ return [];
1041
1752
  }
1042
- if (options.search) {
1043
- const searchLower = options.search.toLowerCase();
1044
- const attrStr = JSON.stringify(event.attributes).toLowerCase();
1045
- if (!attrStr.includes(searchLower) && !event.name.toLowerCase().includes(searchLower))
1046
- return false;
1753
+ // Check cache first
1754
+ const cacheKey = makeCacheKey('evaluations', options);
1755
+ const cached = this.evaluationCache.get(cacheKey);
1756
+ if (cached) {
1757
+ logTiming();
1758
+ // M3 fix: Record cache hit for metrics and span event
1759
+ recordCacheHit('evaluations');
1760
+ if (span) {
1761
+ span.addEvent('cache_hit');
1762
+ span.setAttribute('obs_toolkit.query.result_count', cached.length);
1763
+ }
1764
+ return cached;
1047
1765
  }
1048
- return true;
1049
- };
1050
- try {
1051
- for (const file of files) {
1052
- // Try to use index for pre-filtering
1053
- const matchingLines = this.tryUseIndex(file, 'llm-events', indexOptions);
1054
- if (matchingLines !== null) {
1055
- // Use indexed query - read only matching lines
1056
- const rawRecords = await readLinesByNumber(file, matchingLines);
1057
- for (const event of rawRecords) {
1058
- if (!event.timestamp || !event.name)
1059
- continue;
1060
- if (!applyFilters(event))
1061
- continue;
1062
- results.push({
1063
- timestamp: event.timestamp,
1064
- name: event.name,
1065
- attributes: event.attributes,
1066
- });
1067
- if (hasReachedLimit(results.length, offset, limit)) {
1068
- const paginated = paginateResults(results, offset, limit);
1069
- this.llmEventCache.set(cacheKey, paginated);
1070
- this.circuitBreaker.recordSuccess();
1071
- logTiming();
1072
- return paginated;
1766
+ const files = getFilesInRange(this.telemetryDir, /evaluations-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
1767
+ const results = [];
1768
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1769
+ const offset = options.offset || 0;
1770
+ // Build index query options for indexable filters
1771
+ const indexOptions = {
1772
+ traceId: options.traceId,
1773
+ evaluationName: options.evaluationName,
1774
+ scoreLabel: options.scoreLabel,
1775
+ responseId: options.responseId,
1776
+ evaluator: options.evaluator,
1777
+ };
1778
+ // Pre-load dataset trace IDs when filtering by datasetId
1779
+ let datasetTraceIds;
1780
+ if (options.datasetId) {
1781
+ const allTraces = await this.readDatasetTraces();
1782
+ datasetTraceIds = new Set(allTraces.filter(t => t.datasetId === options.datasetId).map(t => t.traceId));
1783
+ }
1784
+ // Helper to apply non-indexable filters to an evaluation
1785
+ const applyFilters = (evaluation) => {
1786
+ // Dataset membership filter
1787
+ if (datasetTraceIds && (!evaluation.traceId || !datasetTraceIds.has(evaluation.traceId))) {
1788
+ return false;
1789
+ }
1790
+ // Score range filters - only apply when evaluation HAS a scoreValue (P1-1 fix)
1791
+ // Evaluations with only scoreLabel (qualitative) should pass through
1792
+ if (options.scoreMin !== undefined && evaluation.scoreValue !== undefined) {
1793
+ if (evaluation.scoreValue < options.scoreMin)
1794
+ return false;
1795
+ }
1796
+ if (options.scoreMax !== undefined && evaluation.scoreValue !== undefined) {
1797
+ if (evaluation.scoreValue > options.scoreMax)
1798
+ return false;
1799
+ }
1800
+ // Session filter
1801
+ if (options.sessionId && evaluation.sessionId !== options.sessionId) {
1802
+ return false;
1803
+ }
1804
+ // Evaluator type filter (not indexed, so always applied here)
1805
+ if (options.evaluatorType && evaluation.evaluatorType !== options.evaluatorType) {
1806
+ return false;
1807
+ }
1808
+ // Agent-as-Judge filters (Section 10.7)
1809
+ if (options.agentId && evaluation.agentId !== options.agentId) {
1810
+ return false;
1811
+ }
1812
+ if (options.agentName && evaluation.agentName !== options.agentName) {
1813
+ return false;
1814
+ }
1815
+ return true;
1816
+ };
1817
+ try {
1818
+ for (const file of files) {
1819
+ // Try to use index for pre-filtering
1820
+ const matchingLines = this.tryUseIndex(file, 'evaluations', indexOptions);
1821
+ if (matchingLines !== null) {
1822
+ // Use indexed query - read only matching lines
1823
+ const rawRecords = await readLinesByNumber(file, matchingLines);
1824
+ for (const rawLine of rawRecords) {
1825
+ for (const raw of flattenEvaluationRecord(rawLine)) {
1826
+ const evaluation = normalizeEvaluation(raw);
1827
+ if (!evaluation)
1828
+ continue;
1829
+ if (!applyFilters(evaluation))
1830
+ continue;
1831
+ results.push(evaluation);
1832
+ if (hasReachedLimit(results.length, offset, limit)) {
1833
+ const paginated = paginateResults(results, offset, limit);
1834
+ this.evaluationCache.set(cacheKey, paginated);
1835
+ this.circuitBreaker.recordSuccess();
1836
+ logTiming();
1837
+ if (span)
1838
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1839
+ return paginated;
1840
+ }
1841
+ }
1073
1842
  }
1074
1843
  }
1075
- }
1076
- else {
1077
- // Fall back to full file scan
1078
- for await (const event of streamJsonl(file)) {
1079
- if (!event.timestamp || !event.name)
1080
- continue;
1081
- // Apply indexable filters (since no index was used)
1082
- if (options.eventName && !event.name.includes(options.eventName))
1083
- continue;
1084
- if (!applyFilters(event))
1085
- continue;
1086
- results.push({
1087
- timestamp: event.timestamp,
1088
- name: event.name,
1089
- attributes: event.attributes,
1090
- });
1091
- if (hasReachedLimit(results.length, offset, limit)) {
1092
- const paginated = paginateResults(results, offset, limit);
1093
- this.llmEventCache.set(cacheKey, paginated);
1094
- this.circuitBreaker.recordSuccess();
1095
- logTiming();
1096
- return paginated;
1844
+ else {
1845
+ // Fall back to full file scan
1846
+ for await (const rawLine of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
1847
+ for (const raw of flattenEvaluationRecord(rawLine)) {
1848
+ const evaluation = normalizeEvaluation(raw);
1849
+ if (!evaluation)
1850
+ continue;
1851
+ // Apply indexable filters (since no index was used)
1852
+ if (options.traceId && evaluation.traceId !== options.traceId)
1853
+ continue;
1854
+ if (options.evaluationName && !evaluation.evaluationName.includes(options.evaluationName))
1855
+ continue;
1856
+ if (options.scoreLabel && evaluation.scoreLabel !== options.scoreLabel)
1857
+ continue;
1858
+ if (options.responseId && evaluation.responseId !== options.responseId)
1859
+ continue;
1860
+ if (options.evaluator && evaluation.evaluator !== options.evaluator)
1861
+ continue;
1862
+ if (!applyFilters(evaluation))
1863
+ continue;
1864
+ results.push(evaluation);
1865
+ if (hasReachedLimit(results.length, offset, limit)) {
1866
+ const paginated = paginateResults(results, offset, limit);
1867
+ this.evaluationCache.set(cacheKey, paginated);
1868
+ this.circuitBreaker.recordSuccess();
1869
+ logTiming();
1870
+ if (span)
1871
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1872
+ return paginated;
1873
+ }
1874
+ }
1097
1875
  }
1098
1876
  }
1099
1877
  }
1878
+ const paginated = paginateResults(results, offset, limit);
1879
+ this.evaluationCache.set(cacheKey, paginated);
1880
+ this.circuitBreaker.recordSuccess();
1881
+ logTiming();
1882
+ if (span)
1883
+ span.setAttribute('obs_toolkit.query.result_count', paginated.length);
1884
+ return paginated;
1885
+ }
1886
+ catch (error) {
1887
+ this.circuitBreaker.recordFailure();
1888
+ logTiming();
1889
+ throw error;
1890
+ }
1891
+ });
1892
+ }
1893
+ // --- Dataset Management ---
1894
+ get datasetsFile() { return join(this.telemetryDir, 'datasets.jsonl'); }
1895
+ get datasetTracesFile() { return join(this.telemetryDir, 'dataset-traces.jsonl'); }
1896
+ get datasetRunsFile() { return join(this.telemetryDir, 'dataset-runs.jsonl'); }
1897
+ /** Promise-chain mutex — serializes dataset mutations to prevent race conditions */
1898
+ _datasetMutex = Promise.resolve();
1899
+ _withDatasetLock(fn) {
1900
+ const prev = this._datasetMutex;
1901
+ let resolve;
1902
+ this._datasetMutex = new Promise(r => { resolve = r; });
1903
+ return prev.then(fn).finally(() => resolve());
1904
+ }
1905
+ async readDatasets() {
1906
+ try {
1907
+ const content = await readFile(this.datasetsFile, 'utf-8');
1908
+ const results = [];
1909
+ for (const line of content.trim().split('\n').filter(Boolean)) {
1910
+ const parsed = JSON.parse(line);
1911
+ const result = datasetRecordSchema.safeParse(parsed);
1912
+ if (result.success) {
1913
+ results.push(result.data);
1914
+ }
1100
1915
  }
1101
- const paginated = paginateResults(results, offset, limit);
1102
- this.llmEventCache.set(cacheKey, paginated);
1103
- this.circuitBreaker.recordSuccess();
1104
- logTiming();
1105
- return paginated;
1916
+ return results;
1106
1917
  }
1107
- catch (error) {
1108
- this.circuitBreaker.recordFailure();
1109
- logTiming();
1110
- throw error;
1918
+ catch {
1919
+ return [];
1111
1920
  }
1112
1921
  }
1113
- async queryEvaluations(options) {
1114
- const timer = startTiming();
1115
- const logTiming = () => {
1116
- const durationMs = timer.end();
1117
- if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
1118
- console.warn(`[obs-toolkit] Slow query: queryEvaluations took ${durationMs.toFixed(1)}ms`);
1922
+ async readDatasetTraces() {
1923
+ try {
1924
+ const content = await readFile(this.datasetTracesFile, 'utf-8');
1925
+ const results = [];
1926
+ for (const line of content.trim().split('\n').filter(Boolean)) {
1927
+ const parsed = JSON.parse(line);
1928
+ const result = datasetTraceRecordSchema.safeParse(parsed);
1929
+ if (result.success) {
1930
+ results.push(result.data);
1931
+ }
1119
1932
  }
1120
- };
1121
- // Check circuit breaker - fail fast if open
1122
- if (!this.circuitBreaker.canRequest()) {
1123
- logTiming();
1933
+ return results;
1934
+ }
1935
+ catch {
1124
1936
  return [];
1125
1937
  }
1126
- // Check cache first
1127
- const cacheKey = makeCacheKey('evaluations', options);
1128
- const cached = this.evaluationCache.get(cacheKey);
1129
- if (cached) {
1130
- logTiming();
1131
- return cached;
1938
+ }
1939
+ async appendDataset(record) {
1940
+ await mkdir(this.telemetryDir, { recursive: true });
1941
+ await appendFile(this.datasetsFile, JSON.stringify(record) + '\n');
1942
+ }
1943
+ async appendDatasetTraces(records) {
1944
+ if (records.length === 0)
1945
+ return;
1946
+ await mkdir(this.telemetryDir, { recursive: true });
1947
+ const lines = records.map(r => JSON.stringify(r)).join('\n') + '\n';
1948
+ await appendFile(this.datasetTracesFile, lines);
1949
+ }
1950
+ /** Atomic rewrite: write to temp file then rename (POSIX atomic on same volume) */
1951
+ async atomicWriteFile(filePath, content) {
1952
+ await mkdir(this.telemetryDir, { recursive: true });
1953
+ const tmp = `${filePath}.${Date.now()}.tmp`;
1954
+ await writeFile(tmp, content);
1955
+ await rename(tmp, filePath);
1956
+ }
1957
+ async rewriteDatasetTraces(records) {
1958
+ const lines = records.map(r => JSON.stringify(r)).join('\n') + (records.length ? '\n' : '');
1959
+ await this.atomicWriteFile(this.datasetTracesFile, lines);
1960
+ }
1961
+ async readDatasetRuns() {
1962
+ try {
1963
+ const content = await readFile(this.datasetRunsFile, 'utf-8');
1964
+ const results = [];
1965
+ for (const line of content.trim().split('\n').filter(Boolean)) {
1966
+ const parsed = JSON.parse(line);
1967
+ const result = datasetRunRecordSchema.safeParse(parsed);
1968
+ if (result.success) {
1969
+ results.push(result.data);
1970
+ }
1971
+ }
1972
+ return results;
1132
1973
  }
1133
- const files = getFilesInRange(this.telemetryDir, /evaluations-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate);
1134
- const results = [];
1135
- const limit = options.limit || 100;
1136
- const offset = options.offset || 0;
1137
- // Build index query options for indexable filters
1138
- const indexOptions = {
1139
- traceId: options.traceId,
1140
- evaluationName: options.evaluationName,
1141
- scoreLabel: options.scoreLabel,
1142
- responseId: options.responseId,
1143
- evaluator: options.evaluator,
1144
- };
1145
- // Helper to apply non-indexable filters to an evaluation
1146
- const applyFilters = (evaluation) => {
1147
- // Score range filters - only apply when evaluation HAS a scoreValue (P1-1 fix)
1148
- // Evaluations with only scoreLabel (qualitative) should pass through
1149
- if (options.scoreMin !== undefined && evaluation.scoreValue !== undefined) {
1150
- if (evaluation.scoreValue < options.scoreMin)
1151
- return false;
1974
+ catch {
1975
+ return [];
1976
+ }
1977
+ }
1978
+ async appendDatasetRun(record) {
1979
+ await mkdir(this.telemetryDir, { recursive: true });
1980
+ await appendFile(this.datasetRunsFile, JSON.stringify(record) + '\n');
1981
+ }
1982
+ latestVersion(allVersions, datasetId) {
1983
+ return allVersions
1984
+ .filter(d => d.id === datasetId)
1985
+ .sort((a, b) => (b.version ?? 0) - (a.version ?? 0))[0];
1986
+ }
1987
+ async manageDatasets(options) {
1988
+ const MUTATING = new Set(['create', 'delete', 'add_traces', 'remove_traces', 'promote']);
1989
+ if (MUTATING.has(options.action)) {
1990
+ return this._withDatasetLock(() => this._manageDatasetsInner(options));
1991
+ }
1992
+ return this._manageDatasetsInner(options);
1993
+ }
1994
+ async _manageDatasetsInner(options) {
1995
+ const now = new Date().toISOString();
1996
+ switch (options.action) {
1997
+ case 'create': {
1998
+ const id = randomUUID();
1999
+ const traceIds = [...new Set(options.traceIds ?? [])];
2000
+ const record = {
2001
+ id,
2002
+ name: options.name,
2003
+ description: options.description ?? null,
2004
+ createdAt: now,
2005
+ frozen: options.frozen ?? 0,
2006
+ traceCount: traceIds.length,
2007
+ evaluationNames: options.evaluationNames ?? [],
2008
+ dateRangeStart: null,
2009
+ dateRangeEnd: null,
2010
+ selectionCriteria: options.selectionCriteria ?? null,
2011
+ version: 1,
2012
+ parentVersion: null,
2013
+ versionCreatedAt: now,
2014
+ versionReason: 'created',
2015
+ fieldMapping: options.fieldMapping,
2016
+ traceSnapshot: traceIds,
2017
+ };
2018
+ await this.appendDataset(record);
2019
+ const traceRecords = traceIds.map(traceId => ({ datasetId: id, traceId, addedAt: now }));
2020
+ await this.appendDatasetTraces(traceRecords);
2021
+ return { action: 'create', dataset: record };
1152
2022
  }
1153
- if (options.scoreMax !== undefined && evaluation.scoreValue !== undefined) {
1154
- if (evaluation.scoreValue > options.scoreMax)
1155
- return false;
2023
+ case 'list': {
2024
+ const all = await this.readDatasets();
2025
+ const byId = new Map();
2026
+ for (const d of all) {
2027
+ const existing = byId.get(d.id);
2028
+ if (!existing || (d.version ?? 0) > (existing.version ?? 0)) {
2029
+ byId.set(d.id, d);
2030
+ }
2031
+ }
2032
+ let datasets = [...byId.values()];
2033
+ if (options.filterFrozen !== undefined) {
2034
+ datasets = datasets.filter(d => d.frozen === options.filterFrozen);
2035
+ }
2036
+ // Sort by createdAt descending, then id for stable ordering
2037
+ datasets.sort((a, b) => b.createdAt.localeCompare(a.createdAt) || a.id.localeCompare(b.id));
2038
+ // Apply cursor
2039
+ if (options.cursor) {
2040
+ const decoded = decodeCursor(options.cursor);
2041
+ if (decoded?.createdAt && decoded.id) {
2042
+ const cursorAt = decoded.createdAt;
2043
+ const cursorId = decoded.id;
2044
+ const idx = datasets.findIndex(d => d.createdAt < cursorAt ||
2045
+ (d.createdAt === cursorAt && d.id > cursorId));
2046
+ datasets = idx >= 0 ? datasets.slice(idx) : [];
2047
+ }
2048
+ }
2049
+ const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
2050
+ const hasMore = datasets.length > limit;
2051
+ const page = datasets.slice(0, limit);
2052
+ const last = page[page.length - 1];
2053
+ return {
2054
+ action: 'list',
2055
+ datasets: page,
2056
+ count: page.length,
2057
+ hasMore,
2058
+ ...(hasMore && last && { nextCursor: encodeCursor({ createdAt: last.createdAt, id: last.id }) }),
2059
+ };
1156
2060
  }
1157
- // Session filter
1158
- if (options.sessionId && evaluation.sessionId !== options.sessionId) {
1159
- return false;
2061
+ case 'get': {
2062
+ const all = await this.readDatasets();
2063
+ const dataset = this.latestVersion(all, options.datasetId);
2064
+ if (!dataset)
2065
+ throw new Error(`Dataset not found: ${options.datasetId}`);
2066
+ const allTraces = await this.readDatasetTraces();
2067
+ const traces = allTraces.filter(t => t.datasetId === options.datasetId);
2068
+ const truncated = traces.length > DATASET_TRACE_TRUNCATION_LIMIT;
2069
+ return {
2070
+ action: 'get',
2071
+ dataset,
2072
+ traces: truncated ? traces.slice(0, DATASET_TRACE_TRUNCATION_LIMIT) : traces,
2073
+ traceCount: traces.length,
2074
+ truncated,
2075
+ };
1160
2076
  }
1161
- // Evaluator type filter (not indexed, so always applied here)
1162
- if (options.evaluatorType && evaluation.evaluatorType !== options.evaluatorType) {
1163
- return false;
2077
+ case 'delete': {
2078
+ const all = await this.readDatasets();
2079
+ const remaining = all.filter(d => d.id !== options.datasetId);
2080
+ const deleted = remaining.length < all.length;
2081
+ const lines = remaining.map(r => JSON.stringify(r)).join('\n') + (remaining.length ? '\n' : '');
2082
+ await this.atomicWriteFile(this.datasetsFile, lines);
2083
+ const allTraces = await this.readDatasetTraces();
2084
+ const remainingTraces = allTraces.filter(t => t.datasetId !== options.datasetId);
2085
+ await this.rewriteDatasetTraces(remainingTraces);
2086
+ return { action: 'delete', deleted, id: options.datasetId };
1164
2087
  }
1165
- return true;
1166
- };
1167
- try {
1168
- for (const file of files) {
1169
- // Try to use index for pre-filtering
1170
- const matchingLines = this.tryUseIndex(file, 'evaluations', indexOptions);
1171
- if (matchingLines !== null) {
1172
- // Use indexed query - read only matching lines
1173
- const rawRecords = await readLinesByNumber(file, matchingLines);
1174
- for (const raw of rawRecords) {
1175
- const evaluation = normalizeEvaluation(raw);
1176
- if (!evaluation)
1177
- continue;
1178
- if (!applyFilters(evaluation))
1179
- continue;
1180
- results.push(evaluation);
1181
- if (hasReachedLimit(results.length, offset, limit)) {
1182
- const paginated = paginateResults(results, offset, limit);
1183
- this.evaluationCache.set(cacheKey, paginated);
1184
- this.circuitBreaker.recordSuccess();
1185
- logTiming();
1186
- return paginated;
1187
- }
2088
+ case 'add_traces': {
2089
+ const all = await this.readDatasets();
2090
+ const current = this.latestVersion(all, options.datasetId);
2091
+ if (!current)
2092
+ throw new Error(`Dataset not found: ${options.datasetId}`);
2093
+ if (current.frozen === 1)
2094
+ throw new Error('Cannot modify a frozen dataset');
2095
+ const existingTraces = (await this.readDatasetTraces())
2096
+ .filter(t => t.datasetId === options.datasetId);
2097
+ const existingIds = new Set(existingTraces.map(t => t.traceId));
2098
+ const newTraceIds = options.traceIds.filter(id => !existingIds.has(id));
2099
+ const allTraceIds = [...existingIds, ...newTraceIds];
2100
+ const newVersion = {
2101
+ ...current,
2102
+ version: (current.version ?? 1) + 1,
2103
+ parentVersion: current.version ?? 1,
2104
+ versionCreatedAt: now,
2105
+ versionReason: 'traces_added',
2106
+ traceCount: allTraceIds.length,
2107
+ traceSnapshot: allTraceIds,
2108
+ };
2109
+ await this.appendDataset(newVersion);
2110
+ const traceRecords = newTraceIds.map(traceId => ({ datasetId: options.datasetId, traceId, addedAt: now }));
2111
+ await this.appendDatasetTraces(traceRecords);
2112
+ return { action: 'add_traces', dataset: newVersion, tracesAdded: newTraceIds.length };
2113
+ }
2114
+ case 'remove_traces': {
2115
+ const all = await this.readDatasets();
2116
+ const current = this.latestVersion(all, options.datasetId);
2117
+ if (!current)
2118
+ throw new Error(`Dataset not found: ${options.datasetId}`);
2119
+ if (current.frozen === 1)
2120
+ throw new Error('Cannot modify a frozen dataset');
2121
+ const allTraces = await this.readDatasetTraces();
2122
+ const removeSet = new Set(options.traceIds);
2123
+ const before = allTraces.filter(t => t.datasetId === options.datasetId).length;
2124
+ const remaining = allTraces.filter(t => !(t.datasetId === options.datasetId && removeSet.has(t.traceId)));
2125
+ const after = remaining.filter(t => t.datasetId === options.datasetId).length;
2126
+ const removed = before - after;
2127
+ const remainingTraceIds = remaining
2128
+ .filter(t => t.datasetId === options.datasetId)
2129
+ .map(t => t.traceId);
2130
+ const newVersion = {
2131
+ ...current,
2132
+ version: (current.version ?? 1) + 1,
2133
+ parentVersion: current.version ?? 1,
2134
+ versionCreatedAt: now,
2135
+ versionReason: 'traces_removed',
2136
+ traceCount: after,
2137
+ traceSnapshot: remainingTraceIds,
2138
+ };
2139
+ await this.appendDataset(newVersion);
2140
+ await this.rewriteDatasetTraces(remaining);
2141
+ return { action: 'remove_traces', dataset: newVersion, tracesRemoved: removed };
2142
+ }
2143
+ case 'get_version': {
2144
+ const all = await this.readDatasets();
2145
+ const version = all.find(d => d.id === options.datasetId && d.version === options.version);
2146
+ if (!version)
2147
+ throw new Error(`Version ${options.version} not found for dataset ${options.datasetId}`);
2148
+ // Use traceSnapshot if available (populated since L8), otherwise fall back to current traces
2149
+ if (version.traceSnapshot) {
2150
+ const snapshotSet = new Set(version.traceSnapshot);
2151
+ const allTraces = await this.readDatasetTraces();
2152
+ const traces = allTraces.filter(t => t.datasetId === options.datasetId && snapshotSet.has(t.traceId));
2153
+ return { action: 'get_version', dataset: version, traces };
2154
+ }
2155
+ const allTraces = await this.readDatasetTraces();
2156
+ const traces = allTraces.filter(t => t.datasetId === options.datasetId);
2157
+ return { action: 'get_version', dataset: version, traces };
2158
+ }
2159
+ case 'list_versions': {
2160
+ const all = await this.readDatasets();
2161
+ let versions = all
2162
+ .filter(d => d.id === options.datasetId)
2163
+ .sort((a, b) => (b.version ?? 0) - (a.version ?? 0));
2164
+ // Apply cursor (version number, descending)
2165
+ if (options.cursor) {
2166
+ const decoded = decodeCursor(options.cursor);
2167
+ const cursorVersion = decoded?.version;
2168
+ if (cursorVersion != null) {
2169
+ versions = versions.filter(d => (d.version ?? 0) < cursorVersion);
1188
2170
  }
1189
2171
  }
1190
- else {
1191
- // Fall back to full file scan
1192
- for await (const raw of streamJsonl(file)) {
1193
- const evaluation = normalizeEvaluation(raw);
1194
- if (!evaluation)
1195
- continue;
1196
- // Apply indexable filters (since no index was used)
1197
- if (options.traceId && evaluation.traceId !== options.traceId)
1198
- continue;
1199
- if (options.evaluationName && !evaluation.evaluationName.includes(options.evaluationName))
1200
- continue;
1201
- if (options.scoreLabel && evaluation.scoreLabel !== options.scoreLabel)
1202
- continue;
1203
- if (options.responseId && evaluation.responseId !== options.responseId)
1204
- continue;
1205
- if (options.evaluator && evaluation.evaluator !== options.evaluator)
1206
- continue;
1207
- if (!applyFilters(evaluation))
1208
- continue;
1209
- results.push(evaluation);
1210
- if (hasReachedLimit(results.length, offset, limit)) {
1211
- const paginated = paginateResults(results, offset, limit);
1212
- this.evaluationCache.set(cacheKey, paginated);
1213
- this.circuitBreaker.recordSuccess();
1214
- logTiming();
1215
- return paginated;
1216
- }
2172
+ const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
2173
+ const hasMore = versions.length > limit;
2174
+ const page = versions.slice(0, limit);
2175
+ const last = page[page.length - 1];
2176
+ return {
2177
+ action: 'list_versions',
2178
+ versions: page,
2179
+ count: page.length,
2180
+ hasMore,
2181
+ ...(hasMore && last?.version != null && { nextCursor: encodeCursor({ version: last.version }) }),
2182
+ };
2183
+ }
2184
+ case 'promote': {
2185
+ const id = randomUUID();
2186
+ const traceIds = [...new Set(options.traceIds)];
2187
+ const record = {
2188
+ id,
2189
+ name: options.name,
2190
+ description: options.description ?? null,
2191
+ createdAt: now,
2192
+ frozen: options.frozen ?? 1,
2193
+ traceCount: traceIds.length,
2194
+ evaluationNames: options.evaluationNames ?? [],
2195
+ dateRangeStart: null,
2196
+ dateRangeEnd: null,
2197
+ selectionCriteria: null,
2198
+ version: 1,
2199
+ parentVersion: null,
2200
+ versionCreatedAt: now,
2201
+ versionReason: 'created',
2202
+ traceSnapshot: traceIds,
2203
+ };
2204
+ await this.appendDataset(record);
2205
+ const traceRecords = traceIds.map(traceId => ({ datasetId: id, traceId, addedAt: now }));
2206
+ await this.appendDatasetTraces(traceRecords);
2207
+ return { action: 'promote', dataset: record };
2208
+ }
2209
+ case 'list_runs': {
2210
+ const allRuns = await this.readDatasetRuns();
2211
+ let runs = allRuns
2212
+ .filter(r => r.datasetId === options.datasetId)
2213
+ .sort((a, b) => b.runAt.localeCompare(a.runAt));
2214
+ if (options.cursor) {
2215
+ const decoded = decodeCursor(options.cursor);
2216
+ if (decoded?.createdAt != null) {
2217
+ const cursorAt = decoded.createdAt;
2218
+ runs = runs.filter(r => r.runAt < cursorAt);
1217
2219
  }
1218
2220
  }
2221
+ const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
2222
+ const hasMore = runs.length > limit;
2223
+ const page = runs.slice(0, limit);
2224
+ const last = page[page.length - 1];
2225
+ return {
2226
+ action: 'list_runs',
2227
+ runs: page,
2228
+ count: page.length,
2229
+ hasMore,
2230
+ ...(hasMore && last && { nextCursor: encodeCursor({ createdAt: last.runAt }) }),
2231
+ };
1219
2232
  }
1220
- const paginated = paginateResults(results, offset, limit);
1221
- this.evaluationCache.set(cacheKey, paginated);
1222
- this.circuitBreaker.recordSuccess();
1223
- logTiming();
1224
- return paginated;
1225
- }
1226
- catch (error) {
1227
- this.circuitBreaker.recordFailure();
1228
- logTiming();
1229
- throw error;
2233
+ default:
2234
+ throw new Error(`Unknown dataset action: ${options.action}`);
1230
2235
  }
1231
2236
  }
1232
2237
  async healthCheck() {
@@ -1299,7 +2304,7 @@ export class MultiDirectoryBackend {
1299
2304
  return this.directories;
1300
2305
  }
1301
2306
  async queryTraces(options) {
1302
- const limit = options.limit || 100;
2307
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1303
2308
  // Query all backends in parallel
1304
2309
  const allBackendResults = await Promise.all(this.backends.map(b => b.queryTraces({ ...options, limit })));
1305
2310
  // Merge results using bounded insertion for efficient top-K selection
@@ -1312,7 +2317,7 @@ export class MultiDirectoryBackend {
1312
2317
  return topResults;
1313
2318
  }
1314
2319
  async queryLogs(options) {
1315
- const limit = options.limit || 100;
2320
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1316
2321
  // Query all backends in parallel
1317
2322
  const allBackendResults = await Promise.all(this.backends.map(b => b.queryLogs({ ...options, limit })));
1318
2323
  // Merge results using bounded insertion for efficient top-K selection
@@ -1325,7 +2330,7 @@ export class MultiDirectoryBackend {
1325
2330
  return topResults;
1326
2331
  }
1327
2332
  async queryMetrics(options) {
1328
- const limit = options.limit || 100;
2333
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1329
2334
  // Query all backends in parallel
1330
2335
  const allBackendResults = await Promise.all(this.backends.map(b => b.queryMetrics({ ...options, limit })));
1331
2336
  // Flatten and limit results
@@ -1333,7 +2338,7 @@ export class MultiDirectoryBackend {
1333
2338
  return allResults.slice(0, limit);
1334
2339
  }
1335
2340
  async queryLLMEvents(options) {
1336
- const limit = options.limit || 100;
2341
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1337
2342
  // Query all backends in parallel
1338
2343
  const allBackendResults = await Promise.all(this.backends.map(b => b.queryLLMEvents({ ...options, limit })));
1339
2344
  // Merge results using bounded insertion for efficient top-K selection
@@ -1346,7 +2351,7 @@ export class MultiDirectoryBackend {
1346
2351
  return topResults;
1347
2352
  }
1348
2353
  async queryEvaluations(options) {
1349
- const limit = options.limit || 100;
2354
+ const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
1350
2355
  // Query all backends in parallel
1351
2356
  const allBackendResults = await Promise.all(this.backends.map(b => b.queryEvaluations({ ...options, limit })));
1352
2357
  // Merge results using bounded insertion for efficient top-K selection
@@ -1410,6 +2415,105 @@ export class MultiDirectoryBackend {
1410
2415
  }
1411
2416
  return result;
1412
2417
  }
2418
+ /**
2419
+ * Get aggregated parse statistics from all backends
2420
+ */
2421
+ getParseStats() {
2422
+ // Aggregate stats from all backends
2423
+ let totalFiles = 0;
2424
+ let totalLines = 0;
2425
+ let totalParsed = 0;
2426
+ let totalSkipped = 0;
2427
+ const allWorstFiles = [];
2428
+ for (const backend of this.backends) {
2429
+ const stats = backend.getParseStats();
2430
+ totalFiles += stats.totalFiles;
2431
+ totalLines += stats.totalLines;
2432
+ totalParsed += stats.totalParsed;
2433
+ totalSkipped += stats.totalSkipped;
2434
+ // Collect worst files with skip rates
2435
+ for (const file of stats.worstFiles) {
2436
+ const totalProcessed = file.parsedLines + file.skippedLines;
2437
+ if (totalProcessed > 0) {
2438
+ allWorstFiles.push({
2439
+ ...file,
2440
+ skipRate: file.skippedLines / totalProcessed,
2441
+ });
2442
+ }
2443
+ }
2444
+ }
2445
+ // Calculate overall success rate
2446
+ const totalProcessed = totalParsed + totalSkipped;
2447
+ const parseSuccessRate = totalProcessed > 0 ? totalParsed / totalProcessed : 1;
2448
+ // Get top worst files across all backends
2449
+ const worstFiles = allWorstFiles
2450
+ .sort((a, b) => b.skipRate - a.skipRate)
2451
+ .slice(0, WORST_FILES_LIMIT)
2452
+ .map(({ skipRate: _, ...f }) => f);
2453
+ return {
2454
+ totalFiles,
2455
+ totalLines,
2456
+ totalParsed,
2457
+ totalSkipped,
2458
+ parseSuccessRate,
2459
+ worstFiles,
2460
+ };
2461
+ }
2462
+ /**
2463
+ * Get aggregated query latency statistics from all backends
2464
+ */
2465
+ getQueryStats() {
2466
+ // Aggregate histogram data from all backends
2467
+ // For percentiles, we use the first backend's data since aggregating
2468
+ // percentiles across backends requires full data merge
2469
+ // Count and sum can be aggregated directly
2470
+ const types = ['traces', 'logs', 'metrics', 'llmEvents', 'evaluations'];
2471
+ const result = {};
2472
+ for (const type of types) {
2473
+ let totalCount = 0;
2474
+ let weightedP50 = 0;
2475
+ let weightedP95 = 0;
2476
+ let weightedP99 = 0;
2477
+ let minValue = Infinity;
2478
+ let maxValue = -Infinity;
2479
+ let totalSum = 0;
2480
+ for (const backend of this.backends) {
2481
+ const stats = backend.getQueryStats()[type];
2482
+ if (stats.count > 0) {
2483
+ totalCount += stats.count;
2484
+ totalSum += stats.mean * stats.count;
2485
+ weightedP50 += stats.p50 * stats.count;
2486
+ weightedP95 += stats.p95 * stats.count;
2487
+ weightedP99 += stats.p99 * stats.count;
2488
+ minValue = Math.min(minValue, stats.min);
2489
+ maxValue = Math.max(maxValue, stats.max);
2490
+ }
2491
+ }
2492
+ if (totalCount > 0) {
2493
+ result[type] = {
2494
+ min: minValue,
2495
+ max: maxValue,
2496
+ mean: totalSum / totalCount,
2497
+ p50: weightedP50 / totalCount,
2498
+ p95: weightedP95 / totalCount,
2499
+ p99: weightedP99 / totalCount,
2500
+ count: totalCount,
2501
+ };
2502
+ }
2503
+ else {
2504
+ result[type] = {
2505
+ min: 0,
2506
+ max: 0,
2507
+ mean: 0,
2508
+ p50: 0,
2509
+ p95: 0,
2510
+ p99: 0,
2511
+ count: 0,
2512
+ };
2513
+ }
2514
+ }
2515
+ return result;
2516
+ }
1413
2517
  /**
1414
2518
  * Export traces in OTLP JSON format
1415
2519
  */
@@ -1431,5 +2535,110 @@ export class MultiDirectoryBackend {
1431
2535
  const metrics = await this.queryMetrics(options);
1432
2536
  return convertToOTLPMetrics(metrics);
1433
2537
  }
2538
+ async manageDatasets(options) {
2539
+ if (this.backends.length === 0)
2540
+ throw new Error('No telemetry directories available');
2541
+ const READ_ACTIONS = new Set(['list', 'get', 'get_version', 'list_versions', 'list_runs']);
2542
+ // Write actions delegate to first backend
2543
+ if (!READ_ACTIONS.has(options.action)) {
2544
+ return this.backends[0].manageDatasets(options);
2545
+ }
2546
+ // Single backend — no merge needed
2547
+ if (this.backends.length === 1) {
2548
+ return this.backends[0].manageDatasets(options);
2549
+ }
2550
+ // Fan out read actions across all backends and merge
2551
+ switch (options.action) {
2552
+ case 'list': {
2553
+ const results = await Promise.all(this.backends.map(b => b.manageDatasets(options).catch(() => null)));
2554
+ const byId = new Map();
2555
+ for (const r of results) {
2556
+ if (!r || r.action !== 'list')
2557
+ continue;
2558
+ for (const d of r.datasets) {
2559
+ const existing = byId.get(d.id);
2560
+ if (!existing || (d.version ?? 0) > (existing.version ?? 0)) {
2561
+ byId.set(d.id, d);
2562
+ }
2563
+ }
2564
+ }
2565
+ const datasets = [...byId.values()];
2566
+ const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
2567
+ return {
2568
+ action: 'list',
2569
+ datasets: datasets.slice(0, limit),
2570
+ count: datasets.length,
2571
+ hasMore: datasets.length > limit,
2572
+ };
2573
+ }
2574
+ case 'get': {
2575
+ let best = null;
2576
+ for (const b of this.backends) {
2577
+ try {
2578
+ const r = await b.manageDatasets(options);
2579
+ if (r.action === 'get') {
2580
+ if (!best || best.action !== 'get' ||
2581
+ (r.dataset.version ?? 0) > (best.dataset.version ?? 0)) {
2582
+ best = r;
2583
+ }
2584
+ }
2585
+ }
2586
+ catch { /* not found in this backend */ }
2587
+ }
2588
+ if (!best)
2589
+ throw new Error(`Dataset not found: ${options.datasetId}`);
2590
+ return best;
2591
+ }
2592
+ case 'get_version': {
2593
+ for (const b of this.backends) {
2594
+ try {
2595
+ return await b.manageDatasets(options);
2596
+ }
2597
+ catch { /* not in this backend */ }
2598
+ }
2599
+ throw new Error(`Version ${options.version} not found for dataset ${options.datasetId}`);
2600
+ }
2601
+ case 'list_versions': {
2602
+ const results = await Promise.all(this.backends.map(b => b.manageDatasets(options).catch(() => null)));
2603
+ const byVersion = new Map();
2604
+ for (const r of results) {
2605
+ if (!r || r.action !== 'list_versions')
2606
+ continue;
2607
+ for (const v of r.versions) {
2608
+ if (!byVersion.has(v.version ?? 0)) {
2609
+ byVersion.set(v.version ?? 0, v);
2610
+ }
2611
+ }
2612
+ }
2613
+ const versions = [...byVersion.values()].sort((a, b) => (b.version ?? 0) - (a.version ?? 0));
2614
+ const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
2615
+ return {
2616
+ action: 'list_versions',
2617
+ versions: versions.slice(0, limit),
2618
+ count: versions.length,
2619
+ hasMore: versions.length > limit,
2620
+ };
2621
+ }
2622
+ case 'list_runs': {
2623
+ const results = await Promise.all(this.backends.map(b => b.manageDatasets(options).catch(() => null)));
2624
+ const allRuns = [];
2625
+ for (const r of results) {
2626
+ if (!r || r.action !== 'list_runs')
2627
+ continue;
2628
+ allRuns.push(...r.runs);
2629
+ }
2630
+ allRuns.sort((a, b) => b.runAt.localeCompare(a.runAt));
2631
+ const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
2632
+ return {
2633
+ action: 'list_runs',
2634
+ runs: allRuns.slice(0, limit),
2635
+ count: allRuns.length,
2636
+ hasMore: allRuns.length > limit,
2637
+ };
2638
+ }
2639
+ default:
2640
+ return this.backends[0].manageDatasets(options);
2641
+ }
2642
+ }
1434
2643
  }
1435
2644
  //# sourceMappingURL=local-jsonl.js.map