@tangle-network/agent-eval 0.137.0 → 0.139.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 (172) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +34 -0
  3. package/dist/analyst/index.d.ts +485 -104
  4. package/dist/analyst/index.d.ts.map +1 -1
  5. package/dist/analyst/index.js +10 -607
  6. package/dist/analyst/index.js.map +1 -1
  7. package/dist/{benchmark-YDrpumqB.js → benchmark-CYtcIF2V.js} +299 -159
  8. package/dist/benchmark-CYtcIF2V.js.map +1 -0
  9. package/dist/{benchmark-CHX4orG7.d.ts → benchmark-DDVdWcwA.d.ts} +67 -15
  10. package/dist/benchmark-DDVdWcwA.d.ts.map +1 -0
  11. package/dist/benchmark-command-BKfjOBJ5.js +4537 -0
  12. package/dist/benchmark-command-BKfjOBJ5.js.map +1 -0
  13. package/dist/benchmarks/index.d.ts +1 -1
  14. package/dist/benchmarks/index.js +1 -1
  15. package/dist/{benchmarks-DCLkQOmc.js → benchmarks-zxhy1QV3.js} +4 -3
  16. package/dist/{benchmarks-DCLkQOmc.js.map → benchmarks-zxhy1QV3.js.map} +1 -1
  17. package/dist/campaign/index.d.ts +5 -5
  18. package/dist/campaign/index.js +4 -3
  19. package/dist/{campaign-lgObcHFC.js → campaign-DrS6_hLd.js} +19 -11
  20. package/dist/campaign-DrS6_hLd.js.map +1 -0
  21. package/dist/canonical-D011XM8r.js +86 -0
  22. package/dist/canonical-D011XM8r.js.map +1 -0
  23. package/dist/cli.js +10 -3
  24. package/dist/cli.js.map +1 -1
  25. package/dist/{client-C8L6h6Wf.d.ts → client-BohnDFBq.d.ts} +4 -4
  26. package/dist/{client-C8L6h6Wf.d.ts.map → client-BohnDFBq.d.ts.map} +1 -1
  27. package/dist/{completion-verifier-DSyRNVzU.d.ts → completion-verifier-IPoP4fQO.d.ts} +178 -4
  28. package/dist/completion-verifier-IPoP4fQO.d.ts.map +1 -0
  29. package/dist/contract/index.d.ts +10 -10
  30. package/dist/contract/index.js +9 -8
  31. package/dist/contract/index.js.map +1 -1
  32. package/dist/control.d.ts +2 -2
  33. package/dist/{cost-ledger-D-5_-dhi.js → cost-ledger-CZ9diLxY.js} +90 -45
  34. package/dist/cost-ledger-CZ9diLxY.js.map +1 -0
  35. package/dist/{cost-ledger-D2o6JOrL.d.ts → cost-ledger-DKgyIWRj.d.ts} +6 -2
  36. package/dist/cost-ledger-DKgyIWRj.d.ts.map +1 -0
  37. package/dist/default-registry-B8vf7Rmf.d.ts +118 -0
  38. package/dist/default-registry-B8vf7Rmf.d.ts.map +1 -0
  39. package/dist/default-registry-BgJJItGr.js +2364 -0
  40. package/dist/default-registry-BgJJItGr.js.map +1 -0
  41. package/dist/dspy-rlm-engine-DTkVyDX-.js +344 -0
  42. package/dist/dspy-rlm-engine-DTkVyDX-.js.map +1 -0
  43. package/dist/{eval-campaign-CHqfLnff.js → eval-campaign-BmptJj50.js} +2 -2
  44. package/dist/{eval-campaign-CHqfLnff.js.map → eval-campaign-BmptJj50.js.map} +1 -1
  45. package/dist/exact-types-MaaFcllV.d.ts +234 -0
  46. package/dist/exact-types-MaaFcllV.d.ts.map +1 -0
  47. package/dist/external-optimizer-contracts-BrxY2Sli.d.ts +32 -0
  48. package/dist/external-optimizer-contracts-BrxY2Sli.d.ts.map +1 -0
  49. package/dist/{extract-usage-p-56bh8q.js → extract-usage-DZs601Va.js} +2 -2
  50. package/dist/{extract-usage-p-56bh8q.js.map → extract-usage-DZs601Va.js.map} +1 -1
  51. package/dist/{feedback-trajectory-N_F0PwHz.d.ts → feedback-trajectory-BJUWOkJM.d.ts} +3 -2
  52. package/dist/feedback-trajectory-BJUWOkJM.d.ts.map +1 -0
  53. package/dist/fuzz.d.ts +1 -1
  54. package/dist/fuzz.js +1 -1
  55. package/dist/{hf-dataset-DBJXXoY1.js → hf-dataset-XggBupCr.js} +2 -2
  56. package/dist/{hf-dataset-DBJXXoY1.js.map → hf-dataset-XggBupCr.js.map} +1 -1
  57. package/dist/hosted/index.d.ts +3 -3
  58. package/dist/{index-C-Pr4OWg.d.ts → index-BTm_P9aC.d.ts} +12 -11
  59. package/dist/index-BTm_P9aC.d.ts.map +1 -0
  60. package/dist/{index-U3RHOShi.d.ts → index-CWOPCJiw.d.ts} +2 -2
  61. package/dist/{index-U3RHOShi.d.ts.map → index-CWOPCJiw.d.ts.map} +1 -1
  62. package/dist/{index-DRNl6g_N.d.ts → index-CtR1xh4V.d.ts} +3 -3
  63. package/dist/{index-DRNl6g_N.d.ts.map → index-CtR1xh4V.d.ts.map} +1 -1
  64. package/dist/index-DEb46kc6.d.ts.map +1 -1
  65. package/dist/{index-BnP1QJUv.d.ts → index-_66rVpwN.d.ts} +5 -5
  66. package/dist/{index-BnP1QJUv.d.ts.map → index-_66rVpwN.d.ts.map} +1 -1
  67. package/dist/index.d.ts +35 -55
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +55 -514
  70. package/dist/index.js.map +1 -1
  71. package/dist/{insight-report-B9ooYH_g.d.ts → insight-report-Bu5Wi9tG.d.ts} +4 -4
  72. package/dist/{insight-report-B9ooYH_g.d.ts.map → insight-report-Bu5Wi9tG.d.ts.map} +1 -1
  73. package/dist/{integrity-CKxosZ5Z.d.ts → integrity-COTh3DTH.d.ts} +2 -2
  74. package/dist/{integrity-CKxosZ5Z.d.ts.map → integrity-COTh3DTH.d.ts.map} +1 -1
  75. package/dist/kind-factory-CFxA0JQX.js +2133 -0
  76. package/dist/kind-factory-CFxA0JQX.js.map +1 -0
  77. package/dist/ledger-core/index.js +2 -1
  78. package/dist/{ledger-core-t6sItivm.js → ledger-core-Dxz0Rkwa.js} +210 -99
  79. package/dist/ledger-core-Dxz0Rkwa.js.map +1 -0
  80. package/dist/{llm-client-DKB25jV8.js → llm-client-bkztEfIx.js} +5 -5
  81. package/dist/llm-client-bkztEfIx.js.map +1 -0
  82. package/dist/meta-eval/index.d.ts +2 -2
  83. package/dist/multishot/index.d.ts +2 -2
  84. package/dist/openapi.json +1 -1
  85. package/dist/{proposal-findings-DCawte-y.js → proposal-findings-2GIUo1et.js} +2 -68
  86. package/dist/proposal-findings-2GIUo1et.js.map +1 -0
  87. package/dist/{release-report-CofgVNZt.d.ts → release-report-fZarvIm-.d.ts} +3 -3
  88. package/dist/{release-report-CofgVNZt.d.ts.map → release-report-fZarvIm-.d.ts.map} +1 -1
  89. package/dist/{replay-K8FaC0CB.d.ts → replay-DjG4IG60.d.ts} +34 -143
  90. package/dist/replay-DjG4IG60.d.ts.map +1 -0
  91. package/dist/{replay-Bju0T8Ls.js → replay-SA4OB7O7.js} +48 -136
  92. package/dist/replay-SA4OB7O7.js.map +1 -0
  93. package/dist/reporting.d.ts +4 -4
  94. package/dist/{researcher-Da0Wj-bt.d.ts → researcher-BxhtGfKa.d.ts} +5 -5
  95. package/dist/{researcher-Da0Wj-bt.d.ts.map → researcher-BxhtGfKa.d.ts.map} +1 -1
  96. package/dist/{reward-hacking-CQ3hTCO3.d.ts → reward-hacking-CqSLiV51.d.ts} +2 -2
  97. package/dist/{reward-hacking-CQ3hTCO3.d.ts.map → reward-hacking-CqSLiV51.d.ts.map} +1 -1
  98. package/dist/rl.d.ts +5 -5
  99. package/dist/rl.js +1 -1
  100. package/dist/rollout/index.d.ts +1 -1
  101. package/dist/rollout/index.js +2 -2
  102. package/dist/{rollout-DQFl0UXA.js → rollout-8nj3mYvx.js} +2 -2
  103. package/dist/{rollout-DQFl0UXA.js.map → rollout-8nj3mYvx.js.map} +1 -1
  104. package/dist/{rubric-predictive-validity-C4sztLR3.d.ts → rubric-predictive-validity-DQBQj6uV.d.ts} +2 -2
  105. package/dist/{rubric-predictive-validity-C4sztLR3.d.ts.map → rubric-predictive-validity-DQBQj6uV.d.ts.map} +1 -1
  106. package/dist/{run-evidence-BDIircdA.d.ts → run-evidence-C4RcRQT5.d.ts} +3 -3
  107. package/dist/{run-evidence-BDIircdA.d.ts.map → run-evidence-C4RcRQT5.d.ts.map} +1 -1
  108. package/dist/{run-record-BPCa2rQ8.d.ts → run-record-CztDMXVF.d.ts} +2 -2
  109. package/dist/{run-record-BPCa2rQ8.d.ts.map → run-record-CztDMXVF.d.ts.map} +1 -1
  110. package/dist/{semantic-concept-judge-Bz64IckK.js → semantic-concept-judge-BuIJ9IfB.js} +49 -6
  111. package/dist/semantic-concept-judge-BuIJ9IfB.js.map +1 -0
  112. package/dist/{server-KjXZZUDX.js → server-DaCpLfi0.js} +3 -3
  113. package/dist/{server-KjXZZUDX.js.map → server-DaCpLfi0.js.map} +1 -1
  114. package/dist/single-run-lock-BTTtPZ9N.js +989 -0
  115. package/dist/single-run-lock-BTTtPZ9N.js.map +1 -0
  116. package/dist/{skill-usage-CFDLLlhF.d.ts → skill-usage-B-BFS8M2.d.ts} +65 -40
  117. package/dist/skill-usage-B-BFS8M2.d.ts.map +1 -0
  118. package/dist/{skillopt-optimization-method-f4o9sUT4.js → skillopt-optimization-method-BbGnCC53.js} +20 -979
  119. package/dist/skillopt-optimization-method-BbGnCC53.js.map +1 -0
  120. package/dist/{skillopt-optimization-method-BpbnlvAZ.d.ts → skillopt-optimization-method-_s0Tub7Y.d.ts} +11 -39
  121. package/dist/skillopt-optimization-method-_s0Tub7Y.d.ts.map +1 -0
  122. package/dist/{statistics-_7P642CN.d.ts → statistics-B5d0Zd-z.d.ts} +2 -2
  123. package/dist/{statistics-_7P642CN.d.ts.map → statistics-B5d0Zd-z.d.ts.map} +1 -1
  124. package/dist/store-otlp-DX4fGIcf.js +757 -0
  125. package/dist/store-otlp-DX4fGIcf.js.map +1 -0
  126. package/dist/{summary-report-DHipz9Kx.d.ts → summary-report-Cg7BifAM.d.ts} +3 -3
  127. package/dist/{summary-report-DHipz9Kx.d.ts.map → summary-report-Cg7BifAM.d.ts.map} +1 -1
  128. package/dist/tool-groups-CdYq22lX.d.ts +258 -0
  129. package/dist/tool-groups-CdYq22lX.d.ts.map +1 -0
  130. package/dist/traces.d.ts +7 -6
  131. package/dist/traces.js +5 -4
  132. package/dist/{types-CTGbIm57.d.ts → types-BBFNHxSK.d.ts} +5 -5
  133. package/dist/{types-CTGbIm57.d.ts.map → types-BBFNHxSK.d.ts.map} +1 -1
  134. package/dist/{types-CKswbJGO.d.ts → types-DoEYskCd.d.ts} +5 -5
  135. package/dist/{types-CKswbJGO.d.ts.map → types-DoEYskCd.d.ts.map} +1 -1
  136. package/dist/{types-CTvKfr5F.d.ts → types-uPrS6mD-.d.ts} +2 -2
  137. package/dist/{types-CTvKfr5F.d.ts.map → types-uPrS6mD-.d.ts.map} +1 -1
  138. package/dist/usage-receipt-CgxMEBZq.js +134 -0
  139. package/dist/usage-receipt-CgxMEBZq.js.map +1 -0
  140. package/dist/wire/index.d.ts +3 -3
  141. package/dist/wire/index.js +1 -1
  142. package/docs/trace-analysis.md +191 -385
  143. package/package.json +5 -4
  144. package/dist/analyze-runs-PVtnfjvA.d.ts +0 -72
  145. package/dist/analyze-runs-PVtnfjvA.d.ts.map +0 -1
  146. package/dist/benchmark-CHX4orG7.d.ts.map +0 -1
  147. package/dist/benchmark-YDrpumqB.js.map +0 -1
  148. package/dist/campaign-lgObcHFC.js.map +0 -1
  149. package/dist/completion-verifier-DSyRNVzU.d.ts.map +0 -1
  150. package/dist/concurrency-MUjT7VjM.js +0 -109
  151. package/dist/concurrency-MUjT7VjM.js.map +0 -1
  152. package/dist/cost-ledger-D-5_-dhi.js.map +0 -1
  153. package/dist/cost-ledger-D2o6JOrL.d.ts.map +0 -1
  154. package/dist/default-registry-CLXbRt0f.js +0 -2594
  155. package/dist/default-registry-CLXbRt0f.js.map +0 -1
  156. package/dist/default-registry-Dc5D_Loc.d.ts +0 -202
  157. package/dist/default-registry-Dc5D_Loc.d.ts.map +0 -1
  158. package/dist/feedback-trajectory-N_F0PwHz.d.ts.map +0 -1
  159. package/dist/index-C-Pr4OWg.d.ts.map +0 -1
  160. package/dist/ledger-core-t6sItivm.js.map +0 -1
  161. package/dist/llm-client-DKB25jV8.js.map +0 -1
  162. package/dist/proposal-findings-DCawte-y.js.map +0 -1
  163. package/dist/registry-BdM7SuTr.d.ts +0 -124
  164. package/dist/registry-BdM7SuTr.d.ts.map +0 -1
  165. package/dist/replay-Bju0T8Ls.js.map +0 -1
  166. package/dist/replay-K8FaC0CB.d.ts.map +0 -1
  167. package/dist/semantic-concept-judge-Bz64IckK.js.map +0 -1
  168. package/dist/skill-usage-CFDLLlhF.d.ts.map +0 -1
  169. package/dist/skillopt-optimization-method-BpbnlvAZ.d.ts.map +0 -1
  170. package/dist/skillopt-optimization-method-f4o9sUT4.js.map +0 -1
  171. package/dist/tools-DZk2Jn64.js +0 -1876
  172. package/dist/tools-DZk2Jn64.js.map +0 -1
