agent-inspect 5.0.0 → 5.2.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.
@@ -3279,7 +3279,7 @@ function buildRunSummary(events) {
3279
3279
  }
3280
3280
  let totalSteps = 0;
3281
3281
  let llmSteps = 0;
3282
- let toolSteps = 0;
3282
+ let toolSteps2 = 0;
3283
3283
  let logicSteps = 0;
3284
3284
  let errorSteps = 0;
3285
3285
  let maxDepth = 0;
@@ -3309,7 +3309,7 @@ function buildRunSummary(events) {
3309
3309
  for (const [id, s] of steps.entries()) {
3310
3310
  totalSteps += 1;
3311
3311
  if (s.type === "llm") llmSteps += 1;
3312
- else if (s.type === "tool") toolSteps += 1;
3312
+ else if (s.type === "tool") toolSteps2 += 1;
3313
3313
  else logicSteps += 1;
3314
3314
  if (s.status === "error") errorSteps += 1;
3315
3315
  const depth = computeDepth(id);
@@ -3343,7 +3343,7 @@ function buildRunSummary(events) {
3343
3343
  durationMs: durationMs2,
3344
3344
  totalSteps,
3345
3345
  llmSteps,
3346
- toolSteps,
3346
+ toolSteps: toolSteps2,
3347
3347
  logicSteps,
3348
3348
  errorSteps,
3349
3349
  maxDepth,
@@ -4086,9 +4086,9 @@ async function searchTraces(metas, options) {
4086
4086
  }
4087
4087
  const limit = options.limit ?? 50;
4088
4088
  const sessionId = options.session?.trim();
4089
- const observationStatus = parseObservationFilter(options.observation);
4089
+ const observationStatus2 = parseObservationFilter(options.observation);
4090
4090
  const hasContentFilter = Boolean(
4091
- options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus
4091
+ options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus2
4092
4092
  );
4093
4093
  const results = [];
4094
4094
  const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
@@ -4133,9 +4133,9 @@ async function searchTraces(metas, options) {
4133
4133
  statusFilter: options.status
4134
4134
  });
4135
4135
  results.push(...stepMatches);
4136
- if (observationStatus) {
4136
+ if (observationStatus2) {
4137
4137
  const outcomes = extractOutcomesFromTraceEvents(events);
4138
- const matched = outcomes.filter((outcome) => outcome.status === observationStatus);
4138
+ const matched = outcomes.filter((outcome) => outcome.status === observationStatus2);
4139
4139
  for (const outcome of matched) {
4140
4140
  results.push({
4141
4141
  runId: m.runId,
@@ -4971,12 +4971,12 @@ function buildCriticalPath(runs, handoffs) {
4971
4971
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
4972
4972
  );
4973
4973
  const ordered = [...runs].sort(compareRuns);
4974
- const path11 = [];
4974
+ const path12 = [];
4975
4975
  const visited = /* @__PURE__ */ new Set();
4976
4976
  const pushRun = (run, confidence, source) => {
4977
4977
  if (visited.has(run.runId)) return;
4978
4978
  visited.add(run.runId);
4979
- path11.push({
4979
+ path12.push({
4980
4980
  runId: run.runId,
4981
4981
  name: run.name,
4982
4982
  startedAt: run.startedAt,
@@ -5001,7 +5001,7 @@ function buildCriticalPath(runs, handoffs) {
5001
5001
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
5002
5002
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
5003
5003
  }
5004
- return path11;
5004
+ return path12;
5005
5005
  }
5006
5006
  function metaRunIdMatches(run, token, runById) {
5007
5007
  const meta = extractSessionWorkflowMetadata(run.metadata);
@@ -5824,7 +5824,7 @@ function stripPrefix(name, prefixes) {
5824
5824
  }
5825
5825
  return name;
5826
5826
  }
5827
- function eventEvidence(event, path11) {
5827
+ function eventEvidence(event, path12) {
5828
5828
  return {
5829
5829
  runId: event.runId,
5830
5830
  eventId: event.eventId,
@@ -5834,7 +5834,7 @@ function eventEvidence(event, path11) {
5834
5834
  kind: event.kind,
5835
5835
  name: event.name,
5836
5836
  status: event.status,
5837
- ...path11 ? { path: path11 } : {}
5837
+ ...path12 ? { path: path12 } : {}
5838
5838
  };
5839
5839
  }
5840
5840
  function runEvidence(run) {
@@ -8446,6 +8446,923 @@ function renderSuiteReport(result, options = {}) {
8446
8446
  return renderSuiteReportMarkdown(result);
8447
8447
  }
8448
8448
 
8449
+ // packages/core/src/cohort/types.ts
8450
+ var COHORT_METRIC_IDS = [
8451
+ "errorRate",
8452
+ "duration",
8453
+ "toolChoice",
8454
+ "toolOrdering",
8455
+ "llmCallCount",
8456
+ "tokenUsage",
8457
+ "retryCount",
8458
+ "observationFailure",
8459
+ "guardrailFailure",
8460
+ "circuitViolation",
8461
+ "redactionWarning"
8462
+ ];
8463
+
8464
+ // packages/core/src/cohort/compare.ts
8465
+ function compareNumber(metric, label, baseline, candidate, higherIsWorse = true) {
8466
+ if (baseline === void 0 && candidate === void 0) return void 0;
8467
+ const delta = baseline !== void 0 && candidate !== void 0 ? candidate - baseline : void 0;
8468
+ const regression = delta !== void 0 && (higherIsWorse && delta > 0 || !higherIsWorse && delta < 0);
8469
+ return {
8470
+ metric,
8471
+ baseline,
8472
+ candidate,
8473
+ delta,
8474
+ regression,
8475
+ message: `${label}: baseline=${baseline ?? "n/a"} candidate=${candidate ?? "n/a"}${delta !== void 0 ? ` (delta ${delta})` : ""}`
8476
+ };
8477
+ }
8478
+ function pickAggregate(groups, cohortLabel, groupKey) {
8479
+ return groups.find(
8480
+ (group) => group.cohortLabel === cohortLabel && (groupKey === void 0 || group.groupKey === groupKey)
8481
+ );
8482
+ }
8483
+ function compareCohortAggregates(groups, options) {
8484
+ const baselineAgg = pickAggregate(groups, options.baseline, options.groupKey);
8485
+ const candidateAgg = pickAggregate(groups, options.candidate, options.groupKey);
8486
+ const comparisons = [];
8487
+ for (const metric of options.metrics) {
8488
+ switch (metric) {
8489
+ case "errorRate": {
8490
+ const item = compareNumber(
8491
+ metric,
8492
+ "Error rate",
8493
+ baselineAgg?.errorRate,
8494
+ candidateAgg?.errorRate
8495
+ );
8496
+ if (item) comparisons.push(item);
8497
+ break;
8498
+ }
8499
+ case "duration": {
8500
+ const item = compareNumber(
8501
+ metric,
8502
+ "Average duration (ms)",
8503
+ baselineAgg?.avgDurationMs,
8504
+ candidateAgg?.avgDurationMs
8505
+ );
8506
+ if (item) comparisons.push(item);
8507
+ break;
8508
+ }
8509
+ case "llmCallCount": {
8510
+ const item = compareNumber(
8511
+ metric,
8512
+ "Average LLM calls",
8513
+ baselineAgg?.avgLlmCallCount,
8514
+ candidateAgg?.avgLlmCallCount
8515
+ );
8516
+ if (item) comparisons.push(item);
8517
+ break;
8518
+ }
8519
+ case "tokenUsage": {
8520
+ const item = compareNumber(
8521
+ metric,
8522
+ "Average token usage",
8523
+ baselineAgg?.avgTokenUsage,
8524
+ candidateAgg?.avgTokenUsage
8525
+ );
8526
+ if (item) comparisons.push(item);
8527
+ break;
8528
+ }
8529
+ case "retryCount": {
8530
+ const item = compareNumber(
8531
+ metric,
8532
+ "Average retries",
8533
+ baselineAgg?.avgRetryCount,
8534
+ candidateAgg?.avgRetryCount
8535
+ );
8536
+ if (item) comparisons.push(item);
8537
+ break;
8538
+ }
8539
+ case "observationFailure": {
8540
+ const item = compareNumber(
8541
+ metric,
8542
+ "Observation failure rate",
8543
+ baselineAgg?.observationFailureRate,
8544
+ candidateAgg?.observationFailureRate
8545
+ );
8546
+ if (item) comparisons.push(item);
8547
+ break;
8548
+ }
8549
+ case "toolChoice": {
8550
+ const baselineValue = baselineAgg?.dominantToolChoice;
8551
+ const candidateValue = candidateAgg?.dominantToolChoice;
8552
+ comparisons.push({
8553
+ metric,
8554
+ baseline: baselineValue,
8555
+ candidate: candidateValue,
8556
+ delta: baselineValue === candidateValue ? "same" : "changed",
8557
+ regression: baselineValue !== candidateValue,
8558
+ message: `Tool choice: baseline=${baselineValue ?? "n/a"} candidate=${candidateValue ?? "n/a"}`
8559
+ });
8560
+ break;
8561
+ }
8562
+ case "toolOrdering": {
8563
+ const baselineValue = baselineAgg?.toolOrderingSignature;
8564
+ const candidateValue = candidateAgg?.toolOrderingSignature;
8565
+ comparisons.push({
8566
+ metric,
8567
+ baseline: baselineValue,
8568
+ candidate: candidateValue,
8569
+ delta: baselineValue === candidateValue ? "same" : "changed",
8570
+ regression: baselineValue !== candidateValue,
8571
+ message: `Tool ordering: baseline=${baselineValue ?? "n/a"} candidate=${candidateValue ?? "n/a"}`
8572
+ });
8573
+ break;
8574
+ }
8575
+ case "guardrailFailure": {
8576
+ const item = compareNumber(
8577
+ metric,
8578
+ "Guardrail failures",
8579
+ baselineAgg?.avgGuardrailFailures,
8580
+ candidateAgg?.avgGuardrailFailures
8581
+ );
8582
+ if (item) comparisons.push(item);
8583
+ break;
8584
+ }
8585
+ case "circuitViolation": {
8586
+ const item = compareNumber(
8587
+ metric,
8588
+ "Circuit violations",
8589
+ baselineAgg?.avgCircuitViolations,
8590
+ candidateAgg?.avgCircuitViolations
8591
+ );
8592
+ if (item) comparisons.push(item);
8593
+ break;
8594
+ }
8595
+ case "redactionWarning": {
8596
+ const item = compareNumber(
8597
+ metric,
8598
+ "Redaction warnings",
8599
+ baselineAgg?.avgRedactionWarnings,
8600
+ candidateAgg?.avgRedactionWarnings
8601
+ );
8602
+ if (item) comparisons.push(item);
8603
+ break;
8604
+ }
8605
+ }
8606
+ }
8607
+ return comparisons;
8608
+ }
8609
+
8610
+ // packages/core/src/cohort/grouping.ts
8611
+ function parseCohortMetricList(value) {
8612
+ if (value === void 0 || value.trim() === "") return [];
8613
+ return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
8614
+ }
8615
+ function parseGroupBySpec(groupBy) {
8616
+ const raw = (groupBy ?? "model").trim();
8617
+ if (raw === "model") return { kind: "model" };
8618
+ if (raw === "session") return { kind: "session" };
8619
+ if (raw === "group") return { kind: "group" };
8620
+ if (raw.startsWith("metadata.")) {
8621
+ const metadataKey = raw.slice("metadata.".length).trim();
8622
+ if (metadataKey === "") throw new Error("metadata group-by requires a key.");
8623
+ return { kind: "metadata", metadataKey };
8624
+ }
8625
+ throw new Error(`Unsupported --group-by value: ${raw}`);
8626
+ }
8627
+ function metadataString(metadata, key) {
8628
+ const value = metadata?.[key];
8629
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
8630
+ }
8631
+ function resolveRunGroupKey(run, groupBy) {
8632
+ const metadata = run.metadata ?? {};
8633
+ switch (groupBy.kind) {
8634
+ case "model":
8635
+ return metadataString(metadata, "model") ?? "unknown";
8636
+ case "session":
8637
+ return extractSessionWorkflowMetadata(metadata)?.sessionId ?? metadataString(metadata, "sessionId") ?? "__unscoped__";
8638
+ case "group":
8639
+ return extractSessionWorkflowMetadata(metadata)?.groupId ?? metadataString(metadata, "groupId") ?? "__unscoped__";
8640
+ case "metadata":
8641
+ return metadataString(metadata, groupBy.metadataKey) ?? "__missing__";
8642
+ default:
8643
+ return "unknown";
8644
+ }
8645
+ }
8646
+ function resolveCohortLabel(run, cohortKey, baseline, candidate) {
8647
+ const label = metadataString(run.metadata, cohortKey);
8648
+ if (label === void 0) return void 0;
8649
+ if (baseline !== void 0 && label === baseline) return baseline;
8650
+ if (candidate !== void 0 && label === candidate) return candidate;
8651
+ if (baseline === void 0 && candidate === void 0) return label;
8652
+ return void 0;
8653
+ }
8654
+ function filterRunsForCohort(runs, options) {
8655
+ const warnings = [];
8656
+ if (options.baseline === void 0 && options.candidate === void 0) {
8657
+ return { runs: [...runs], warnings };
8658
+ }
8659
+ const selected = [];
8660
+ for (const run of runs) {
8661
+ const label = resolveCohortLabel(
8662
+ run,
8663
+ options.cohortKey,
8664
+ options.baseline,
8665
+ options.candidate
8666
+ );
8667
+ if (label !== void 0) selected.push(run);
8668
+ }
8669
+ if (selected.length === 0) {
8670
+ warnings.push(
8671
+ `No runs matched baseline/candidate labels on metadata.${options.cohortKey}.`
8672
+ );
8673
+ }
8674
+ return { runs: selected, warnings };
8675
+ }
8676
+
8677
+ // packages/core/src/cohort/metrics.ts
8678
+ function asNumber(value) {
8679
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8680
+ }
8681
+ function toolSteps(events) {
8682
+ const ordering = [];
8683
+ const choices = /* @__PURE__ */ new Set();
8684
+ const sorted = [...events].filter((event) => event.event === "step_started").sort((a, b) => a.timestamp - b.timestamp);
8685
+ for (const event of sorted) {
8686
+ const step = event;
8687
+ if (step.type !== "tool") continue;
8688
+ const name = typeof step.metadata?.toolName === "string" ? step.metadata.toolName : step.name;
8689
+ ordering.push(name);
8690
+ choices.add(name);
8691
+ }
8692
+ return { choices: [...choices].sort(), ordering };
8693
+ }
8694
+ async function computeCohortRunMetrics(input) {
8695
+ const events = await readTraceEventsFromFile(input.filePath);
8696
+ const summary = buildRunSummary(events);
8697
+ const tools = toolSteps(events);
8698
+ const outcomes = extractOutcomesFromTraceEvents(events);
8699
+ const observationFailures = outcomes.filter((item) => item.status === "failed").length;
8700
+ const metadata = input.metadata ?? {};
8701
+ const retryCount = asNumber(metadata.attempt) !== void 0 && asNumber(metadata.attempt) > 1 ? asNumber(metadata.attempt) - 1 : typeof metadata.retryOf === "string" ? 1 : 0;
8702
+ return {
8703
+ runId: input.runId,
8704
+ ...input.cohortLabel !== void 0 ? { cohortLabel: input.cohortLabel } : {},
8705
+ groupKey: input.groupKey,
8706
+ status: summary.status,
8707
+ error: summary.status === "error",
8708
+ durationMs: summary.durationMs ?? input.durationMs,
8709
+ llmCallCount: summary.llmSteps,
8710
+ tokenUsageTotal: summary.totalTokens?.total,
8711
+ retryCount,
8712
+ observationFailures,
8713
+ guardrailFailures: asNumber(metadata.guardrailFailures) ?? 0,
8714
+ circuitViolations: asNumber(metadata.circuitViolations) ?? 0,
8715
+ redactionWarnings: asNumber(metadata.redactionWarnings) ?? 0,
8716
+ toolChoices: tools.choices,
8717
+ toolOrdering: tools.ordering
8718
+ };
8719
+ }
8720
+ function percentile2(values, p) {
8721
+ if (values.length === 0) return void 0;
8722
+ const sorted = [...values].sort((a, b) => a - b);
8723
+ const idx = Math.min(
8724
+ sorted.length - 1,
8725
+ Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
8726
+ );
8727
+ return sorted[idx];
8728
+ }
8729
+ function dominantToolChoice(runs) {
8730
+ const counts = /* @__PURE__ */ new Map();
8731
+ for (const run of runs) {
8732
+ const signature = run.toolChoices.join(",");
8733
+ if (signature === "") continue;
8734
+ counts.set(signature, (counts.get(signature) ?? 0) + 1);
8735
+ }
8736
+ let best;
8737
+ let bestCount = 0;
8738
+ for (const [key, count] of counts) {
8739
+ if (count > bestCount) {
8740
+ best = key;
8741
+ bestCount = count;
8742
+ }
8743
+ }
8744
+ return best;
8745
+ }
8746
+ function orderingSignature(runs) {
8747
+ const counts = /* @__PURE__ */ new Map();
8748
+ for (const run of runs) {
8749
+ const signature = run.toolOrdering.join(">");
8750
+ if (signature === "") continue;
8751
+ counts.set(signature, (counts.get(signature) ?? 0) + 1);
8752
+ }
8753
+ let best;
8754
+ let bestCount = 0;
8755
+ for (const [key, count] of counts) {
8756
+ if (count > bestCount) {
8757
+ best = key;
8758
+ bestCount = count;
8759
+ }
8760
+ }
8761
+ return best;
8762
+ }
8763
+ function aggregateCohortMetrics(runs, groupKey, cohortLabel) {
8764
+ const durations = runs.map((run) => run.durationMs).filter((value) => typeof value === "number");
8765
+ const tokenValues = runs.map((run) => run.tokenUsageTotal).filter((value) => typeof value === "number");
8766
+ const errors = runs.filter((run) => run.error).length;
8767
+ const observationFailures = runs.reduce((sum, run) => sum + run.observationFailures, 0);
8768
+ return {
8769
+ groupKey,
8770
+ ...cohortLabel !== void 0 ? { cohortLabel } : {},
8771
+ runCount: runs.length,
8772
+ errorRate: runs.length > 0 ? errors / runs.length : 0,
8773
+ avgDurationMs: durations.length > 0 ? durations.reduce((sum, value) => sum + value, 0) / durations.length : void 0,
8774
+ p95DurationMs: percentile2(durations, 95),
8775
+ avgLlmCallCount: runs.length > 0 ? runs.reduce((sum, run) => sum + run.llmCallCount, 0) / runs.length : 0,
8776
+ avgTokenUsage: tokenValues.length > 0 ? tokenValues.reduce((sum, value) => sum + value, 0) / tokenValues.length : void 0,
8777
+ avgRetryCount: runs.length > 0 ? runs.reduce((sum, run) => sum + run.retryCount, 0) / runs.length : 0,
8778
+ observationFailureRate: runs.length > 0 ? observationFailures / runs.length : 0,
8779
+ avgGuardrailFailures: runs.length > 0 ? runs.reduce((sum, run) => sum + run.guardrailFailures, 0) / runs.length : 0,
8780
+ avgCircuitViolations: runs.length > 0 ? runs.reduce((sum, run) => sum + run.circuitViolations, 0) / runs.length : 0,
8781
+ avgRedactionWarnings: runs.length > 0 ? runs.reduce((sum, run) => sum + run.redactionWarnings, 0) / runs.length : 0,
8782
+ dominantToolChoice: dominantToolChoice(runs),
8783
+ toolOrderingSignature: orderingSignature(runs)
8784
+ };
8785
+ }
8786
+
8787
+ // packages/core/src/cohort/analyze.ts
8788
+ var DEFAULT_METRICS = [
8789
+ "errorRate",
8790
+ "duration",
8791
+ "toolChoice",
8792
+ "observationFailure"
8793
+ ];
8794
+ function normalizeMetrics(metrics) {
8795
+ if (metrics === void 0 || metrics.length === 0) return [...DEFAULT_METRICS];
8796
+ const allowed = new Set(COHORT_METRIC_IDS);
8797
+ return metrics.filter((metric) => allowed.has(metric));
8798
+ }
8799
+ async function analyzeCohort(runsInput, options) {
8800
+ const cohortKey = options.cohortKey ?? "cohort";
8801
+ const groupBySpec = parseGroupBySpec(options.groupBy);
8802
+ const metrics = normalizeMetrics(options.metrics);
8803
+ const { runs: filteredRuns, warnings } = filterRunsForCohort(runsInput, {
8804
+ cohortKey,
8805
+ baseline: options.baseline,
8806
+ candidate: options.candidate
8807
+ });
8808
+ const runMetrics = [];
8809
+ for (const run of filteredRuns) {
8810
+ if (run.filePath === void 0) continue;
8811
+ const cohortLabel = resolveCohortLabel(
8812
+ run,
8813
+ cohortKey,
8814
+ options.baseline,
8815
+ options.candidate
8816
+ );
8817
+ runMetrics.push(
8818
+ await computeCohortRunMetrics({
8819
+ runId: run.runId,
8820
+ filePath: run.filePath,
8821
+ metadata: run.metadata,
8822
+ status: run.status,
8823
+ durationMs: run.durationMs,
8824
+ groupKey: resolveRunGroupKey(run, groupBySpec),
8825
+ cohortLabel
8826
+ })
8827
+ );
8828
+ }
8829
+ const groupMap = /* @__PURE__ */ new Map();
8830
+ for (const run of runMetrics) {
8831
+ const key = `${run.cohortLabel ?? "*"}::${run.groupKey}`;
8832
+ const bucket = groupMap.get(key) ?? [];
8833
+ bucket.push(run);
8834
+ groupMap.set(key, bucket);
8835
+ }
8836
+ const groups = [...groupMap.entries()].sort(([a], [b]) => a.localeCompare(b)).map(
8837
+ ([, bucket]) => aggregateCohortMetrics(
8838
+ bucket,
8839
+ bucket[0].groupKey,
8840
+ bucket[0]?.cohortLabel
8841
+ )
8842
+ );
8843
+ const comparisons = options.baseline !== void 0 && options.candidate !== void 0 ? (() => {
8844
+ const groupKeys = [
8845
+ ...new Set(groups.map((group) => group.groupKey))
8846
+ ].sort((a, b) => a.localeCompare(b));
8847
+ const items = [];
8848
+ for (const groupKey of groupKeys) {
8849
+ items.push(
8850
+ ...compareCohortAggregates(groups, {
8851
+ baseline: options.baseline,
8852
+ candidate: options.candidate,
8853
+ metrics,
8854
+ groupKey
8855
+ })
8856
+ );
8857
+ }
8858
+ return items;
8859
+ })() : [];
8860
+ const regression = comparisons.some((item) => item.regression);
8861
+ return {
8862
+ ok: !regression,
8863
+ traceDir: options.traceDir,
8864
+ ...options.baseline !== void 0 ? { baseline: options.baseline } : {},
8865
+ ...options.candidate !== void 0 ? { candidate: options.candidate } : {},
8866
+ cohortKey,
8867
+ groupBy: options.groupBy ?? "model",
8868
+ metrics,
8869
+ groups,
8870
+ comparisons,
8871
+ runs: runMetrics,
8872
+ warnings
8873
+ };
8874
+ }
8875
+
8876
+ // packages/core/src/exporters/helpers.ts
8877
+ function escapeHtml(value) {
8878
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
8879
+ }
8880
+
8881
+ // packages/core/src/cohort/render.ts
8882
+ function formatRate(value) {
8883
+ if (value === void 0) return "n/a";
8884
+ return `${(value * 100).toFixed(1)}%`;
8885
+ }
8886
+ function renderCohortSummaryMarkdown(result) {
8887
+ const lines = [];
8888
+ lines.push("# Cohort analysis");
8889
+ lines.push("");
8890
+ lines.push(`Trace directory: \`${result.traceDir}\``);
8891
+ lines.push(`Group by: \`${result.groupBy}\``);
8892
+ if (result.baseline !== void 0 && result.candidate !== void 0) {
8893
+ lines.push(
8894
+ `Baseline/Candidate key: \`${result.cohortKey}\` (${result.baseline} vs ${result.candidate})`
8895
+ );
8896
+ }
8897
+ lines.push(`Status: **${result.ok ? "PASS" : "REGRESSION"}**`);
8898
+ lines.push("");
8899
+ if (result.warnings.length > 0) {
8900
+ lines.push("## Warnings");
8901
+ for (const warning of result.warnings) lines.push(`- ${warning}`);
8902
+ lines.push("");
8903
+ }
8904
+ lines.push("## Groups");
8905
+ for (const group of result.groups) {
8906
+ lines.push(
8907
+ `### ${group.cohortLabel ?? "all"} / ${group.groupKey} (${group.runCount} runs)`
8908
+ );
8909
+ lines.push(`- Error rate: ${formatRate(group.errorRate)}`);
8910
+ if (group.avgDurationMs !== void 0) {
8911
+ lines.push(`- Avg duration: ${Math.round(group.avgDurationMs)} ms`);
8912
+ }
8913
+ if (group.dominantToolChoice !== void 0) {
8914
+ lines.push(`- Dominant tools: ${group.dominantToolChoice}`);
8915
+ }
8916
+ lines.push(
8917
+ `- Observation failure rate: ${formatRate(group.observationFailureRate)}`
8918
+ );
8919
+ lines.push("");
8920
+ }
8921
+ if (result.comparisons.length > 0) {
8922
+ lines.push("## Comparisons");
8923
+ for (const comparison of result.comparisons) {
8924
+ const flag = comparison.regression ? " **REGRESSION**" : "";
8925
+ lines.push(`- ${comparison.message}${flag}`);
8926
+ }
8927
+ lines.push("");
8928
+ }
8929
+ return lines.join("\n").trimEnd();
8930
+ }
8931
+ function renderCohortReportHtml(result) {
8932
+ const rows = result.groups.map(
8933
+ (group) => `<tr><td>${escapeHtml(group.cohortLabel ?? "all")}</td><td>${escapeHtml(group.groupKey)}</td><td>${group.runCount}</td><td>${escapeHtml(formatRate(group.errorRate))}</td><td>${group.avgDurationMs !== void 0 ? Math.round(group.avgDurationMs) : "n/a"}</td></tr>`
8934
+ ).join("");
8935
+ const comparisons = result.comparisons.map(
8936
+ (item) => `<li>${escapeHtml(item.message)}${item.regression ? " <strong>REGRESSION</strong>" : ""}</li>`
8937
+ ).join("");
8938
+ return `<!DOCTYPE html>
8939
+ <html lang="en">
8940
+ <head>
8941
+ <meta charset="utf-8" />
8942
+ <title>Cohort report</title>
8943
+ <style>
8944
+ body { font-family: system-ui, sans-serif; margin: 2rem; }
8945
+ table { border-collapse: collapse; width: 100%; }
8946
+ th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
8947
+ th { background: #f6f6f6; }
8948
+ </style>
8949
+ </head>
8950
+ <body>
8951
+ <h1>Cohort analysis</h1>
8952
+ <p>Status: <strong>${result.ok ? "PASS" : "REGRESSION"}</strong></p>
8953
+ <p>Trace directory: <code>${escapeHtml(result.traceDir)}</code></p>
8954
+ <h2>Groups</h2>
8955
+ <table>
8956
+ <thead><tr><th>Cohort</th><th>Group</th><th>Runs</th><th>Error rate</th><th>Avg duration (ms)</th></tr></thead>
8957
+ <tbody>${rows}</tbody>
8958
+ </table>
8959
+ ${result.comparisons.length > 0 ? `<h2>Comparisons</h2><ul>${comparisons}</ul>` : ""}
8960
+ </body>
8961
+ </html>`;
8962
+ }
8963
+ function renderCohortReport(result, options = {}) {
8964
+ const format = options.format ?? "markdown";
8965
+ if (format === "json") return JSON.stringify(result, null, 2);
8966
+ if (format === "html") return renderCohortReportHtml(result);
8967
+ return renderCohortSummaryMarkdown(result);
8968
+ }
8969
+
8970
+ // packages/core/src/gate/parse.ts
8971
+ function parseGateList(value) {
8972
+ if (value === void 0 || value.trim() === "") return [];
8973
+ return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
8974
+ }
8975
+ function parseGateNumber(value, label) {
8976
+ if (value === void 0 || value.trim() === "") return void 0;
8977
+ const parsed = Number(value);
8978
+ if (!Number.isFinite(parsed)) {
8979
+ throw new Error(`Invalid ${label}: ${value}`);
8980
+ }
8981
+ return parsed;
8982
+ }
8983
+
8984
+ // packages/core/src/gate/evaluate.ts
8985
+ function percentile3(values, p) {
8986
+ if (values.length === 0) return void 0;
8987
+ const sorted = [...values].sort((a, b) => a - b);
8988
+ const idx = Math.min(
8989
+ sorted.length - 1,
8990
+ Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
8991
+ );
8992
+ return sorted[idx];
8993
+ }
8994
+ function hasThresholds(options) {
8995
+ return options.maxErrorRate !== void 0 || options.maxP95DurationMs !== void 0 || (options.forbidTools?.length ?? 0) > 0 || (options.requireObservations?.length ?? 0) > 0;
8996
+ }
8997
+ function gateHasThresholds(options) {
8998
+ return hasThresholds(options);
8999
+ }
9000
+ async function loadRunMetrics(runs) {
9001
+ const metrics = [];
9002
+ for (const run of runs) {
9003
+ if (run.filePath === void 0) continue;
9004
+ metrics.push(
9005
+ await computeCohortRunMetrics({
9006
+ runId: run.runId,
9007
+ filePath: run.filePath,
9008
+ metadata: run.metadata,
9009
+ status: run.status,
9010
+ durationMs: run.durationMs,
9011
+ groupKey: "all"
9012
+ })
9013
+ );
9014
+ }
9015
+ return metrics;
9016
+ }
9017
+ async function observationStatus(filePath, name) {
9018
+ const events = await readTraceEventsFromFile(filePath);
9019
+ const outcomes = extractOutcomesFromTraceEvents(events);
9020
+ const match = outcomes.find((item) => item.name === name);
9021
+ if (!match) return "missing";
9022
+ return match.status === "passed" ? "passed" : "failed";
9023
+ }
9024
+ async function evaluateGateThresholds(runs, options) {
9025
+ const checks = [];
9026
+ const readErrors = [];
9027
+ if (!hasThresholds(options)) {
9028
+ return { checks, readErrors };
9029
+ }
9030
+ if (runs.length === 0) {
9031
+ readErrors.push("No trace runs found in the gate directory.");
9032
+ return { checks, readErrors };
9033
+ }
9034
+ let runMetrics;
9035
+ try {
9036
+ runMetrics = await loadRunMetrics(runs);
9037
+ } catch (error) {
9038
+ const message = error instanceof Error ? error.message : String(error);
9039
+ readErrors.push(message);
9040
+ return { checks, readErrors };
9041
+ }
9042
+ if (options.maxErrorRate !== void 0) {
9043
+ const errors = runMetrics.filter((run) => run.error).length;
9044
+ const actual = runMetrics.length > 0 ? errors / runMetrics.length * 100 : 0;
9045
+ const ok = actual <= options.maxErrorRate;
9046
+ checks.push({
9047
+ id: "maxErrorRate",
9048
+ name: "Max error rate",
9049
+ ok,
9050
+ expected: options.maxErrorRate,
9051
+ actual: Math.round(actual * 10) / 10,
9052
+ message: ok ? `Error rate ${actual.toFixed(1)}% within limit ${options.maxErrorRate}%` : `Error rate ${actual.toFixed(1)}% exceeds limit ${options.maxErrorRate}%`
9053
+ });
9054
+ }
9055
+ if (options.maxP95DurationMs !== void 0) {
9056
+ const durations = runMetrics.map((run) => run.durationMs).filter((value) => typeof value === "number");
9057
+ const actual = percentile3(durations, 95);
9058
+ const ok = actual !== void 0 && actual <= options.maxP95DurationMs;
9059
+ checks.push({
9060
+ id: "maxP95Duration",
9061
+ name: "Max p95 duration (ms)",
9062
+ ok,
9063
+ expected: options.maxP95DurationMs,
9064
+ actual: actual ?? "n/a",
9065
+ message: actual === void 0 ? "No duration samples available for p95 check." : ok ? `P95 duration ${Math.round(actual)} ms within limit ${options.maxP95DurationMs} ms` : `P95 duration ${Math.round(actual)} ms exceeds limit ${options.maxP95DurationMs} ms`
9066
+ });
9067
+ }
9068
+ for (const tool of options.forbidTools ?? []) {
9069
+ let violated = false;
9070
+ for (const run of runMetrics) {
9071
+ const used = run.toolChoices.includes(tool) || run.toolOrdering.includes(tool);
9072
+ if (used) {
9073
+ violated = true;
9074
+ checks.push({
9075
+ id: "forbidTool",
9076
+ name: `Forbid tool: ${tool}`,
9077
+ ok: false,
9078
+ expected: `not used`,
9079
+ actual: "used",
9080
+ runId: run.runId,
9081
+ message: `Forbidden tool "${tool}" used in run ${run.runId}`
9082
+ });
9083
+ }
9084
+ }
9085
+ if (!violated) {
9086
+ checks.push({
9087
+ id: "forbidTool",
9088
+ name: `Forbid tool: ${tool}`,
9089
+ ok: true,
9090
+ message: `Forbidden tool "${tool}" not used`
9091
+ });
9092
+ }
9093
+ }
9094
+ for (const observation of options.requireObservations ?? []) {
9095
+ for (const run of runs) {
9096
+ if (run.filePath === void 0) continue;
9097
+ try {
9098
+ const status = await observationStatus(run.filePath, observation);
9099
+ const ok = status === "passed";
9100
+ checks.push({
9101
+ id: "requireObservation",
9102
+ name: `Require observation: ${observation}`,
9103
+ ok,
9104
+ expected: "passed",
9105
+ actual: status,
9106
+ runId: run.runId,
9107
+ message: ok ? `Observation "${observation}" passed in run ${run.runId}` : status === "missing" ? `Observation "${observation}" missing in run ${run.runId}` : `Observation "${observation}" failed in run ${run.runId}`
9108
+ });
9109
+ } catch (error) {
9110
+ const message = error instanceof Error ? error.message : String(error);
9111
+ readErrors.push(`Run ${run.runId}: ${message}`);
9112
+ }
9113
+ }
9114
+ }
9115
+ return { checks, readErrors };
9116
+ }
9117
+ function checksFromSuiteResult(suiteResult) {
9118
+ const checks = [
9119
+ {
9120
+ id: "suite",
9121
+ name: `Suite: ${suiteResult.suiteName}`,
9122
+ ok: suiteResult.ok,
9123
+ message: suiteResult.ok ? `Suite passed (${suiteResult.summary.passed} cases)` : `Suite failed (${suiteResult.summary.failed} failed, ${suiteResult.summary.errors} errors)`
9124
+ }
9125
+ ];
9126
+ for (const suiteCase of suiteResult.cases) {
9127
+ if (suiteCase.status === "pass") continue;
9128
+ checks.push({
9129
+ id: "suite",
9130
+ name: `Case: ${suiteCase.id}`,
9131
+ ok: false,
9132
+ message: suiteCase.message ?? `Case status: ${suiteCase.status}`
9133
+ });
9134
+ }
9135
+ return checks;
9136
+ }
9137
+ function resolveExitCode(input) {
9138
+ if (input.configError) return 2;
9139
+ if (input.readError) return 3;
9140
+ if (!input.ok) return 1;
9141
+ return 0;
9142
+ }
9143
+ function validateOptions(options) {
9144
+ const errors = [];
9145
+ const hasSuite = options.suitePath !== void 0 && options.suitePath.trim() !== "";
9146
+ const hasThresholds2 = gateHasThresholds(options);
9147
+ if (!hasSuite && !hasThresholds2) {
9148
+ errors.push(
9149
+ "No gate rules specified. Pass --suite or at least one threshold flag."
9150
+ );
9151
+ }
9152
+ if (hasThresholds2 && (options.traceDir === void 0 || options.traceDir.trim() === "")) {
9153
+ if (!hasSuite) {
9154
+ errors.push("Threshold flags require --dir <trace-directory>.");
9155
+ }
9156
+ }
9157
+ if (options.maxErrorRate !== void 0 && options.maxErrorRate < 0) {
9158
+ errors.push("--max-error-rate must be a non-negative percentage.");
9159
+ }
9160
+ if (options.maxP95DurationMs !== void 0 && options.maxP95DurationMs < 0) {
9161
+ errors.push("--max-p95-duration must be a non-negative millisecond value.");
9162
+ }
9163
+ return errors;
9164
+ }
9165
+ function isConfigLoadError(error) {
9166
+ if (!(error instanceof Error)) return false;
9167
+ const ext = path__default.default.extname(error.message);
9168
+ if (error.message.includes("Unsupported suite config extension")) return true;
9169
+ if (error.message.includes("TypeScript suite configs require")) return true;
9170
+ if (error.message.includes("No suite config found")) return true;
9171
+ if (error.message.includes("AI_SUITE_CONFIG")) return true;
9172
+ if (ext === ".ts" || ext === ".mts" || ext === ".cts") return true;
9173
+ return "diagnostics" in error;
9174
+ }
9175
+ async function runGate(runs, options) {
9176
+ const diagnostics = [];
9177
+ const checks = [];
9178
+ const validationErrors = validateOptions(options);
9179
+ if (validationErrors.length > 0) {
9180
+ return {
9181
+ ok: false,
9182
+ exitCode: 2,
9183
+ runCount: 0,
9184
+ checks,
9185
+ diagnostics: validationErrors
9186
+ };
9187
+ }
9188
+ let traceDir = options.traceDir?.trim();
9189
+ let suiteResult;
9190
+ if (options.suitePath !== void 0 && options.suitePath.trim() !== "") {
9191
+ try {
9192
+ suiteResult = await runSuite({
9193
+ configPath: options.suitePath,
9194
+ cwd: options.cwd
9195
+ });
9196
+ traceDir = traceDir ?? suiteResult.tracesDir;
9197
+ checks.push(...checksFromSuiteResult(suiteResult));
9198
+ } catch (error) {
9199
+ const message = error instanceof Error ? error.message : String(error);
9200
+ diagnostics.push(message);
9201
+ return {
9202
+ ok: false,
9203
+ exitCode: isConfigLoadError(error) ? 2 : 3,
9204
+ traceDir,
9205
+ suitePath: options.suitePath,
9206
+ runCount: 0,
9207
+ checks,
9208
+ diagnostics
9209
+ };
9210
+ }
9211
+ }
9212
+ if (gateHasThresholds(options)) {
9213
+ const thresholdDir = traceDir;
9214
+ if (thresholdDir === void 0 || thresholdDir.trim() === "") {
9215
+ return {
9216
+ ok: false,
9217
+ exitCode: 2,
9218
+ traceDir,
9219
+ suitePath: options.suitePath,
9220
+ runCount: runs.length,
9221
+ checks,
9222
+ diagnostics: ["Threshold evaluation requires a trace directory."],
9223
+ ...suiteResult !== void 0 ? { suiteResult } : {}
9224
+ };
9225
+ }
9226
+ const thresholdRuns = runs.length > 0 ? runs : [];
9227
+ const { checks: thresholdChecks, readErrors } = await evaluateGateThresholds(
9228
+ thresholdRuns,
9229
+ options
9230
+ );
9231
+ checks.push(...thresholdChecks);
9232
+ diagnostics.push(...readErrors);
9233
+ if (readErrors.length > 0) {
9234
+ const ok2 = checks.length > 0 && checks.every((item) => item.ok);
9235
+ return {
9236
+ ok: ok2,
9237
+ exitCode: resolveExitCode({
9238
+ ok: ok2,
9239
+ configError: false,
9240
+ readError: true
9241
+ }),
9242
+ traceDir: thresholdDir,
9243
+ suitePath: options.suitePath,
9244
+ runCount: thresholdRuns.length,
9245
+ checks,
9246
+ diagnostics,
9247
+ ...suiteResult !== void 0 ? { suiteResult } : {}
9248
+ };
9249
+ }
9250
+ }
9251
+ const ok = checks.length > 0 && checks.every((item) => item.ok);
9252
+ return {
9253
+ ok,
9254
+ exitCode: resolveExitCode({ ok, configError: false, readError: false }),
9255
+ traceDir,
9256
+ suitePath: options.suitePath,
9257
+ runCount: runs.length,
9258
+ checks,
9259
+ diagnostics,
9260
+ ...suiteResult !== void 0 ? { suiteResult } : {}
9261
+ };
9262
+ }
9263
+
9264
+ // packages/core/src/gate/render.ts
9265
+ function escapeXml(value) {
9266
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
9267
+ }
9268
+ function renderGateSummaryMarkdown(result) {
9269
+ const lines = [];
9270
+ lines.push("# AgentInspect gate");
9271
+ lines.push("");
9272
+ lines.push(`Status: **${result.ok ? "PASS" : "FAIL"}** (exit ${result.exitCode})`);
9273
+ if (result.traceDir !== void 0) {
9274
+ lines.push(`Trace directory: \`${result.traceDir}\``);
9275
+ }
9276
+ if (result.suitePath !== void 0) {
9277
+ lines.push(`Suite config: \`${result.suitePath}\``);
9278
+ }
9279
+ lines.push(`Runs evaluated: ${result.runCount}`);
9280
+ lines.push("");
9281
+ if (result.diagnostics.length > 0) {
9282
+ lines.push("## Diagnostics");
9283
+ for (const item of result.diagnostics) lines.push(`- ${item}`);
9284
+ lines.push("");
9285
+ }
9286
+ lines.push("## Checks");
9287
+ for (const check of result.checks) {
9288
+ const flag = check.ok ? "PASS" : "FAIL";
9289
+ lines.push(`- [${flag}] ${check.name}: ${check.message}`);
9290
+ }
9291
+ lines.push("");
9292
+ return lines.join("\n").trimEnd();
9293
+ }
9294
+ function renderGateGithubStepSummary(result) {
9295
+ const lines = [];
9296
+ lines.push(`## AgentInspect gate: ${result.ok ? "PASS" : "FAIL"}`);
9297
+ lines.push("");
9298
+ lines.push("| Check | Status | Details |");
9299
+ lines.push("| --- | --- | --- |");
9300
+ for (const check of result.checks) {
9301
+ lines.push(
9302
+ `| ${check.name} | ${check.ok ? "pass" : "fail"} | ${check.message.replace(/\|/g, "/")} |`
9303
+ );
9304
+ }
9305
+ if (result.diagnostics.length > 0) {
9306
+ lines.push("");
9307
+ lines.push("**Diagnostics**");
9308
+ for (const item of result.diagnostics) lines.push(`- ${item}`);
9309
+ }
9310
+ return lines.join("\n").trimEnd();
9311
+ }
9312
+ function renderGateReportHtml(result) {
9313
+ const rows = result.checks.map(
9314
+ (check) => `<tr><td>${escapeHtml(check.name)}</td><td>${check.ok ? "PASS" : "FAIL"}</td><td>${escapeHtml(check.message)}</td></tr>`
9315
+ ).join("");
9316
+ return `<!DOCTYPE html>
9317
+ <html lang="en">
9318
+ <head>
9319
+ <meta charset="utf-8" />
9320
+ <title>Gate report</title>
9321
+ <style>
9322
+ body { font-family: system-ui, sans-serif; margin: 2rem; }
9323
+ table { border-collapse: collapse; width: 100%; }
9324
+ th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
9325
+ th { background: #f6f6f6; }
9326
+ </style>
9327
+ </head>
9328
+ <body>
9329
+ <h1>AgentInspect gate</h1>
9330
+ <p>Status: <strong>${result.ok ? "PASS" : "FAIL"}</strong> (exit ${result.exitCode})</p>
9331
+ <h2>Checks</h2>
9332
+ <table>
9333
+ <thead><tr><th>Check</th><th>Status</th><th>Details</th></tr></thead>
9334
+ <tbody>${rows}</tbody>
9335
+ </table>
9336
+ </body>
9337
+ </html>`;
9338
+ }
9339
+ function renderGateJUnit(result) {
9340
+ const failures = result.checks.filter((check) => !check.ok).length;
9341
+ const tests = result.checks.length;
9342
+ const cases = result.checks.map((check) => {
9343
+ if (check.ok) {
9344
+ return ` <testcase name="${escapeXml(check.name)}" classname="gate" />`;
9345
+ }
9346
+ return ` <testcase name="${escapeXml(check.name)}" classname="gate">
9347
+ <failure message="${escapeXml(check.message)}">${escapeXml(check.message)}</failure>
9348
+ </testcase>`;
9349
+ }).join("\n");
9350
+ return `<?xml version="1.0" encoding="UTF-8"?>
9351
+ <testsuites tests="${tests}" failures="${failures}" errors="0" time="0">
9352
+ <testsuite name="agent-inspect-gate" tests="${tests}" failures="${failures}" errors="0" time="0">
9353
+ ${cases}
9354
+ </testsuite>
9355
+ </testsuites>`;
9356
+ }
9357
+ function renderGateReport(result, options = {}) {
9358
+ const format = options.format ?? "markdown";
9359
+ if (format === "json") return JSON.stringify(result, null, 2);
9360
+ if (format === "html") return renderGateReportHtml(result);
9361
+ if (format === "junit") return renderGateJUnit(result);
9362
+ if (format === "github") return renderGateGithubStepSummary(result);
9363
+ return renderGateSummaryMarkdown(result);
9364
+ }
9365
+
8449
9366
  // packages/core/src/inspect-run.ts
8450
9367
  function normalizeRunName(name) {
8451
9368
  if (typeof name !== "string" || name.trim() === "") {
@@ -8576,6 +9493,7 @@ async function maybeInspectRun(name, fn, options) {
8576
9493
  return inspectRun(name, fn, options);
8577
9494
  }
8578
9495
 
9496
+ exports.COHORT_METRIC_IDS = COHORT_METRIC_IDS;
8579
9497
  exports.DEFAULT_MAX_EVENT_BYTES = DEFAULT_MAX_EVENT_BYTES;
8580
9498
  exports.DEFAULT_MAX_METADATA_VALUE_LENGTH = DEFAULT_MAX_METADATA_VALUE_LENGTH;
8581
9499
  exports.DEFAULT_MAX_PREVIEW_LENGTH = DEFAULT_MAX_PREVIEW_LENGTH;
@@ -8592,6 +9510,7 @@ exports.TERMINAL_INDENT = TERMINAL_INDENT;
8592
9510
  exports.TraceDirectory = TraceDirectory;
8593
9511
  exports.aggregateBundleSafeStatus = aggregateBundleSafeStatus;
8594
9512
  exports.aggregateSessionCheckResults = aggregateSessionCheckResults;
9513
+ exports.analyzeCohort = analyzeCohort;
8595
9514
  exports.buildActivitySummary = buildActivitySummary;
8596
9515
  exports.buildBundleMetadata = buildBundleMetadata;
8597
9516
  exports.buildBundleSummaryMarkdown = buildBundleSummaryMarkdown;
@@ -8603,6 +9522,7 @@ exports.buildRunWhatSummary = buildRunWhatSummary;
8603
9522
  exports.buildSessionIndex = buildSessionIndex;
8604
9523
  exports.buildTraceStats = buildTraceStats;
8605
9524
  exports.bundleFailsOnSafety = bundleFailsOnSafety;
9525
+ exports.compareCohortAggregates = compareCohortAggregates;
8606
9526
  exports.createInspector = createInspector;
8607
9527
  exports.createInspectorRuntime = createInspectorRuntime;
8608
9528
  exports.createRunId = createRunId;
@@ -8621,6 +9541,7 @@ exports.formatDuration = formatDuration2;
8621
9541
  exports.formatError = formatError;
8622
9542
  exports.formatTerminalName = formatTerminalName;
8623
9543
  exports.formatTimestamp = formatTimestamp;
9544
+ exports.gateHasThresholds = gateHasThresholds;
8624
9545
  exports.getCurrentContext = getCurrentContext;
8625
9546
  exports.getCurrentCorrelationMetadata = getCurrentCorrelationMetadata;
8626
9547
  exports.getCurrentDepth = getCurrentDepth;
@@ -8650,8 +9571,12 @@ exports.loadTraceMetadataList = loadTraceMetadataList;
8650
9571
  exports.maybeInspectRun = maybeInspectRun;
8651
9572
  exports.normalizeBundleOutputPath = normalizeBundleOutputPath;
8652
9573
  exports.normalizeSuiteConfig = normalizeSuiteConfig;
9574
+ exports.parseCohortMetricList = parseCohortMetricList;
8653
9575
  exports.parseDuration = parseDuration;
8654
9576
  exports.parseDurationFilter = parseDurationFilter;
9577
+ exports.parseGateList = parseGateList;
9578
+ exports.parseGateNumber = parseGateNumber;
9579
+ exports.parseGroupBySpec = parseGroupBySpec;
8655
9580
  exports.parseTraceJsonl = parseTraceJsonl;
8656
9581
  exports.prepareMetadataForDisk = prepareMetadataForDisk;
8657
9582
  exports.prepareTraceEventForDisk = prepareTraceEventForDisk;
@@ -8664,7 +9589,13 @@ exports.printStepStart = printStepStart;
8664
9589
  exports.readTraceEvents = readTraceEvents;
8665
9590
  exports.readTraceFile = readTraceFile;
8666
9591
  exports.renderActivitySummaryHuman = renderActivitySummaryHuman;
9592
+ exports.renderCohortReport = renderCohortReport;
9593
+ exports.renderCohortSummaryMarkdown = renderCohortSummaryMarkdown;
8667
9594
  exports.renderErrorLine = renderErrorLine;
9595
+ exports.renderGateGithubStepSummary = renderGateGithubStepSummary;
9596
+ exports.renderGateJUnit = renderGateJUnit;
9597
+ exports.renderGateReport = renderGateReport;
9598
+ exports.renderGateSummaryMarkdown = renderGateSummaryMarkdown;
8668
9599
  exports.renderRunSummary = renderRunSummary;
8669
9600
  exports.renderRunWhat = renderRunWhat;
8670
9601
  exports.renderStepLine = renderStepLine;
@@ -8678,6 +9609,7 @@ exports.resolveSuiteCaseTrace = resolveSuiteCaseTrace;
8678
9609
  exports.resolveSuiteConfigPath = resolveSuiteConfigPath;
8679
9610
  exports.resolveTraceDir = resolveTraceDir;
8680
9611
  exports.resolveTraceSafetyOptions = resolveTraceSafetyOptions;
9612
+ exports.runGate = runGate;
8681
9613
  exports.runSuite = runSuite;
8682
9614
  exports.runWithContext = runWithContext;
8683
9615
  exports.runWithStepContext = runWithStepContext;