@tangle-network/agent-eval 0.93.0 → 0.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/otel.d.ts +4 -4
- package/dist/analyst/index.d.ts +10 -10
- package/dist/analyst/index.js +6 -6
- package/dist/{analyze-runs-DwCEkpO_.d.ts → analyze-runs-B6Ljo_dI.d.ts} +2 -2
- package/dist/{baseline-DE36-Np7.d.ts → baseline-Bbid3WoO.d.ts} +1 -1
- package/dist/belief-state/index.d.ts +2 -2
- package/dist/belief-state/index.js +1 -1
- package/dist/builder-eval/index.d.ts +3 -3
- package/dist/builder-eval/index.js +2 -2
- package/dist/{calibration-Cpr3WaX3.d.ts → calibration-BPmzuVPk.d.ts} +1 -1
- package/dist/campaign/index.d.ts +13 -13
- package/dist/campaign/index.js +8 -8
- package/dist/{chunk-TWS7AZEY.js → chunk-2K6UUZ7P.js} +2 -2
- package/dist/{chunk-Y47J2LJ3.js → chunk-2KNZHH3P.js} +5 -5
- package/dist/{chunk-LMZQ2Z4U.js → chunk-56GC6VYO.js} +126 -1
- package/dist/chunk-56GC6VYO.js.map +1 -0
- package/dist/{chunk-QG2OVF2D.js → chunk-CTBHKLEU.js} +99 -30
- package/dist/chunk-CTBHKLEU.js.map +1 -0
- package/dist/{chunk-CVVHBFGN.js → chunk-CWNP4DV4.js} +53 -5
- package/dist/chunk-CWNP4DV4.js.map +1 -0
- package/dist/{chunk-QS3RBQPI.js → chunk-D5KBVULY.js} +2 -2
- package/dist/{chunk-ZFIBGEOL.js → chunk-EGPMSBEZ.js} +4 -4
- package/dist/{chunk-GSH6QNNS.js → chunk-KW53MSA5.js} +101 -78
- package/dist/chunk-KW53MSA5.js.map +1 -0
- package/dist/{chunk-UMMZHCPB.js → chunk-MIFZUPEK.js} +7 -5
- package/dist/chunk-MIFZUPEK.js.map +1 -0
- package/dist/{chunk-XY4DDNEG.js → chunk-MPQWFX6Y.js} +2 -2
- package/dist/{chunk-L3JOU6XM.js → chunk-NACM5RS7.js} +3 -3
- package/dist/{chunk-D3V5B42D.js → chunk-Q5LIB7BC.js} +7 -4
- package/dist/{chunk-D3V5B42D.js.map → chunk-Q5LIB7BC.js.map} +1 -1
- package/dist/{chunk-QAY5UIJO.js → chunk-QMUEXQJS.js} +99 -57
- package/dist/chunk-QMUEXQJS.js.map +1 -0
- package/dist/{chunk-4FBZZIYD.js → chunk-QTMYW64F.js} +2 -2
- package/dist/{chunk-6SOJM3VR.js → chunk-SD2YFWQQ.js} +4 -4
- package/dist/{chunk-UHMJT4T7.js → chunk-TBDR6PAI.js} +2 -2
- package/dist/{chunk-47X6LRCE.js → chunk-UFSG7ACU.js} +10 -7
- package/dist/chunk-UFSG7ACU.js.map +1 -0
- package/dist/{chunk-CY6U5S3X.js → chunk-URWVKRCS.js} +2 -2
- package/dist/{chunk-T375SUOZ.js → chunk-ZOPLCJSY.js} +86 -31
- package/dist/chunk-ZOPLCJSY.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/contract/index.d.ts +17 -17
- package/dist/contract/index.js +11 -11
- package/dist/{control-_Qb7skHX.d.ts → control-D6qwHXIR.d.ts} +4 -4
- package/dist/{control-runtime-DuFBYg7A.d.ts → control-runtime-Acf9CGhw.d.ts} +2 -2
- package/dist/control.d.ts +5 -5
- package/dist/{corpus-BoR-041R.d.ts → corpus-B8A4BDR3.d.ts} +1 -1
- package/dist/{counterfactual-Dwibr5IW.d.ts → counterfactual-DlOz8PBx.d.ts} +3 -3
- package/dist/{default-registry-zoGHUQEH.d.ts → default-registry-6dhErQbs.d.ts} +2 -2
- package/dist/diagnose.d.ts +9 -9
- package/dist/diagnose.js +1 -1
- package/dist/{emitter-DEZwY14K.d.ts → emitter-C2rqGH_l.d.ts} +1 -1
- package/dist/{failure-cluster-CL7IVgkJ.d.ts → failure-cluster-DH9Flgcf.d.ts} +1 -1
- package/dist/{feedback-trajectory-D9OVLrg9.d.ts → feedback-trajectory-BxY0cKfs.d.ts} +1 -1
- package/dist/governance/index.d.ts +2 -2
- package/dist/{harness-optimizer-EnEnQPsr.d.ts → harness-optimizer-mOl9XX_O.d.ts} +1 -1
- package/dist/hosted/index.d.ts +4 -4
- package/dist/index.d.ts +68 -46
- package/dist/index.js +261 -87
- package/dist/index.js.map +1 -1
- package/dist/{insight-report-BBwvOh6x.d.ts → insight-report-DWl3z9tl.d.ts} +1 -1
- package/dist/{integrity-VJ9A7aST.d.ts → integrity-D2t12mMw.d.ts} +1 -1
- package/dist/{kind-factory-5b7xXXOr.d.ts → kind-factory-0BhLSI27.d.ts} +1 -1
- package/dist/knowledge/index.d.ts +3 -3
- package/dist/{llm-client-BeEcAokY.d.ts → llm-client-Bj7g0rqu.d.ts} +31 -0
- package/dist/meta-eval/index.d.ts +4 -4
- package/dist/meta-eval/index.js +1 -1
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.d.ts +6 -6
- package/dist/pipelines/index.js +40 -15
- package/dist/pipelines/index.js.map +1 -1
- package/dist/prm/index.d.ts +11 -7
- package/dist/prm/index.js +55 -12
- package/dist/prm/index.js.map +1 -1
- package/dist/{provenance-LnqRT0sS.d.ts → provenance-P-bCL2Fo.d.ts} +3 -3
- package/dist/{query-CqTxMwDw.d.ts → query-B7GGjRox.d.ts} +2 -1
- package/dist/{red-team-BXHil6c8.d.ts → red-team-BWdoyleI.d.ts} +1 -1
- package/dist/{release-report-euXIV_Sk.d.ts → release-report-BEbWmVYj.d.ts} +1 -1
- package/dist/reporting.d.ts +6 -6
- package/dist/reporting.js +3 -3
- package/dist/{researcher-DE6Gpnb4.d.ts → researcher-B0C2_fVO.d.ts} +5 -5
- package/dist/rl.d.ts +10 -10
- package/dist/rl.js +4 -4
- package/dist/{rubric-BOfxn4ja.d.ts → rubric-Cc6UHvUb.d.ts} +2 -2
- package/dist/{run-campaign-RDGAM5KJ.js → run-campaign-7WNXMDSN.js} +3 -3
- package/dist/{run-critic-BAIjX99r.d.ts → run-critic-CmMf05uV.d.ts} +1 -1
- package/dist/{run-improvement-loop-5z_l5zDz.d.ts → run-improvement-loop-DBahB8Ax.d.ts} +1 -1
- package/dist/{semantic-concept-judge-Dn8Z6KEG.d.ts → semantic-concept-judge-B9MgmBnM.d.ts} +3 -3
- package/dist/{statistics-C7PozGrZ.d.ts → statistics-CCJpTGOS.d.ts} +117 -2
- package/dist/{store-CKUAgsJz.d.ts → store-BcFXE6LG.d.ts} +16 -1
- package/dist/{summary-report-DGmUucwQ.d.ts → summary-report-BDOFevaT.d.ts} +1 -1
- package/dist/{test-graded-scenario-BdVaPyHT.d.ts → test-graded-scenario-DeODGLra.d.ts} +21 -2
- package/dist/traces.d.ts +19 -6
- package/dist/traces.js +4 -4
- package/dist/{trajectory-GEdXJCL5.d.ts → trajectory-2TkpSEVh.d.ts} +1 -1
- package/dist/{types-Croy5h7V.d.ts → types-C7DGg5ex.d.ts} +2 -0
- package/dist/{types-2VVIL04s.d.ts → types-Ce17tDlG.d.ts} +2 -2
- package/dist/wire/index.d.ts +4 -4
- package/dist/wire/index.js +2 -2
- package/dist/workflow/index.d.ts +13 -13
- package/dist/workflow/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-47X6LRCE.js.map +0 -1
- package/dist/chunk-CVVHBFGN.js.map +0 -1
- package/dist/chunk-GSH6QNNS.js.map +0 -1
- package/dist/chunk-LMZQ2Z4U.js.map +0 -1
- package/dist/chunk-QAY5UIJO.js.map +0 -1
- package/dist/chunk-QG2OVF2D.js.map +0 -1
- package/dist/chunk-T375SUOZ.js.map +0 -1
- package/dist/chunk-UMMZHCPB.js.map +0 -1
- /package/dist/{chunk-TWS7AZEY.js.map → chunk-2K6UUZ7P.js.map} +0 -0
- /package/dist/{chunk-Y47J2LJ3.js.map → chunk-2KNZHH3P.js.map} +0 -0
- /package/dist/{chunk-QS3RBQPI.js.map → chunk-D5KBVULY.js.map} +0 -0
- /package/dist/{chunk-ZFIBGEOL.js.map → chunk-EGPMSBEZ.js.map} +0 -0
- /package/dist/{chunk-XY4DDNEG.js.map → chunk-MPQWFX6Y.js.map} +0 -0
- /package/dist/{chunk-L3JOU6XM.js.map → chunk-NACM5RS7.js.map} +0 -0
- /package/dist/{chunk-4FBZZIYD.js.map → chunk-QTMYW64F.js.map} +0 -0
- /package/dist/{chunk-6SOJM3VR.js.map → chunk-SD2YFWQQ.js.map} +0 -0
- /package/dist/{chunk-UHMJT4T7.js.map → chunk-TBDR6PAI.js.map} +0 -0
- /package/dist/{chunk-CY6U5S3X.js.map → chunk-URWVKRCS.js.map} +0 -0
- /package/dist/{run-campaign-RDGAM5KJ.js.map → run-campaign-7WNXMDSN.js.map} +0 -0
|
@@ -111,6 +111,15 @@ function firstStringAttr(attrs, keys) {
|
|
|
111
111
|
}
|
|
112
112
|
return null;
|
|
113
113
|
}
|
|
114
|
+
function spanEpochMillis(ts) {
|
|
115
|
+
if (!ts) return null;
|
|
116
|
+
if (/^\d+$/.test(ts)) return Number(ts);
|
|
117
|
+
const n = Date.parse(ts);
|
|
118
|
+
return Number.isNaN(n) ? null : n;
|
|
119
|
+
}
|
|
120
|
+
function compareSpanTime(a, b) {
|
|
121
|
+
return (spanEpochMillis(a) ?? 0) - (spanEpochMillis(b) ?? 0);
|
|
122
|
+
}
|
|
114
123
|
|
|
115
124
|
// src/trace-analyst/types.ts
|
|
116
125
|
var DEFAULT_TRACE_ANALYST_BUDGETS = {
|
|
@@ -147,12 +156,18 @@ function truncateForBudget(value, byteCap) {
|
|
|
147
156
|
}
|
|
148
157
|
|
|
149
158
|
// src/trace-analyst/store-otlp.ts
|
|
159
|
+
var INDEX_YIELD_LINES = 5e3;
|
|
160
|
+
function yieldToEventLoop() {
|
|
161
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
162
|
+
}
|
|
163
|
+
var DEFAULT_MAX_TRACE_FILE_BYTES = 256 * 1024 * 1024;
|
|
150
164
|
var OtlpFileTraceStore = class {
|
|
151
165
|
path;
|
|
152
166
|
perAttributeViewBudget;
|
|
153
167
|
perAttributeSpanBudget;
|
|
154
168
|
perCallByteCeiling;
|
|
155
169
|
perMatchTextBudget;
|
|
170
|
+
maxFileBytes;
|
|
156
171
|
indexPromise;
|
|
157
172
|
/** Cached UTF-8 buffer of the file. We pin it once because every
|
|
158
173
|
* read needs slice access and re-reading on each call balloons the
|
|
@@ -164,6 +179,7 @@ var OtlpFileTraceStore = class {
|
|
|
164
179
|
this.perAttributeSpanBudget = opts.perAttributeSpanBudget ?? DEFAULT_TRACE_ANALYST_BUDGETS.perAttributeSpanBudget;
|
|
165
180
|
this.perCallByteCeiling = opts.perCallByteCeiling ?? DEFAULT_TRACE_ANALYST_BUDGETS.perCallByteCeiling;
|
|
166
181
|
this.perMatchTextBudget = opts.perMatchTextBudget ?? DEFAULT_TRACE_ANALYST_BUDGETS.perMatchTextBudget;
|
|
182
|
+
this.maxFileBytes = opts.maxFileBytes ?? DEFAULT_MAX_TRACE_FILE_BYTES;
|
|
167
183
|
}
|
|
168
184
|
// ─── Public API ────────────────────────────────────────────────────
|
|
169
185
|
async getOverview(filters) {
|
|
@@ -185,8 +201,8 @@ var OtlpFileTraceStore = class {
|
|
|
185
201
|
for (const m of t.models) models.add(m);
|
|
186
202
|
for (const tn of t.tools) tools.add(tn);
|
|
187
203
|
rawBytes += t.raw_jsonl_bytes;
|
|
188
|
-
if (!earliest || t.start_time <
|
|
189
|
-
if (!latest || t.end_time >
|
|
204
|
+
if (!earliest || compareSpanTime(t.start_time, earliest) < 0) earliest = t.start_time;
|
|
205
|
+
if (!latest || compareSpanTime(t.end_time, latest) > 0) latest = t.end_time;
|
|
190
206
|
if (t.has_errors) {
|
|
191
207
|
errorTraceCount += 1;
|
|
192
208
|
for (const s of t.spans) {
|
|
@@ -243,8 +259,9 @@ var OtlpFileTraceStore = class {
|
|
|
243
259
|
const spans = [];
|
|
244
260
|
let runningBytes = 0;
|
|
245
261
|
let span_response_bytes_max = 0;
|
|
262
|
+
const counter = { value: 0 };
|
|
246
263
|
for (const s of trace.spans) {
|
|
247
|
-
const projected =
|
|
264
|
+
const projected = this.projectSpan(buf, trace.trace_id, s, cap, counter);
|
|
248
265
|
const bytes = Buffer.byteLength(JSON.stringify(projected), "utf8");
|
|
249
266
|
span_response_bytes_max = Math.max(span_response_bytes_max, bytes);
|
|
250
267
|
runningBytes += bytes;
|
|
@@ -279,12 +296,10 @@ var OtlpFileTraceStore = class {
|
|
|
279
296
|
const missing = opts.span_ids.filter((id) => !found.some((f2) => f2.span_id === id));
|
|
280
297
|
const buf = await this.buffer();
|
|
281
298
|
const spans = [];
|
|
282
|
-
|
|
299
|
+
const counter = { value: 0 };
|
|
283
300
|
let runningBytes = 0;
|
|
284
301
|
for (const s of found) {
|
|
285
|
-
const
|
|
286
|
-
const projected = await this.projectSpan(buf, trace.trace_id, s, cap);
|
|
287
|
-
truncated += before.delta();
|
|
302
|
+
const projected = this.projectSpan(buf, trace.trace_id, s, cap, counter);
|
|
288
303
|
const bytes = Buffer.byteLength(JSON.stringify(projected), "utf8");
|
|
289
304
|
runningBytes += bytes;
|
|
290
305
|
if (runningBytes > this.perCallByteCeiling) {
|
|
@@ -296,7 +311,7 @@ var OtlpFileTraceStore = class {
|
|
|
296
311
|
trace_id: trace.trace_id,
|
|
297
312
|
spans,
|
|
298
313
|
missing_span_ids: missing,
|
|
299
|
-
truncated_attribute_count:
|
|
314
|
+
truncated_attribute_count: counter.value
|
|
300
315
|
};
|
|
301
316
|
}
|
|
302
317
|
async searchTrace(opts) {
|
|
@@ -329,14 +344,14 @@ var OtlpFileTraceStore = class {
|
|
|
329
344
|
}
|
|
330
345
|
if (hits.length >= max_matches) {
|
|
331
346
|
capped = true;
|
|
332
|
-
total = Math.max(total, hits.length + 1);
|
|
333
347
|
break;
|
|
334
348
|
}
|
|
335
349
|
}
|
|
336
350
|
return {
|
|
337
351
|
trace_id: trace.trace_id,
|
|
338
352
|
hits,
|
|
339
|
-
|
|
353
|
+
// Uncapped: every span scanned to completion, so `total` is exact.
|
|
354
|
+
total_matches: capped ? hits.length : total,
|
|
340
355
|
has_more: capped || total > hits.length
|
|
341
356
|
};
|
|
342
357
|
}
|
|
@@ -378,10 +393,28 @@ var OtlpFileTraceStore = class {
|
|
|
378
393
|
}
|
|
379
394
|
async buffer() {
|
|
380
395
|
if (!this.bufferPromise) {
|
|
381
|
-
this.bufferPromise =
|
|
396
|
+
this.bufferPromise = this.readGuarded();
|
|
382
397
|
}
|
|
383
398
|
return this.bufferPromise;
|
|
384
399
|
}
|
|
400
|
+
/** Stat-then-read so an oversized file fails loud BEFORE we allocate a
|
|
401
|
+
* multi-hundred-MB Buffer and OOM the process. A missing file surfaces
|
|
402
|
+
* as TraceFileMissingError; any other stat/read error propagates. */
|
|
403
|
+
async readGuarded() {
|
|
404
|
+
let stats;
|
|
405
|
+
try {
|
|
406
|
+
stats = await stat(this.path);
|
|
407
|
+
} catch (err) {
|
|
408
|
+
if (err?.code === "ENOENT") {
|
|
409
|
+
throw new TraceFileMissingError(this.path);
|
|
410
|
+
}
|
|
411
|
+
throw err;
|
|
412
|
+
}
|
|
413
|
+
if (stats.size > this.maxFileBytes) {
|
|
414
|
+
throw new TraceFileTooLargeError(this.path, stats.size, this.maxFileBytes);
|
|
415
|
+
}
|
|
416
|
+
return readFile(this.path);
|
|
417
|
+
}
|
|
385
418
|
async index() {
|
|
386
419
|
if (!this.indexPromise) {
|
|
387
420
|
this.indexPromise = this.buildIndex();
|
|
@@ -389,19 +422,15 @@ var OtlpFileTraceStore = class {
|
|
|
389
422
|
return this.indexPromise;
|
|
390
423
|
}
|
|
391
424
|
async buildIndex() {
|
|
392
|
-
|
|
393
|
-
try {
|
|
394
|
-
buf = await this.buffer();
|
|
395
|
-
} catch (err) {
|
|
396
|
-
const stats = await stat(this.path).catch(() => null);
|
|
397
|
-
if (!stats) {
|
|
398
|
-
throw new TraceFileMissingError(this.path);
|
|
399
|
-
}
|
|
400
|
-
throw err;
|
|
401
|
-
}
|
|
425
|
+
const buf = await this.buffer();
|
|
402
426
|
const byTrace = /* @__PURE__ */ new Map();
|
|
403
427
|
let cursor = 0;
|
|
428
|
+
let sinceYield = 0;
|
|
404
429
|
while (cursor < buf.length) {
|
|
430
|
+
if (++sinceYield >= INDEX_YIELD_LINES) {
|
|
431
|
+
sinceYield = 0;
|
|
432
|
+
await yieldToEventLoop();
|
|
433
|
+
}
|
|
405
434
|
const newlineIndex = buf.indexOf(10, cursor);
|
|
406
435
|
const lineEnd = newlineIndex === -1 ? buf.length : newlineIndex;
|
|
407
436
|
const lineLength = lineEnd - cursor;
|
|
@@ -463,8 +492,8 @@ var OtlpFileTraceStore = class {
|
|
|
463
492
|
entry.span_count += 1;
|
|
464
493
|
entry.raw_jsonl_bytes += lineLength + 1;
|
|
465
494
|
if (span.status === "ERROR") entry.has_errors = true;
|
|
466
|
-
if (span.start_time
|
|
467
|
-
if (span.end_time
|
|
495
|
+
if (compareSpanTime(span.start_time, entry.start_time) < 0) entry.start_time = span.start_time;
|
|
496
|
+
if (compareSpanTime(span.end_time, entry.end_time) > 0) entry.end_time = span.end_time;
|
|
468
497
|
if (span.model_name) entry.models.add(span.model_name);
|
|
469
498
|
if (span.tool_name) entry.tools.add(span.tool_name);
|
|
470
499
|
}
|
|
@@ -472,9 +501,11 @@ var OtlpFileTraceStore = class {
|
|
|
472
501
|
for (const t of byTrace.values()) {
|
|
473
502
|
totalRawBytes += t.raw_jsonl_bytes;
|
|
474
503
|
t.spans.sort(
|
|
475
|
-
(a, b) => a.start_time
|
|
504
|
+
(a, b) => compareSpanTime(a.start_time, b.start_time) || a.line_byte_offset - b.line_byte_offset
|
|
476
505
|
);
|
|
477
|
-
|
|
506
|
+
const startMs = spanEpochMillis(t.start_time);
|
|
507
|
+
const endMs = spanEpochMillis(t.end_time);
|
|
508
|
+
t.duration_ms = startMs === null || endMs === null ? 0 : Math.max(0, endMs - startMs);
|
|
478
509
|
}
|
|
479
510
|
const sortedTraceIds = [...byTrace.keys()].sort();
|
|
480
511
|
return { byTrace, totalRawBytes, sortedTraceIds };
|
|
@@ -534,7 +565,7 @@ var OtlpFileTraceStore = class {
|
|
|
534
565
|
};
|
|
535
566
|
}
|
|
536
567
|
// ─── Span projection (lazy attribute reads) ────────────────────────
|
|
537
|
-
|
|
568
|
+
projectSpan(buf, trace_id, s, perAttrCap, counter) {
|
|
538
569
|
const slice = buf.subarray(s.line_byte_offset, s.line_byte_offset + s.line_byte_length).toString("utf8");
|
|
539
570
|
let raw = {};
|
|
540
571
|
try {
|
|
@@ -547,13 +578,13 @@ var OtlpFileTraceStore = class {
|
|
|
547
578
|
for (const [k, v] of Object.entries(attrs)) {
|
|
548
579
|
if (typeof v === "string") {
|
|
549
580
|
const trunc = truncateForBudget(v, perAttrCap);
|
|
550
|
-
if (trunc !== v)
|
|
581
|
+
if (trunc !== v) counter.value += 1;
|
|
551
582
|
projected[k] = trunc;
|
|
552
583
|
} else if (Array.isArray(v) || v && typeof v === "object") {
|
|
553
584
|
const json = JSON.stringify(v);
|
|
554
585
|
const trunc = truncateForBudget(json, perAttrCap);
|
|
555
586
|
if (trunc !== json) {
|
|
556
|
-
|
|
587
|
+
counter.value += 1;
|
|
557
588
|
projected[k] = trunc;
|
|
558
589
|
} else {
|
|
559
590
|
projected[k] = v;
|
|
@@ -635,6 +666,19 @@ var TraceFileMissingError = class extends NotFoundError {
|
|
|
635
666
|
super(`trace file not found: ${path}`);
|
|
636
667
|
}
|
|
637
668
|
};
|
|
669
|
+
var TraceFileTooLargeError = class extends Error {
|
|
670
|
+
path;
|
|
671
|
+
size_bytes;
|
|
672
|
+
max_bytes;
|
|
673
|
+
constructor(path, size_bytes, max_bytes) {
|
|
674
|
+
super(
|
|
675
|
+
`trace file ${path} is ${size_bytes} bytes, over the ${max_bytes}-byte limit; raise OtlpFileTraceStoreOptions.maxFileBytes or pre-split the file`
|
|
676
|
+
);
|
|
677
|
+
this.path = path;
|
|
678
|
+
this.size_bytes = size_bytes;
|
|
679
|
+
this.max_bytes = max_bytes;
|
|
680
|
+
}
|
|
681
|
+
};
|
|
638
682
|
var TraceNotFoundError = class extends NotFoundError {
|
|
639
683
|
trace_id;
|
|
640
684
|
constructor(trace_id) {
|
|
@@ -654,37 +698,33 @@ var SpanNotFoundError = class extends NotFoundError {
|
|
|
654
698
|
function isPresent(v) {
|
|
655
699
|
return v !== void 0;
|
|
656
700
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
let
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
701
|
+
function isUnescapedQuote(slice, idx) {
|
|
702
|
+
if (slice[idx] !== '"') return false;
|
|
703
|
+
let backslashes = 0;
|
|
704
|
+
let b = idx - 1;
|
|
705
|
+
while (b >= 0 && slice[b] === "\\") {
|
|
706
|
+
backslashes += 1;
|
|
707
|
+
b -= 1;
|
|
708
|
+
}
|
|
709
|
+
return backslashes % 2 === 0;
|
|
665
710
|
}
|
|
666
|
-
function
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
return after - before;
|
|
672
|
-
}
|
|
673
|
-
};
|
|
711
|
+
function prevUnescapedQuote(slice, from) {
|
|
712
|
+
for (let i = from; i >= 0; i -= 1) {
|
|
713
|
+
if (slice[i] === '"' && isUnescapedQuote(slice, i)) return i;
|
|
714
|
+
}
|
|
715
|
+
return -1;
|
|
674
716
|
}
|
|
675
717
|
function bestAttributePathForOffset(slice, offset) {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
if (l <= 0) return null;
|
|
687
|
-
return slice.slice(l + 1, k);
|
|
718
|
+
const valueQuote = prevUnescapedQuote(slice, Math.min(offset, slice.length - 1));
|
|
719
|
+
if (valueQuote < 1) return null;
|
|
720
|
+
let j = valueQuote - 1;
|
|
721
|
+
while (j >= 0 && slice[j] !== ":") j -= 1;
|
|
722
|
+
if (j < 1) return null;
|
|
723
|
+
const keyClose = prevUnescapedQuote(slice, j - 1);
|
|
724
|
+
if (keyClose < 1) return null;
|
|
725
|
+
const keyOpen = prevUnescapedQuote(slice, keyClose - 1);
|
|
726
|
+
if (keyOpen < 0) return null;
|
|
727
|
+
return slice.slice(keyOpen + 1, keyClose);
|
|
688
728
|
}
|
|
689
729
|
var ERROR_CLUSTER_MAX = 50;
|
|
690
730
|
var ERROR_CLUSTER_EXEMPLARS = 5;
|
|
@@ -903,6 +943,8 @@ export {
|
|
|
903
943
|
asNumber,
|
|
904
944
|
firstNumberAttr,
|
|
905
945
|
firstStringAttr,
|
|
946
|
+
spanEpochMillis,
|
|
947
|
+
compareSpanTime,
|
|
906
948
|
DEFAULT_TRACE_ANALYST_BUDGETS,
|
|
907
949
|
TRACE_ANALYST_TRUNCATION_MARKER_PREFIX,
|
|
908
950
|
OtlpFileTraceStore,
|
|
@@ -912,4 +954,4 @@ export {
|
|
|
912
954
|
buildTraceAnalystTools,
|
|
913
955
|
traceAnalystFunctionGroup
|
|
914
956
|
};
|
|
915
|
-
//# sourceMappingURL=chunk-
|
|
957
|
+
//# sourceMappingURL=chunk-QMUEXQJS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/trace-analyst/otlp-span.ts","../src/trace-analyst/types.ts","../src/trace-analyst/store-otlp.ts","../src/trace-analyst/store.ts","../src/trace-analyst/tools.ts"],"sourcesContent":["/**\n * Canonical OTLP-flat-line readers shared by every consumer of the\n * OTLP-JSONL wire shape (one OTLP span per line; the form\n * `flattenOtlpExportToNdjson` produces and the form AppWorld / HALO\n * emit via their OpenInference OTLP exporter).\n *\n * `OtlpFileTraceStore` indexes spans with these; `otlpToRunRecords`\n * aggregates spans into `RunRecord`s with the same readers. One parser,\n * one vocabulary — a divergence between the analyst's view of a trace and\n * the RunRecord projected from it is a class of bug this consolidation\n * removes by construction.\n *\n * Vocabulary. The readers understand BOTH dialects that appear in the\n * wild:\n * - the substrate's own `llm.*` / `tool.*` / `span.kind` attributes\n * (`flattenSpanAttributes` in `trace/otel.ts`), and\n * - the OpenInference / inference-export attributes AppWorld / HALO\n * emit (`openinference.span.kind`, `inference.observation_kind`,\n * `inference.llm.input_tokens`, `llm.token_count.prompt`, …).\n *\n * Pure, no I/O.\n */\n\nimport type { TraceAnalystSpanKind, TraceAnalystSpanStatus } from './types'\n\n/**\n * The structural fields a flat OTLP-JSONL line projects to. `attributes`\n * is the merged resource+span attribute map (span overrides resource);\n * the named fields are the pivots every reader of a trace needs without\n * paying the full attribute materialisation.\n */\nexport interface ProjectedOtlpSpan {\n trace_id: string\n span_id: string\n parent_span_id: string | null\n name: string\n kind: TraceAnalystSpanKind\n start_time: string\n end_time: string\n duration_ms: number\n status: TraceAnalystSpanStatus\n status_message: string | undefined\n service_name: string | null\n agent_name: string | null\n model_name: string | null\n tool_name: string | null\n /** Merged resource + span attributes, span winning on overlap. */\n attributes: Record<string, unknown>\n}\n\n/**\n * Project one parsed OTLP-JSONL object to `ProjectedOtlpSpan`, or `null`\n * when the line is missing the mandatory `trace_id` + `span_id`.\n */\nexport function projectOtlpFlatLine(raw: Record<string, unknown>): ProjectedOtlpSpan | null {\n const trace_id = stringField(raw, 'trace_id') ?? stringField(raw, 'traceId')\n const span_id = stringField(raw, 'span_id') ?? stringField(raw, 'spanId')\n if (!trace_id || !span_id) return null\n\n const parent_id = stringField(raw, 'parent_span_id') ?? stringField(raw, 'parentSpanId') ?? null\n const name = stringField(raw, 'name') ?? 'unknown'\n const start_time = stringField(raw, 'start_time') ?? stringField(raw, 'startTime') ?? ''\n const end_time = stringField(raw, 'end_time') ?? stringField(raw, 'endTime') ?? start_time\n\n const status = readOtlpStatus(raw)\n const attributes = extractOtlpAttributes(raw)\n\n const service_name =\n asString(attributes['service.name']) ??\n asString(attributes['resource.attributes.service.name']) ??\n null\n const agent_name =\n asString(attributes['agent.name']) ??\n asString(attributes['inference.agent.name']) ??\n asString(attributes['inference.agent_name']) ??\n null\n const model_name =\n asString(attributes['llm.model_name']) ??\n asString(attributes['inference.llm.model_name']) ??\n asString(attributes['llm.model']) ??\n null\n const tool_name =\n asString(attributes['tool.name']) ?? asString(attributes['inference.tool.name']) ?? null\n\n const kind = inferOtlpKind(attributes)\n\n let duration_ms = 0\n if (start_time && end_time) {\n const a = Date.parse(start_time)\n const b = Date.parse(end_time)\n if (!Number.isNaN(a) && !Number.isNaN(b)) duration_ms = Math.max(0, b - a)\n }\n\n return {\n trace_id,\n span_id,\n parent_span_id: parent_id && parent_id.length > 0 ? parent_id : null,\n name,\n kind,\n start_time,\n end_time,\n duration_ms,\n status: status.code,\n status_message: status.message,\n service_name,\n agent_name,\n model_name,\n tool_name,\n attributes,\n }\n}\n\nexport function readOtlpStatus(raw: Record<string, unknown>): {\n code: TraceAnalystSpanStatus\n message: string | undefined\n} {\n const status = raw.status\n if (status && typeof status === 'object' && !Array.isArray(status)) {\n const codeRaw = (status as Record<string, unknown>).code\n const code: TraceAnalystSpanStatus =\n codeRaw === 'STATUS_CODE_OK' || codeRaw === 'OK'\n ? 'OK'\n : codeRaw === 'STATUS_CODE_ERROR' || codeRaw === 'ERROR'\n ? 'ERROR'\n : 'UNSET'\n const messageRaw = (status as Record<string, unknown>).message\n const message = typeof messageRaw === 'string' && messageRaw.length > 0 ? messageRaw : undefined\n return { code, message }\n }\n return { code: 'UNSET', message: undefined }\n}\n\nexport function inferOtlpKind(attrs: Record<string, unknown>): TraceAnalystSpanKind {\n const opik =\n asString(attrs['openinference.span.kind']) ?? asString(attrs['inference.observation_kind'])\n if (opik) {\n const upper = opik.toUpperCase()\n if (\n upper === 'AGENT' ||\n upper === 'LLM' ||\n upper === 'TOOL' ||\n upper === 'CHAIN' ||\n upper === 'GUARDRAIL' ||\n upper === 'SPAN'\n ) {\n return upper as TraceAnalystSpanKind\n }\n }\n return 'UNKNOWN'\n}\n\n/**\n * Flatten OTLP `attributes` + `resource.attributes` into a single\n * dotted-key map. Span attributes override resource attributes when keys\n * overlap. Nested objects/arrays are preserved as-is.\n */\nexport function extractOtlpAttributes(raw: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n const resource = raw.resource\n if (resource && typeof resource === 'object' && !Array.isArray(resource)) {\n const ra = (resource as Record<string, unknown>).attributes\n if (ra && typeof ra === 'object' && !Array.isArray(ra)) {\n for (const [k, v] of Object.entries(ra as Record<string, unknown>)) {\n out[k] = v\n }\n }\n }\n const spanAttrs = raw.attributes\n if (spanAttrs && typeof spanAttrs === 'object' && !Array.isArray(spanAttrs)) {\n for (const [k, v] of Object.entries(spanAttrs as Record<string, unknown>)) {\n out[k] = v\n }\n }\n return out\n}\n\nexport function stringField(raw: Record<string, unknown>, key: string): string | undefined {\n const v = raw[key]\n return typeof v === 'string' ? v : undefined\n}\n\nexport function asString(v: unknown): string | null {\n return typeof v === 'string' && v.length > 0 ? v : null\n}\n\n/** Read a numeric attribute, tolerating numeric strings; `null` if absent/NaN. */\nexport function asNumber(v: unknown): number | null {\n if (typeof v === 'number') return Number.isFinite(v) ? v : null\n if (typeof v === 'string' && v.length > 0) {\n const n = Number(v)\n return Number.isFinite(n) ? n : null\n }\n return null\n}\n\n/** First finite numeric value across a list of candidate attribute keys. */\nexport function firstNumberAttr(\n attrs: Record<string, unknown>,\n keys: readonly string[],\n): number | null {\n for (const k of keys) {\n const n = asNumber(attrs[k])\n if (n !== null) return n\n }\n return null\n}\n\n/** First non-empty string value across a list of candidate attribute keys. */\nexport function firstStringAttr(\n attrs: Record<string, unknown>,\n keys: readonly string[],\n): string | null {\n for (const k of keys) {\n const s = asString(attrs[k])\n if (s !== null) return s\n }\n return null\n}\n\n/**\n * Parse a span timestamp to epoch millis, or null when empty/unparseable. The\n * OTLP readers accept BOTH ISO-8601 and epoch-millis-string dialects, so raw\n * string comparison (`<`, `localeCompare`) mis-orders across dialects and\n * `Date.parse` returns NaN for a bare epoch-millis string.\n */\nexport function spanEpochMillis(ts: string | undefined | null): number | null {\n if (!ts) return null\n if (/^\\d+$/.test(ts)) return Number(ts)\n const n = Date.parse(ts)\n return Number.isNaN(n) ? null : n\n}\n\n/**\n * Order comparator for span timestamps across mixed ISO/epoch dialects.\n * Unparseable timestamps sort as epoch 0 (earliest), never NaN (which would\n * make the sort non-deterministic).\n */\nexport function compareSpanTime(a: string, b: string): number {\n return (spanEpochMillis(a) ?? 0) - (spanEpochMillis(b) ?? 0)\n}\n","/**\n * Shared types for the trace-analyst module.\n *\n * Wire format. The store interface speaks `OtlpSpanLike` rows — one JSONL\n * line per span, OTLP-shaped. We do NOT depend on a specific tracing\n * vendor at the type level. Adapter\n * layers map upstream shapes onto this interface.\n *\n * Design constraint. Every read operation that can return arbitrary\n * payload must carry a byte budget so the agent's tool result stays\n * bounded regardless of input trace size. Oversized responses\n * substitute a deterministic summary instead of bytes — see\n * `ViewTraceOversized`.\n */\n\n/** OTLP span kind (subset we actually use). */\nexport type TraceAnalystSpanKind =\n | 'AGENT'\n | 'LLM'\n | 'TOOL'\n | 'CHAIN'\n | 'GUARDRAIL'\n | 'SPAN'\n | 'UNKNOWN'\n\nexport type TraceAnalystSpanStatus = 'OK' | 'ERROR' | 'UNSET'\n\n/** Subset of OTLP span fields the analyst exposes to the agent. The\n * store's job is to project upstream's full span shape down to this\n * view — the analyst never sees vendor extensions directly. */\nexport interface TraceAnalystSpan {\n trace_id: string\n span_id: string\n parent_span_id: string | null\n name: string\n kind: TraceAnalystSpanKind\n start_time: string\n end_time: string\n duration_ms: number\n status: TraceAnalystSpanStatus\n status_message?: string\n service_name: string | null\n agent_name: string | null\n model_name: string | null\n tool_name: string | null\n /** Raw JSON-serialisable attribute map. May contain large strings;\n * callers must respect the per-attribute byte cap. */\n attributes: Record<string, unknown>\n}\n\nexport interface TraceAnalystTraceSummary {\n trace_id: string\n service_name: string | null\n agent_name: string | null\n span_count: number\n has_errors: boolean\n start_time: string\n end_time: string\n duration_ms: number\n raw_jsonl_bytes: number\n models: string[]\n tools: string[]\n}\n\nexport interface TraceAnalystFilters {\n /** Restrict to traces that contain at least one error span. */\n has_errors?: boolean\n /** Match if any span's `service.name` is in this list. */\n service_names?: string[]\n /** Match if any span's `agent.name` is in this list. */\n agent_names?: string[]\n /** Match if any LLM span's `llm.model_name` is in this list. */\n model_names?: string[]\n /** Match if any tool span's `tool.name` is in this list. */\n tool_names?: string[]\n /** ISO-8601 lower bound on the trace's earliest start time. */\n start_time_after?: string\n /** ISO-8601 upper bound on the trace's earliest start time. */\n start_time_before?: string\n /** Single regex applied to raw JSONL bytes for the trace. Opt-in;\n * expensive on large datasets. Use the indexed filters above first. */\n regex_pattern?: string\n}\n\n/** One distinct error signature across the dataset — the deterministic unit of\n * failure coverage. Signatures normalize volatile tokens (digits, hex/uuids,\n * paths, durations) out of the span `status_message` so semantically identical\n * failures collapse into one cluster. An analyst that accounts for every\n * cluster has, by construction, covered every distinct failure mode. */\nexport interface ErrorCluster {\n /** Normalized status_message — the cluster key. */\n signature: string\n /** A verbatim, un-normalized exemplar message (for exact-string citation). */\n status_message_sample: string\n /** The span name that most often carries this signature, if any. */\n span_name: string | null\n /** The tool that most often carries this signature, if any. */\n tool_name: string | null\n trace_count: number\n span_count: number\n /** trace_count / total error traces in the matched set (0..1). */\n prevalence: number\n /** Real trace ids carrying this signature (capped), passable to view/search. */\n exemplar_trace_ids: string[]\n /** Real span ids carrying this signature (capped). */\n exemplar_span_ids: string[]\n}\n\nexport interface DatasetOverview {\n total_traces: number\n raw_jsonl_bytes: number\n services: string[]\n agents: string[]\n models: string[]\n tool_names: string[]\n /** Up to 20 real trace ids the agent may pass to view/search tools. */\n sample_trace_ids: string[]\n errors: { trace_count: number; span_count: number }\n /** The COMPLETE deterministic error-signature population, sorted by\n * trace_count desc. This is the failure-coverage checklist: an analysis is\n * complete only when every cluster here is accounted for. Empty when the\n * matched set has no error spans. */\n error_clusters: ErrorCluster[]\n time_range: { earliest: string; latest: string } | null\n}\n\nexport interface QueryTracesPage {\n traces: TraceAnalystTraceSummary[]\n total: number\n has_more: boolean\n}\n\n/** Full-trace view. When the response would exceed the per-call byte\n * budget, `oversized` is populated INSTEAD of `spans` so the agent\n * knows to switch to `searchTrace` / `viewSpans`. */\nexport interface ViewTraceResult {\n trace_id: string\n spans?: TraceAnalystSpan[]\n oversized?: ViewTraceOversized\n}\n\nexport interface ViewTraceOversized {\n span_count: number\n /** Names with their counts, sorted desc. Capped at 20 entries. */\n top_span_names: Array<[string, number]>\n /** Largest single span body (bytes after attribute-cap projection). */\n span_response_bytes_max: number\n error_span_count: number\n}\n\nexport interface ViewSpansResult {\n trace_id: string\n spans: TraceAnalystSpan[]\n /** Number of requested span ids that were not found in the trace. */\n missing_span_ids: string[]\n /** Number of attribute fields truncated to fit the per-attribute cap. */\n truncated_attribute_count: number\n}\n\nexport interface SpanMatchRecord {\n trace_id: string\n span_id: string\n span_name: string\n span_kind: TraceAnalystSpanKind\n /** JSON pointer-style path to the matched value, e.g.\n * `attributes.\"llm.input_messages\"[2].content`. */\n attribute_path: string\n matched_text: string\n context_before: string\n context_after: string\n match_offset: number\n}\n\nexport interface SearchTraceResult {\n trace_id: string\n hits: SpanMatchRecord[]\n total_matches: number\n has_more: boolean\n}\n\nexport interface SearchSpanResult {\n trace_id: string\n span_id: string\n hits: SpanMatchRecord[]\n total_matches: number\n has_more: boolean\n}\n\n/** Tunable byte budgets for bounded RLM tool output. */\nexport interface TraceAnalystByteBudgets {\n /** Max bytes any single tool response may emit. Hard ceiling enforced\n * by the store; oversized → summary. Default 150_000. */\n perCallByteCeiling: number\n /** Per-attribute string truncation cap on `viewTrace` (discovery scan).\n * Default 4096. */\n perAttributeViewBudget: number\n /** Per-attribute string truncation cap on `viewSpans` (surgical reads).\n * Default 16384. */\n perAttributeSpanBudget: number\n /** Per-attribute cap on a single match record's `matched_text` and\n * context window. Default 1024. */\n perMatchTextBudget: number\n}\n\nexport const DEFAULT_TRACE_ANALYST_BUDGETS: TraceAnalystByteBudgets = {\n perCallByteCeiling: 150_000,\n perAttributeViewBudget: 4_096,\n perAttributeSpanBudget: 16_384,\n perMatchTextBudget: 1_024,\n}\n\n/** Marker substituted in place of truncated string payloads. Callers\n * parsing tool output can detect it deterministically. */\nexport const TRACE_ANALYST_TRUNCATION_MARKER_PREFIX = '[trace-analyst truncated:'\n","/**\n * `OtlpFileTraceStore` — read-only OTLP-JSONL trace store for the\n * trace-analyst.\n *\n * Wire shape. Each line of the input file is one OTLP-shaped span. The\n * store understands flattened OTLP JSONL plus the OpenInference vocab.\n * We project upstream's full\n * span shape down to `TraceAnalystSpan` lazily — full materialisation\n * only happens for the spans the agent actually requests.\n *\n * Indexing. On first read the store builds an in-memory index keyed\n * by `trace_id` carrying:\n * - byte offsets + lengths for each span line (for surgical reads\n * without re-parsing the whole file)\n * - a `TraceAnalystTraceSummary` rollup\n * - sets of services / agents / models / tools / has_errors\n * - byte size of the trace's JSONL slab\n *\n * Memory bound. The index keeps span metadata only — names, kinds,\n * offsets, status. Attribute payloads stay on disk until requested.\n * For a 50MB JSONL with 50k spans, the index is ~5MB.\n *\n * Concurrency. The store builds the index once on first read and\n * caches it. Subsequent reads reuse the index. The file is opened on\n * each read; we never hold a long-lived FD.\n */\n\nimport { readFile, stat } from 'node:fs/promises'\nimport { NotFoundError } from '../errors'\nimport {\n compareSpanTime,\n extractOtlpAttributes,\n projectOtlpFlatLine,\n spanEpochMillis,\n} from './otlp-span'\nimport { compileSearchRegex, type TraceAnalysisStore, truncateForBudget } from './store'\nimport {\n type DatasetOverview,\n DEFAULT_TRACE_ANALYST_BUDGETS,\n type ErrorCluster,\n type QueryTracesPage,\n type SearchSpanResult,\n type SearchTraceResult,\n type SpanMatchRecord,\n type TraceAnalystFilters,\n type TraceAnalystSpan,\n type TraceAnalystSpanKind,\n type TraceAnalystSpanStatus,\n type TraceAnalystTraceSummary,\n type ViewSpansResult,\n type ViewTraceOversized,\n type ViewTraceResult,\n} from './types'\n\n/** Lines indexed between event-loop yields. Bounded synchronous work per\n * tick keeps the index build from starving other tasks on large files\n * while staying coarse enough that the yields are cheap. */\nconst INDEX_YIELD_LINES = 5000\n\n/** Hand control back to the event loop without busy-waiting. */\nfunction yieldToEventLoop(): Promise<void> {\n return new Promise((resolve) => setImmediate(resolve))\n}\n\ninterface SpanIndexEntry {\n span_id: string\n parent_span_id: string | null\n name: string\n kind: TraceAnalystSpanKind\n start_time: string\n end_time: string\n duration_ms: number\n status: TraceAnalystSpanStatus\n status_message: string | undefined\n service_name: string | null\n agent_name: string | null\n model_name: string | null\n tool_name: string | null\n /** Byte offset in the raw JSONL file to the start of this span's line. */\n line_byte_offset: number\n /** Length of this line in bytes (excluding the trailing newline). */\n line_byte_length: number\n}\n\ninterface TraceIndexEntry {\n trace_id: string\n service_name: string | null\n agent_name: string | null\n span_count: number\n has_errors: boolean\n start_time: string\n end_time: string\n duration_ms: number\n raw_jsonl_bytes: number\n models: Set<string>\n tools: Set<string>\n spans: SpanIndexEntry[]\n /** Sorted by line offset for stable iteration. */\n}\n\ninterface DatasetIndex {\n byTrace: Map<string, TraceIndexEntry>\n totalRawBytes: number\n // Pre-computed sorted trace_ids for sample/query stability.\n sortedTraceIds: string[]\n}\n\nexport interface OtlpFileTraceStoreOptions {\n /** Path to the OTLP-JSONL file. */\n path: string\n /** Override the discovery (`viewTrace`) per-attribute byte cap. */\n perAttributeViewBudget?: number\n /** Override the surgical (`viewSpans`) per-attribute byte cap. */\n perAttributeSpanBudget?: number\n /** Override the per-call ceiling that triggers oversized summaries. */\n perCallByteCeiling?: number\n /** Override the per-match text budget. */\n perMatchTextBudget?: number\n /**\n * Hard ceiling on the trace file size in bytes. The store reads the\n * whole file into one Buffer and indexes it in memory, so an\n * unbounded file OOMs the process. Above this size the store fails\n * loud with `TraceFileTooLargeError` instead of degrading silently.\n * Default 256 MiB.\n */\n maxFileBytes?: number\n}\n\n/** Default ceiling for {@link OtlpFileTraceStoreOptions.maxFileBytes}. */\nexport const DEFAULT_MAX_TRACE_FILE_BYTES = 256 * 1024 * 1024\n\nexport class OtlpFileTraceStore implements TraceAnalysisStore {\n private readonly path: string\n private readonly perAttributeViewBudget: number\n private readonly perAttributeSpanBudget: number\n private readonly perCallByteCeiling: number\n private readonly perMatchTextBudget: number\n private readonly maxFileBytes: number\n private indexPromise?: Promise<DatasetIndex>\n /** Cached UTF-8 buffer of the file. We pin it once because every\n * read needs slice access and re-reading on each call balloons the\n * syscall count. */\n private bufferPromise?: Promise<Buffer>\n\n constructor(opts: OtlpFileTraceStoreOptions) {\n this.path = opts.path\n this.perAttributeViewBudget =\n opts.perAttributeViewBudget ?? DEFAULT_TRACE_ANALYST_BUDGETS.perAttributeViewBudget\n this.perAttributeSpanBudget =\n opts.perAttributeSpanBudget ?? DEFAULT_TRACE_ANALYST_BUDGETS.perAttributeSpanBudget\n this.perCallByteCeiling =\n opts.perCallByteCeiling ?? DEFAULT_TRACE_ANALYST_BUDGETS.perCallByteCeiling\n this.perMatchTextBudget =\n opts.perMatchTextBudget ?? DEFAULT_TRACE_ANALYST_BUDGETS.perMatchTextBudget\n this.maxFileBytes = opts.maxFileBytes ?? DEFAULT_MAX_TRACE_FILE_BYTES\n }\n\n // ─── Public API ────────────────────────────────────────────────────\n\n async getOverview(filters?: TraceAnalystFilters): Promise<DatasetOverview> {\n const idx = await this.index()\n const matched = await this.matchedTraces(idx, filters)\n\n const services = new Set<string>()\n const agents = new Set<string>()\n const models = new Set<string>()\n const tools = new Set<string>()\n let rawBytes = 0\n let earliest: string | null = null\n let latest: string | null = null\n let errorTraceCount = 0\n let errorSpanCount = 0\n const clusters = new Map<string, ErrorClusterAccumulator>()\n\n for (const t of matched) {\n if (t.service_name) services.add(t.service_name)\n if (t.agent_name) agents.add(t.agent_name)\n for (const m of t.models) models.add(m)\n for (const tn of t.tools) tools.add(tn)\n rawBytes += t.raw_jsonl_bytes\n if (!earliest || compareSpanTime(t.start_time, earliest) < 0) earliest = t.start_time\n if (!latest || compareSpanTime(t.end_time, latest) > 0) latest = t.end_time\n if (t.has_errors) {\n errorTraceCount += 1\n for (const s of t.spans) {\n if (s.status !== 'ERROR') continue\n errorSpanCount += 1\n accumulateErrorCluster(clusters, t.trace_id, s)\n }\n }\n }\n\n const sample_trace_ids = matched.slice(0, 20).map((t) => t.trace_id)\n return {\n total_traces: matched.length,\n raw_jsonl_bytes: rawBytes,\n services: [...services].sort(),\n agents: [...agents].sort(),\n models: [...models].sort(),\n tool_names: [...tools].sort(),\n sample_trace_ids,\n errors: { trace_count: errorTraceCount, span_count: errorSpanCount },\n error_clusters: finalizeErrorClusters(clusters, errorTraceCount),\n time_range: earliest && latest ? { earliest, latest } : null,\n }\n }\n\n async queryTraces(opts: {\n filters?: TraceAnalystFilters\n limit: number\n offset?: number\n }): Promise<QueryTracesPage> {\n if (!Number.isInteger(opts.limit) || opts.limit < 1 || opts.limit > 200) {\n throw new RangeError(`queryTraces.limit must be 1..200, got ${opts.limit}`)\n }\n const offset = opts.offset ?? 0\n if (!Number.isInteger(offset) || offset < 0) {\n throw new RangeError(`queryTraces.offset must be >=0, got ${offset}`)\n }\n\n const idx = await this.index()\n const matched = await this.matchedTraces(idx, opts.filters)\n const slice = matched.slice(offset, offset + opts.limit)\n return {\n traces: slice.map((t) => this.toSummary(t)),\n total: matched.length,\n has_more: offset + slice.length < matched.length,\n }\n }\n\n async countTraces(filters?: TraceAnalystFilters): Promise<number> {\n const idx = await this.index()\n const matched = await this.matchedTraces(idx, filters)\n return matched.length\n }\n\n async viewTrace(opts: {\n trace_id: string\n per_attribute_byte_cap?: number\n }): Promise<ViewTraceResult> {\n const idx = await this.index()\n const trace = idx.byTrace.get(opts.trace_id)\n if (!trace) {\n throw new TraceNotFoundError(opts.trace_id)\n }\n const cap = opts.per_attribute_byte_cap ?? this.perAttributeViewBudget\n\n // Probe size first — if the materialised payload would exceed\n // the per-call ceiling we return an oversized summary rather than\n // blowing the agent's context.\n const buf = await this.buffer()\n const spans: TraceAnalystSpan[] = []\n let runningBytes = 0\n let span_response_bytes_max = 0\n const counter: TruncationCounter = { value: 0 }\n for (const s of trace.spans) {\n const projected = this.projectSpan(buf, trace.trace_id, s, cap, counter)\n const bytes = Buffer.byteLength(JSON.stringify(projected), 'utf8')\n span_response_bytes_max = Math.max(span_response_bytes_max, bytes)\n runningBytes += bytes\n if (runningBytes > this.perCallByteCeiling) {\n return {\n trace_id: trace.trace_id,\n oversized: this.buildOversizedSummary(trace, span_response_bytes_max),\n }\n }\n spans.push(projected)\n }\n return { trace_id: trace.trace_id, spans }\n }\n\n async viewSpans(opts: {\n trace_id: string\n span_ids: readonly string[]\n per_attribute_byte_cap?: number\n }): Promise<ViewSpansResult> {\n const idx = await this.index()\n const trace = idx.byTrace.get(opts.trace_id)\n if (!trace) throw new TraceNotFoundError(opts.trace_id)\n if (opts.span_ids.length === 0) {\n return {\n trace_id: trace.trace_id,\n spans: [],\n missing_span_ids: [],\n truncated_attribute_count: 0,\n }\n }\n if (opts.span_ids.length > 100) {\n throw new RangeError(`viewSpans.span_ids cap is 100, got ${opts.span_ids.length}`)\n }\n const cap = opts.per_attribute_byte_cap ?? this.perAttributeSpanBudget\n\n const wantSet = new Set(opts.span_ids)\n const found = trace.spans.filter((s) => wantSet.has(s.span_id))\n const missing = opts.span_ids.filter((id) => !found.some((f) => f.span_id === id))\n\n const buf = await this.buffer()\n const spans: TraceAnalystSpan[] = []\n const counter: TruncationCounter = { value: 0 }\n let runningBytes = 0\n for (const s of found) {\n const projected = this.projectSpan(buf, trace.trace_id, s, cap, counter)\n const bytes = Buffer.byteLength(JSON.stringify(projected), 'utf8')\n runningBytes += bytes\n if (runningBytes > this.perCallByteCeiling) {\n // Stop adding further spans rather than truncate mid-list.\n // Callers can refetch the rest with a smaller `span_ids`.\n break\n }\n spans.push(projected)\n }\n return {\n trace_id: trace.trace_id,\n spans,\n missing_span_ids: missing,\n truncated_attribute_count: counter.value,\n }\n }\n\n async searchTrace(opts: {\n trace_id: string\n regex_pattern: string\n max_matches?: number\n }): Promise<SearchTraceResult> {\n const max_matches = opts.max_matches ?? 50\n if (!Number.isInteger(max_matches) || max_matches < 1 || max_matches > 500) {\n throw new RangeError(`searchTrace.max_matches must be 1..500, got ${max_matches}`)\n }\n const idx = await this.index()\n const trace = idx.byTrace.get(opts.trace_id)\n if (!trace) throw new TraceNotFoundError(opts.trace_id)\n const re = compileSearchRegex(opts.regex_pattern)\n\n const buf = await this.buffer()\n const hits: SpanMatchRecord[] = []\n let total = 0\n let capped = false\n for (const s of trace.spans) {\n const remaining = max_matches - hits.length\n const localHits = await this.scanSpanForMatches(\n buf,\n trace.trace_id,\n s,\n re,\n this.perMatchTextBudget,\n remaining,\n )\n total += localHits.total\n for (const h of localHits.records) {\n if (hits.length >= max_matches) break\n hits.push(h)\n }\n if (hits.length >= max_matches) {\n // Capped: we stopped scanning, so `total` is a lower bound on the\n // real match count — never report it as exact. has_more signals\n // \"more exist\"; total_matches mirrors hits so callers don't read a\n // fabricated number.\n capped = true\n break\n }\n }\n return {\n trace_id: trace.trace_id,\n hits,\n // Uncapped: every span scanned to completion, so `total` is exact.\n total_matches: capped ? hits.length : total,\n has_more: capped || total > hits.length,\n }\n }\n\n async searchSpan(opts: {\n trace_id: string\n span_id: string\n regex_pattern: string\n max_matches?: number\n }): Promise<SearchSpanResult> {\n const max_matches = opts.max_matches ?? 50\n if (!Number.isInteger(max_matches) || max_matches < 1 || max_matches > 500) {\n throw new RangeError(`searchSpan.max_matches must be 1..500, got ${max_matches}`)\n }\n const idx = await this.index()\n const trace = idx.byTrace.get(opts.trace_id)\n if (!trace) throw new TraceNotFoundError(opts.trace_id)\n const span = trace.spans.find((s) => s.span_id === opts.span_id)\n if (!span) {\n throw new SpanNotFoundError(opts.trace_id, opts.span_id)\n }\n const re = compileSearchRegex(opts.regex_pattern)\n const buf = await this.buffer()\n const localHits = await this.scanSpanForMatches(\n buf,\n trace.trace_id,\n span,\n re,\n this.perMatchTextBudget,\n max_matches,\n )\n return {\n trace_id: trace.trace_id,\n span_id: span.span_id,\n hits: localHits.records,\n total_matches: localHits.total,\n has_more: localHits.total > localHits.records.length,\n }\n }\n\n // ─── Index building ────────────────────────────────────────────────\n\n /** Force the index to materialise. Useful to amortise startup cost\n * before the first agent call. */\n async ensureIndexed(): Promise<void> {\n await this.index()\n }\n\n private async buffer(): Promise<Buffer> {\n if (!this.bufferPromise) {\n this.bufferPromise = this.readGuarded()\n }\n return this.bufferPromise\n }\n\n /** Stat-then-read so an oversized file fails loud BEFORE we allocate a\n * multi-hundred-MB Buffer and OOM the process. A missing file surfaces\n * as TraceFileMissingError; any other stat/read error propagates. */\n private async readGuarded(): Promise<Buffer> {\n let stats: Awaited<ReturnType<typeof stat>>\n try {\n stats = await stat(this.path)\n } catch (err) {\n if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') {\n throw new TraceFileMissingError(this.path)\n }\n throw err\n }\n if (stats.size > this.maxFileBytes) {\n throw new TraceFileTooLargeError(this.path, stats.size, this.maxFileBytes)\n }\n return readFile(this.path)\n }\n\n private async index(): Promise<DatasetIndex> {\n if (!this.indexPromise) {\n this.indexPromise = this.buildIndex()\n }\n return this.indexPromise\n }\n\n private async buildIndex(): Promise<DatasetIndex> {\n // readGuarded surfaces missing/oversized files as typed errors.\n const buf = await this.buffer()\n\n const byTrace = new Map<string, TraceIndexEntry>()\n let cursor = 0\n let sinceYield = 0\n while (cursor < buf.length) {\n // Yield to the event loop every INDEX_YIELD_LINES lines so a huge\n // file doesn't monopolise the thread for the whole index build.\n if (++sinceYield >= INDEX_YIELD_LINES) {\n sinceYield = 0\n await yieldToEventLoop()\n }\n const newlineIndex = buf.indexOf(0x0a, cursor) // \\n\n const lineEnd = newlineIndex === -1 ? buf.length : newlineIndex\n const lineLength = lineEnd - cursor\n if (lineLength === 0) {\n cursor = lineEnd + 1\n continue\n }\n const lineSlice = buf.subarray(cursor, lineEnd).toString('utf8')\n const lineOffset = cursor\n cursor = lineEnd + 1\n\n let parsed: unknown\n try {\n parsed = JSON.parse(lineSlice)\n } catch {\n // Skip malformed lines silently. The agent shouldn't see them\n // but we don't want one bad line to nuke an entire dataset.\n continue\n }\n if (!parsed || typeof parsed !== 'object') continue\n const span = projectOtlpFlatLine(parsed as Record<string, unknown>)\n if (!span) continue\n\n let entry = byTrace.get(span.trace_id)\n if (!entry) {\n entry = {\n trace_id: span.trace_id,\n service_name: span.service_name,\n agent_name: span.agent_name,\n span_count: 0,\n has_errors: false,\n start_time: span.start_time,\n end_time: span.end_time,\n duration_ms: 0,\n raw_jsonl_bytes: 0,\n models: new Set(),\n tools: new Set(),\n spans: [],\n }\n byTrace.set(span.trace_id, entry)\n } else {\n // Pin the trace's service/agent to the first AGENT span we\n // Prefer the first agent/service fields that appear in the trace.\n if (!entry.service_name && span.service_name) entry.service_name = span.service_name\n if (!entry.agent_name && span.agent_name) entry.agent_name = span.agent_name\n }\n\n const indexEntry: SpanIndexEntry = {\n span_id: span.span_id,\n parent_span_id: span.parent_span_id,\n name: span.name,\n kind: span.kind,\n start_time: span.start_time,\n end_time: span.end_time,\n duration_ms: span.duration_ms,\n status: span.status,\n status_message: span.status_message,\n service_name: span.service_name,\n agent_name: span.agent_name,\n model_name: span.model_name,\n tool_name: span.tool_name,\n line_byte_offset: lineOffset,\n line_byte_length: lineLength,\n }\n entry.spans.push(indexEntry)\n entry.span_count += 1\n entry.raw_jsonl_bytes += lineLength + 1 // +1 newline byte\n if (span.status === 'ERROR') entry.has_errors = true\n if (compareSpanTime(span.start_time, entry.start_time) < 0) entry.start_time = span.start_time\n if (compareSpanTime(span.end_time, entry.end_time) > 0) entry.end_time = span.end_time\n if (span.model_name) entry.models.add(span.model_name)\n if (span.tool_name) entry.tools.add(span.tool_name)\n }\n\n // Compute trace duration once, sort spans by start time for\n // stable iteration.\n let totalRawBytes = 0\n for (const t of byTrace.values()) {\n totalRawBytes += t.raw_jsonl_bytes\n t.spans.sort(\n (a, b) =>\n compareSpanTime(a.start_time, b.start_time) || a.line_byte_offset - b.line_byte_offset,\n )\n // Duration is 0 unless BOTH bounds parse — a missing/garbage timestamp\n // yields 0, never a NaN (→ null in JSON) or a bogus epoch-from-zero span.\n const startMs = spanEpochMillis(t.start_time)\n const endMs = spanEpochMillis(t.end_time)\n t.duration_ms = startMs === null || endMs === null ? 0 : Math.max(0, endMs - startMs)\n }\n const sortedTraceIds = [...byTrace.keys()].sort()\n\n return { byTrace, totalRawBytes, sortedTraceIds }\n }\n\n // ─── Filter pipeline ───────────────────────────────────────────────\n\n private async matchedTraces(\n idx: DatasetIndex,\n filters: TraceAnalystFilters | undefined,\n ): Promise<TraceIndexEntry[]> {\n const traces = idx.sortedTraceIds.map((id) => idx.byTrace.get(id)).filter(isPresent)\n if (!filters) return traces\n\n const indexedFiltered = traces.filter((t) => {\n if (filters.has_errors !== undefined && t.has_errors !== filters.has_errors) return false\n if (filters.service_names && filters.service_names.length > 0) {\n if (!t.service_name || !filters.service_names.includes(t.service_name)) return false\n }\n if (filters.agent_names && filters.agent_names.length > 0) {\n if (!t.agent_name || !filters.agent_names.includes(t.agent_name)) return false\n }\n if (filters.model_names && filters.model_names.length > 0) {\n if (![...t.models].some((m) => filters.model_names!.includes(m))) return false\n }\n if (filters.tool_names && filters.tool_names.length > 0) {\n if (![...t.tools].some((tn) => filters.tool_names!.includes(tn))) return false\n }\n if (filters.start_time_after && t.start_time < filters.start_time_after) return false\n if (filters.start_time_before && t.start_time > filters.start_time_before) return false\n return true\n })\n\n if (!filters.regex_pattern) return indexedFiltered\n\n // Opt-in raw-bytes scan — only over the already-narrowed set.\n const re = compileSearchRegex(filters.regex_pattern)\n const buf = await this.buffer()\n const out: TraceIndexEntry[] = []\n for (const t of indexedFiltered) {\n let matched = false\n for (const s of t.spans) {\n const slice = buf.subarray(s.line_byte_offset, s.line_byte_offset + s.line_byte_length)\n // Buffer.toString allocates; tolerate it because regex_pattern\n // is opt-in. Future optimisation: byte-level fast-path for\n // ASCII-only patterns.\n if (re.test(slice.toString('utf8'))) {\n matched = true\n break\n }\n }\n if (matched) out.push(t)\n }\n return out\n }\n\n private toSummary(t: TraceIndexEntry): TraceAnalystTraceSummary {\n return {\n trace_id: t.trace_id,\n service_name: t.service_name,\n agent_name: t.agent_name,\n span_count: t.span_count,\n has_errors: t.has_errors,\n start_time: t.start_time,\n end_time: t.end_time,\n duration_ms: t.duration_ms,\n raw_jsonl_bytes: t.raw_jsonl_bytes,\n models: [...t.models].sort(),\n tools: [...t.tools].sort(),\n }\n }\n\n // ─── Span projection (lazy attribute reads) ────────────────────────\n\n private projectSpan(\n buf: Buffer,\n trace_id: string,\n s: SpanIndexEntry,\n perAttrCap: number,\n counter: TruncationCounter,\n ): TraceAnalystSpan {\n const slice = buf\n .subarray(s.line_byte_offset, s.line_byte_offset + s.line_byte_length)\n .toString('utf8')\n let raw: Record<string, unknown> = {}\n try {\n const parsed = JSON.parse(slice)\n if (parsed && typeof parsed === 'object') raw = parsed as Record<string, unknown>\n } catch {\n // Should not happen — index pre-validated.\n }\n const attrs = extractOtlpAttributes(raw)\n const projected: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(attrs)) {\n if (typeof v === 'string') {\n const trunc = truncateForBudget(v, perAttrCap)\n if (trunc !== v) counter.value += 1\n projected[k] = trunc\n } else if (Array.isArray(v) || (v && typeof v === 'object')) {\n const json = JSON.stringify(v)\n const trunc = truncateForBudget(json, perAttrCap)\n if (trunc !== json) {\n counter.value += 1\n projected[k] = trunc\n } else {\n projected[k] = v\n }\n } else {\n projected[k] = v\n }\n }\n return {\n trace_id,\n span_id: s.span_id,\n parent_span_id: s.parent_span_id,\n name: s.name,\n kind: s.kind,\n start_time: s.start_time,\n end_time: s.end_time,\n duration_ms: s.duration_ms,\n status: s.status,\n status_message: s.status_message,\n service_name: s.service_name,\n agent_name: s.agent_name,\n model_name: s.model_name,\n tool_name: s.tool_name,\n attributes: projected,\n }\n }\n\n private buildOversizedSummary(\n t: TraceIndexEntry,\n span_response_bytes_max: number,\n ): ViewTraceOversized {\n const counts = new Map<string, number>()\n let errorCount = 0\n for (const s of t.spans) {\n counts.set(s.name, (counts.get(s.name) ?? 0) + 1)\n if (s.status === 'ERROR') errorCount += 1\n }\n const top = [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20)\n return {\n span_count: t.span_count,\n top_span_names: top,\n span_response_bytes_max,\n error_span_count: errorCount,\n }\n }\n\n private async scanSpanForMatches(\n buf: Buffer,\n trace_id: string,\n s: SpanIndexEntry,\n re: RegExp,\n textBudget: number,\n recordCap: number,\n ): Promise<{ records: SpanMatchRecord[]; total: number; hasMore: boolean }> {\n // We scan against the original raw JSONL slice for each span and\n // record byte positions; the matched_text + context window is\n // truncated to `textBudget` bytes per record so total tool output\n // stays bounded even if hits cluster.\n const slice = buf\n .subarray(s.line_byte_offset, s.line_byte_offset + s.line_byte_length)\n .toString('utf8')\n const records: SpanMatchRecord[] = []\n const globalRe = new RegExp(re.source, re.flags.includes('g') ? re.flags : `${re.flags}g`)\n let total = 0\n let hasMore = false\n let m: RegExpExecArray | null = globalRe.exec(slice)\n while (m !== null) {\n total += 1\n if (m.index === globalRe.lastIndex) globalRe.lastIndex += 1 // zero-width guard\n if (records.length >= recordCap) {\n hasMore = true\n break\n }\n const before = slice.slice(Math.max(0, m.index - textBudget / 2), m.index)\n const after = slice.slice(\n m.index + m[0].length,\n m.index + m[0].length + Math.floor(textBudget / 2),\n )\n records.push({\n trace_id,\n span_id: s.span_id,\n span_name: s.name,\n span_kind: s.kind,\n attribute_path: bestAttributePathForOffset(slice, m.index) ?? 'span.raw',\n matched_text: truncateForBudget(m[0], textBudget),\n context_before: truncateForBudget(before, textBudget),\n context_after: truncateForBudget(after, textBudget),\n match_offset: m.index,\n })\n m = globalRe.exec(slice)\n }\n return { records, total, hasMore }\n }\n}\n\n// ─── Errors ──────────────────────────────────────────────────────────\n\nexport class TraceFileMissingError extends NotFoundError {\n constructor(path: string) {\n super(`trace file not found: ${path}`)\n }\n}\nexport class TraceFileTooLargeError extends Error {\n readonly path: string\n readonly size_bytes: number\n readonly max_bytes: number\n constructor(path: string, size_bytes: number, max_bytes: number) {\n super(\n `trace file ${path} is ${size_bytes} bytes, over the ${max_bytes}-byte limit; ` +\n 'raise OtlpFileTraceStoreOptions.maxFileBytes or pre-split the file',\n )\n this.path = path\n this.size_bytes = size_bytes\n this.max_bytes = max_bytes\n }\n}\nexport class TraceNotFoundError extends NotFoundError {\n readonly trace_id: string\n constructor(trace_id: string) {\n super(`trace not found: ${trace_id}`)\n this.trace_id = trace_id\n }\n}\nexport class SpanNotFoundError extends NotFoundError {\n readonly trace_id: string\n readonly span_id: string\n constructor(trace_id: string, span_id: string) {\n super(`span ${span_id} not found in trace ${trace_id}`)\n this.trace_id = trace_id\n this.span_id = span_id\n }\n}\n\n// ─── OTLP shape readers ──────────────────────────────────────────────\n//\n// The per-line projection lives in `./otlp-span` so the index here and\n// `otlpToRunRecords` read the same vocabulary off the same parser.\n\nfunction isPresent<T>(v: T | undefined): v is T {\n return v !== undefined\n}\n\n// Per-call truncation counter. Each public read that projects spans\n// owns one of these and threads it through projectSpan; a store-keyed\n// counter would let two concurrent reads on the same store report each\n// other's truncation counts.\ninterface TruncationCounter {\n value: number\n}\n\n/** A `\"` at `idx` is a real JSON delimiter only when the run of `\\`\n * immediately preceding it is even-length; an odd run means the quote\n * is escaped (`\\\"`) and is part of a string value, not a boundary. */\nfunction isUnescapedQuote(slice: string, idx: number): boolean {\n if (slice[idx] !== '\"') return false\n let backslashes = 0\n let b = idx - 1\n while (b >= 0 && slice[b] === '\\\\') {\n backslashes += 1\n b -= 1\n }\n return backslashes % 2 === 0\n}\n\n/** Scan backwards from `from` (inclusive) for the nearest UNescaped `\"`.\n * Returns its index, or -1 when none is found. */\nfunction prevUnescapedQuote(slice: string, from: number): number {\n for (let i = from; i >= 0; i -= 1) {\n if (slice[i] === '\"' && isUnescapedQuote(slice, i)) return i\n }\n return -1\n}\n\n/**\n * Best-effort: locate the JSON path for the substring at `offset` in\n * a single span's JSONL slice. The slice is '...,\"key\":\"value...\"' — we\n * walk back from `offset` to the value-opening quote, past the `:`, to\n * the key's closing then opening quote, skipping `\\\"`-escaped quotes that\n * live inside string values. Returns `null` when the offset doesn't fall\n * inside a recognisable string field.\n */\nfunction bestAttributePathForOffset(slice: string, offset: number): string | null {\n // Value-opening quote: nearest unescaped '\"' at or before the offset.\n const valueQuote = prevUnescapedQuote(slice, Math.min(offset, slice.length - 1))\n if (valueQuote < 1) return null\n // The ':' separating key and value sits before the value quote.\n let j = valueQuote - 1\n while (j >= 0 && slice[j] !== ':') j -= 1\n if (j < 1) return null\n // Key closing quote, then key opening quote — both unescaped.\n const keyClose = prevUnescapedQuote(slice, j - 1)\n if (keyClose < 1) return null\n const keyOpen = prevUnescapedQuote(slice, keyClose - 1)\n if (keyOpen < 0) return null\n return slice.slice(keyOpen + 1, keyClose)\n}\n\n// ─── Error-cluster extraction ────────────────────────────────────────\n//\n// Deterministic failure-coverage population. The error-span loop in\n// getOverview already visits every ERROR span; bucketing them by a\n// normalized status_message signature turns \"N error spans\" into \"K\n// distinct failure modes\" — the checklist an analyst must close. No LLM.\n\nconst ERROR_CLUSTER_MAX = 50\nconst ERROR_CLUSTER_EXEMPLARS = 5\nconst SIGNATURE_MAX_CHARS = 160\n\ninterface ErrorClusterAccumulator {\n signature: string\n sample: string\n traceIds: Set<string>\n spanIds: string[]\n spanCount: number\n spanNames: Map<string, number>\n toolNames: Map<string, number>\n}\n\n/** Collapse volatile tokens so semantically identical failures share a key:\n * hex/uuid ids → <id>, numbers → #, quoted/abs paths → <path>, durations →\n * <dur>, whitespace collapsed. Empty/absent messages fall back to the span\n * name so a no-message error still forms a real cluster. */\nfunction normalizeErrorSignature(message: string | undefined, spanName: string): string {\n const raw = (message ?? '').trim()\n const base = raw.length > 0 ? raw : `(${spanName || 'error'} — no message)`\n const norm = base\n .replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '<id>')\n .replace(/\\b[0-9a-f]{12,}\\b/gi, '<id>')\n .replace(/(?:\\/[\\w.\\-@]+){2,}/g, '<path>')\n .replace(/\\b\\d+(?:\\.\\d+)?(ms|s|m|h|kb|mb|gb)?\\b/gi, (_m, u) => (u ? `#${u}` : '#'))\n .replace(/\\s+/g, ' ')\n .trim()\n return norm.length > SIGNATURE_MAX_CHARS ? `${norm.slice(0, SIGNATURE_MAX_CHARS)}…` : norm\n}\n\nfunction bump(map: Map<string, number>, key: string | null): void {\n if (!key) return\n map.set(key, (map.get(key) ?? 0) + 1)\n}\n\nfunction topKey(map: Map<string, number>): string | null {\n let best: string | null = null\n let bestN = 0\n for (const [k, n] of map)\n if (n > bestN) {\n best = k\n bestN = n\n }\n return best\n}\n\nfunction accumulateErrorCluster(\n clusters: Map<string, ErrorClusterAccumulator>,\n traceId: string,\n span: SpanIndexEntry,\n): void {\n const signature = normalizeErrorSignature(span.status_message, span.name)\n let acc = clusters.get(signature)\n if (!acc) {\n acc = {\n signature,\n sample: (span.status_message ?? span.name ?? '').slice(0, 500),\n traceIds: new Set(),\n spanIds: [],\n spanCount: 0,\n spanNames: new Map(),\n toolNames: new Map(),\n }\n clusters.set(signature, acc)\n }\n acc.traceIds.add(traceId)\n acc.spanCount += 1\n if (acc.spanIds.length < ERROR_CLUSTER_EXEMPLARS && !acc.spanIds.includes(span.span_id)) {\n acc.spanIds.push(span.span_id)\n }\n bump(acc.spanNames, span.name)\n bump(acc.toolNames, span.tool_name)\n}\n\nfunction finalizeErrorClusters(\n clusters: Map<string, ErrorClusterAccumulator>,\n errorTraceCount: number,\n): ErrorCluster[] {\n const out = [...clusters.values()].map(\n (acc): ErrorCluster => ({\n signature: acc.signature,\n status_message_sample: acc.sample,\n span_name: topKey(acc.spanNames),\n tool_name: topKey(acc.toolNames),\n trace_count: acc.traceIds.size,\n span_count: acc.spanCount,\n prevalence: errorTraceCount > 0 ? acc.traceIds.size / errorTraceCount : 0,\n exemplar_trace_ids: [...acc.traceIds].slice(0, ERROR_CLUSTER_EXEMPLARS),\n exemplar_span_ids: acc.spanIds.slice(0, ERROR_CLUSTER_EXEMPLARS),\n }),\n )\n out.sort((a, b) => b.trace_count - a.trace_count || b.span_count - a.span_count)\n return out.slice(0, ERROR_CLUSTER_MAX)\n}\n","/**\n * `TraceAnalysisStore` — read-side interface the trace-analyst calls\n * through. Six operations, all bounded:\n *\n * - `getOverview(filters?)` — dataset rollup + sample trace ids.\n * - `queryTraces(filters?, limit, offset)` — paginated summaries.\n * - `countTraces(filters?)` — cheap count without materialisation.\n * - `viewTrace(trace_id, perAttrCap)` — full span list, oversized → summary.\n * - `viewSpans(trace_id, span_ids, perAttrCap)` — surgical span fetch.\n * - `searchTrace(trace_id, regex, max_matches)` — bounded regex hits.\n * - `searchSpan(trace_id, span_id, regex, max_matches)` — single-span search.\n *\n * Multiple implementations ship in the core (`OtlpFileTraceStore`).\n * Downstream callers can supply their own — e.g. a DuckDB-backed\n * adapter or an in-memory adapter for tests — by implementing this\n * interface.\n *\n * Filters compose with AND semantics. Empty/undefined fields impose\n * no constraint. `regex_pattern` is the only opt-in raw-bytes scan —\n * implementations may skip it via `count`/`overview` when not set.\n */\n\nimport type {\n DatasetOverview,\n QueryTracesPage,\n SearchSpanResult,\n SearchTraceResult,\n TraceAnalystFilters,\n ViewSpansResult,\n ViewTraceResult,\n} from './types'\n\nexport interface TraceAnalysisStore {\n getOverview(filters?: TraceAnalystFilters): Promise<DatasetOverview>\n\n queryTraces(opts: {\n filters?: TraceAnalystFilters\n limit: number\n offset?: number\n }): Promise<QueryTracesPage>\n\n countTraces(filters?: TraceAnalystFilters): Promise<number>\n\n viewTrace(opts: {\n trace_id: string\n /** Override per-attribute byte cap. Defaults to discovery budget. */\n per_attribute_byte_cap?: number\n }): Promise<ViewTraceResult>\n\n viewSpans(opts: {\n trace_id: string\n span_ids: readonly string[]\n /** Override per-attribute byte cap. Defaults to surgical budget. */\n per_attribute_byte_cap?: number\n }): Promise<ViewSpansResult>\n\n searchTrace(opts: {\n trace_id: string\n regex_pattern: string\n /** Hard cap on matches returned. Default 50. */\n max_matches?: number\n }): Promise<SearchTraceResult>\n\n searchSpan(opts: {\n trace_id: string\n span_id: string\n regex_pattern: string\n max_matches?: number\n }): Promise<SearchSpanResult>\n}\n\n/** Compile a regex with the same anchoring + flags semantics across\n * implementations. Throws on invalid pattern — callers should surface\n * that to the agent so it can refine instead of looping. */\nexport function compileSearchRegex(pattern: string): RegExp {\n let source = pattern\n let flags = 'm'\n if (source.startsWith('(?i)')) {\n source = source.slice(4)\n flags += 'i'\n }\n return new RegExp(source, flags)\n}\n\n/** Truncate string payload deterministically for tool responses.\n * Marker is parseable so downstream consumers can detect truncation\n * and decide whether to fetch surgically. */\nexport function truncateForBudget(value: string, byteCap: number): string {\n // We measure UTF-8 byte length conservatively via Buffer.byteLength;\n // for predictability the truncation point is in CHARS, never inside\n // a code point.\n const original = Buffer.byteLength(value, 'utf8')\n if (original <= byteCap) return value\n\n // Step back from the cap until we're at a valid char boundary.\n // Slice by char count proportional to byte ratio, then re-measure.\n const ratio = byteCap / original\n let cut = Math.max(0, Math.floor(value.length * ratio))\n while (cut > 0 && Buffer.byteLength(value.slice(0, cut), 'utf8') > byteCap) {\n cut -= 1\n }\n return `${value.slice(0, cut)}\\n[trace-analyst truncated: original ${original} bytes]`\n}\n","/**\n * Trace-analyst tool surface — six namespaced AxFunctions the analyst\n * agent calls from generated JS code via `traces.<name>(...)`.\n *\n * Discovery → narrow → deep-read protocol. Tool names + ordering\n * support RLM discovery:\n *\n * 1. `getDatasetOverview` (cheap) — first call, sizes the dataset\n * 2. `queryTraces` — paginated summaries with `raw_jsonl_bytes`\n * 3. `countTraces` — cheap pre-flight before regex\n * 4. `viewTrace` — full span list, oversized → summary\n * 5. `viewSpans` — surgical 16KB-cap reads\n * 6. `searchTrace` / `searchSpan` — bounded regex hits\n *\n * Failure mode. Tool handlers throw on bad input (invalid trace ids,\n * out-of-range pagination, malformed regex). Ax converts thrown errors\n * into actor-visible `[ERROR]` strings so the analyst can adjust on\n * the next turn instead of looping.\n */\n\nimport type { AxFunction } from '@ax-llm/ax'\nimport { f, fn } from '@ax-llm/ax'\n\nimport type { TraceAnalysisStore } from './store'\nimport type { TraceAnalystFilters } from './types'\n\nconst NAMESPACE = 'traces'\n\ninterface BuildTraceAnalystToolsOpts {\n store: TraceAnalysisStore\n /** Override the default sample-trace-id slot count (20). Mostly for tests. */\n sampleTraceLimit?: number\n}\n\nconst filtersField = f\n .json('Filter set. ALL fields are AND-composed. Leave empty to scan everything.')\n .optional()\n\n/**\n * Build the trace-analyst function set. Pass the result into\n * `agent(...).functions.local`.\n */\nexport function buildTraceAnalystTools(opts: BuildTraceAnalystToolsOpts): AxFunction[] {\n const { store } = opts\n\n const getDatasetOverview = fn('getDatasetOverview')\n .description(\n 'Dataset rollup: total traces, raw_jsonl_bytes, services, agents, ' +\n 'models, tools, and sample_trace_ids (real ids passable to ' +\n 'view/search). Always call this FIRST without a regex_pattern.',\n )\n .namespace(NAMESPACE)\n .arg('filters', filtersField)\n .returns(f.json('DatasetOverview'))\n .handler(async ({ filters }) => store.getOverview(parseFilters(filters)))\n .build()\n\n const queryTraces = fn('queryTraces')\n .description(\n 'Paginated trace summaries. Each summary carries raw_jsonl_bytes — ' +\n 'use it to size traces BEFORE calling viewTrace. Narrow with indexed ' +\n 'filters before adding regex_pattern.',\n )\n .namespace(NAMESPACE)\n .arg('filters', filtersField)\n .arg('limit', f.number('Page size, 1..200'))\n .arg('offset', f.number('Page offset; default 0').optional())\n .returns(f.json('QueryTracesPage'))\n .handler(async ({ filters, limit, offset }) =>\n store.queryTraces({\n filters: parseFilters(filters),\n limit: assertPageLimit(limit),\n offset: assertOffset(offset),\n }),\n )\n .build()\n\n const countTraces = fn('countTraces')\n .description(\n 'Count traces matching `filters`. Use as a cheap pre-flight ' +\n 'before opting into a regex_pattern scan.',\n )\n .namespace(NAMESPACE)\n .arg('filters', filtersField)\n .returns(f.number('count'))\n .handler(async ({ filters }) => store.countTraces(parseFilters(filters)))\n .build()\n\n const viewTrace = fn('viewTrace')\n .description(\n 'Return ALL spans for a single trace, with each attribute capped at ' +\n '~4KB. If the response would exceed the per-call ceiling the result ' +\n 'carries `oversized` instead of `spans` — DO NOT retry with the same ' +\n 'trace_id; switch to searchTrace / viewSpans.',\n )\n .namespace(NAMESPACE)\n .arg('trace_id', f.string('Real trace id from a prior overview/query'))\n .returns(f.json('ViewTraceResult'))\n .handler(async ({ trace_id }) =>\n store.viewTrace({ trace_id: assertString(trace_id, 'trace_id') }),\n )\n .build()\n\n const viewSpans = fn('viewSpans')\n .description(\n 'Surgical read of specific spans within a trace, with each ' +\n 'attribute capped at ~16KB (4× the discovery cap). Use after ' +\n 'searchTrace narrows to specific span_ids.',\n )\n .namespace(NAMESPACE)\n .arg('trace_id', f.string('Real trace id'))\n .arg('span_ids', f.string('Span ids to fetch').array())\n .returns(f.json('ViewSpansResult'))\n .handler(async ({ trace_id, span_ids }) =>\n store.viewSpans({\n trace_id: assertString(trace_id, 'trace_id'),\n span_ids: assertStringArray(span_ids, 'span_ids'),\n }),\n )\n .build()\n\n const searchTrace = fn('searchTrace')\n .description(\n 'Regex search across all spans of one trace. Returns ' +\n '`{trace_id, hits: SpanMatchRecord[], total_matches, has_more}`. ' +\n '**Iterate `result.hits`, NOT `result.matches`** — the field is ' +\n '`hits`. Each hit has `{span_id, span_name, span_kind, ' +\n 'attribute_path, matched_text, context_before, context_after, ' +\n 'match_offset}`. Bounded regardless of trace size by max_matches ' +\n '(1..500, default 50). If has_more=true, REFINE the regex rather ' +\n 'than blindly raising max_matches.',\n )\n .namespace(NAMESPACE)\n .arg('trace_id', f.string('Real trace id'))\n .arg('regex_pattern', f.string('JS-compatible regex, multiline'))\n .arg('max_matches', f.number('Max records returned, 1..500; default 50').optional())\n .returns(f.json('SearchTraceResult'))\n .handler(async ({ trace_id, regex_pattern, max_matches }) =>\n store.searchTrace({\n trace_id: assertString(trace_id, 'trace_id'),\n regex_pattern: assertRegex(regex_pattern),\n max_matches: assertMaxMatches(max_matches),\n }),\n )\n .build()\n\n const searchSpan = fn('searchSpan')\n .description(\n 'Regex search inside a single span. Use when viewSpans returned ' +\n 'a 16KB-truncated payload and you need to narrow further. ' +\n 'Returns `{trace_id, span_id, hits: SpanMatchRecord[], ' +\n 'total_matches, has_more}` — iterate `result.hits`, NOT ' +\n '`result.matches`.',\n )\n .namespace(NAMESPACE)\n .arg('trace_id', f.string('Real trace id'))\n .arg('span_id', f.string('Real span id within trace'))\n .arg('regex_pattern', f.string('JS-compatible regex, multiline'))\n .arg('max_matches', f.number('Max records, 1..500; default 50').optional())\n .returns(f.json('SearchSpanResult'))\n .handler(async ({ trace_id, span_id, regex_pattern, max_matches }) =>\n store.searchSpan({\n trace_id: assertString(trace_id, 'trace_id'),\n span_id: assertString(span_id, 'span_id'),\n regex_pattern: assertRegex(regex_pattern),\n max_matches: assertMaxMatches(max_matches),\n }),\n )\n .build()\n\n return [\n getDatasetOverview,\n queryTraces,\n countTraces,\n viewTrace,\n viewSpans,\n searchTrace,\n searchSpan,\n ]\n}\n\n/**\n * Convenience: same shape as `buildTraceAnalystTools` but returns the\n * grouped form expected when registering trace tools alongside other\n * agent function modules. */\nexport function traceAnalystFunctionGroup(opts: BuildTraceAnalystToolsOpts): {\n namespace: string\n title: string\n selectionCriteria: string\n description: string\n functions: AxFunction[]\n} {\n return {\n namespace: NAMESPACE,\n title: 'Trace Analysis',\n selectionCriteria: 'Use for any inspection of OTLP-shaped trace data.',\n description:\n 'Discovery → narrow → deep-read tools over a JSONL trace dataset. ' +\n 'Always call getDatasetOverview first.',\n functions: buildTraceAnalystTools(opts),\n }\n}\n\n// ─── Argument validation ─────────────────────────────────────────────\n\nfunction parseFilters(input: unknown): TraceAnalystFilters | undefined {\n if (input == null) return undefined\n if (typeof input !== 'object' || Array.isArray(input)) {\n throw new TypeError(`filters must be an object, got ${typeof input}`)\n }\n const f = input as Record<string, unknown>\n const out: TraceAnalystFilters = {}\n if (typeof f.has_errors === 'boolean') out.has_errors = f.has_errors\n out.service_names = stringArrayOrUndefined(f.service_names, 'service_names')\n out.agent_names = stringArrayOrUndefined(f.agent_names, 'agent_names')\n out.model_names = stringArrayOrUndefined(f.model_names, 'model_names')\n out.tool_names = stringArrayOrUndefined(f.tool_names, 'tool_names')\n if (typeof f.start_time_after === 'string') out.start_time_after = f.start_time_after\n if (typeof f.start_time_before === 'string') out.start_time_before = f.start_time_before\n if (typeof f.regex_pattern === 'string') {\n if (f.regex_pattern.length === 0) {\n throw new TypeError('filters.regex_pattern cannot be empty')\n }\n out.regex_pattern = f.regex_pattern\n }\n return out\n}\n\nfunction stringArrayOrUndefined(v: unknown, label: string): string[] | undefined {\n if (v === undefined || v === null) return undefined\n if (!Array.isArray(v)) throw new TypeError(`${label} must be an array of strings`)\n if (v.some((x) => typeof x !== 'string')) {\n throw new TypeError(`${label} entries must be strings`)\n }\n return v as string[]\n}\n\nfunction assertPageLimit(limit: unknown): number {\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit < 1 || limit > 200) {\n throw new RangeError(`limit must be an integer 1..200`)\n }\n return limit\n}\nfunction assertOffset(offset: unknown): number | undefined {\n if (offset === undefined) return undefined\n if (typeof offset !== 'number' || !Number.isInteger(offset) || offset < 0) {\n throw new RangeError(`offset must be a non-negative integer`)\n }\n return offset\n}\nfunction assertRegex(pattern: unknown): string {\n if (typeof pattern !== 'string' || pattern.length === 0) {\n throw new TypeError(`regex_pattern must be a non-empty string`)\n }\n // Compile-and-discard to fail fast — store will recompile, but we\n // want a deterministic error from the agent's side rather than\n // a downstream exception.\n // eslint-disable-next-line no-new\n new RegExp(pattern, 'm')\n return pattern\n}\nfunction assertMaxMatches(n: unknown): number | undefined {\n if (n === undefined) return undefined\n if (typeof n !== 'number' || !Number.isInteger(n) || n < 1 || n > 500) {\n throw new RangeError(`max_matches must be an integer 1..500`)\n }\n return n\n}\n\nfunction assertString(v: unknown, label: string): string {\n if (typeof v !== 'string' || v.length === 0) {\n throw new TypeError(`${label} must be a non-empty string`)\n }\n return v\n}\n\nfunction assertStringArray(v: unknown, label: string): string[] {\n if (!Array.isArray(v)) throw new TypeError(`${label} must be an array of strings`)\n if (v.some((x) => typeof x !== 'string')) {\n throw new TypeError(`${label} entries must be strings`)\n }\n return v as string[]\n}\n"],"mappings":";;;;;AAsDO,SAAS,oBAAoB,KAAwD;AAC1F,QAAM,WAAW,YAAY,KAAK,UAAU,KAAK,YAAY,KAAK,SAAS;AAC3E,QAAM,UAAU,YAAY,KAAK,SAAS,KAAK,YAAY,KAAK,QAAQ;AACxE,MAAI,CAAC,YAAY,CAAC,QAAS,QAAO;AAElC,QAAM,YAAY,YAAY,KAAK,gBAAgB,KAAK,YAAY,KAAK,cAAc,KAAK;AAC5F,QAAM,OAAO,YAAY,KAAK,MAAM,KAAK;AACzC,QAAM,aAAa,YAAY,KAAK,YAAY,KAAK,YAAY,KAAK,WAAW,KAAK;AACtF,QAAM,WAAW,YAAY,KAAK,UAAU,KAAK,YAAY,KAAK,SAAS,KAAK;AAEhF,QAAM,SAAS,eAAe,GAAG;AACjC,QAAM,aAAa,sBAAsB,GAAG;AAE5C,QAAM,eACJ,SAAS,WAAW,cAAc,CAAC,KACnC,SAAS,WAAW,kCAAkC,CAAC,KACvD;AACF,QAAM,aACJ,SAAS,WAAW,YAAY,CAAC,KACjC,SAAS,WAAW,sBAAsB,CAAC,KAC3C,SAAS,WAAW,sBAAsB,CAAC,KAC3C;AACF,QAAM,aACJ,SAAS,WAAW,gBAAgB,CAAC,KACrC,SAAS,WAAW,0BAA0B,CAAC,KAC/C,SAAS,WAAW,WAAW,CAAC,KAChC;AACF,QAAM,YACJ,SAAS,WAAW,WAAW,CAAC,KAAK,SAAS,WAAW,qBAAqB,CAAC,KAAK;AAEtF,QAAM,OAAO,cAAc,UAAU;AAErC,MAAI,cAAc;AAClB,MAAI,cAAc,UAAU;AAC1B,UAAM,IAAI,KAAK,MAAM,UAAU;AAC/B,UAAM,IAAI,KAAK,MAAM,QAAQ;AAC7B,QAAI,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,EAAG,eAAc,KAAK,IAAI,GAAG,IAAI,CAAC;AAAA,EAC3E;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,gBAAgB,aAAa,UAAU,SAAS,IAAI,YAAY;AAAA,IAChE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,OAAO;AAAA,IACf,gBAAgB,OAAO;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,eAAe,KAG7B;AACA,QAAM,SAAS,IAAI;AACnB,MAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,UAAM,UAAW,OAAmC;AACpD,UAAM,OACJ,YAAY,oBAAoB,YAAY,OACxC,OACA,YAAY,uBAAuB,YAAY,UAC7C,UACA;AACR,UAAM,aAAc,OAAmC;AACvD,UAAM,UAAU,OAAO,eAAe,YAAY,WAAW,SAAS,IAAI,aAAa;AACvF,WAAO,EAAE,MAAM,QAAQ;AAAA,EACzB;AACA,SAAO,EAAE,MAAM,SAAS,SAAS,OAAU;AAC7C;AAEO,SAAS,cAAc,OAAsD;AAClF,QAAM,OACJ,SAAS,MAAM,yBAAyB,CAAC,KAAK,SAAS,MAAM,4BAA4B,CAAC;AAC5F,MAAI,MAAM;AACR,UAAM,QAAQ,KAAK,YAAY;AAC/B,QACE,UAAU,WACV,UAAU,SACV,UAAU,UACV,UAAU,WACV,UAAU,eACV,UAAU,QACV;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAOO,SAAS,sBAAsB,KAAuD;AAC3F,QAAM,MAA+B,CAAC;AACtC,QAAM,WAAW,IAAI;AACrB,MAAI,YAAY,OAAO,aAAa,YAAY,CAAC,MAAM,QAAQ,QAAQ,GAAG;AACxE,UAAM,KAAM,SAAqC;AACjD,QAAI,MAAM,OAAO,OAAO,YAAY,CAAC,MAAM,QAAQ,EAAE,GAAG;AACtD,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,EAA6B,GAAG;AAClE,YAAI,CAAC,IAAI;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACA,QAAM,YAAY,IAAI;AACtB,MAAI,aAAa,OAAO,cAAc,YAAY,CAAC,MAAM,QAAQ,SAAS,GAAG;AAC3E,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,SAAoC,GAAG;AACzE,UAAI,CAAC,IAAI;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,YAAY,KAA8B,KAAiC;AACzF,QAAM,IAAI,IAAI,GAAG;AACjB,SAAO,OAAO,MAAM,WAAW,IAAI;AACrC;AAEO,SAAS,SAAS,GAA2B;AAClD,SAAO,OAAO,MAAM,YAAY,EAAE,SAAS,IAAI,IAAI;AACrD;AAGO,SAAS,SAAS,GAA2B;AAClD,MAAI,OAAO,MAAM,SAAU,QAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAC3D,MAAI,OAAO,MAAM,YAAY,EAAE,SAAS,GAAG;AACzC,UAAM,IAAI,OAAO,CAAC;AAClB,WAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAAA,EAClC;AACA,SAAO;AACT;AAGO,SAAS,gBACd,OACA,MACe;AACf,aAAW,KAAK,MAAM;AACpB,UAAM,IAAI,SAAS,MAAM,CAAC,CAAC;AAC3B,QAAI,MAAM,KAAM,QAAO;AAAA,EACzB;AACA,SAAO;AACT;AAGO,SAAS,gBACd,OACA,MACe;AACf,aAAW,KAAK,MAAM;AACpB,UAAM,IAAI,SAAS,MAAM,CAAC,CAAC;AAC3B,QAAI,MAAM,KAAM,QAAO;AAAA,EACzB;AACA,SAAO;AACT;AAQO,SAAS,gBAAgB,IAA8C;AAC5E,MAAI,CAAC,GAAI,QAAO;AAChB,MAAI,QAAQ,KAAK,EAAE,EAAG,QAAO,OAAO,EAAE;AACtC,QAAM,IAAI,KAAK,MAAM,EAAE;AACvB,SAAO,OAAO,MAAM,CAAC,IAAI,OAAO;AAClC;AAOO,SAAS,gBAAgB,GAAW,GAAmB;AAC5D,UAAQ,gBAAgB,CAAC,KAAK,MAAM,gBAAgB,CAAC,KAAK;AAC5D;;;ACnCO,IAAM,gCAAyD;AAAA,EACpE,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,wBAAwB;AAAA,EACxB,oBAAoB;AACtB;AAIO,IAAM,yCAAyC;;;AC1LtD,SAAS,UAAU,YAAY;;;AC+CxB,SAAS,mBAAmB,SAAyB;AAC1D,MAAI,SAAS;AACb,MAAI,QAAQ;AACZ,MAAI,OAAO,WAAW,MAAM,GAAG;AAC7B,aAAS,OAAO,MAAM,CAAC;AACvB,aAAS;AAAA,EACX;AACA,SAAO,IAAI,OAAO,QAAQ,KAAK;AACjC;AAKO,SAAS,kBAAkB,OAAe,SAAyB;AAIxE,QAAM,WAAW,OAAO,WAAW,OAAO,MAAM;AAChD,MAAI,YAAY,QAAS,QAAO;AAIhC,QAAM,QAAQ,UAAU;AACxB,MAAI,MAAM,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,SAAS,KAAK,CAAC;AACtD,SAAO,MAAM,KAAK,OAAO,WAAW,MAAM,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,SAAS;AAC1E,WAAO;AAAA,EACT;AACA,SAAO,GAAG,MAAM,MAAM,GAAG,GAAG,CAAC;AAAA,qCAAwC,QAAQ;AAC/E;;;AD7CA,IAAM,oBAAoB;AAG1B,SAAS,mBAAkC;AACzC,SAAO,IAAI,QAAQ,CAAC,YAAY,aAAa,OAAO,CAAC;AACvD;AAmEO,IAAM,+BAA+B,MAAM,OAAO;AAElD,IAAM,qBAAN,MAAuD;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA,EAER,YAAY,MAAiC;AAC3C,SAAK,OAAO,KAAK;AACjB,SAAK,yBACH,KAAK,0BAA0B,8BAA8B;AAC/D,SAAK,yBACH,KAAK,0BAA0B,8BAA8B;AAC/D,SAAK,qBACH,KAAK,sBAAsB,8BAA8B;AAC3D,SAAK,qBACH,KAAK,sBAAsB,8BAA8B;AAC3D,SAAK,eAAe,KAAK,gBAAgB;AAAA,EAC3C;AAAA;AAAA,EAIA,MAAM,YAAY,SAAyD;AACzE,UAAM,MAAM,MAAM,KAAK,MAAM;AAC7B,UAAM,UAAU,MAAM,KAAK,cAAc,KAAK,OAAO;AAErD,UAAM,WAAW,oBAAI,IAAY;AACjC,UAAM,SAAS,oBAAI,IAAY;AAC/B,UAAM,SAAS,oBAAI,IAAY;AAC/B,UAAM,QAAQ,oBAAI,IAAY;AAC9B,QAAI,WAAW;AACf,QAAI,WAA0B;AAC9B,QAAI,SAAwB;AAC5B,QAAI,kBAAkB;AACtB,QAAI,iBAAiB;AACrB,UAAM,WAAW,oBAAI,IAAqC;AAE1D,eAAW,KAAK,SAAS;AACvB,UAAI,EAAE,aAAc,UAAS,IAAI,EAAE,YAAY;AAC/C,UAAI,EAAE,WAAY,QAAO,IAAI,EAAE,UAAU;AACzC,iBAAW,KAAK,EAAE,OAAQ,QAAO,IAAI,CAAC;AACtC,iBAAW,MAAM,EAAE,MAAO,OAAM,IAAI,EAAE;AACtC,kBAAY,EAAE;AACd,UAAI,CAAC,YAAY,gBAAgB,EAAE,YAAY,QAAQ,IAAI,EAAG,YAAW,EAAE;AAC3E,UAAI,CAAC,UAAU,gBAAgB,EAAE,UAAU,MAAM,IAAI,EAAG,UAAS,EAAE;AACnE,UAAI,EAAE,YAAY;AAChB,2BAAmB;AACnB,mBAAW,KAAK,EAAE,OAAO;AACvB,cAAI,EAAE,WAAW,QAAS;AAC1B,4BAAkB;AAClB,iCAAuB,UAAU,EAAE,UAAU,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF;AAEA,UAAM,mBAAmB,QAAQ,MAAM,GAAG,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ;AACnE,WAAO;AAAA,MACL,cAAc,QAAQ;AAAA,MACtB,iBAAiB;AAAA,MACjB,UAAU,CAAC,GAAG,QAAQ,EAAE,KAAK;AAAA,MAC7B,QAAQ,CAAC,GAAG,MAAM,EAAE,KAAK;AAAA,MACzB,QAAQ,CAAC,GAAG,MAAM,EAAE,KAAK;AAAA,MACzB,YAAY,CAAC,GAAG,KAAK,EAAE,KAAK;AAAA,MAC5B;AAAA,MACA,QAAQ,EAAE,aAAa,iBAAiB,YAAY,eAAe;AAAA,MACnE,gBAAgB,sBAAsB,UAAU,eAAe;AAAA,MAC/D,YAAY,YAAY,SAAS,EAAE,UAAU,OAAO,IAAI;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,MAIW;AAC3B,QAAI,CAAC,OAAO,UAAU,KAAK,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK,QAAQ,KAAK;AACvE,YAAM,IAAI,WAAW,yCAAyC,KAAK,KAAK,EAAE;AAAA,IAC5E;AACA,UAAM,SAAS,KAAK,UAAU;AAC9B,QAAI,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,GAAG;AAC3C,YAAM,IAAI,WAAW,uCAAuC,MAAM,EAAE;AAAA,IACtE;AAEA,UAAM,MAAM,MAAM,KAAK,MAAM;AAC7B,UAAM,UAAU,MAAM,KAAK,cAAc,KAAK,KAAK,OAAO;AAC1D,UAAM,QAAQ,QAAQ,MAAM,QAAQ,SAAS,KAAK,KAAK;AACvD,WAAO;AAAA,MACL,QAAQ,MAAM,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,MAC1C,OAAO,QAAQ;AAAA,MACf,UAAU,SAAS,MAAM,SAAS,QAAQ;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,SAAgD;AAChE,UAAM,MAAM,MAAM,KAAK,MAAM;AAC7B,UAAM,UAAU,MAAM,KAAK,cAAc,KAAK,OAAO;AACrD,WAAO,QAAQ;AAAA,EACjB;AAAA,EAEA,MAAM,UAAU,MAGa;AAC3B,UAAM,MAAM,MAAM,KAAK,MAAM;AAC7B,UAAM,QAAQ,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC3C,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,mBAAmB,KAAK,QAAQ;AAAA,IAC5C;AACA,UAAM,MAAM,KAAK,0BAA0B,KAAK;AAKhD,UAAM,MAAM,MAAM,KAAK,OAAO;AAC9B,UAAM,QAA4B,CAAC;AACnC,QAAI,eAAe;AACnB,QAAI,0BAA0B;AAC9B,UAAM,UAA6B,EAAE,OAAO,EAAE;AAC9C,eAAW,KAAK,MAAM,OAAO;AAC3B,YAAM,YAAY,KAAK,YAAY,KAAK,MAAM,UAAU,GAAG,KAAK,OAAO;AACvE,YAAM,QAAQ,OAAO,WAAW,KAAK,UAAU,SAAS,GAAG,MAAM;AACjE,gCAA0B,KAAK,IAAI,yBAAyB,KAAK;AACjE,sBAAgB;AAChB,UAAI,eAAe,KAAK,oBAAoB;AAC1C,eAAO;AAAA,UACL,UAAU,MAAM;AAAA,UAChB,WAAW,KAAK,sBAAsB,OAAO,uBAAuB;AAAA,QACtE;AAAA,MACF;AACA,YAAM,KAAK,SAAS;AAAA,IACtB;AACA,WAAO,EAAE,UAAU,MAAM,UAAU,MAAM;AAAA,EAC3C;AAAA,EAEA,MAAM,UAAU,MAIa;AAC3B,UAAM,MAAM,MAAM,KAAK,MAAM;AAC7B,UAAM,QAAQ,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC3C,QAAI,CAAC,MAAO,OAAM,IAAI,mBAAmB,KAAK,QAAQ;AACtD,QAAI,KAAK,SAAS,WAAW,GAAG;AAC9B,aAAO;AAAA,QACL,UAAU,MAAM;AAAA,QAChB,OAAO,CAAC;AAAA,QACR,kBAAkB,CAAC;AAAA,QACnB,2BAA2B;AAAA,MAC7B;AAAA,IACF;AACA,QAAI,KAAK,SAAS,SAAS,KAAK;AAC9B,YAAM,IAAI,WAAW,sCAAsC,KAAK,SAAS,MAAM,EAAE;AAAA,IACnF;AACA,UAAM,MAAM,KAAK,0BAA0B,KAAK;AAEhD,UAAM,UAAU,IAAI,IAAI,KAAK,QAAQ;AACrC,UAAM,QAAQ,MAAM,MAAM,OAAO,CAAC,MAAM,QAAQ,IAAI,EAAE,OAAO,CAAC;AAC9D,UAAM,UAAU,KAAK,SAAS,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAACA,OAAMA,GAAE,YAAY,EAAE,CAAC;AAEjF,UAAM,MAAM,MAAM,KAAK,OAAO;AAC9B,UAAM,QAA4B,CAAC;AACnC,UAAM,UAA6B,EAAE,OAAO,EAAE;AAC9C,QAAI,eAAe;AACnB,eAAW,KAAK,OAAO;AACrB,YAAM,YAAY,KAAK,YAAY,KAAK,MAAM,UAAU,GAAG,KAAK,OAAO;AACvE,YAAM,QAAQ,OAAO,WAAW,KAAK,UAAU,SAAS,GAAG,MAAM;AACjE,sBAAgB;AAChB,UAAI,eAAe,KAAK,oBAAoB;AAG1C;AAAA,MACF;AACA,YAAM,KAAK,SAAS;AAAA,IACtB;AACA,WAAO;AAAA,MACL,UAAU,MAAM;AAAA,MAChB;AAAA,MACA,kBAAkB;AAAA,MAClB,2BAA2B,QAAQ;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,MAIa;AAC7B,UAAM,cAAc,KAAK,eAAe;AACxC,QAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,KAAK,cAAc,KAAK;AAC1E,YAAM,IAAI,WAAW,+CAA+C,WAAW,EAAE;AAAA,IACnF;AACA,UAAM,MAAM,MAAM,KAAK,MAAM;AAC7B,UAAM,QAAQ,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC3C,QAAI,CAAC,MAAO,OAAM,IAAI,mBAAmB,KAAK,QAAQ;AACtD,UAAM,KAAK,mBAAmB,KAAK,aAAa;AAEhD,UAAM,MAAM,MAAM,KAAK,OAAO;AAC9B,UAAM,OAA0B,CAAC;AACjC,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,eAAW,KAAK,MAAM,OAAO;AAC3B,YAAM,YAAY,cAAc,KAAK;AACrC,YAAM,YAAY,MAAM,KAAK;AAAA,QAC3B;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,KAAK;AAAA,QACL;AAAA,MACF;AACA,eAAS,UAAU;AACnB,iBAAW,KAAK,UAAU,SAAS;AACjC,YAAI,KAAK,UAAU,YAAa;AAChC,aAAK,KAAK,CAAC;AAAA,MACb;AACA,UAAI,KAAK,UAAU,aAAa;AAK9B,iBAAS;AACT;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,UAAU,MAAM;AAAA,MAChB;AAAA;AAAA,MAEA,eAAe,SAAS,KAAK,SAAS;AAAA,MACtC,UAAU,UAAU,QAAQ,KAAK;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,MAKa;AAC5B,UAAM,cAAc,KAAK,eAAe;AACxC,QAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,KAAK,cAAc,KAAK;AAC1E,YAAM,IAAI,WAAW,8CAA8C,WAAW,EAAE;AAAA,IAClF;AACA,UAAM,MAAM,MAAM,KAAK,MAAM;AAC7B,UAAM,QAAQ,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC3C,QAAI,CAAC,MAAO,OAAM,IAAI,mBAAmB,KAAK,QAAQ;AACtD,UAAM,OAAO,MAAM,MAAM,KAAK,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO;AAC/D,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,kBAAkB,KAAK,UAAU,KAAK,OAAO;AAAA,IACzD;AACA,UAAM,KAAK,mBAAmB,KAAK,aAAa;AAChD,UAAM,MAAM,MAAM,KAAK,OAAO;AAC9B,UAAM,YAAY,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL;AAAA,IACF;AACA,WAAO;AAAA,MACL,UAAU,MAAM;AAAA,MAChB,SAAS,KAAK;AAAA,MACd,MAAM,UAAU;AAAA,MAChB,eAAe,UAAU;AAAA,MACzB,UAAU,UAAU,QAAQ,UAAU,QAAQ;AAAA,IAChD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBAA+B;AACnC,UAAM,KAAK,MAAM;AAAA,EACnB;AAAA,EAEA,MAAc,SAA0B;AACtC,QAAI,CAAC,KAAK,eAAe;AACvB,WAAK,gBAAgB,KAAK,YAAY;AAAA,IACxC;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAA+B;AAC3C,QAAI;AACJ,QAAI;AACF,cAAQ,MAAM,KAAK,KAAK,IAAI;AAAA,IAC9B,SAAS,KAAK;AACZ,UAAK,KAA+B,SAAS,UAAU;AACrD,cAAM,IAAI,sBAAsB,KAAK,IAAI;AAAA,MAC3C;AACA,YAAM;AAAA,IACR;AACA,QAAI,MAAM,OAAO,KAAK,cAAc;AAClC,YAAM,IAAI,uBAAuB,KAAK,MAAM,MAAM,MAAM,KAAK,YAAY;AAAA,IAC3E;AACA,WAAO,SAAS,KAAK,IAAI;AAAA,EAC3B;AAAA,EAEA,MAAc,QAA+B;AAC3C,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,eAAe,KAAK,WAAW;AAAA,IACtC;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,aAAoC;AAEhD,UAAM,MAAM,MAAM,KAAK,OAAO;AAE9B,UAAM,UAAU,oBAAI,IAA6B;AACjD,QAAI,SAAS;AACb,QAAI,aAAa;AACjB,WAAO,SAAS,IAAI,QAAQ;AAG1B,UAAI,EAAE,cAAc,mBAAmB;AACrC,qBAAa;AACb,cAAM,iBAAiB;AAAA,MACzB;AACA,YAAM,eAAe,IAAI,QAAQ,IAAM,MAAM;AAC7C,YAAM,UAAU,iBAAiB,KAAK,IAAI,SAAS;AACnD,YAAM,aAAa,UAAU;AAC7B,UAAI,eAAe,GAAG;AACpB,iBAAS,UAAU;AACnB;AAAA,MACF;AACA,YAAM,YAAY,IAAI,SAAS,QAAQ,OAAO,EAAE,SAAS,MAAM;AAC/D,YAAM,aAAa;AACnB,eAAS,UAAU;AAEnB,UAAI;AACJ,UAAI;AACF,iBAAS,KAAK,MAAM,SAAS;AAAA,MAC/B,QAAQ;AAGN;AAAA,MACF;AACA,UAAI,CAAC,UAAU,OAAO,WAAW,SAAU;AAC3C,YAAM,OAAO,oBAAoB,MAAiC;AAClE,UAAI,CAAC,KAAM;AAEX,UAAI,QAAQ,QAAQ,IAAI,KAAK,QAAQ;AACrC,UAAI,CAAC,OAAO;AACV,gBAAQ;AAAA,UACN,UAAU,KAAK;AAAA,UACf,cAAc,KAAK;AAAA,UACnB,YAAY,KAAK;AAAA,UACjB,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,YAAY,KAAK;AAAA,UACjB,UAAU,KAAK;AAAA,UACf,aAAa;AAAA,UACb,iBAAiB;AAAA,UACjB,QAAQ,oBAAI,IAAI;AAAA,UAChB,OAAO,oBAAI,IAAI;AAAA,UACf,OAAO,CAAC;AAAA,QACV;AACA,gBAAQ,IAAI,KAAK,UAAU,KAAK;AAAA,MAClC,OAAO;AAGL,YAAI,CAAC,MAAM,gBAAgB,KAAK,aAAc,OAAM,eAAe,KAAK;AACxE,YAAI,CAAC,MAAM,cAAc,KAAK,WAAY,OAAM,aAAa,KAAK;AAAA,MACpE;AAEA,YAAM,aAA6B;AAAA,QACjC,SAAS,KAAK;AAAA,QACd,gBAAgB,KAAK;AAAA,QACrB,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,YAAY,KAAK;AAAA,QACjB,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,QAAQ,KAAK;AAAA,QACb,gBAAgB,KAAK;AAAA,QACrB,cAAc,KAAK;AAAA,QACnB,YAAY,KAAK;AAAA,QACjB,YAAY,KAAK;AAAA,QACjB,WAAW,KAAK;AAAA,QAChB,kBAAkB;AAAA,QAClB,kBAAkB;AAAA,MACpB;AACA,YAAM,MAAM,KAAK,UAAU;AAC3B,YAAM,cAAc;AACpB,YAAM,mBAAmB,aAAa;AACtC,UAAI,KAAK,WAAW,QAAS,OAAM,aAAa;AAChD,UAAI,gBAAgB,KAAK,YAAY,MAAM,UAAU,IAAI,EAAG,OAAM,aAAa,KAAK;AACpF,UAAI,gBAAgB,KAAK,UAAU,MAAM,QAAQ,IAAI,EAAG,OAAM,WAAW,KAAK;AAC9E,UAAI,KAAK,WAAY,OAAM,OAAO,IAAI,KAAK,UAAU;AACrD,UAAI,KAAK,UAAW,OAAM,MAAM,IAAI,KAAK,SAAS;AAAA,IACpD;AAIA,QAAI,gBAAgB;AACpB,eAAW,KAAK,QAAQ,OAAO,GAAG;AAChC,uBAAiB,EAAE;AACnB,QAAE,MAAM;AAAA,QACN,CAAC,GAAG,MACF,gBAAgB,EAAE,YAAY,EAAE,UAAU,KAAK,EAAE,mBAAmB,EAAE;AAAA,MAC1E;AAGA,YAAM,UAAU,gBAAgB,EAAE,UAAU;AAC5C,YAAM,QAAQ,gBAAgB,EAAE,QAAQ;AACxC,QAAE,cAAc,YAAY,QAAQ,UAAU,OAAO,IAAI,KAAK,IAAI,GAAG,QAAQ,OAAO;AAAA,IACtF;AACA,UAAM,iBAAiB,CAAC,GAAG,QAAQ,KAAK,CAAC,EAAE,KAAK;AAEhD,WAAO,EAAE,SAAS,eAAe,eAAe;AAAA,EAClD;AAAA;AAAA,EAIA,MAAc,cACZ,KACA,SAC4B;AAC5B,UAAM,SAAS,IAAI,eAAe,IAAI,CAAC,OAAO,IAAI,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,SAAS;AACnF,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,kBAAkB,OAAO,OAAO,CAAC,MAAM;AAC3C,UAAI,QAAQ,eAAe,UAAa,EAAE,eAAe,QAAQ,WAAY,QAAO;AACpF,UAAI,QAAQ,iBAAiB,QAAQ,cAAc,SAAS,GAAG;AAC7D,YAAI,CAAC,EAAE,gBAAgB,CAAC,QAAQ,cAAc,SAAS,EAAE,YAAY,EAAG,QAAO;AAAA,MACjF;AACA,UAAI,QAAQ,eAAe,QAAQ,YAAY,SAAS,GAAG;AACzD,YAAI,CAAC,EAAE,cAAc,CAAC,QAAQ,YAAY,SAAS,EAAE,UAAU,EAAG,QAAO;AAAA,MAC3E;AACA,UAAI,QAAQ,eAAe,QAAQ,YAAY,SAAS,GAAG;AACzD,YAAI,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,QAAQ,YAAa,SAAS,CAAC,CAAC,EAAG,QAAO;AAAA,MAC3E;AACA,UAAI,QAAQ,cAAc,QAAQ,WAAW,SAAS,GAAG;AACvD,YAAI,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,QAAQ,WAAY,SAAS,EAAE,CAAC,EAAG,QAAO;AAAA,MAC3E;AACA,UAAI,QAAQ,oBAAoB,EAAE,aAAa,QAAQ,iBAAkB,QAAO;AAChF,UAAI,QAAQ,qBAAqB,EAAE,aAAa,QAAQ,kBAAmB,QAAO;AAClF,aAAO;AAAA,IACT,CAAC;AAED,QAAI,CAAC,QAAQ,cAAe,QAAO;AAGnC,UAAM,KAAK,mBAAmB,QAAQ,aAAa;AACnD,UAAM,MAAM,MAAM,KAAK,OAAO;AAC9B,UAAM,MAAyB,CAAC;AAChC,eAAW,KAAK,iBAAiB;AAC/B,UAAI,UAAU;AACd,iBAAW,KAAK,EAAE,OAAO;AACvB,cAAM,QAAQ,IAAI,SAAS,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB;AAItF,YAAI,GAAG,KAAK,MAAM,SAAS,MAAM,CAAC,GAAG;AACnC,oBAAU;AACV;AAAA,QACF;AAAA,MACF;AACA,UAAI,QAAS,KAAI,KAAK,CAAC;AAAA,IACzB;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,GAA8C;AAC9D,WAAO;AAAA,MACL,UAAU,EAAE;AAAA,MACZ,cAAc,EAAE;AAAA,MAChB,YAAY,EAAE;AAAA,MACd,YAAY,EAAE;AAAA,MACd,YAAY,EAAE;AAAA,MACd,YAAY,EAAE;AAAA,MACd,UAAU,EAAE;AAAA,MACZ,aAAa,EAAE;AAAA,MACf,iBAAiB,EAAE;AAAA,MACnB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK;AAAA,MAC3B,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA,EAIQ,YACN,KACA,UACA,GACA,YACA,SACkB;AAClB,UAAM,QAAQ,IACX,SAAS,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EACpE,SAAS,MAAM;AAClB,QAAI,MAA+B,CAAC;AACpC,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,UAAI,UAAU,OAAO,WAAW,SAAU,OAAM;AAAA,IAClD,QAAQ;AAAA,IAER;AACA,UAAM,QAAQ,sBAAsB,GAAG;AACvC,UAAM,YAAqC,CAAC;AAC5C,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,UAAI,OAAO,MAAM,UAAU;AACzB,cAAM,QAAQ,kBAAkB,GAAG,UAAU;AAC7C,YAAI,UAAU,EAAG,SAAQ,SAAS;AAClC,kBAAU,CAAC,IAAI;AAAA,MACjB,WAAW,MAAM,QAAQ,CAAC,KAAM,KAAK,OAAO,MAAM,UAAW;AAC3D,cAAM,OAAO,KAAK,UAAU,CAAC;AAC7B,cAAM,QAAQ,kBAAkB,MAAM,UAAU;AAChD,YAAI,UAAU,MAAM;AAClB,kBAAQ,SAAS;AACjB,oBAAU,CAAC,IAAI;AAAA,QACjB,OAAO;AACL,oBAAU,CAAC,IAAI;AAAA,QACjB;AAAA,MACF,OAAO;AACL,kBAAU,CAAC,IAAI;AAAA,MACjB;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA,SAAS,EAAE;AAAA,MACX,gBAAgB,EAAE;AAAA,MAClB,MAAM,EAAE;AAAA,MACR,MAAM,EAAE;AAAA,MACR,YAAY,EAAE;AAAA,MACd,UAAU,EAAE;AAAA,MACZ,aAAa,EAAE;AAAA,MACf,QAAQ,EAAE;AAAA,MACV,gBAAgB,EAAE;AAAA,MAClB,cAAc,EAAE;AAAA,MAChB,YAAY,EAAE;AAAA,MACd,YAAY,EAAE;AAAA,MACd,WAAW,EAAE;AAAA,MACb,YAAY;AAAA,IACd;AAAA,EACF;AAAA,EAEQ,sBACN,GACA,yBACoB;AACpB,UAAM,SAAS,oBAAI,IAAoB;AACvC,QAAI,aAAa;AACjB,eAAW,KAAK,EAAE,OAAO;AACvB,aAAO,IAAI,EAAE,OAAO,OAAO,IAAI,EAAE,IAAI,KAAK,KAAK,CAAC;AAChD,UAAI,EAAE,WAAW,QAAS,eAAc;AAAA,IAC1C;AACA,UAAM,MAAM,CAAC,GAAG,OAAO,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,EAAE;AACzE,WAAO;AAAA,MACL,YAAY,EAAE;AAAA,MACd,gBAAgB;AAAA,MAChB;AAAA,MACA,kBAAkB;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,MAAc,mBACZ,KACA,UACA,GACA,IACA,YACA,WAC0E;AAK1E,UAAM,QAAQ,IACX,SAAS,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,gBAAgB,EACpE,SAAS,MAAM;AAClB,UAAM,UAA6B,CAAC;AACpC,UAAM,WAAW,IAAI,OAAO,GAAG,QAAQ,GAAG,MAAM,SAAS,GAAG,IAAI,GAAG,QAAQ,GAAG,GAAG,KAAK,GAAG;AACzF,QAAI,QAAQ;AACZ,QAAI,UAAU;AACd,QAAI,IAA4B,SAAS,KAAK,KAAK;AACnD,WAAO,MAAM,MAAM;AACjB,eAAS;AACT,UAAI,EAAE,UAAU,SAAS,UAAW,UAAS,aAAa;AAC1D,UAAI,QAAQ,UAAU,WAAW;AAC/B,kBAAU;AACV;AAAA,MACF;AACA,YAAM,SAAS,MAAM,MAAM,KAAK,IAAI,GAAG,EAAE,QAAQ,aAAa,CAAC,GAAG,EAAE,KAAK;AACzE,YAAM,QAAQ,MAAM;AAAA,QAClB,EAAE,QAAQ,EAAE,CAAC,EAAE;AAAA,QACf,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,KAAK,MAAM,aAAa,CAAC;AAAA,MACnD;AACA,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,SAAS,EAAE;AAAA,QACX,WAAW,EAAE;AAAA,QACb,WAAW,EAAE;AAAA,QACb,gBAAgB,2BAA2B,OAAO,EAAE,KAAK,KAAK;AAAA,QAC9D,cAAc,kBAAkB,EAAE,CAAC,GAAG,UAAU;AAAA,QAChD,gBAAgB,kBAAkB,QAAQ,UAAU;AAAA,QACpD,eAAe,kBAAkB,OAAO,UAAU;AAAA,QAClD,cAAc,EAAE;AAAA,MAClB,CAAC;AACD,UAAI,SAAS,KAAK,KAAK;AAAA,IACzB;AACA,WAAO,EAAE,SAAS,OAAO,QAAQ;AAAA,EACnC;AACF;AAIO,IAAM,wBAAN,cAAoC,cAAc;AAAA,EACvD,YAAY,MAAc;AACxB,UAAM,yBAAyB,IAAI,EAAE;AAAA,EACvC;AACF;AACO,IAAM,yBAAN,cAAqC,MAAM;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACT,YAAY,MAAc,YAAoB,WAAmB;AAC/D;AAAA,MACE,cAAc,IAAI,OAAO,UAAU,oBAAoB,SAAS;AAAA,IAElE;AACA,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,YAAY;AAAA,EACnB;AACF;AACO,IAAM,qBAAN,cAAiC,cAAc;AAAA,EAC3C;AAAA,EACT,YAAY,UAAkB;AAC5B,UAAM,oBAAoB,QAAQ,EAAE;AACpC,SAAK,WAAW;AAAA,EAClB;AACF;AACO,IAAM,oBAAN,cAAgC,cAAc;AAAA,EAC1C;AAAA,EACA;AAAA,EACT,YAAY,UAAkB,SAAiB;AAC7C,UAAM,QAAQ,OAAO,uBAAuB,QAAQ,EAAE;AACtD,SAAK,WAAW;AAChB,SAAK,UAAU;AAAA,EACjB;AACF;AAOA,SAAS,UAAa,GAA0B;AAC9C,SAAO,MAAM;AACf;AAaA,SAAS,iBAAiB,OAAe,KAAsB;AAC7D,MAAI,MAAM,GAAG,MAAM,IAAK,QAAO;AAC/B,MAAI,cAAc;AAClB,MAAI,IAAI,MAAM;AACd,SAAO,KAAK,KAAK,MAAM,CAAC,MAAM,MAAM;AAClC,mBAAe;AACf,SAAK;AAAA,EACP;AACA,SAAO,cAAc,MAAM;AAC7B;AAIA,SAAS,mBAAmB,OAAe,MAAsB;AAC/D,WAAS,IAAI,MAAM,KAAK,GAAG,KAAK,GAAG;AACjC,QAAI,MAAM,CAAC,MAAM,OAAO,iBAAiB,OAAO,CAAC,EAAG,QAAO;AAAA,EAC7D;AACA,SAAO;AACT;AAUA,SAAS,2BAA2B,OAAe,QAA+B;AAEhF,QAAM,aAAa,mBAAmB,OAAO,KAAK,IAAI,QAAQ,MAAM,SAAS,CAAC,CAAC;AAC/E,MAAI,aAAa,EAAG,QAAO;AAE3B,MAAI,IAAI,aAAa;AACrB,SAAO,KAAK,KAAK,MAAM,CAAC,MAAM,IAAK,MAAK;AACxC,MAAI,IAAI,EAAG,QAAO;AAElB,QAAM,WAAW,mBAAmB,OAAO,IAAI,CAAC;AAChD,MAAI,WAAW,EAAG,QAAO;AACzB,QAAM,UAAU,mBAAmB,OAAO,WAAW,CAAC;AACtD,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO,MAAM,MAAM,UAAU,GAAG,QAAQ;AAC1C;AASA,IAAM,oBAAoB;AAC1B,IAAM,0BAA0B;AAChC,IAAM,sBAAsB;AAgB5B,SAAS,wBAAwB,SAA6B,UAA0B;AACtF,QAAM,OAAO,WAAW,IAAI,KAAK;AACjC,QAAM,OAAO,IAAI,SAAS,IAAI,MAAM,IAAI,YAAY,OAAO;AAC3D,QAAM,OAAO,KACV,QAAQ,kEAAkE,MAAM,EAChF,QAAQ,uBAAuB,MAAM,EACrC,QAAQ,wBAAwB,QAAQ,EACxC,QAAQ,2CAA2C,CAAC,IAAI,MAAO,IAAI,IAAI,CAAC,KAAK,GAAI,EACjF,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACR,SAAO,KAAK,SAAS,sBAAsB,GAAG,KAAK,MAAM,GAAG,mBAAmB,CAAC,WAAM;AACxF;AAEA,SAAS,KAAK,KAA0B,KAA0B;AAChE,MAAI,CAAC,IAAK;AACV,MAAI,IAAI,MAAM,IAAI,IAAI,GAAG,KAAK,KAAK,CAAC;AACtC;AAEA,SAAS,OAAO,KAAyC;AACvD,MAAI,OAAsB;AAC1B,MAAI,QAAQ;AACZ,aAAW,CAAC,GAAG,CAAC,KAAK;AACnB,QAAI,IAAI,OAAO;AACb,aAAO;AACP,cAAQ;AAAA,IACV;AACF,SAAO;AACT;AAEA,SAAS,uBACP,UACA,SACA,MACM;AACN,QAAM,YAAY,wBAAwB,KAAK,gBAAgB,KAAK,IAAI;AACxE,MAAI,MAAM,SAAS,IAAI,SAAS;AAChC,MAAI,CAAC,KAAK;AACR,UAAM;AAAA,MACJ;AAAA,MACA,SAAS,KAAK,kBAAkB,KAAK,QAAQ,IAAI,MAAM,GAAG,GAAG;AAAA,MAC7D,UAAU,oBAAI,IAAI;AAAA,MAClB,SAAS,CAAC;AAAA,MACV,WAAW;AAAA,MACX,WAAW,oBAAI,IAAI;AAAA,MACnB,WAAW,oBAAI,IAAI;AAAA,IACrB;AACA,aAAS,IAAI,WAAW,GAAG;AAAA,EAC7B;AACA,MAAI,SAAS,IAAI,OAAO;AACxB,MAAI,aAAa;AACjB,MAAI,IAAI,QAAQ,SAAS,2BAA2B,CAAC,IAAI,QAAQ,SAAS,KAAK,OAAO,GAAG;AACvF,QAAI,QAAQ,KAAK,KAAK,OAAO;AAAA,EAC/B;AACA,OAAK,IAAI,WAAW,KAAK,IAAI;AAC7B,OAAK,IAAI,WAAW,KAAK,SAAS;AACpC;AAEA,SAAS,sBACP,UACA,iBACgB;AAChB,QAAM,MAAM,CAAC,GAAG,SAAS,OAAO,CAAC,EAAE;AAAA,IACjC,CAAC,SAAuB;AAAA,MACtB,WAAW,IAAI;AAAA,MACf,uBAAuB,IAAI;AAAA,MAC3B,WAAW,OAAO,IAAI,SAAS;AAAA,MAC/B,WAAW,OAAO,IAAI,SAAS;AAAA,MAC/B,aAAa,IAAI,SAAS;AAAA,MAC1B,YAAY,IAAI;AAAA,MAChB,YAAY,kBAAkB,IAAI,IAAI,SAAS,OAAO,kBAAkB;AAAA,MACxE,oBAAoB,CAAC,GAAG,IAAI,QAAQ,EAAE,MAAM,GAAG,uBAAuB;AAAA,MACtE,mBAAmB,IAAI,QAAQ,MAAM,GAAG,uBAAuB;AAAA,IACjE;AAAA,EACF;AACA,MAAI,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU;AAC/E,SAAO,IAAI,MAAM,GAAG,iBAAiB;AACvC;;;AEl6BA,SAAS,GAAG,UAAU;AAKtB,IAAM,YAAY;AAQlB,IAAM,eAAe,EAClB,KAAK,0EAA0E,EAC/E,SAAS;AAML,SAAS,uBAAuB,MAAgD;AACrF,QAAM,EAAE,MAAM,IAAI;AAElB,QAAM,qBAAqB,GAAG,oBAAoB,EAC/C;AAAA,IACC;AAAA,EAGF,EACC,UAAU,SAAS,EACnB,IAAI,WAAW,YAAY,EAC3B,QAAQ,EAAE,KAAK,iBAAiB,CAAC,EACjC,QAAQ,OAAO,EAAE,QAAQ,MAAM,MAAM,YAAY,aAAa,OAAO,CAAC,CAAC,EACvE,MAAM;AAET,QAAM,cAAc,GAAG,aAAa,EACjC;AAAA,IACC;AAAA,EAGF,EACC,UAAU,SAAS,EACnB,IAAI,WAAW,YAAY,EAC3B,IAAI,SAAS,EAAE,OAAO,mBAAmB,CAAC,EAC1C,IAAI,UAAU,EAAE,OAAO,wBAAwB,EAAE,SAAS,CAAC,EAC3D,QAAQ,EAAE,KAAK,iBAAiB,CAAC,EACjC;AAAA,IAAQ,OAAO,EAAE,SAAS,OAAO,OAAO,MACvC,MAAM,YAAY;AAAA,MAChB,SAAS,aAAa,OAAO;AAAA,MAC7B,OAAO,gBAAgB,KAAK;AAAA,MAC5B,QAAQ,aAAa,MAAM;AAAA,IAC7B,CAAC;AAAA,EACH,EACC,MAAM;AAET,QAAM,cAAc,GAAG,aAAa,EACjC;AAAA,IACC;AAAA,EAEF,EACC,UAAU,SAAS,EACnB,IAAI,WAAW,YAAY,EAC3B,QAAQ,EAAE,OAAO,OAAO,CAAC,EACzB,QAAQ,OAAO,EAAE,QAAQ,MAAM,MAAM,YAAY,aAAa,OAAO,CAAC,CAAC,EACvE,MAAM;AAET,QAAM,YAAY,GAAG,WAAW,EAC7B;AAAA,IACC;AAAA,EAIF,EACC,UAAU,SAAS,EACnB,IAAI,YAAY,EAAE,OAAO,2CAA2C,CAAC,EACrE,QAAQ,EAAE,KAAK,iBAAiB,CAAC,EACjC;AAAA,IAAQ,OAAO,EAAE,SAAS,MACzB,MAAM,UAAU,EAAE,UAAU,aAAa,UAAU,UAAU,EAAE,CAAC;AAAA,EAClE,EACC,MAAM;AAET,QAAM,YAAY,GAAG,WAAW,EAC7B;AAAA,IACC;AAAA,EAGF,EACC,UAAU,SAAS,EACnB,IAAI,YAAY,EAAE,OAAO,eAAe,CAAC,EACzC,IAAI,YAAY,EAAE,OAAO,mBAAmB,EAAE,MAAM,CAAC,EACrD,QAAQ,EAAE,KAAK,iBAAiB,CAAC,EACjC;AAAA,IAAQ,OAAO,EAAE,UAAU,SAAS,MACnC,MAAM,UAAU;AAAA,MACd,UAAU,aAAa,UAAU,UAAU;AAAA,MAC3C,UAAU,kBAAkB,UAAU,UAAU;AAAA,IAClD,CAAC;AAAA,EACH,EACC,MAAM;AAET,QAAM,cAAc,GAAG,aAAa,EACjC;AAAA,IACC;AAAA,EAQF,EACC,UAAU,SAAS,EACnB,IAAI,YAAY,EAAE,OAAO,eAAe,CAAC,EACzC,IAAI,iBAAiB,EAAE,OAAO,gCAAgC,CAAC,EAC/D,IAAI,eAAe,EAAE,OAAO,0CAA0C,EAAE,SAAS,CAAC,EAClF,QAAQ,EAAE,KAAK,mBAAmB,CAAC,EACnC;AAAA,IAAQ,OAAO,EAAE,UAAU,eAAe,YAAY,MACrD,MAAM,YAAY;AAAA,MAChB,UAAU,aAAa,UAAU,UAAU;AAAA,MAC3C,eAAe,YAAY,aAAa;AAAA,MACxC,aAAa,iBAAiB,WAAW;AAAA,IAC3C,CAAC;AAAA,EACH,EACC,MAAM;AAET,QAAM,aAAa,GAAG,YAAY,EAC/B;AAAA,IACC;AAAA,EAKF,EACC,UAAU,SAAS,EACnB,IAAI,YAAY,EAAE,OAAO,eAAe,CAAC,EACzC,IAAI,WAAW,EAAE,OAAO,2BAA2B,CAAC,EACpD,IAAI,iBAAiB,EAAE,OAAO,gCAAgC,CAAC,EAC/D,IAAI,eAAe,EAAE,OAAO,iCAAiC,EAAE,SAAS,CAAC,EACzE,QAAQ,EAAE,KAAK,kBAAkB,CAAC,EAClC;AAAA,IAAQ,OAAO,EAAE,UAAU,SAAS,eAAe,YAAY,MAC9D,MAAM,WAAW;AAAA,MACf,UAAU,aAAa,UAAU,UAAU;AAAA,MAC3C,SAAS,aAAa,SAAS,SAAS;AAAA,MACxC,eAAe,YAAY,aAAa;AAAA,MACxC,aAAa,iBAAiB,WAAW;AAAA,IAC3C,CAAC;AAAA,EACH,EACC,MAAM;AAET,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,0BAA0B,MAMxC;AACA,SAAO;AAAA,IACL,WAAW;AAAA,IACX,OAAO;AAAA,IACP,mBAAmB;AAAA,IACnB,aACE;AAAA,IAEF,WAAW,uBAAuB,IAAI;AAAA,EACxC;AACF;AAIA,SAAS,aAAa,OAAiD;AACrE,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AACrD,UAAM,IAAI,UAAU,kCAAkC,OAAO,KAAK,EAAE;AAAA,EACtE;AACA,QAAMC,KAAI;AACV,QAAM,MAA2B,CAAC;AAClC,MAAI,OAAOA,GAAE,eAAe,UAAW,KAAI,aAAaA,GAAE;AAC1D,MAAI,gBAAgB,uBAAuBA,GAAE,eAAe,eAAe;AAC3E,MAAI,cAAc,uBAAuBA,GAAE,aAAa,aAAa;AACrE,MAAI,cAAc,uBAAuBA,GAAE,aAAa,aAAa;AACrE,MAAI,aAAa,uBAAuBA,GAAE,YAAY,YAAY;AAClE,MAAI,OAAOA,GAAE,qBAAqB,SAAU,KAAI,mBAAmBA,GAAE;AACrE,MAAI,OAAOA,GAAE,sBAAsB,SAAU,KAAI,oBAAoBA,GAAE;AACvE,MAAI,OAAOA,GAAE,kBAAkB,UAAU;AACvC,QAAIA,GAAE,cAAc,WAAW,GAAG;AAChC,YAAM,IAAI,UAAU,uCAAuC;AAAA,IAC7D;AACA,QAAI,gBAAgBA,GAAE;AAAA,EACxB;AACA,SAAO;AACT;AAEA,SAAS,uBAAuB,GAAY,OAAqC;AAC/E,MAAI,MAAM,UAAa,MAAM,KAAM,QAAO;AAC1C,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,OAAM,IAAI,UAAU,GAAG,KAAK,8BAA8B;AACjF,MAAI,EAAE,KAAK,CAAC,MAAM,OAAO,MAAM,QAAQ,GAAG;AACxC,UAAM,IAAI,UAAU,GAAG,KAAK,0BAA0B;AAAA,EACxD;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,QAAQ,KAAK;AACrF,UAAM,IAAI,WAAW,iCAAiC;AAAA,EACxD;AACA,SAAO;AACT;AACA,SAAS,aAAa,QAAqC;AACzD,MAAI,WAAW,OAAW,QAAO;AACjC,MAAI,OAAO,WAAW,YAAY,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,GAAG;AACzE,UAAM,IAAI,WAAW,uCAAuC;AAAA,EAC9D;AACA,SAAO;AACT;AACA,SAAS,YAAY,SAA0B;AAC7C,MAAI,OAAO,YAAY,YAAY,QAAQ,WAAW,GAAG;AACvD,UAAM,IAAI,UAAU,0CAA0C;AAAA,EAChE;AAKA,MAAI,OAAO,SAAS,GAAG;AACvB,SAAO;AACT;AACA,SAAS,iBAAiB,GAAgC;AACxD,MAAI,MAAM,OAAW,QAAO;AAC5B,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK,IAAI,KAAK;AACrE,UAAM,IAAI,WAAW,uCAAuC;AAAA,EAC9D;AACA,SAAO;AACT;AAEA,SAAS,aAAa,GAAY,OAAuB;AACvD,MAAI,OAAO,MAAM,YAAY,EAAE,WAAW,GAAG;AAC3C,UAAM,IAAI,UAAU,GAAG,KAAK,6BAA6B;AAAA,EAC3D;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,GAAY,OAAyB;AAC9D,MAAI,CAAC,MAAM,QAAQ,CAAC,EAAG,OAAM,IAAI,UAAU,GAAG,KAAK,8BAA8B;AACjF,MAAI,EAAE,KAAK,CAAC,MAAM,OAAO,MAAM,QAAQ,GAAG;AACxC,UAAM,IAAI,UAAU,GAAG,KAAK,0BAA0B;AAAA,EACxD;AACA,SAAO;AACT;","names":["f","f"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
summaryTable
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-URWVKRCS.js";
|
|
4
4
|
import {
|
|
5
5
|
VerificationError
|
|
6
6
|
} from "./chunk-3BFEG2F6.js";
|
|
@@ -574,4 +574,4 @@ export {
|
|
|
574
574
|
judgeReplayGate,
|
|
575
575
|
renderReleaseReport
|
|
576
576
|
};
|
|
577
|
-
//# sourceMappingURL=chunk-
|
|
577
|
+
//# sourceMappingURL=chunk-QTMYW64F.js.map
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runCanaries,
|
|
3
3
|
scoreRedTeamOutput
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EGPMSBEZ.js";
|
|
5
5
|
import {
|
|
6
6
|
runCampaign
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2K6UUZ7P.js";
|
|
8
8
|
import {
|
|
9
9
|
detectRewardHacking
|
|
10
10
|
} from "./chunk-5HRORJQY.js";
|
|
11
11
|
import {
|
|
12
12
|
pairedBootstrap
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-56GC6VYO.js";
|
|
14
14
|
|
|
15
15
|
// src/campaign/drivers/evolutionary.ts
|
|
16
16
|
function evolutionaryDriver(opts) {
|
|
@@ -432,4 +432,4 @@ export {
|
|
|
432
432
|
paretoSignificanceGate,
|
|
433
433
|
runEval
|
|
434
434
|
};
|
|
435
|
-
//# sourceMappingURL=chunk-
|
|
435
|
+
//# sourceMappingURL=chunk-SD2YFWQQ.js.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
OtlpFileTraceStore,
|
|
3
3
|
TraceFileMissingError,
|
|
4
4
|
buildTraceAnalystTools
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-QMUEXQJS.js";
|
|
6
6
|
|
|
7
7
|
// src/trace-analyst/prompts.ts
|
|
8
8
|
var TRACE_ANALYST_ACTOR_DESCRIPTION = `You answer questions about an OTLP-shaped JSONL trace dataset using the trace tools provided in the \`traces\` namespace.
|
|
@@ -197,4 +197,4 @@ export {
|
|
|
197
197
|
TRACE_ANALYST_SUBAGENT_DESCRIPTION,
|
|
198
198
|
analyzeTraces
|
|
199
199
|
};
|
|
200
|
-
//# sourceMappingURL=chunk-
|
|
200
|
+
//# sourceMappingURL=chunk-TBDR6PAI.js.map
|
|
@@ -37,12 +37,12 @@ function argHash(args) {
|
|
|
37
37
|
return stableStringify(args);
|
|
38
38
|
}
|
|
39
39
|
function stableStringify(value) {
|
|
40
|
-
if (value ===
|
|
40
|
+
if (value === void 0) return "undefined";
|
|
41
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "undefined";
|
|
41
42
|
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
);
|
|
43
|
+
const obj = value;
|
|
44
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
45
|
+
const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
|
|
46
46
|
return `{${parts.join(",")}}`;
|
|
47
47
|
}
|
|
48
48
|
function aggregateLlm(spans) {
|
|
@@ -51,9 +51,12 @@ function aggregateLlm(spans) {
|
|
|
51
51
|
inputTokens: acc.inputTokens + (s.inputTokens ?? 0),
|
|
52
52
|
outputTokens: acc.outputTokens + (s.outputTokens ?? 0),
|
|
53
53
|
cachedTokens: acc.cachedTokens + (s.cachedTokens ?? 0),
|
|
54
|
+
// reasoningTokens is on LlmSpan but was omitted here — reasoning usage was
|
|
55
|
+
// invisible to any cost/perf analysis reading this aggregate.
|
|
56
|
+
reasoningTokens: acc.reasoningTokens + (s.reasoningTokens ?? 0),
|
|
54
57
|
costUsd: acc.costUsd + (s.costUsd ?? 0)
|
|
55
58
|
}),
|
|
56
|
-
{ inputTokens: 0, outputTokens: 0, cachedTokens: 0, costUsd: 0 }
|
|
59
|
+
{ inputTokens: 0, outputTokens: 0, cachedTokens: 0, reasoningTokens: 0, costUsd: 0 }
|
|
57
60
|
);
|
|
58
61
|
}
|
|
59
62
|
function runFailureClass(run) {
|
|
@@ -73,4 +76,4 @@ export {
|
|
|
73
76
|
aggregateLlm,
|
|
74
77
|
runFailureClass
|
|
75
78
|
};
|
|
76
|
-
//# sourceMappingURL=chunk-
|
|
79
|
+
//# sourceMappingURL=chunk-UFSG7ACU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/trace/query.ts"],"sourcesContent":["/**\n * Typed query helpers over TraceStore.\n *\n * Not a full SQL engine — a minimal, composable set of operators that\n * cover the canned-pipeline use cases. For ad-hoc analytics, persist to\n * NDJSON and point DuckDB at it; the schema is stable so external SQL\n * tooling works out of the box.\n */\n\nimport type { FailureClass, JudgeSpan, LlmSpan, Run, ToolSpan } from './schema'\nimport { isJudgeSpan, isLlmSpan, isToolSpan } from './schema'\nimport type { TraceStore } from './store'\n\nexport async function runsForScenario(store: TraceStore, scenarioId: string): Promise<Run[]> {\n return store.listRuns({ scenarioId })\n}\n\nexport async function llmSpans(store: TraceStore, runId?: string): Promise<LlmSpan[]> {\n const spans = await store.spans({ runId, kind: 'llm' })\n return spans.filter(isLlmSpan)\n}\n\nexport async function toolSpans(\n store: TraceStore,\n runId?: string,\n toolName?: string,\n): Promise<ToolSpan[]> {\n const spans = await store.spans({ runId, kind: 'tool', toolName })\n return spans.filter(isToolSpan)\n}\n\nexport async function judgeSpans(store: TraceStore, runId?: string): Promise<JudgeSpan[]> {\n const spans = await store.spans({ runId, kind: 'judge' })\n return spans.filter(isJudgeSpan)\n}\n\n/** Group spans by any key selector. */\nexport function groupBy<T, K extends string | number>(items: T[], key: (t: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>()\n for (const item of items) {\n const k = key(item)\n let bucket = map.get(k)\n if (!bucket) {\n bucket = []\n map.set(k, bucket)\n }\n bucket.push(item)\n }\n return map\n}\n\n/** Hash tool arguments to an orderless-key-stable string for de-duplication. */\nexport function argHash(args: unknown): string {\n return stableStringify(args)\n}\n\nfunction stableStringify(value: unknown): string {\n // Must ALWAYS return a string: JSON.stringify(undefined) — and stringify of\n // functions/symbols — yields the JS value `undefined`, which would make\n // argHash return a non-string and silently break the de-dup keys behind\n // stuck-loop and failure-cluster.\n if (value === undefined) return 'undefined'\n if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'undefined'\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`\n // Drop undefined-valued keys to match JSON.stringify semantics, so\n // `{a:1}` and `{a:1,b:undefined}` hash identically.\n const obj = value as Record<string, unknown>\n const keys = Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort()\n const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`)\n return `{${parts.join(',')}}`\n}\n\n/** Sum an LLM-span array into aggregate token + cost. */\nexport function aggregateLlm(spans: LlmSpan[]): {\n inputTokens: number\n outputTokens: number\n cachedTokens: number\n reasoningTokens: number\n costUsd: number\n} {\n return spans.reduce(\n (acc, s) => ({\n inputTokens: acc.inputTokens + (s.inputTokens ?? 0),\n outputTokens: acc.outputTokens + (s.outputTokens ?? 0),\n cachedTokens: acc.cachedTokens + (s.cachedTokens ?? 0),\n // reasoningTokens is on LlmSpan but was omitted here — reasoning usage was\n // invisible to any cost/perf analysis reading this aggregate.\n reasoningTokens: acc.reasoningTokens + (s.reasoningTokens ?? 0),\n costUsd: acc.costUsd + (s.costUsd ?? 0),\n }),\n { inputTokens: 0, outputTokens: 0, cachedTokens: 0, reasoningTokens: 0, costUsd: 0 },\n )\n}\n\n/** Pick the outcome's failure class when present, else derive 'success' from run status. */\nexport function runFailureClass(run: Run): FailureClass {\n if (run.outcome?.failureClass) return run.outcome.failureClass\n if (run.status === 'completed' && run.outcome?.pass !== false) return 'success'\n if (run.status === 'aborted') return 'budget_exceeded'\n return 'unknown'\n}\n"],"mappings":";;;;;;;AAaA,eAAsB,gBAAgB,OAAmB,YAAoC;AAC3F,SAAO,MAAM,SAAS,EAAE,WAAW,CAAC;AACtC;AAEA,eAAsB,SAAS,OAAmB,OAAoC;AACpF,QAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM,MAAM,CAAC;AACtD,SAAO,MAAM,OAAO,SAAS;AAC/B;AAEA,eAAsB,UACpB,OACA,OACA,UACqB;AACrB,QAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM,QAAQ,SAAS,CAAC;AACjE,SAAO,MAAM,OAAO,UAAU;AAChC;AAEA,eAAsB,WAAW,OAAmB,OAAsC;AACxF,QAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM,QAAQ,CAAC;AACxD,SAAO,MAAM,OAAO,WAAW;AACjC;AAGO,SAAS,QAAsC,OAAY,KAA+B;AAC/F,QAAM,MAAM,oBAAI,IAAY;AAC5B,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,IAAI,IAAI;AAClB,QAAI,SAAS,IAAI,IAAI,CAAC;AACtB,QAAI,CAAC,QAAQ;AACX,eAAS,CAAC;AACV,UAAI,IAAI,GAAG,MAAM;AAAA,IACnB;AACA,WAAO,KAAK,IAAI;AAAA,EAClB;AACA,SAAO;AACT;AAGO,SAAS,QAAQ,MAAuB;AAC7C,SAAO,gBAAgB,IAAI;AAC7B;AAEA,SAAS,gBAAgB,OAAwB;AAK/C,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,KAAK,KAAK;AACjF,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,IAAI,MAAM,IAAI,eAAe,EAAE,KAAK,GAAG,CAAC;AAGzE,QAAM,MAAM;AACZ,QAAM,OAAO,OAAO,KAAK,GAAG,EACzB,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,MAAS,EAClC,KAAK;AACR,QAAM,QAAQ,KAAK,IAAI,CAAC,MAAM,GAAG,KAAK,UAAU,CAAC,CAAC,IAAI,gBAAgB,IAAI,CAAC,CAAC,CAAC,EAAE;AAC/E,SAAO,IAAI,MAAM,KAAK,GAAG,CAAC;AAC5B;AAGO,SAAS,aAAa,OAM3B;AACA,SAAO,MAAM;AAAA,IACX,CAAC,KAAK,OAAO;AAAA,MACX,aAAa,IAAI,eAAe,EAAE,eAAe;AAAA,MACjD,cAAc,IAAI,gBAAgB,EAAE,gBAAgB;AAAA,MACpD,cAAc,IAAI,gBAAgB,EAAE,gBAAgB;AAAA;AAAA;AAAA,MAGpD,iBAAiB,IAAI,mBAAmB,EAAE,mBAAmB;AAAA,MAC7D,SAAS,IAAI,WAAW,EAAE,WAAW;AAAA,IACvC;AAAA,IACA,EAAE,aAAa,GAAG,cAAc,GAAG,cAAc,GAAG,iBAAiB,GAAG,SAAS,EAAE;AAAA,EACrF;AACF;AAGO,SAAS,gBAAgB,KAAwB;AACtD,MAAI,IAAI,SAAS,aAAc,QAAO,IAAI,QAAQ;AAClD,MAAI,IAAI,WAAW,eAAe,IAAI,SAAS,SAAS,MAAO,QAAO;AACtE,MAAI,IAAI,WAAW,UAAW,QAAO;AACrC,SAAO;AACT;","names":[]}
|