@@ -0,0 +1,757 @@
1
+ import { n as CaptureIntegrityError } from "./errors-D-LKuDhb.js";
2
+ import { $ as spanEpochMillis, C as TraceNotFoundError, J as extractOtlpAttributes, S as TraceFileTooLargeError, Z as projectOtlpFlatLine, _ as TraceAnalysisLimitError, b as TraceFileMalformedError, d as truncateForBudget, f as validateInteger, g as SpanNotFoundError, h as TRACE_ANALYSIS_LIMITS, p as DEFAULT_TRACE_ANALYST_BUDGETS, q as compareSpanTime, u as compileSearchRegex, x as TraceFileMissingError, y as TraceAnalysisValidationError } from "./kind-factory-CFxA0JQX.js";
3
+ import { readFile, stat } from "node:fs/promises";
4
+ //#region src/trace-analyst/shared-abortable-task.ts
5
+ function createSharedAbortableTask(factory) {
6
+ const controller = new AbortController();
7
+ const task = {
8
+ controller,
9
+ promise: factory(controller.signal),
10
+ consumers: 0,
11
+ settled: false
12
+ };
13
+ task.promise.then(() => {
14
+ task.settled = true;
15
+ }, () => {
16
+ task.settled = true;
17
+ });
18
+ return task;
19
+ }
20
+ function waitForSharedTask(task, signal) {
21
+ signal?.throwIfAborted();
22
+ task.consumers += 1;
23
+ return new Promise((resolve, reject) => {
24
+ let finished = false;
25
+ const finish = (callback) => {
26
+ if (finished) return;
27
+ finished = true;
28
+ signal?.removeEventListener("abort", onAbort);
29
+ task.consumers -= 1;
30
+ callback();
31
+ };
32
+ const onAbort = () => {
33
+ finish(() => {
34
+ reject(signal?.reason);
35
+ if (!task.settled && task.consumers === 0) task.controller.abort(signal?.reason);
36
+ });
37
+ };
38
+ signal?.addEventListener("abort", onAbort, { once: true });
39
+ task.promise.then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error)));
40
+ if (signal?.aborted) onAbort();
41
+ });
42
+ }
43
+ //#endregion
44
+ //#region src/trace-analyst/store-otlp.ts
45
+ /**
46
+ * `OtlpFileTraceStore` — read-only OTLP-JSONL trace store for the
47
+ * trace-analyst.
48
+ *
49
+ * Wire shape. Each line of the input file is one OTLP-shaped span. The
50
+ * store understands flattened OTLP JSONL plus the OpenInference vocab.
51
+ * We project upstream's full
52
+ * span shape down to `TraceAnalystSpan` lazily — full materialisation
53
+ * only happens for the spans the agent actually requests.
54
+ *
55
+ * Indexing. On first read the store builds an in-memory index keyed
56
+ * by `trace_id` carrying:
57
+ * - byte offsets + lengths for each span line (for surgical reads
58
+ * without re-parsing the whole file)
59
+ * - a `TraceAnalystTraceSummary` rollup
60
+ * - sets of services / agents / models / tools / has_errors
61
+ * - byte size of the trace's JSONL slab
62
+ *
63
+ * Memory bound. The index keeps span metadata only — names, kinds,
64
+ * offsets, status. Attribute payloads stay on disk until requested.
65
+ * For a 50MB JSONL with 50k spans, the index is ~5MB.
66
+ *
67
+ * Concurrency. The store builds the index once on first read and
68
+ * caches it. Subsequent reads reuse the index. The file is opened on
69
+ * each read; we never hold a long-lived FD.
70
+ */
71
+ /** Lines indexed between event-loop yields. Bounded synchronous work per
72
+ * tick keeps the index build from starving other tasks on large files
73
+ * while staying coarse enough that the yields are cheap. */
74
+ const INDEX_YIELD_LINES = 5e3;
75
+ const SCAN_YIELD_ITEMS = 500;
76
+ /** Hand control back to the event loop without busy-waiting. */
77
+ function yieldToEventLoop() {
78
+ return new Promise((resolve) => setImmediate(resolve));
79
+ }
80
+ async function scanCheckpoint(signal, item) {
81
+ signal?.throwIfAborted();
82
+ if (item > 0 && item % SCAN_YIELD_ITEMS === 0) {
83
+ await yieldToEventLoop();
84
+ signal?.throwIfAborted();
85
+ }
86
+ }
87
+ /** Default ceiling for {@link OtlpFileTraceStoreOptions.maxFileBytes}. */
88
+ const DEFAULT_MAX_TRACE_FILE_BYTES = 256 * 1024 * 1024;
89
+ var BufferedOtlpTraceStore = class {
90
+ perAttributeViewBudget;
91
+ perAttributeSpanBudget;
92
+ perCallByteCeiling;
93
+ perMatchTextBudget;
94
+ indexValue;
95
+ indexTask;
96
+ bufferValue;
97
+ bufferTask;
98
+ constructor(opts) {
99
+ this.perAttributeViewBudget = validateInteger(opts.perAttributeViewBudget ?? DEFAULT_TRACE_ANALYST_BUDGETS.perAttributeViewBudget, "perAttributeViewBudget", TRACE_ANALYSIS_LIMITS.minimumTextBudget);
100
+ this.perAttributeSpanBudget = validateInteger(opts.perAttributeSpanBudget ?? DEFAULT_TRACE_ANALYST_BUDGETS.perAttributeSpanBudget, "perAttributeSpanBudget", TRACE_ANALYSIS_LIMITS.minimumTextBudget);
101
+ this.perCallByteCeiling = validateInteger(opts.perCallByteCeiling ?? DEFAULT_TRACE_ANALYST_BUDGETS.perCallByteCeiling, "perCallByteCeiling", 1);
102
+ this.perMatchTextBudget = validateInteger(opts.perMatchTextBudget ?? DEFAULT_TRACE_ANALYST_BUDGETS.perMatchTextBudget, "perMatchTextBudget", TRACE_ANALYSIS_LIMITS.minimumTextBudget);
103
+ }
104
+ async hasTrace(trace_id, context) {
105
+ context?.signal?.throwIfAborted();
106
+ const idx = await this.index(context);
107
+ context?.signal?.throwIfAborted();
108
+ return idx.byTrace.has(trace_id);
109
+ }
110
+ async hasSpans(opts, context) {
111
+ context?.signal?.throwIfAborted();
112
+ const trace = (await this.index(context)).byTrace.get(opts.trace_id);
113
+ if (!trace) return [];
114
+ const requested = new Set(opts.span_ids);
115
+ const found = [];
116
+ for (let index = 0; index < trace.spans.length; index += 1) {
117
+ await scanCheckpoint(context?.signal, index);
118
+ const spanId = trace.spans[index].span_id;
119
+ if (requested.has(spanId)) found.push(spanId);
120
+ }
121
+ return found;
122
+ }
123
+ async getOverview(filters, context) {
124
+ const idx = await this.index(context);
125
+ const matched = await this.matchedTraces(idx, filters, context);
126
+ const services = /* @__PURE__ */ new Set();
127
+ const agents = /* @__PURE__ */ new Set();
128
+ const models = /* @__PURE__ */ new Set();
129
+ const tools = /* @__PURE__ */ new Set();
130
+ let rawBytes = 0;
131
+ let earliest = null;
132
+ let latest = null;
133
+ let errorTraceCount = 0;
134
+ let errorSpanCount = 0;
135
+ const clusters = /* @__PURE__ */ new Map();
136
+ let traceIndex = 0;
137
+ for (const t of matched) {
138
+ await scanCheckpoint(context?.signal, traceIndex++);
139
+ if (t.service_name) services.add(t.service_name);
140
+ if (t.agent_name) agents.add(t.agent_name);
141
+ for (const m of t.models) models.add(m);
142
+ for (const tn of t.tools) tools.add(tn);
143
+ rawBytes += t.raw_jsonl_bytes;
144
+ if (!earliest || compareSpanTime(t.start_time, earliest) < 0) earliest = t.start_time;
145
+ if (!latest || compareSpanTime(t.end_time, latest) > 0) latest = t.end_time;
146
+ if (t.has_errors) {
147
+ errorTraceCount += 1;
148
+ let spanIndex = 0;
149
+ for (const s of t.spans) {
150
+ await scanCheckpoint(context?.signal, spanIndex++);
151
+ if (s.status !== "ERROR") continue;
152
+ errorSpanCount += 1;
153
+ accumulateErrorCluster(clusters, t.trace_id, s);
154
+ }
155
+ }
156
+ }
157
+ const sample_trace_ids = matched.slice(0, 20).map((t) => t.trace_id);
158
+ return {
159
+ total_traces: matched.length,
160
+ raw_jsonl_bytes: rawBytes,
161
+ services: [...services].sort(),
162
+ agents: [...agents].sort(),
163
+ models: [...models].sort(),
164
+ tool_names: [...tools].sort(),
165
+ sample_trace_ids,
166
+ errors: {
167
+ trace_count: errorTraceCount,
168
+ span_count: errorSpanCount
169
+ },
170
+ error_clusters: finalizeErrorClusters(clusters, errorTraceCount),
171
+ time_range: earliest && latest ? {
172
+ earliest,
173
+ latest
174
+ } : null
175
+ };
176
+ }
177
+ async queryTraces(opts, context) {
178
+ const limit = validateInteger(opts.limit, "queryTraces.limit", 1, TRACE_ANALYSIS_LIMITS.queryTraces);
179
+ const offset = validateInteger(opts.offset ?? 0, "queryTraces.offset", 0);
180
+ const idx = await this.index(context);
181
+ const matched = await this.matchedTraces(idx, opts.filters, context);
182
+ const slice = matched.slice(offset, offset + limit);
183
+ return {
184
+ traces: slice.map((t) => this.toSummary(t)),
185
+ total: matched.length,
186
+ has_more: offset + slice.length < matched.length
187
+ };
188
+ }
189
+ async countTraces(filters, context) {
190
+ const idx = await this.index(context);
191
+ return (await this.matchedTraces(idx, filters, context)).length;
192
+ }
193
+ async viewTrace(opts, context) {
194
+ context?.signal?.throwIfAborted();
195
+ const trace = (await this.index(context)).byTrace.get(opts.trace_id);
196
+ if (!trace) throw new TraceNotFoundError(opts.trace_id);
197
+ const cap = validateInteger(opts.per_attribute_byte_cap ?? this.perAttributeViewBudget, "viewTrace.per_attribute_byte_cap", 1);
198
+ const buf = await this.buffer(context);
199
+ const spans = [];
200
+ let runningBytes = 0;
201
+ let span_response_bytes_max = 0;
202
+ const counter = { value: 0 };
203
+ let spanIndex = 0;
204
+ for (const s of trace.spans) {
205
+ await scanCheckpoint(context?.signal, spanIndex++);
206
+ const projected = this.projectSpan(buf, trace.trace_id, s, cap, counter);
207
+ const bytes = Buffer.byteLength(JSON.stringify(projected), "utf8");
208
+ span_response_bytes_max = Math.max(span_response_bytes_max, bytes);
209
+ runningBytes += bytes;
210
+ if (runningBytes > this.perCallByteCeiling) return {
211
+ trace_id: trace.trace_id,
212
+ oversized: this.buildOversizedSummary(trace, span_response_bytes_max)
213
+ };
214
+ spans.push(projected);
215
+ }
216
+ return {
217
+ trace_id: trace.trace_id,
218
+ spans
219
+ };
220
+ }
221
+ async viewSpans(opts, context) {
222
+ context?.signal?.throwIfAborted();
223
+ const trace = (await this.index(context)).byTrace.get(opts.trace_id);
224
+ if (!trace) throw new TraceNotFoundError(opts.trace_id);
225
+ if (opts.span_ids.length < 1 || opts.span_ids.length > TRACE_ANALYSIS_LIMITS.viewSpans) throw new TraceAnalysisValidationError(`viewSpans.span_ids must contain 1..${TRACE_ANALYSIS_LIMITS.viewSpans} ids, got ${opts.span_ids.length}`);
226
+ const cap = validateInteger(opts.per_attribute_byte_cap ?? this.perAttributeSpanBudget, "viewSpans.per_attribute_byte_cap", 1);
227
+ const requested = [...new Set(opts.span_ids)];
228
+ const wantSet = new Set(requested);
229
+ const foundById = /* @__PURE__ */ new Map();
230
+ for (let index = 0; index < trace.spans.length; index += 1) {
231
+ await scanCheckpoint(context?.signal, index);
232
+ const span = trace.spans[index];
233
+ if (wantSet.has(span.span_id)) foundById.set(span.span_id, span);
234
+ }
235
+ const missing = requested.filter((id) => !foundById.has(id));
236
+ const buf = await this.buffer(context);
237
+ const spans = [];
238
+ let truncatedAttributeCount = 0;
239
+ const omitted = new Set(requested.filter((id) => foundById.has(id)));
240
+ const buildResult = () => ({
241
+ trace_id: trace.trace_id,
242
+ spans,
243
+ missing_span_ids: missing,
244
+ omitted_span_ids: requested.filter((id) => omitted.has(id)),
245
+ has_more: omitted.size > 0,
246
+ truncated_attribute_count: truncatedAttributeCount
247
+ });
248
+ const metadataBytes = Buffer.byteLength(JSON.stringify(buildResult()), "utf8");
249
+ if (metadataBytes > this.perCallByteCeiling) throw new TraceAnalysisLimitError("viewSpans", metadataBytes, this.perCallByteCeiling, `viewSpans accounting requires ${metadataBytes} bytes, over the ${this.perCallByteCeiling}-byte response limit`);
250
+ let spanIndex = 0;
251
+ let smallestRejectedBytes = Number.POSITIVE_INFINITY;
252
+ for (const id of requested) {
253
+ await scanCheckpoint(context?.signal, spanIndex++);
254
+ const s = foundById.get(id);
255
+ if (!s) continue;
256
+ const counter = { value: 0 };
257
+ const projected = this.projectSpan(buf, trace.trace_id, s, cap, counter);
258
+ spans.push(projected);
259
+ omitted.delete(id);
260
+ truncatedAttributeCount += counter.value;
261
+ const responseBytes = Buffer.byteLength(JSON.stringify(buildResult()), "utf8");
262
+ if (responseBytes <= this.perCallByteCeiling) continue;
263
+ smallestRejectedBytes = Math.min(smallestRejectedBytes, responseBytes);
264
+ if (requested.length === 1) throw new TraceAnalysisLimitError("viewSpans", responseBytes, this.perCallByteCeiling, `viewSpans cannot fit span ${JSON.stringify(id)} in the ${this.perCallByteCeiling}-byte response limit`);
265
+ spans.pop();
266
+ omitted.add(id);
267
+ truncatedAttributeCount -= counter.value;
268
+ }
269
+ if (foundById.size > 0 && spans.length === 0) throw new TraceAnalysisLimitError("viewSpans", smallestRejectedBytes, this.perCallByteCeiling, `viewSpans cannot fit one requested span in the ${this.perCallByteCeiling}-byte response limit`);
270
+ return buildResult();
271
+ }
272
+ async searchTrace(opts, context) {
273
+ const max_matches = validateInteger(opts.max_matches ?? 50, "searchTrace.max_matches", 1, TRACE_ANALYSIS_LIMITS.searchMatches);
274
+ const trace = (await this.index(context)).byTrace.get(opts.trace_id);
275
+ if (!trace) throw new TraceNotFoundError(opts.trace_id);
276
+ const re = compileSearchRegex(opts.regex_pattern);
277
+ const buf = await this.buffer(context);
278
+ const hits = [];
279
+ let hasMore = false;
280
+ let spanIndex = 0;
281
+ for (const s of trace.spans) {
282
+ await scanCheckpoint(context?.signal, spanIndex++);
283
+ const remaining = max_matches - hits.length;
284
+ const localHits = await this.scanSpanForMatches(buf, trace.trace_id, s, re, this.perMatchTextBudget, remaining, context);
285
+ for (const h of localHits.records) {
286
+ if (hits.length >= max_matches) break;
287
+ hits.push(h);
288
+ }
289
+ if (localHits.hasMore) {
290
+ hasMore = true;
291
+ break;
292
+ }
293
+ }
294
+ return {
295
+ trace_id: trace.trace_id,
296
+ hits,
297
+ has_more: hasMore
298
+ };
299
+ }
300
+ async searchSpan(opts, context) {
301
+ const max_matches = validateInteger(opts.max_matches ?? 50, "searchSpan.max_matches", 1, TRACE_ANALYSIS_LIMITS.searchMatches);
302
+ const trace = (await this.index(context)).byTrace.get(opts.trace_id);
303
+ if (!trace) throw new TraceNotFoundError(opts.trace_id);
304
+ let span;
305
+ for (let index = 0; index < trace.spans.length; index += 1) {
306
+ await scanCheckpoint(context?.signal, index);
307
+ const candidate = trace.spans[index];
308
+ if (candidate.span_id === opts.span_id) {
309
+ span = candidate;
310
+ break;
311
+ }
312
+ }
313
+ if (!span) throw new SpanNotFoundError(opts.trace_id, opts.span_id);
314
+ const re = compileSearchRegex(opts.regex_pattern);
315
+ const buf = await this.buffer(context);
316
+ const localHits = await this.scanSpanForMatches(buf, trace.trace_id, span, re, this.perMatchTextBudget, max_matches, context);
317
+ return {
318
+ trace_id: trace.trace_id,
319
+ span_id: span.span_id,
320
+ hits: localHits.records,
321
+ has_more: localHits.hasMore
322
+ };
323
+ }
324
+ /** Force the index to materialise. Useful to amortise startup cost
325
+ * before the first agent call. */
326
+ async ensureIndexed(context) {
327
+ await this.index(context);
328
+ }
329
+ async buffer(context) {
330
+ context?.signal?.throwIfAborted();
331
+ if (this.bufferValue) return this.bufferValue;
332
+ if (!this.bufferTask) {
333
+ const task = createSharedAbortableTask((signal) => this.readBuffer(signal));
334
+ this.bufferTask = task;
335
+ task.promise.then((value) => {
336
+ this.bufferValue = value;
337
+ if (this.bufferTask === task) this.bufferTask = void 0;
338
+ }, () => {
339
+ if (this.bufferTask === task) this.bufferTask = void 0;
340
+ });
341
+ }
342
+ return waitForSharedTask(this.bufferTask, context?.signal);
343
+ }
344
+ async index(context) {
345
+ context?.signal?.throwIfAborted();
346
+ if (this.indexValue) return this.indexValue;
347
+ if (!this.indexTask) {
348
+ const task = createSharedAbortableTask((signal) => this.buildIndex(signal));
349
+ this.indexTask = task;
350
+ task.promise.then((value) => {
351
+ this.indexValue = value;
352
+ if (this.indexTask === task) this.indexTask = void 0;
353
+ }, () => {
354
+ if (this.indexTask === task) this.indexTask = void 0;
355
+ });
356
+ }
357
+ return waitForSharedTask(this.indexTask, context?.signal);
358
+ }
359
+ async buildIndex(signal) {
360
+ const buf = await this.buffer({ signal });
361
+ signal?.throwIfAborted();
362
+ const byTrace = /* @__PURE__ */ new Map();
363
+ let cursor = 0;
364
+ let lineNumber = 0;
365
+ let sinceYield = 0;
366
+ while (cursor < buf.length) {
367
+ lineNumber += 1;
368
+ if (++sinceYield >= INDEX_YIELD_LINES) {
369
+ sinceYield = 0;
370
+ await yieldToEventLoop();
371
+ signal?.throwIfAborted();
372
+ }
373
+ const newlineIndex = buf.indexOf(10, cursor);
374
+ const lineEnd = newlineIndex === -1 ? buf.length : newlineIndex;
375
+ const lineLength = lineEnd - cursor;
376
+ if (lineLength === 0) {
377
+ cursor = lineEnd + 1;
378
+ continue;
379
+ }
380
+ const lineSlice = buf.subarray(cursor, lineEnd).toString("utf8");
381
+ const lineOffset = cursor;
382
+ cursor = lineEnd + 1;
383
+ let parsed;
384
+ try {
385
+ parsed = JSON.parse(lineSlice);
386
+ } catch (cause) {
387
+ throw new TraceFileMalformedError(this.sourcePath(), lineNumber, lineOffset, cause);
388
+ }
389
+ if (!parsed || typeof parsed !== "object") throw new TraceFileMalformedError(this.sourcePath(), lineNumber, lineOffset);
390
+ const span = projectOtlpFlatLine(parsed);
391
+ if (!span) throw new TraceFileMalformedError(this.sourcePath(), lineNumber, lineOffset);
392
+ let entry = byTrace.get(span.trace_id);
393
+ if (!entry) {
394
+ entry = {
395
+ trace_id: span.trace_id,
396
+ service_name: span.service_name,
397
+ agent_name: span.agent_name,
398
+ span_count: 0,
399
+ has_errors: false,
400
+ start_time: span.start_time,
401
+ end_time: span.end_time,
402
+ duration_ms: 0,
403
+ raw_jsonl_bytes: 0,
404
+ models: /* @__PURE__ */ new Set(),
405
+ tools: /* @__PURE__ */ new Set(),
406
+ spans: []
407
+ };
408
+ byTrace.set(span.trace_id, entry);
409
+ } else {
410
+ if (!entry.service_name && span.service_name) entry.service_name = span.service_name;
411
+ if (!entry.agent_name && span.agent_name) entry.agent_name = span.agent_name;
412
+ }
413
+ const indexEntry = {
414
+ span_id: span.span_id,
415
+ parent_span_id: span.parent_span_id,
416
+ name: span.name,
417
+ kind: span.kind,
418
+ start_time: span.start_time,
419
+ end_time: span.end_time,
420
+ duration_ms: span.duration_ms,
421
+ status: span.status,
422
+ status_message: span.status_message,
423
+ service_name: span.service_name,
424
+ agent_name: span.agent_name,
425
+ model_name: span.model_name,
426
+ tool_name: span.tool_name,
427
+ line_byte_offset: lineOffset,
428
+ line_byte_length: lineLength
429
+ };
430
+ entry.spans.push(indexEntry);
431
+ entry.span_count += 1;
432
+ entry.raw_jsonl_bytes += lineLength + 1;
433
+ if (span.status === "ERROR") entry.has_errors = true;
434
+ if (compareSpanTime(span.start_time, entry.start_time) < 0) entry.start_time = span.start_time;
435
+ if (compareSpanTime(span.end_time, entry.end_time) > 0) entry.end_time = span.end_time;
436
+ if (span.model_name) entry.models.add(span.model_name);
437
+ if (span.tool_name) entry.tools.add(span.tool_name);
438
+ }
439
+ let totalRawBytes = 0;
440
+ for (const t of byTrace.values()) {
441
+ signal?.throwIfAborted();
442
+ totalRawBytes += t.raw_jsonl_bytes;
443
+ t.spans.sort((a, b) => compareSpanTime(a.start_time, b.start_time) || a.line_byte_offset - b.line_byte_offset);
444
+ const startMs = spanEpochMillis(t.start_time);
445
+ const endMs = spanEpochMillis(t.end_time);
446
+ t.duration_ms = startMs === null || endMs === null ? 0 : Math.max(0, endMs - startMs);
447
+ }
448
+ const sortedTraceIds = [...byTrace.keys()].sort();
449
+ signal?.throwIfAborted();
450
+ return {
451
+ byTrace,
452
+ totalRawBytes,
453
+ sortedTraceIds
454
+ };
455
+ }
456
+ async matchedTraces(idx, filters, context) {
457
+ context?.signal?.throwIfAborted();
458
+ const traces = [];
459
+ for (let index = 0; index < idx.sortedTraceIds.length; index += 1) {
460
+ await scanCheckpoint(context?.signal, index);
461
+ const trace = idx.byTrace.get(idx.sortedTraceIds[index]);
462
+ if (trace) traces.push(trace);
463
+ }
464
+ if (!filters) return traces;
465
+ const indexedFiltered = [];
466
+ for (let index = 0; index < traces.length; index += 1) {
467
+ await scanCheckpoint(context?.signal, index);
468
+ const t = traces[index];
469
+ if (filters.has_errors !== void 0 && t.has_errors !== filters.has_errors) continue;
470
+ if (filters.service_names && filters.service_names.length > 0) {
471
+ if (!t.service_name || !filters.service_names.includes(t.service_name)) continue;
472
+ }
473
+ if (filters.agent_names && filters.agent_names.length > 0) {
474
+ if (!t.agent_name || !filters.agent_names.includes(t.agent_name)) continue;
475
+ }
476
+ if (filters.model_names && filters.model_names.length > 0) {
477
+ if (![...t.models].some((m) => filters.model_names.includes(m))) continue;
478
+ }
479
+ if (filters.tool_names && filters.tool_names.length > 0) {
480
+ if (![...t.tools].some((tn) => filters.tool_names.includes(tn))) continue;
481
+ }
482
+ if (filters.start_time_after && t.start_time < filters.start_time_after) continue;
483
+ if (filters.start_time_before && t.start_time > filters.start_time_before) continue;
484
+ indexedFiltered.push(t);
485
+ }
486
+ if (!filters.regex_pattern) return indexedFiltered;
487
+ const re = compileSearchRegex(filters.regex_pattern);
488
+ const buf = await this.buffer(context);
489
+ const out = [];
490
+ let traceIndex = 0;
491
+ for (const t of indexedFiltered) {
492
+ await scanCheckpoint(context?.signal, traceIndex++);
493
+ let matched = false;
494
+ let spanIndex = 0;
495
+ for (const s of t.spans) {
496
+ await scanCheckpoint(context?.signal, spanIndex++);
497
+ const slice = buf.subarray(s.line_byte_offset, s.line_byte_offset + s.line_byte_length);
498
+ if (re.test(slice.toString("utf8"))) {
499
+ matched = true;
500
+ break;
501
+ }
502
+ }
503
+ if (matched) out.push(t);
504
+ }
505
+ return out;
506
+ }
507
+ toSummary(t) {
508
+ return {
509
+ trace_id: t.trace_id,
510
+ service_name: t.service_name,
511
+ agent_name: t.agent_name,
512
+ span_count: t.span_count,
513
+ has_errors: t.has_errors,
514
+ start_time: t.start_time,
515
+ end_time: t.end_time,
516
+ duration_ms: t.duration_ms,
517
+ raw_jsonl_bytes: t.raw_jsonl_bytes,
518
+ models: [...t.models].sort(),
519
+ tools: [...t.tools].sort()
520
+ };
521
+ }
522
+ projectSpan(buf, trace_id, s, perAttrCap, counter) {
523
+ const slice = buf.subarray(s.line_byte_offset, s.line_byte_offset + s.line_byte_length).toString("utf8");
524
+ let raw = {};
525
+ try {
526
+ const parsed = JSON.parse(slice);
527
+ if (parsed && typeof parsed === "object") raw = parsed;
528
+ } catch {}
529
+ const attrs = extractOtlpAttributes(raw);
530
+ const projected = {};
531
+ for (const [k, v] of Object.entries(attrs)) if (typeof v === "string") {
532
+ const trunc = truncateForBudget(v, perAttrCap);
533
+ if (trunc !== v) counter.value += 1;
534
+ projected[k] = trunc;
535
+ } else if (Array.isArray(v) || v && typeof v === "object") {
536
+ const json = JSON.stringify(v);
537
+ const trunc = truncateForBudget(json, perAttrCap);
538
+ if (trunc !== json) {
539
+ counter.value += 1;
540
+ projected[k] = trunc;
541
+ } else projected[k] = v;
542
+ } else projected[k] = v;
543
+ return {
544
+ trace_id,
545
+ span_id: s.span_id,
546
+ parent_span_id: s.parent_span_id,
547
+ name: s.name,
548
+ kind: s.kind,
549
+ start_time: s.start_time,
550
+ end_time: s.end_time,
551
+ duration_ms: s.duration_ms,
552
+ status: s.status,
553
+ status_message: s.status_message,
554
+ service_name: s.service_name,
555
+ agent_name: s.agent_name,
556
+ model_name: s.model_name,
557
+ tool_name: s.tool_name,
558
+ attributes: projected
559
+ };
560
+ }
561
+ buildOversizedSummary(t, span_response_bytes_max) {
562
+ const counts = /* @__PURE__ */ new Map();
563
+ let errorCount = 0;
564
+ for (const s of t.spans) {
565
+ counts.set(s.name, (counts.get(s.name) ?? 0) + 1);
566
+ if (s.status === "ERROR") errorCount += 1;
567
+ }
568
+ const top = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20);
569
+ return {
570
+ span_count: t.span_count,
571
+ top_span_names: top,
572
+ span_response_bytes_max,
573
+ error_span_count: errorCount
574
+ };
575
+ }
576
+ async scanSpanForMatches(buf, trace_id, s, re, textBudget, recordCap, context) {
577
+ const slice = buf.subarray(s.line_byte_offset, s.line_byte_offset + s.line_byte_length).toString("utf8");
578
+ const records = [];
579
+ let hasMore = false;
580
+ let matchIndex = 0;
581
+ for (const match of re.matchAll(slice)) {
582
+ await scanCheckpoint(context?.signal, matchIndex++);
583
+ if (records.length >= recordCap) {
584
+ hasMore = true;
585
+ break;
586
+ }
587
+ const offset = match.index ?? 0;
588
+ const before = slice.slice(Math.max(0, offset - textBudget / 2), offset);
589
+ const after = slice.slice(offset + match[0].length, offset + match[0].length + Math.floor(textBudget / 2));
590
+ records.push({
591
+ trace_id,
592
+ span_id: s.span_id,
593
+ span_name: s.name,
594
+ span_kind: s.kind,
595
+ attribute_path: bestAttributePathForOffset(slice, offset) ?? "span.raw",
596
+ matched_text: truncateForBudget(match[0], textBudget),
597
+ context_before: truncateForBudget(before, textBudget),
598
+ context_after: truncateForBudget(after, textBudget),
599
+ match_offset: offset
600
+ });
601
+ }
602
+ return {
603
+ records,
604
+ hasMore
605
+ };
606
+ }
607
+ };
608
+ var OtlpFileTraceStore = class extends BufferedOtlpTraceStore {
609
+ path;
610
+ maxFileBytes;
611
+ constructor(opts) {
612
+ super(opts);
613
+ this.path = opts.path;
614
+ this.maxFileBytes = validateInteger(opts.maxFileBytes ?? 268435456, "maxFileBytes", 1);
615
+ }
616
+ /** Stat-then-read so an oversized file fails loud before allocating the
617
+ * source buffer. Missing files remain distinct from malformed traces. */
618
+ async readBuffer(signal) {
619
+ signal?.throwIfAborted();
620
+ let stats;
621
+ try {
622
+ stats = await stat(this.path);
623
+ } catch (err) {
624
+ if (err?.code === "ENOENT") throw new TraceFileMissingError(this.path);
625
+ throw err;
626
+ }
627
+ if (stats.size > this.maxFileBytes) throw new TraceFileTooLargeError(this.path, stats.size, this.maxFileBytes);
628
+ signal?.throwIfAborted();
629
+ return readFile(this.path, { signal });
630
+ }
631
+ sourcePath() {
632
+ return this.path;
633
+ }
634
+ };
635
+ var OtlpBufferTraceStore = class extends BufferedOtlpTraceStore {
636
+ source;
637
+ constructor(source, opts) {
638
+ super(opts);
639
+ this.source = source;
640
+ }
641
+ async readBuffer(signal) {
642
+ signal?.throwIfAborted();
643
+ return this.source;
644
+ }
645
+ sourcePath() {
646
+ return "<memory>";
647
+ }
648
+ };
649
+ /** Build the bounded trace store directly from OpenInference JSONL text. */
650
+ function otlpTextToTraceAnalysisStore(text, opts = {}) {
651
+ if (!text.trim()) throw new CaptureIntegrityError("OTLP trace text must not be empty");
652
+ return new OtlpBufferTraceStore(Buffer.from(text, "utf8"), opts);
653
+ }
654
+ function createOtlpBufferTraceStore(source, options = {}) {
655
+ return new OtlpBufferTraceStore(source, options);
656
+ }
657
+ /** A `"` at `idx` is a real JSON delimiter only when the run of `\`
658
+ * immediately preceding it is even-length; an odd run means the quote
659
+ * is escaped (`\"`) and is part of a string value, not a boundary. */
660
+ function isUnescapedQuote(slice, idx) {
661
+ if (slice[idx] !== "\"") return false;
662
+ let backslashes = 0;
663
+ let b = idx - 1;
664
+ while (b >= 0 && slice[b] === "\\") {
665
+ backslashes += 1;
666
+ b -= 1;
667
+ }
668
+ return backslashes % 2 === 0;
669
+ }
670
+ /** Scan backwards from `from` (inclusive) for the nearest UNescaped `"`.
671
+ * Returns its index, or -1 when none is found. */
672
+ function prevUnescapedQuote(slice, from) {
673
+ for (let i = from; i >= 0; i -= 1) if (slice[i] === "\"" && isUnescapedQuote(slice, i)) return i;
674
+ return -1;
675
+ }
676
+ /**
677
+ * Best-effort: locate the JSON path for the substring at `offset` in
678
+ * a single span's JSONL slice. The slice is '...,"key":"value..."' — we
679
+ * walk back from `offset` to the value-opening quote, past the `:`, to
680
+ * the key's closing then opening quote, skipping `\"`-escaped quotes that
681
+ * live inside string values. Returns `null` when the offset doesn't fall
682
+ * inside a recognisable string field.
683
+ */
684
+ function bestAttributePathForOffset(slice, offset) {
685
+ const valueQuote = prevUnescapedQuote(slice, Math.min(offset, slice.length - 1));
686
+ if (valueQuote < 1) return null;
687
+ let j = valueQuote - 1;
688
+ while (j >= 0 && slice[j] !== ":") j -= 1;
689
+ if (j < 1) return null;
690
+ const keyClose = prevUnescapedQuote(slice, j - 1);
691
+ if (keyClose < 1) return null;
692
+ const keyOpen = prevUnescapedQuote(slice, keyClose - 1);
693
+ if (keyOpen < 0) return null;
694
+ return slice.slice(keyOpen + 1, keyClose);
695
+ }
696
+ const ERROR_CLUSTER_EXEMPLARS = 5;
697
+ /** Collapse volatile tokens so semantically identical failures share a key:
698
+ * hex/uuid ids → <id>, numbers → #, quoted/abs paths → <path>, durations →
699
+ * <dur>, whitespace collapsed. Empty/absent messages fall back to the span
700
+ * name so a no-message error still forms a real cluster. */
701
+ function normalizeErrorSignature(message, spanName) {
702
+ const raw = (message ?? "").trim();
703
+ return (raw.length > 0 ? raw : `(${spanName || "error"} — no message)`).replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<id>").replace(/\b[0-9a-f]{12,}\b/gi, "<id>").replace(/(?:\/[\w.\-@]+){2,}/g, "<path>").replace(/\b\d+(?:\.\d+)?(ms|s|m|h|kb|mb|gb)?\b/gi, (_m, u) => u ? `#${u}` : "#").replace(/\s+/g, " ").trim();
704
+ }
705
+ function bump(map, key) {
706
+ if (!key) return;
707
+ map.set(key, (map.get(key) ?? 0) + 1);
708
+ }
709
+ function topKey(map) {
710
+ let best = null;
711
+ let bestN = 0;
712
+ for (const [k, n] of map) if (n > bestN) {
713
+ best = k;
714
+ bestN = n;
715
+ }
716
+ return best;
717
+ }
718
+ function accumulateErrorCluster(clusters, traceId, span) {
719
+ const signature = normalizeErrorSignature(span.status_message, span.name);
720
+ let acc = clusters.get(signature);
721
+ if (!acc) {
722
+ acc = {
723
+ signature,
724
+ sample: span.status_message ?? span.name ?? "",
725
+ traceIds: /* @__PURE__ */ new Set(),
726
+ spanIds: [],
727
+ spanCount: 0,
728
+ spanNames: /* @__PURE__ */ new Map(),
729
+ toolNames: /* @__PURE__ */ new Map()
730
+ };
731
+ clusters.set(signature, acc);
732
+ }
733
+ acc.traceIds.add(traceId);
734
+ acc.spanCount += 1;
735
+ if (acc.spanIds.length < ERROR_CLUSTER_EXEMPLARS && !acc.spanIds.includes(span.span_id)) acc.spanIds.push(span.span_id);
736
+ bump(acc.spanNames, span.name);
737
+ bump(acc.toolNames, span.tool_name);
738
+ }
739
+ function finalizeErrorClusters(clusters, errorTraceCount) {
740
+ const out = [...clusters.values()].map((acc) => ({
741
+ signature: acc.signature,
742
+ status_message_sample: acc.sample,
743
+ span_name: topKey(acc.spanNames),
744
+ tool_name: topKey(acc.toolNames),
745
+ trace_count: acc.traceIds.size,
746
+ span_count: acc.spanCount,
747
+ prevalence: errorTraceCount > 0 ? acc.traceIds.size / errorTraceCount : 0,
748
+ exemplar_trace_ids: [...acc.traceIds].slice(0, ERROR_CLUSTER_EXEMPLARS),
749
+ exemplar_span_ids: acc.spanIds.slice(0, ERROR_CLUSTER_EXEMPLARS)
750
+ }));
751
+ out.sort((a, b) => b.trace_count - a.trace_count || b.span_count - a.span_count);
752
+ return out;
753
+ }
754
+ //#endregion
755
+ export { otlpTextToTraceAnalysisStore as i, OtlpFileTraceStore as n, createOtlpBufferTraceStore as r, DEFAULT_MAX_TRACE_FILE_BYTES as t };
756
+
757
+ //# sourceMappingURL=store-otlp-DX4fGIcf.js.map