agent-inspect 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/docs/CLI.md +33 -0
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-TB45H3QT.mjs → chunk-TSIQUIPF.mjs} +630 -6
- package/packages/cli/dist/chunk-TSIQUIPF.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +879 -207
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +141 -164
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-ZRFSYML6.mjs → src-2SZWGKMM.mjs} +3 -3
- package/packages/cli/dist/{src-ZRFSYML6.mjs.map → src-2SZWGKMM.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +531 -3
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +88 -1
- package/packages/core/dist/advanced.d.ts +88 -1
- package/packages/core/dist/advanced.mjs +518 -1
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/cli/dist/chunk-TB45H3QT.mjs.map +0 -1
|
@@ -2100,7 +2100,7 @@ function buildRunSummary(events) {
|
|
|
2100
2100
|
}
|
|
2101
2101
|
let totalSteps = 0;
|
|
2102
2102
|
let llmSteps = 0;
|
|
2103
|
-
let
|
|
2103
|
+
let toolSteps2 = 0;
|
|
2104
2104
|
let logicSteps = 0;
|
|
2105
2105
|
let errorSteps = 0;
|
|
2106
2106
|
let maxDepth = 0;
|
|
@@ -2130,7 +2130,7 @@ function buildRunSummary(events) {
|
|
|
2130
2130
|
for (const [id, s] of steps.entries()) {
|
|
2131
2131
|
totalSteps += 1;
|
|
2132
2132
|
if (s.type === "llm") llmSteps += 1;
|
|
2133
|
-
else if (s.type === "tool")
|
|
2133
|
+
else if (s.type === "tool") toolSteps2 += 1;
|
|
2134
2134
|
else logicSteps += 1;
|
|
2135
2135
|
if (s.status === "error") errorSteps += 1;
|
|
2136
2136
|
const depth = computeDepth(id);
|
|
@@ -2164,7 +2164,7 @@ function buildRunSummary(events) {
|
|
|
2164
2164
|
durationMs: durationMs2,
|
|
2165
2165
|
totalSteps,
|
|
2166
2166
|
llmSteps,
|
|
2167
|
-
toolSteps,
|
|
2167
|
+
toolSteps: toolSteps2,
|
|
2168
2168
|
logicSteps,
|
|
2169
2169
|
errorSteps,
|
|
2170
2170
|
maxDepth,
|
|
@@ -3786,12 +3786,12 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3786
3786
|
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
3787
3787
|
);
|
|
3788
3788
|
const ordered = [...runs].sort(compareRuns);
|
|
3789
|
-
const
|
|
3789
|
+
const path31 = [];
|
|
3790
3790
|
const visited = /* @__PURE__ */ new Set();
|
|
3791
3791
|
const pushRun = (run, confidence, source) => {
|
|
3792
3792
|
if (visited.has(run.runId)) return;
|
|
3793
3793
|
visited.add(run.runId);
|
|
3794
|
-
|
|
3794
|
+
path31.push({
|
|
3795
3795
|
runId: run.runId,
|
|
3796
3796
|
name: run.name,
|
|
3797
3797
|
startedAt: run.startedAt,
|
|
@@ -3816,7 +3816,7 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3816
3816
|
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
3817
3817
|
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
3818
3818
|
}
|
|
3819
|
-
return
|
|
3819
|
+
return path31;
|
|
3820
3820
|
}
|
|
3821
3821
|
function metaRunIdMatches(run, token, runById) {
|
|
3822
3822
|
const meta2 = extractSessionWorkflowMetadata(run.metadata);
|
|
@@ -4716,7 +4716,7 @@ function stripPrefix(name, prefixes) {
|
|
|
4716
4716
|
}
|
|
4717
4717
|
return name;
|
|
4718
4718
|
}
|
|
4719
|
-
function eventEvidence(event,
|
|
4719
|
+
function eventEvidence(event, path31) {
|
|
4720
4720
|
return {
|
|
4721
4721
|
runId: event.runId,
|
|
4722
4722
|
eventId: event.eventId,
|
|
@@ -4726,7 +4726,7 @@ function eventEvidence(event, path30) {
|
|
|
4726
4726
|
kind: event.kind,
|
|
4727
4727
|
name: event.name,
|
|
4728
4728
|
status: event.status,
|
|
4729
|
-
...
|
|
4729
|
+
...path31 ? { path: path31 } : {}
|
|
4730
4730
|
};
|
|
4731
4731
|
}
|
|
4732
4732
|
function runEvidence(run) {
|
|
@@ -4789,9 +4789,9 @@ function eventEndMs(event) {
|
|
|
4789
4789
|
function normalizedKey(value) {
|
|
4790
4790
|
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
4791
4791
|
}
|
|
4792
|
-
function lastPathSegment(
|
|
4793
|
-
const parts =
|
|
4794
|
-
return parts[parts.length - 1] ??
|
|
4792
|
+
function lastPathSegment(path31) {
|
|
4793
|
+
const parts = path31.split(".");
|
|
4794
|
+
return parts[parts.length - 1] ?? path31;
|
|
4795
4795
|
}
|
|
4796
4796
|
function valueType(value) {
|
|
4797
4797
|
if (Array.isArray(value)) return "array";
|
|
@@ -4805,12 +4805,12 @@ function serializedByteLength(value) {
|
|
|
4805
4805
|
return void 0;
|
|
4806
4806
|
}
|
|
4807
4807
|
}
|
|
4808
|
-
function pushValueEntries(entries, event, value,
|
|
4809
|
-
entries.push({ event, path:
|
|
4808
|
+
function pushValueEntries(entries, event, value, path31, key, depth = 0) {
|
|
4809
|
+
entries.push({ event, path: path31, key, value });
|
|
4810
4810
|
if (depth >= 8) return;
|
|
4811
4811
|
if (Array.isArray(value)) {
|
|
4812
4812
|
for (const [index, item] of value.entries()) {
|
|
4813
|
-
pushValueEntries(entries, event, item, `${
|
|
4813
|
+
pushValueEntries(entries, event, item, `${path31}.${index}`, String(index), depth + 1);
|
|
4814
4814
|
}
|
|
4815
4815
|
return;
|
|
4816
4816
|
}
|
|
@@ -4820,7 +4820,7 @@ function pushValueEntries(entries, event, value, path30, key, depth = 0) {
|
|
|
4820
4820
|
entries,
|
|
4821
4821
|
event,
|
|
4822
4822
|
value[nestedKey],
|
|
4823
|
-
`${
|
|
4823
|
+
`${path31}.${nestedKey}`,
|
|
4824
4824
|
nestedKey,
|
|
4825
4825
|
depth + 1
|
|
4826
4826
|
);
|
|
@@ -4901,9 +4901,9 @@ function eventDurationMs(event) {
|
|
|
4901
4901
|
}
|
|
4902
4902
|
function treeShape(nodes) {
|
|
4903
4903
|
const lines = [];
|
|
4904
|
-
const visit = (node,
|
|
4905
|
-
lines.push(`${
|
|
4906
|
-
node.children.forEach((child, index) => visit(child, `${
|
|
4904
|
+
const visit = (node, path31) => {
|
|
4905
|
+
lines.push(`${path31}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
|
|
4906
|
+
node.children.forEach((child, index) => visit(child, `${path31}.${index}`));
|
|
4907
4907
|
};
|
|
4908
4908
|
nodes.forEach((node, index) => visit(node, String(index)));
|
|
4909
4909
|
return lines;
|
|
@@ -4952,9 +4952,9 @@ function retrievalShape(context) {
|
|
|
4952
4952
|
function guardrailShape(context) {
|
|
4953
4953
|
return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
|
|
4954
4954
|
}
|
|
4955
|
-
function firstEvidenceForKind(context, kind,
|
|
4955
|
+
function firstEvidenceForKind(context, kind, path31) {
|
|
4956
4956
|
const event = context.events.find((candidate) => candidate.kind === kind);
|
|
4957
|
-
return event ? [eventEvidence(event,
|
|
4957
|
+
return event ? [eventEvidence(event, path31)] : runEvidence(context.selectedRun);
|
|
4958
4958
|
}
|
|
4959
4959
|
function baselineDiffFinding(message, evidence, expected, actual) {
|
|
4960
4960
|
return failFinding("baseline.regression", message, evidence, expected, actual);
|
|
@@ -5304,13 +5304,13 @@ function createStructureCycleRule() {
|
|
|
5304
5304
|
const seenCycles = /* @__PURE__ */ new Set();
|
|
5305
5305
|
const findings = [];
|
|
5306
5306
|
for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
|
|
5307
|
-
const
|
|
5307
|
+
const path31 = [];
|
|
5308
5308
|
const seenAt = /* @__PURE__ */ new Map();
|
|
5309
5309
|
let current = event;
|
|
5310
5310
|
while (current) {
|
|
5311
5311
|
const existing = seenAt.get(current.eventId);
|
|
5312
5312
|
if (existing !== void 0) {
|
|
5313
|
-
const cycle =
|
|
5313
|
+
const cycle = path31.slice(existing);
|
|
5314
5314
|
const key = cycle.map((item) => item.eventId).sort().join("\0");
|
|
5315
5315
|
if (!seenCycles.has(key)) {
|
|
5316
5316
|
seenCycles.add(key);
|
|
@@ -5326,8 +5326,8 @@ function createStructureCycleRule() {
|
|
|
5326
5326
|
}
|
|
5327
5327
|
break;
|
|
5328
5328
|
}
|
|
5329
|
-
seenAt.set(current.eventId,
|
|
5330
|
-
|
|
5329
|
+
seenAt.set(current.eventId, path31.length);
|
|
5330
|
+
path31.push(current);
|
|
5331
5331
|
current = current.parentId ? byId.get(current.parentId) : void 0;
|
|
5332
5332
|
}
|
|
5333
5333
|
}
|
|
@@ -8275,6 +8275,682 @@ var init_suite = __esm({
|
|
|
8275
8275
|
}
|
|
8276
8276
|
});
|
|
8277
8277
|
|
|
8278
|
+
// packages/core/src/cohort/types.ts
|
|
8279
|
+
var COHORT_METRIC_IDS;
|
|
8280
|
+
var init_types5 = __esm({
|
|
8281
|
+
"packages/core/src/cohort/types.ts"() {
|
|
8282
|
+
COHORT_METRIC_IDS = [
|
|
8283
|
+
"errorRate",
|
|
8284
|
+
"duration",
|
|
8285
|
+
"toolChoice",
|
|
8286
|
+
"toolOrdering",
|
|
8287
|
+
"llmCallCount",
|
|
8288
|
+
"tokenUsage",
|
|
8289
|
+
"retryCount",
|
|
8290
|
+
"observationFailure",
|
|
8291
|
+
"guardrailFailure",
|
|
8292
|
+
"circuitViolation",
|
|
8293
|
+
"redactionWarning"
|
|
8294
|
+
];
|
|
8295
|
+
}
|
|
8296
|
+
});
|
|
8297
|
+
|
|
8298
|
+
// packages/core/src/cohort/compare.ts
|
|
8299
|
+
function compareNumber(metric, label, baseline, candidate, higherIsWorse = true) {
|
|
8300
|
+
if (baseline === void 0 && candidate === void 0) return void 0;
|
|
8301
|
+
const delta = baseline !== void 0 && candidate !== void 0 ? candidate - baseline : void 0;
|
|
8302
|
+
const regression = delta !== void 0 && (higherIsWorse && delta > 0 || !higherIsWorse && delta < 0);
|
|
8303
|
+
return {
|
|
8304
|
+
metric,
|
|
8305
|
+
baseline,
|
|
8306
|
+
candidate,
|
|
8307
|
+
delta,
|
|
8308
|
+
regression,
|
|
8309
|
+
message: `${label}: baseline=${baseline ?? "n/a"} candidate=${candidate ?? "n/a"}${delta !== void 0 ? ` (delta ${delta})` : ""}`
|
|
8310
|
+
};
|
|
8311
|
+
}
|
|
8312
|
+
function pickAggregate(groups, cohortLabel, groupKey) {
|
|
8313
|
+
return groups.find(
|
|
8314
|
+
(group) => group.cohortLabel === cohortLabel && (groupKey === void 0 || group.groupKey === groupKey)
|
|
8315
|
+
);
|
|
8316
|
+
}
|
|
8317
|
+
function compareCohortAggregates(groups, options) {
|
|
8318
|
+
const baselineAgg = pickAggregate(groups, options.baseline, options.groupKey);
|
|
8319
|
+
const candidateAgg = pickAggregate(groups, options.candidate, options.groupKey);
|
|
8320
|
+
const comparisons = [];
|
|
8321
|
+
for (const metric of options.metrics) {
|
|
8322
|
+
switch (metric) {
|
|
8323
|
+
case "errorRate": {
|
|
8324
|
+
const item = compareNumber(
|
|
8325
|
+
metric,
|
|
8326
|
+
"Error rate",
|
|
8327
|
+
baselineAgg?.errorRate,
|
|
8328
|
+
candidateAgg?.errorRate
|
|
8329
|
+
);
|
|
8330
|
+
if (item) comparisons.push(item);
|
|
8331
|
+
break;
|
|
8332
|
+
}
|
|
8333
|
+
case "duration": {
|
|
8334
|
+
const item = compareNumber(
|
|
8335
|
+
metric,
|
|
8336
|
+
"Average duration (ms)",
|
|
8337
|
+
baselineAgg?.avgDurationMs,
|
|
8338
|
+
candidateAgg?.avgDurationMs
|
|
8339
|
+
);
|
|
8340
|
+
if (item) comparisons.push(item);
|
|
8341
|
+
break;
|
|
8342
|
+
}
|
|
8343
|
+
case "llmCallCount": {
|
|
8344
|
+
const item = compareNumber(
|
|
8345
|
+
metric,
|
|
8346
|
+
"Average LLM calls",
|
|
8347
|
+
baselineAgg?.avgLlmCallCount,
|
|
8348
|
+
candidateAgg?.avgLlmCallCount
|
|
8349
|
+
);
|
|
8350
|
+
if (item) comparisons.push(item);
|
|
8351
|
+
break;
|
|
8352
|
+
}
|
|
8353
|
+
case "tokenUsage": {
|
|
8354
|
+
const item = compareNumber(
|
|
8355
|
+
metric,
|
|
8356
|
+
"Average token usage",
|
|
8357
|
+
baselineAgg?.avgTokenUsage,
|
|
8358
|
+
candidateAgg?.avgTokenUsage
|
|
8359
|
+
);
|
|
8360
|
+
if (item) comparisons.push(item);
|
|
8361
|
+
break;
|
|
8362
|
+
}
|
|
8363
|
+
case "retryCount": {
|
|
8364
|
+
const item = compareNumber(
|
|
8365
|
+
metric,
|
|
8366
|
+
"Average retries",
|
|
8367
|
+
baselineAgg?.avgRetryCount,
|
|
8368
|
+
candidateAgg?.avgRetryCount
|
|
8369
|
+
);
|
|
8370
|
+
if (item) comparisons.push(item);
|
|
8371
|
+
break;
|
|
8372
|
+
}
|
|
8373
|
+
case "observationFailure": {
|
|
8374
|
+
const item = compareNumber(
|
|
8375
|
+
metric,
|
|
8376
|
+
"Observation failure rate",
|
|
8377
|
+
baselineAgg?.observationFailureRate,
|
|
8378
|
+
candidateAgg?.observationFailureRate
|
|
8379
|
+
);
|
|
8380
|
+
if (item) comparisons.push(item);
|
|
8381
|
+
break;
|
|
8382
|
+
}
|
|
8383
|
+
case "toolChoice": {
|
|
8384
|
+
const baselineValue = baselineAgg?.dominantToolChoice;
|
|
8385
|
+
const candidateValue = candidateAgg?.dominantToolChoice;
|
|
8386
|
+
comparisons.push({
|
|
8387
|
+
metric,
|
|
8388
|
+
baseline: baselineValue,
|
|
8389
|
+
candidate: candidateValue,
|
|
8390
|
+
delta: baselineValue === candidateValue ? "same" : "changed",
|
|
8391
|
+
regression: baselineValue !== candidateValue,
|
|
8392
|
+
message: `Tool choice: baseline=${baselineValue ?? "n/a"} candidate=${candidateValue ?? "n/a"}`
|
|
8393
|
+
});
|
|
8394
|
+
break;
|
|
8395
|
+
}
|
|
8396
|
+
case "toolOrdering": {
|
|
8397
|
+
const baselineValue = baselineAgg?.toolOrderingSignature;
|
|
8398
|
+
const candidateValue = candidateAgg?.toolOrderingSignature;
|
|
8399
|
+
comparisons.push({
|
|
8400
|
+
metric,
|
|
8401
|
+
baseline: baselineValue,
|
|
8402
|
+
candidate: candidateValue,
|
|
8403
|
+
delta: baselineValue === candidateValue ? "same" : "changed",
|
|
8404
|
+
regression: baselineValue !== candidateValue,
|
|
8405
|
+
message: `Tool ordering: baseline=${baselineValue ?? "n/a"} candidate=${candidateValue ?? "n/a"}`
|
|
8406
|
+
});
|
|
8407
|
+
break;
|
|
8408
|
+
}
|
|
8409
|
+
case "guardrailFailure": {
|
|
8410
|
+
const item = compareNumber(
|
|
8411
|
+
metric,
|
|
8412
|
+
"Guardrail failures",
|
|
8413
|
+
baselineAgg?.avgGuardrailFailures,
|
|
8414
|
+
candidateAgg?.avgGuardrailFailures
|
|
8415
|
+
);
|
|
8416
|
+
if (item) comparisons.push(item);
|
|
8417
|
+
break;
|
|
8418
|
+
}
|
|
8419
|
+
case "circuitViolation": {
|
|
8420
|
+
const item = compareNumber(
|
|
8421
|
+
metric,
|
|
8422
|
+
"Circuit violations",
|
|
8423
|
+
baselineAgg?.avgCircuitViolations,
|
|
8424
|
+
candidateAgg?.avgCircuitViolations
|
|
8425
|
+
);
|
|
8426
|
+
if (item) comparisons.push(item);
|
|
8427
|
+
break;
|
|
8428
|
+
}
|
|
8429
|
+
case "redactionWarning": {
|
|
8430
|
+
const item = compareNumber(
|
|
8431
|
+
metric,
|
|
8432
|
+
"Redaction warnings",
|
|
8433
|
+
baselineAgg?.avgRedactionWarnings,
|
|
8434
|
+
candidateAgg?.avgRedactionWarnings
|
|
8435
|
+
);
|
|
8436
|
+
if (item) comparisons.push(item);
|
|
8437
|
+
break;
|
|
8438
|
+
}
|
|
8439
|
+
}
|
|
8440
|
+
}
|
|
8441
|
+
return comparisons;
|
|
8442
|
+
}
|
|
8443
|
+
var init_compare = __esm({
|
|
8444
|
+
"packages/core/src/cohort/compare.ts"() {
|
|
8445
|
+
}
|
|
8446
|
+
});
|
|
8447
|
+
|
|
8448
|
+
// packages/core/src/cohort/grouping.ts
|
|
8449
|
+
function parseCohortMetricList(value) {
|
|
8450
|
+
if (value === void 0 || value.trim() === "") return [];
|
|
8451
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
8452
|
+
}
|
|
8453
|
+
function parseGroupBySpec(groupBy) {
|
|
8454
|
+
const raw = (groupBy ?? "model").trim();
|
|
8455
|
+
if (raw === "model") return { kind: "model" };
|
|
8456
|
+
if (raw === "session") return { kind: "session" };
|
|
8457
|
+
if (raw === "group") return { kind: "group" };
|
|
8458
|
+
if (raw.startsWith("metadata.")) {
|
|
8459
|
+
const metadataKey = raw.slice("metadata.".length).trim();
|
|
8460
|
+
if (metadataKey === "") throw new Error("metadata group-by requires a key.");
|
|
8461
|
+
return { kind: "metadata", metadataKey };
|
|
8462
|
+
}
|
|
8463
|
+
throw new Error(`Unsupported --group-by value: ${raw}`);
|
|
8464
|
+
}
|
|
8465
|
+
function metadataString(metadata, key) {
|
|
8466
|
+
const value = metadata?.[key];
|
|
8467
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
8468
|
+
}
|
|
8469
|
+
function resolveRunGroupKey(run, groupBy) {
|
|
8470
|
+
const metadata = run.metadata ?? {};
|
|
8471
|
+
switch (groupBy.kind) {
|
|
8472
|
+
case "model":
|
|
8473
|
+
return metadataString(metadata, "model") ?? "unknown";
|
|
8474
|
+
case "session":
|
|
8475
|
+
return extractSessionWorkflowMetadata(metadata)?.sessionId ?? metadataString(metadata, "sessionId") ?? "__unscoped__";
|
|
8476
|
+
case "group":
|
|
8477
|
+
return extractSessionWorkflowMetadata(metadata)?.groupId ?? metadataString(metadata, "groupId") ?? "__unscoped__";
|
|
8478
|
+
case "metadata":
|
|
8479
|
+
return metadataString(metadata, groupBy.metadataKey) ?? "__missing__";
|
|
8480
|
+
default:
|
|
8481
|
+
return "unknown";
|
|
8482
|
+
}
|
|
8483
|
+
}
|
|
8484
|
+
function resolveCohortLabel(run, cohortKey, baseline, candidate) {
|
|
8485
|
+
const label = metadataString(run.metadata, cohortKey);
|
|
8486
|
+
if (label === void 0) return void 0;
|
|
8487
|
+
if (baseline !== void 0 && label === baseline) return baseline;
|
|
8488
|
+
if (candidate !== void 0 && label === candidate) return candidate;
|
|
8489
|
+
if (baseline === void 0 && candidate === void 0) return label;
|
|
8490
|
+
return void 0;
|
|
8491
|
+
}
|
|
8492
|
+
function filterRunsForCohort(runs, options) {
|
|
8493
|
+
const warnings = [];
|
|
8494
|
+
if (options.baseline === void 0 && options.candidate === void 0) {
|
|
8495
|
+
return { runs: [...runs], warnings };
|
|
8496
|
+
}
|
|
8497
|
+
const selected = [];
|
|
8498
|
+
for (const run of runs) {
|
|
8499
|
+
const label = resolveCohortLabel(
|
|
8500
|
+
run,
|
|
8501
|
+
options.cohortKey,
|
|
8502
|
+
options.baseline,
|
|
8503
|
+
options.candidate
|
|
8504
|
+
);
|
|
8505
|
+
if (label !== void 0) selected.push(run);
|
|
8506
|
+
}
|
|
8507
|
+
if (selected.length === 0) {
|
|
8508
|
+
warnings.push(
|
|
8509
|
+
`No runs matched baseline/candidate labels on metadata.${options.cohortKey}.`
|
|
8510
|
+
);
|
|
8511
|
+
}
|
|
8512
|
+
return { runs: selected, warnings };
|
|
8513
|
+
}
|
|
8514
|
+
var init_grouping = __esm({
|
|
8515
|
+
"packages/core/src/cohort/grouping.ts"() {
|
|
8516
|
+
init_metadata();
|
|
8517
|
+
}
|
|
8518
|
+
});
|
|
8519
|
+
|
|
8520
|
+
// packages/core/src/cohort/metrics.ts
|
|
8521
|
+
function asNumber(value) {
|
|
8522
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
8523
|
+
}
|
|
8524
|
+
function toolSteps(events) {
|
|
8525
|
+
const ordering = [];
|
|
8526
|
+
const choices = /* @__PURE__ */ new Set();
|
|
8527
|
+
const sorted = [...events].filter((event) => event.event === "step_started").sort((a, b) => a.timestamp - b.timestamp);
|
|
8528
|
+
for (const event of sorted) {
|
|
8529
|
+
const step = event;
|
|
8530
|
+
if (step.type !== "tool") continue;
|
|
8531
|
+
const name = typeof step.metadata?.toolName === "string" ? step.metadata.toolName : step.name;
|
|
8532
|
+
ordering.push(name);
|
|
8533
|
+
choices.add(name);
|
|
8534
|
+
}
|
|
8535
|
+
return { choices: [...choices].sort(), ordering };
|
|
8536
|
+
}
|
|
8537
|
+
async function computeCohortRunMetrics(input3) {
|
|
8538
|
+
const events = await readTraceEventsFromFile(input3.filePath);
|
|
8539
|
+
const summary = buildRunSummary(events);
|
|
8540
|
+
const tools = toolSteps(events);
|
|
8541
|
+
const outcomes = extractOutcomesFromTraceEvents(events);
|
|
8542
|
+
const observationFailures = outcomes.filter((item) => item.status === "failed").length;
|
|
8543
|
+
const metadata = input3.metadata ?? {};
|
|
8544
|
+
const retryCount2 = asNumber(metadata.attempt) !== void 0 && asNumber(metadata.attempt) > 1 ? asNumber(metadata.attempt) - 1 : typeof metadata.retryOf === "string" ? 1 : 0;
|
|
8545
|
+
return {
|
|
8546
|
+
runId: input3.runId,
|
|
8547
|
+
...input3.cohortLabel !== void 0 ? { cohortLabel: input3.cohortLabel } : {},
|
|
8548
|
+
groupKey: input3.groupKey,
|
|
8549
|
+
status: summary.status,
|
|
8550
|
+
error: summary.status === "error",
|
|
8551
|
+
durationMs: summary.durationMs ?? input3.durationMs,
|
|
8552
|
+
llmCallCount: summary.llmSteps,
|
|
8553
|
+
tokenUsageTotal: summary.totalTokens?.total,
|
|
8554
|
+
retryCount: retryCount2,
|
|
8555
|
+
observationFailures,
|
|
8556
|
+
guardrailFailures: asNumber(metadata.guardrailFailures) ?? 0,
|
|
8557
|
+
circuitViolations: asNumber(metadata.circuitViolations) ?? 0,
|
|
8558
|
+
redactionWarnings: asNumber(metadata.redactionWarnings) ?? 0,
|
|
8559
|
+
toolChoices: tools.choices,
|
|
8560
|
+
toolOrdering: tools.ordering
|
|
8561
|
+
};
|
|
8562
|
+
}
|
|
8563
|
+
function percentile2(values, p) {
|
|
8564
|
+
if (values.length === 0) return void 0;
|
|
8565
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
8566
|
+
const idx = Math.min(
|
|
8567
|
+
sorted.length - 1,
|
|
8568
|
+
Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
|
|
8569
|
+
);
|
|
8570
|
+
return sorted[idx];
|
|
8571
|
+
}
|
|
8572
|
+
function dominantToolChoice(runs) {
|
|
8573
|
+
const counts = /* @__PURE__ */ new Map();
|
|
8574
|
+
for (const run of runs) {
|
|
8575
|
+
const signature = run.toolChoices.join(",");
|
|
8576
|
+
if (signature === "") continue;
|
|
8577
|
+
counts.set(signature, (counts.get(signature) ?? 0) + 1);
|
|
8578
|
+
}
|
|
8579
|
+
let best;
|
|
8580
|
+
let bestCount = 0;
|
|
8581
|
+
for (const [key, count] of counts) {
|
|
8582
|
+
if (count > bestCount) {
|
|
8583
|
+
best = key;
|
|
8584
|
+
bestCount = count;
|
|
8585
|
+
}
|
|
8586
|
+
}
|
|
8587
|
+
return best;
|
|
8588
|
+
}
|
|
8589
|
+
function orderingSignature(runs) {
|
|
8590
|
+
const counts = /* @__PURE__ */ new Map();
|
|
8591
|
+
for (const run of runs) {
|
|
8592
|
+
const signature = run.toolOrdering.join(">");
|
|
8593
|
+
if (signature === "") continue;
|
|
8594
|
+
counts.set(signature, (counts.get(signature) ?? 0) + 1);
|
|
8595
|
+
}
|
|
8596
|
+
let best;
|
|
8597
|
+
let bestCount = 0;
|
|
8598
|
+
for (const [key, count] of counts) {
|
|
8599
|
+
if (count > bestCount) {
|
|
8600
|
+
best = key;
|
|
8601
|
+
bestCount = count;
|
|
8602
|
+
}
|
|
8603
|
+
}
|
|
8604
|
+
return best;
|
|
8605
|
+
}
|
|
8606
|
+
function aggregateCohortMetrics(runs, groupKey, cohortLabel) {
|
|
8607
|
+
const durations = runs.map((run) => run.durationMs).filter((value) => typeof value === "number");
|
|
8608
|
+
const tokenValues = runs.map((run) => run.tokenUsageTotal).filter((value) => typeof value === "number");
|
|
8609
|
+
const errors = runs.filter((run) => run.error).length;
|
|
8610
|
+
const observationFailures = runs.reduce((sum, run) => sum + run.observationFailures, 0);
|
|
8611
|
+
return {
|
|
8612
|
+
groupKey,
|
|
8613
|
+
...cohortLabel !== void 0 ? { cohortLabel } : {},
|
|
8614
|
+
runCount: runs.length,
|
|
8615
|
+
errorRate: runs.length > 0 ? errors / runs.length : 0,
|
|
8616
|
+
avgDurationMs: durations.length > 0 ? durations.reduce((sum, value) => sum + value, 0) / durations.length : void 0,
|
|
8617
|
+
p95DurationMs: percentile2(durations, 95),
|
|
8618
|
+
avgLlmCallCount: runs.length > 0 ? runs.reduce((sum, run) => sum + run.llmCallCount, 0) / runs.length : 0,
|
|
8619
|
+
avgTokenUsage: tokenValues.length > 0 ? tokenValues.reduce((sum, value) => sum + value, 0) / tokenValues.length : void 0,
|
|
8620
|
+
avgRetryCount: runs.length > 0 ? runs.reduce((sum, run) => sum + run.retryCount, 0) / runs.length : 0,
|
|
8621
|
+
observationFailureRate: runs.length > 0 ? observationFailures / runs.length : 0,
|
|
8622
|
+
avgGuardrailFailures: runs.length > 0 ? runs.reduce((sum, run) => sum + run.guardrailFailures, 0) / runs.length : 0,
|
|
8623
|
+
avgCircuitViolations: runs.length > 0 ? runs.reduce((sum, run) => sum + run.circuitViolations, 0) / runs.length : 0,
|
|
8624
|
+
avgRedactionWarnings: runs.length > 0 ? runs.reduce((sum, run) => sum + run.redactionWarnings, 0) / runs.length : 0,
|
|
8625
|
+
dominantToolChoice: dominantToolChoice(runs),
|
|
8626
|
+
toolOrderingSignature: orderingSignature(runs)
|
|
8627
|
+
};
|
|
8628
|
+
}
|
|
8629
|
+
var init_metrics = __esm({
|
|
8630
|
+
"packages/core/src/cohort/metrics.ts"() {
|
|
8631
|
+
init_extract();
|
|
8632
|
+
init_trace_metadata();
|
|
8633
|
+
init_storage();
|
|
8634
|
+
}
|
|
8635
|
+
});
|
|
8636
|
+
|
|
8637
|
+
// packages/core/src/cohort/analyze.ts
|
|
8638
|
+
function normalizeMetrics(metrics) {
|
|
8639
|
+
if (metrics === void 0 || metrics.length === 0) return [...DEFAULT_METRICS];
|
|
8640
|
+
const allowed = new Set(COHORT_METRIC_IDS);
|
|
8641
|
+
return metrics.filter((metric) => allowed.has(metric));
|
|
8642
|
+
}
|
|
8643
|
+
async function analyzeCohort(runsInput, options) {
|
|
8644
|
+
const cohortKey = options.cohortKey ?? "cohort";
|
|
8645
|
+
const groupBySpec = parseGroupBySpec(options.groupBy);
|
|
8646
|
+
const metrics = normalizeMetrics(options.metrics);
|
|
8647
|
+
const { runs: filteredRuns, warnings } = filterRunsForCohort(runsInput, {
|
|
8648
|
+
cohortKey,
|
|
8649
|
+
baseline: options.baseline,
|
|
8650
|
+
candidate: options.candidate
|
|
8651
|
+
});
|
|
8652
|
+
const runMetrics = [];
|
|
8653
|
+
for (const run of filteredRuns) {
|
|
8654
|
+
if (run.filePath === void 0) continue;
|
|
8655
|
+
const cohortLabel = resolveCohortLabel(
|
|
8656
|
+
run,
|
|
8657
|
+
cohortKey,
|
|
8658
|
+
options.baseline,
|
|
8659
|
+
options.candidate
|
|
8660
|
+
);
|
|
8661
|
+
runMetrics.push(
|
|
8662
|
+
await computeCohortRunMetrics({
|
|
8663
|
+
runId: run.runId,
|
|
8664
|
+
filePath: run.filePath,
|
|
8665
|
+
metadata: run.metadata,
|
|
8666
|
+
status: run.status,
|
|
8667
|
+
durationMs: run.durationMs,
|
|
8668
|
+
groupKey: resolveRunGroupKey(run, groupBySpec),
|
|
8669
|
+
cohortLabel
|
|
8670
|
+
})
|
|
8671
|
+
);
|
|
8672
|
+
}
|
|
8673
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
8674
|
+
for (const run of runMetrics) {
|
|
8675
|
+
const key = `${run.cohortLabel ?? "*"}::${run.groupKey}`;
|
|
8676
|
+
const bucket = groupMap.get(key) ?? [];
|
|
8677
|
+
bucket.push(run);
|
|
8678
|
+
groupMap.set(key, bucket);
|
|
8679
|
+
}
|
|
8680
|
+
const groups = [...groupMap.entries()].sort(([a], [b]) => a.localeCompare(b)).map(
|
|
8681
|
+
([, bucket]) => aggregateCohortMetrics(
|
|
8682
|
+
bucket,
|
|
8683
|
+
bucket[0].groupKey,
|
|
8684
|
+
bucket[0]?.cohortLabel
|
|
8685
|
+
)
|
|
8686
|
+
);
|
|
8687
|
+
const comparisons = options.baseline !== void 0 && options.candidate !== void 0 ? (() => {
|
|
8688
|
+
const groupKeys = [
|
|
8689
|
+
...new Set(groups.map((group) => group.groupKey))
|
|
8690
|
+
].sort((a, b) => a.localeCompare(b));
|
|
8691
|
+
const items = [];
|
|
8692
|
+
for (const groupKey of groupKeys) {
|
|
8693
|
+
items.push(
|
|
8694
|
+
...compareCohortAggregates(groups, {
|
|
8695
|
+
baseline: options.baseline,
|
|
8696
|
+
candidate: options.candidate,
|
|
8697
|
+
metrics,
|
|
8698
|
+
groupKey
|
|
8699
|
+
})
|
|
8700
|
+
);
|
|
8701
|
+
}
|
|
8702
|
+
return items;
|
|
8703
|
+
})() : [];
|
|
8704
|
+
const regression = comparisons.some((item) => item.regression);
|
|
8705
|
+
return {
|
|
8706
|
+
ok: !regression,
|
|
8707
|
+
traceDir: options.traceDir,
|
|
8708
|
+
...options.baseline !== void 0 ? { baseline: options.baseline } : {},
|
|
8709
|
+
...options.candidate !== void 0 ? { candidate: options.candidate } : {},
|
|
8710
|
+
cohortKey,
|
|
8711
|
+
groupBy: options.groupBy ?? "model",
|
|
8712
|
+
metrics,
|
|
8713
|
+
groups,
|
|
8714
|
+
comparisons,
|
|
8715
|
+
runs: runMetrics,
|
|
8716
|
+
warnings
|
|
8717
|
+
};
|
|
8718
|
+
}
|
|
8719
|
+
var DEFAULT_METRICS;
|
|
8720
|
+
var init_analyze = __esm({
|
|
8721
|
+
"packages/core/src/cohort/analyze.ts"() {
|
|
8722
|
+
init_compare();
|
|
8723
|
+
init_grouping();
|
|
8724
|
+
init_metrics();
|
|
8725
|
+
init_types5();
|
|
8726
|
+
DEFAULT_METRICS = [
|
|
8727
|
+
"errorRate",
|
|
8728
|
+
"duration",
|
|
8729
|
+
"toolChoice",
|
|
8730
|
+
"observationFailure"
|
|
8731
|
+
];
|
|
8732
|
+
}
|
|
8733
|
+
});
|
|
8734
|
+
|
|
8735
|
+
// packages/core/src/exporters/helpers.ts
|
|
8736
|
+
function shouldRedactKey(key) {
|
|
8737
|
+
const k = key.toLowerCase();
|
|
8738
|
+
for (const s of REDACT_SUBSTRINGS) {
|
|
8739
|
+
if (k.includes(s)) return true;
|
|
8740
|
+
}
|
|
8741
|
+
return false;
|
|
8742
|
+
}
|
|
8743
|
+
function safeString(value, maxLength) {
|
|
8744
|
+
if (value === null || value === void 0) return "";
|
|
8745
|
+
let s;
|
|
8746
|
+
if (typeof value === "string") s = value;
|
|
8747
|
+
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
8748
|
+
else s = stableJson(value, false);
|
|
8749
|
+
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
8750
|
+
return `${s.slice(0, maxLength)}\u2026`;
|
|
8751
|
+
}
|
|
8752
|
+
return s;
|
|
8753
|
+
}
|
|
8754
|
+
function escapeMarkdown(value) {
|
|
8755
|
+
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
8756
|
+
}
|
|
8757
|
+
function escapeHtml(value) {
|
|
8758
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
8759
|
+
}
|
|
8760
|
+
function sortKeysDeep(input3) {
|
|
8761
|
+
if (input3 === null || typeof input3 !== "object") return input3;
|
|
8762
|
+
if (Array.isArray(input3)) return input3.map(sortKeysDeep);
|
|
8763
|
+
const o = input3;
|
|
8764
|
+
const out = {};
|
|
8765
|
+
for (const k of Object.keys(o).sort()) {
|
|
8766
|
+
out[k] = sortKeysDeep(o[k]);
|
|
8767
|
+
}
|
|
8768
|
+
return out;
|
|
8769
|
+
}
|
|
8770
|
+
function stableJson(value, pretty) {
|
|
8771
|
+
const sorted = sortKeysDeep(value);
|
|
8772
|
+
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
8773
|
+
}
|
|
8774
|
+
function compactAttributes3(attrs, options) {
|
|
8775
|
+
if (attrs === void 0) return {};
|
|
8776
|
+
const maxLen = options?.maxLength ?? 500;
|
|
8777
|
+
const redacted = options?.redacted ?? true;
|
|
8778
|
+
const out = {};
|
|
8779
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
8780
|
+
if (redacted && shouldRedactKey(key)) {
|
|
8781
|
+
out[key] = "[REDACTED]";
|
|
8782
|
+
continue;
|
|
8783
|
+
}
|
|
8784
|
+
const v = attrs[key];
|
|
8785
|
+
out[key] = compactValue(v, maxLen, redacted);
|
|
8786
|
+
}
|
|
8787
|
+
return out;
|
|
8788
|
+
}
|
|
8789
|
+
function compactValue(value, maxLen, redacted) {
|
|
8790
|
+
if (value === null || typeof value !== "object") {
|
|
8791
|
+
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
8792
|
+
}
|
|
8793
|
+
if (Array.isArray(value)) {
|
|
8794
|
+
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
8795
|
+
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
8796
|
+
return arr;
|
|
8797
|
+
}
|
|
8798
|
+
const o = value;
|
|
8799
|
+
const inner = {};
|
|
8800
|
+
for (const k of Object.keys(o)) {
|
|
8801
|
+
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
8802
|
+
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
8803
|
+
}
|
|
8804
|
+
return inner;
|
|
8805
|
+
}
|
|
8806
|
+
function flattenTree(tree) {
|
|
8807
|
+
const out = [];
|
|
8808
|
+
function walk(nodes) {
|
|
8809
|
+
for (const n of nodes) {
|
|
8810
|
+
out.push(n);
|
|
8811
|
+
if (n.children.length > 0) walk(n.children);
|
|
8812
|
+
}
|
|
8813
|
+
}
|
|
8814
|
+
walk(tree.children);
|
|
8815
|
+
return out;
|
|
8816
|
+
}
|
|
8817
|
+
function zeroKinds() {
|
|
8818
|
+
return {
|
|
8819
|
+
RUN: 0,
|
|
8820
|
+
AGENT: 0,
|
|
8821
|
+
LLM: 0,
|
|
8822
|
+
TOOL: 0,
|
|
8823
|
+
CHAIN: 0,
|
|
8824
|
+
RETRIEVER: 0,
|
|
8825
|
+
DECISION: 0,
|
|
8826
|
+
RESULT: 0,
|
|
8827
|
+
ERROR: 0,
|
|
8828
|
+
LOGIC: 0,
|
|
8829
|
+
LOG: 0,
|
|
8830
|
+
OUTCOME: 0
|
|
8831
|
+
};
|
|
8832
|
+
}
|
|
8833
|
+
var REDACT_SUBSTRINGS;
|
|
8834
|
+
var init_helpers = __esm({
|
|
8835
|
+
"packages/core/src/exporters/helpers.ts"() {
|
|
8836
|
+
REDACT_SUBSTRINGS = [
|
|
8837
|
+
"authorization",
|
|
8838
|
+
"cookie",
|
|
8839
|
+
"token",
|
|
8840
|
+
"apikey",
|
|
8841
|
+
"password",
|
|
8842
|
+
"secret",
|
|
8843
|
+
"email"
|
|
8844
|
+
];
|
|
8845
|
+
}
|
|
8846
|
+
});
|
|
8847
|
+
|
|
8848
|
+
// packages/core/src/cohort/render.ts
|
|
8849
|
+
function formatRate(value) {
|
|
8850
|
+
if (value === void 0) return "n/a";
|
|
8851
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
8852
|
+
}
|
|
8853
|
+
function renderCohortSummaryMarkdown(result) {
|
|
8854
|
+
const lines = [];
|
|
8855
|
+
lines.push("# Cohort analysis");
|
|
8856
|
+
lines.push("");
|
|
8857
|
+
lines.push(`Trace directory: \`${result.traceDir}\``);
|
|
8858
|
+
lines.push(`Group by: \`${result.groupBy}\``);
|
|
8859
|
+
if (result.baseline !== void 0 && result.candidate !== void 0) {
|
|
8860
|
+
lines.push(
|
|
8861
|
+
`Baseline/Candidate key: \`${result.cohortKey}\` (${result.baseline} vs ${result.candidate})`
|
|
8862
|
+
);
|
|
8863
|
+
}
|
|
8864
|
+
lines.push(`Status: **${result.ok ? "PASS" : "REGRESSION"}**`);
|
|
8865
|
+
lines.push("");
|
|
8866
|
+
if (result.warnings.length > 0) {
|
|
8867
|
+
lines.push("## Warnings");
|
|
8868
|
+
for (const warning of result.warnings) lines.push(`- ${warning}`);
|
|
8869
|
+
lines.push("");
|
|
8870
|
+
}
|
|
8871
|
+
lines.push("## Groups");
|
|
8872
|
+
for (const group of result.groups) {
|
|
8873
|
+
lines.push(
|
|
8874
|
+
`### ${group.cohortLabel ?? "all"} / ${group.groupKey} (${group.runCount} runs)`
|
|
8875
|
+
);
|
|
8876
|
+
lines.push(`- Error rate: ${formatRate(group.errorRate)}`);
|
|
8877
|
+
if (group.avgDurationMs !== void 0) {
|
|
8878
|
+
lines.push(`- Avg duration: ${Math.round(group.avgDurationMs)} ms`);
|
|
8879
|
+
}
|
|
8880
|
+
if (group.dominantToolChoice !== void 0) {
|
|
8881
|
+
lines.push(`- Dominant tools: ${group.dominantToolChoice}`);
|
|
8882
|
+
}
|
|
8883
|
+
lines.push(
|
|
8884
|
+
`- Observation failure rate: ${formatRate(group.observationFailureRate)}`
|
|
8885
|
+
);
|
|
8886
|
+
lines.push("");
|
|
8887
|
+
}
|
|
8888
|
+
if (result.comparisons.length > 0) {
|
|
8889
|
+
lines.push("## Comparisons");
|
|
8890
|
+
for (const comparison of result.comparisons) {
|
|
8891
|
+
const flag = comparison.regression ? " **REGRESSION**" : "";
|
|
8892
|
+
lines.push(`- ${comparison.message}${flag}`);
|
|
8893
|
+
}
|
|
8894
|
+
lines.push("");
|
|
8895
|
+
}
|
|
8896
|
+
return lines.join("\n").trimEnd();
|
|
8897
|
+
}
|
|
8898
|
+
function renderCohortReportHtml(result) {
|
|
8899
|
+
const rows = result.groups.map(
|
|
8900
|
+
(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>`
|
|
8901
|
+
).join("");
|
|
8902
|
+
const comparisons = result.comparisons.map(
|
|
8903
|
+
(item) => `<li>${escapeHtml(item.message)}${item.regression ? " <strong>REGRESSION</strong>" : ""}</li>`
|
|
8904
|
+
).join("");
|
|
8905
|
+
return `<!DOCTYPE html>
|
|
8906
|
+
<html lang="en">
|
|
8907
|
+
<head>
|
|
8908
|
+
<meta charset="utf-8" />
|
|
8909
|
+
<title>Cohort report</title>
|
|
8910
|
+
<style>
|
|
8911
|
+
body { font-family: system-ui, sans-serif; margin: 2rem; }
|
|
8912
|
+
table { border-collapse: collapse; width: 100%; }
|
|
8913
|
+
th, td { border: 1px solid #ddd; padding: 0.5rem; text-align: left; }
|
|
8914
|
+
th { background: #f6f6f6; }
|
|
8915
|
+
</style>
|
|
8916
|
+
</head>
|
|
8917
|
+
<body>
|
|
8918
|
+
<h1>Cohort analysis</h1>
|
|
8919
|
+
<p>Status: <strong>${result.ok ? "PASS" : "REGRESSION"}</strong></p>
|
|
8920
|
+
<p>Trace directory: <code>${escapeHtml(result.traceDir)}</code></p>
|
|
8921
|
+
<h2>Groups</h2>
|
|
8922
|
+
<table>
|
|
8923
|
+
<thead><tr><th>Cohort</th><th>Group</th><th>Runs</th><th>Error rate</th><th>Avg duration (ms)</th></tr></thead>
|
|
8924
|
+
<tbody>${rows}</tbody>
|
|
8925
|
+
</table>
|
|
8926
|
+
${result.comparisons.length > 0 ? `<h2>Comparisons</h2><ul>${comparisons}</ul>` : ""}
|
|
8927
|
+
</body>
|
|
8928
|
+
</html>`;
|
|
8929
|
+
}
|
|
8930
|
+
function renderCohortReport(result, options = {}) {
|
|
8931
|
+
const format = options.format ?? "markdown";
|
|
8932
|
+
if (format === "json") return JSON.stringify(result, null, 2);
|
|
8933
|
+
if (format === "html") return renderCohortReportHtml(result);
|
|
8934
|
+
return renderCohortSummaryMarkdown(result);
|
|
8935
|
+
}
|
|
8936
|
+
var init_render2 = __esm({
|
|
8937
|
+
"packages/core/src/cohort/render.ts"() {
|
|
8938
|
+
init_helpers();
|
|
8939
|
+
}
|
|
8940
|
+
});
|
|
8941
|
+
|
|
8942
|
+
// packages/core/src/cohort/index.ts
|
|
8943
|
+
var init_cohort2 = __esm({
|
|
8944
|
+
"packages/core/src/cohort/index.ts"() {
|
|
8945
|
+
init_types5();
|
|
8946
|
+
init_analyze();
|
|
8947
|
+
init_compare();
|
|
8948
|
+
init_grouping();
|
|
8949
|
+
init_metrics();
|
|
8950
|
+
init_render2();
|
|
8951
|
+
}
|
|
8952
|
+
});
|
|
8953
|
+
|
|
8278
8954
|
// packages/core/src/inspect-run.ts
|
|
8279
8955
|
var init_inspect_run = __esm({
|
|
8280
8956
|
"packages/core/src/inspect-run.ts"() {
|
|
@@ -8320,13 +8996,14 @@ var init_advanced = __esm({
|
|
|
8320
8996
|
init_duration();
|
|
8321
8997
|
init_bundle();
|
|
8322
8998
|
init_suite();
|
|
8999
|
+
init_cohort2();
|
|
8323
9000
|
init_maybe_inspect_run();
|
|
8324
9001
|
}
|
|
8325
9002
|
});
|
|
8326
9003
|
|
|
8327
9004
|
// packages/index-sqlite/src/types.ts
|
|
8328
9005
|
var INDEX_SCHEMA_VERSION, INDEX_DB_FILENAME;
|
|
8329
|
-
var
|
|
9006
|
+
var init_types6 = __esm({
|
|
8330
9007
|
"packages/index-sqlite/src/types.ts"() {
|
|
8331
9008
|
INDEX_SCHEMA_VERSION = "1";
|
|
8332
9009
|
INDEX_DB_FILENAME = "trace-index.sqlite";
|
|
@@ -8384,19 +9061,19 @@ var require_file_uri_to_path = __commonJS({
|
|
|
8384
9061
|
var rest = decodeURI(uri.substring(7));
|
|
8385
9062
|
var firstSlash = rest.indexOf("/");
|
|
8386
9063
|
var host = rest.substring(0, firstSlash);
|
|
8387
|
-
var
|
|
9064
|
+
var path31 = rest.substring(firstSlash + 1);
|
|
8388
9065
|
if ("localhost" == host) host = "";
|
|
8389
9066
|
if (host) {
|
|
8390
9067
|
host = sep + sep + host;
|
|
8391
9068
|
}
|
|
8392
|
-
|
|
9069
|
+
path31 = path31.replace(/^(.+)\|/, "$1:");
|
|
8393
9070
|
if (sep == "\\") {
|
|
8394
|
-
|
|
9071
|
+
path31 = path31.replace(/\//g, "\\");
|
|
8395
9072
|
}
|
|
8396
|
-
if (/^.+\:/.test(
|
|
8397
|
-
|
|
9073
|
+
if (/^.+\:/.test(path31)) ; else {
|
|
9074
|
+
path31 = sep + path31;
|
|
8398
9075
|
}
|
|
8399
|
-
return host +
|
|
9076
|
+
return host + path31;
|
|
8400
9077
|
}
|
|
8401
9078
|
}
|
|
8402
9079
|
});
|
|
@@ -8405,18 +9082,18 @@ var require_file_uri_to_path = __commonJS({
|
|
|
8405
9082
|
var require_bindings = __commonJS({
|
|
8406
9083
|
"node_modules/.pnpm/bindings@1.5.0/node_modules/bindings/bindings.js"(exports$1, module) {
|
|
8407
9084
|
var fs = __require("fs");
|
|
8408
|
-
var
|
|
9085
|
+
var path31 = __require("path");
|
|
8409
9086
|
var fileURLToPath2 = require_file_uri_to_path();
|
|
8410
|
-
var join =
|
|
8411
|
-
var dirname =
|
|
8412
|
-
var exists2 = fs.accessSync && function(
|
|
9087
|
+
var join = path31.join;
|
|
9088
|
+
var dirname = path31.dirname;
|
|
9089
|
+
var exists2 = fs.accessSync && function(path32) {
|
|
8413
9090
|
try {
|
|
8414
|
-
fs.accessSync(
|
|
9091
|
+
fs.accessSync(path32);
|
|
8415
9092
|
} catch (e) {
|
|
8416
9093
|
return false;
|
|
8417
9094
|
}
|
|
8418
9095
|
return true;
|
|
8419
|
-
} || fs.existsSync ||
|
|
9096
|
+
} || fs.existsSync || path31.existsSync;
|
|
8420
9097
|
var defaults = {
|
|
8421
9098
|
arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
|
|
8422
9099
|
compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
|
|
@@ -8461,7 +9138,7 @@ var require_bindings = __commonJS({
|
|
|
8461
9138
|
if (!opts.module_root) {
|
|
8462
9139
|
opts.module_root = exports$1.getRoot(exports$1.getFileName());
|
|
8463
9140
|
}
|
|
8464
|
-
if (
|
|
9141
|
+
if (path31.extname(opts.bindings) != ".node") {
|
|
8465
9142
|
opts.bindings += ".node";
|
|
8466
9143
|
}
|
|
8467
9144
|
var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
@@ -8696,7 +9373,7 @@ var require_pragma = __commonJS({
|
|
|
8696
9373
|
var require_backup = __commonJS({
|
|
8697
9374
|
"node_modules/.pnpm/better-sqlite3@11.10.0/node_modules/better-sqlite3/lib/methods/backup.js"(exports$1, module) {
|
|
8698
9375
|
var fs = __require("fs");
|
|
8699
|
-
var
|
|
9376
|
+
var path31 = __require("path");
|
|
8700
9377
|
var { promisify } = __require("util");
|
|
8701
9378
|
var { cppdb } = require_util();
|
|
8702
9379
|
var fsAccess = promisify(fs.access);
|
|
@@ -8712,7 +9389,7 @@ var require_backup = __commonJS({
|
|
|
8712
9389
|
if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
|
|
8713
9390
|
if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
|
|
8714
9391
|
if (handler != null && typeof handler !== "function") throw new TypeError('Expected the "progress" option to be a function');
|
|
8715
|
-
await fsAccess(
|
|
9392
|
+
await fsAccess(path31.dirname(filename)).catch(() => {
|
|
8716
9393
|
throw new TypeError("Cannot save backup because the directory does not exist");
|
|
8717
9394
|
});
|
|
8718
9395
|
const isNewFile = await fsAccess(filename).then(() => false, () => true);
|
|
@@ -9012,7 +9689,7 @@ var require_inspect = __commonJS({
|
|
|
9012
9689
|
var require_database = __commonJS({
|
|
9013
9690
|
"node_modules/.pnpm/better-sqlite3@11.10.0/node_modules/better-sqlite3/lib/database.js"(exports$1, module) {
|
|
9014
9691
|
var fs = __require("fs");
|
|
9015
|
-
var
|
|
9692
|
+
var path31 = __require("path");
|
|
9016
9693
|
var util = require_util();
|
|
9017
9694
|
var SqliteError = require_sqlite_error();
|
|
9018
9695
|
var DEFAULT_ADDON;
|
|
@@ -9048,7 +9725,7 @@ var require_database = __commonJS({
|
|
|
9048
9725
|
addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
|
|
9049
9726
|
} else if (typeof nativeBinding === "string") {
|
|
9050
9727
|
const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
9051
|
-
addon = requireFunc(
|
|
9728
|
+
addon = requireFunc(path31.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
|
|
9052
9729
|
} else {
|
|
9053
9730
|
addon = nativeBinding;
|
|
9054
9731
|
}
|
|
@@ -9056,7 +9733,7 @@ var require_database = __commonJS({
|
|
|
9056
9733
|
addon.setErrorConstructor(SqliteError);
|
|
9057
9734
|
addon.isInitialized = true;
|
|
9058
9735
|
}
|
|
9059
|
-
if (!anonymous && !fs.existsSync(
|
|
9736
|
+
if (!anonymous && !fs.existsSync(path31.dirname(filename))) {
|
|
9060
9737
|
throw new TypeError("Cannot open database because the directory does not exist");
|
|
9061
9738
|
}
|
|
9062
9739
|
Object.defineProperties(this, {
|
|
@@ -9104,7 +9781,7 @@ function metaDefaults(sourceDir, fileCount) {
|
|
|
9104
9781
|
var INDEX_SCHEMA_SQL, DERIVE_SESSIONS_SQL, META_KEYS;
|
|
9105
9782
|
var init_schema = __esm({
|
|
9106
9783
|
"packages/index-sqlite/src/schema.ts"() {
|
|
9107
|
-
|
|
9784
|
+
init_types6();
|
|
9108
9785
|
INDEX_SCHEMA_SQL = `
|
|
9109
9786
|
DROP TABLE IF EXISTS meta;
|
|
9110
9787
|
DROP TABLE IF EXISTS errors;
|
|
@@ -9357,7 +10034,7 @@ var init_builder = __esm({
|
|
|
9357
10034
|
import_better_sqlite3 = __toESM(require_lib());
|
|
9358
10035
|
init_advanced();
|
|
9359
10036
|
init_schema();
|
|
9360
|
-
|
|
10037
|
+
init_types6();
|
|
9361
10038
|
DEFAULT_MAX_RUNS = 1e4;
|
|
9362
10039
|
rebuildIndex = buildIndex;
|
|
9363
10040
|
}
|
|
@@ -9507,14 +10184,14 @@ __export(src_exports, {
|
|
|
9507
10184
|
});
|
|
9508
10185
|
var init_src = __esm({
|
|
9509
10186
|
"packages/index-sqlite/src/index.ts"() {
|
|
9510
|
-
|
|
10187
|
+
init_types6();
|
|
9511
10188
|
init_builder();
|
|
9512
10189
|
init_query();
|
|
9513
10190
|
}
|
|
9514
10191
|
});
|
|
9515
10192
|
|
|
9516
10193
|
// package.json
|
|
9517
|
-
var version = "5.
|
|
10194
|
+
var version = "5.1.0";
|
|
9518
10195
|
|
|
9519
10196
|
// packages/cli/src/list.ts
|
|
9520
10197
|
init_advanced();
|
|
@@ -10459,7 +11136,7 @@ init_nanoid();
|
|
|
10459
11136
|
function isFiniteNumber2(v) {
|
|
10460
11137
|
return typeof v === "number" && Number.isFinite(v);
|
|
10461
11138
|
}
|
|
10462
|
-
function
|
|
11139
|
+
function safeString2(v) {
|
|
10463
11140
|
if (typeof v !== "string") return void 0;
|
|
10464
11141
|
const t = v.trim();
|
|
10465
11142
|
return t === "" ? void 0 : t;
|
|
@@ -10509,7 +11186,7 @@ var EventNormalizer = class {
|
|
|
10509
11186
|
let runId;
|
|
10510
11187
|
for (const k of cfg.runIdKeys) {
|
|
10511
11188
|
const v = raw[k];
|
|
10512
|
-
const s =
|
|
11189
|
+
const s = safeString2(v);
|
|
10513
11190
|
if (s) {
|
|
10514
11191
|
runId = s;
|
|
10515
11192
|
break;
|
|
@@ -10525,7 +11202,7 @@ var EventNormalizer = class {
|
|
|
10525
11202
|
}
|
|
10526
11203
|
};
|
|
10527
11204
|
}
|
|
10528
|
-
const eventName =
|
|
11205
|
+
const eventName = safeString2(raw[cfg.eventKey]);
|
|
10529
11206
|
if (!eventName) {
|
|
10530
11207
|
return {
|
|
10531
11208
|
warning: {
|
|
@@ -10543,7 +11220,7 @@ var EventNormalizer = class {
|
|
|
10543
11220
|
const timestamp = parsedTs ?? Date.now();
|
|
10544
11221
|
const timestampMissing = parsedTs === void 0;
|
|
10545
11222
|
const parentIdKey = cfg.parentIdKey;
|
|
10546
|
-
const parentId = parentIdKey ?
|
|
11223
|
+
const parentId = parentIdKey ? safeString2(raw[parentIdKey]) : void 0;
|
|
10547
11224
|
let durationMs2;
|
|
10548
11225
|
const durationKey = cfg.durationKey;
|
|
10549
11226
|
if (durationKey) {
|
|
@@ -10554,7 +11231,7 @@ var EventNormalizer = class {
|
|
|
10554
11231
|
}
|
|
10555
11232
|
let status;
|
|
10556
11233
|
const statusKey = cfg.statusKey;
|
|
10557
|
-
const statusRaw = statusKey ?
|
|
11234
|
+
const statusRaw = statusKey ? safeString2(raw[statusKey]) : void 0;
|
|
10558
11235
|
if (statusRaw === "running" || statusRaw === "ok" || statusRaw === "error") {
|
|
10559
11236
|
status = statusRaw;
|
|
10560
11237
|
} else if (mapping?.status) {
|
|
@@ -11498,115 +12175,8 @@ function redactRunTreeForExport(tree, options) {
|
|
|
11498
12175
|
return clone;
|
|
11499
12176
|
}
|
|
11500
12177
|
|
|
11501
|
-
// packages/core/src/exporters/helpers.ts
|
|
11502
|
-
var REDACT_SUBSTRINGS = [
|
|
11503
|
-
"authorization",
|
|
11504
|
-
"cookie",
|
|
11505
|
-
"token",
|
|
11506
|
-
"apikey",
|
|
11507
|
-
"password",
|
|
11508
|
-
"secret",
|
|
11509
|
-
"email"
|
|
11510
|
-
];
|
|
11511
|
-
function shouldRedactKey(key) {
|
|
11512
|
-
const k = key.toLowerCase();
|
|
11513
|
-
for (const s of REDACT_SUBSTRINGS) {
|
|
11514
|
-
if (k.includes(s)) return true;
|
|
11515
|
-
}
|
|
11516
|
-
return false;
|
|
11517
|
-
}
|
|
11518
|
-
function safeString2(value, maxLength) {
|
|
11519
|
-
if (value === null || value === void 0) return "";
|
|
11520
|
-
let s;
|
|
11521
|
-
if (typeof value === "string") s = value;
|
|
11522
|
-
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
11523
|
-
else s = stableJson(value, false);
|
|
11524
|
-
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
11525
|
-
return `${s.slice(0, maxLength)}\u2026`;
|
|
11526
|
-
}
|
|
11527
|
-
return s;
|
|
11528
|
-
}
|
|
11529
|
-
function escapeMarkdown(value) {
|
|
11530
|
-
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
11531
|
-
}
|
|
11532
|
-
function escapeHtml(value) {
|
|
11533
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
11534
|
-
}
|
|
11535
|
-
function sortKeysDeep(input3) {
|
|
11536
|
-
if (input3 === null || typeof input3 !== "object") return input3;
|
|
11537
|
-
if (Array.isArray(input3)) return input3.map(sortKeysDeep);
|
|
11538
|
-
const o = input3;
|
|
11539
|
-
const out = {};
|
|
11540
|
-
for (const k of Object.keys(o).sort()) {
|
|
11541
|
-
out[k] = sortKeysDeep(o[k]);
|
|
11542
|
-
}
|
|
11543
|
-
return out;
|
|
11544
|
-
}
|
|
11545
|
-
function stableJson(value, pretty) {
|
|
11546
|
-
const sorted = sortKeysDeep(value);
|
|
11547
|
-
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
11548
|
-
}
|
|
11549
|
-
function compactAttributes3(attrs, options) {
|
|
11550
|
-
if (attrs === void 0) return {};
|
|
11551
|
-
const maxLen = options?.maxLength ?? 500;
|
|
11552
|
-
const redacted = options?.redacted ?? true;
|
|
11553
|
-
const out = {};
|
|
11554
|
-
for (const key of Object.keys(attrs).sort()) {
|
|
11555
|
-
if (redacted && shouldRedactKey(key)) {
|
|
11556
|
-
out[key] = "[REDACTED]";
|
|
11557
|
-
continue;
|
|
11558
|
-
}
|
|
11559
|
-
const v = attrs[key];
|
|
11560
|
-
out[key] = compactValue(v, maxLen, redacted);
|
|
11561
|
-
}
|
|
11562
|
-
return out;
|
|
11563
|
-
}
|
|
11564
|
-
function compactValue(value, maxLen, redacted) {
|
|
11565
|
-
if (value === null || typeof value !== "object") {
|
|
11566
|
-
return typeof value === "string" ? safeString2(value, maxLen) : value;
|
|
11567
|
-
}
|
|
11568
|
-
if (Array.isArray(value)) {
|
|
11569
|
-
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
11570
|
-
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
11571
|
-
return arr;
|
|
11572
|
-
}
|
|
11573
|
-
const o = value;
|
|
11574
|
-
const inner = {};
|
|
11575
|
-
for (const k of Object.keys(o)) {
|
|
11576
|
-
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
11577
|
-
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
11578
|
-
}
|
|
11579
|
-
return inner;
|
|
11580
|
-
}
|
|
11581
|
-
function flattenTree(tree) {
|
|
11582
|
-
const out = [];
|
|
11583
|
-
function walk(nodes) {
|
|
11584
|
-
for (const n of nodes) {
|
|
11585
|
-
out.push(n);
|
|
11586
|
-
if (n.children.length > 0) walk(n.children);
|
|
11587
|
-
}
|
|
11588
|
-
}
|
|
11589
|
-
walk(tree.children);
|
|
11590
|
-
return out;
|
|
11591
|
-
}
|
|
11592
|
-
function zeroKinds() {
|
|
11593
|
-
return {
|
|
11594
|
-
RUN: 0,
|
|
11595
|
-
AGENT: 0,
|
|
11596
|
-
LLM: 0,
|
|
11597
|
-
TOOL: 0,
|
|
11598
|
-
CHAIN: 0,
|
|
11599
|
-
RETRIEVER: 0,
|
|
11600
|
-
DECISION: 0,
|
|
11601
|
-
RESULT: 0,
|
|
11602
|
-
ERROR: 0,
|
|
11603
|
-
LOGIC: 0,
|
|
11604
|
-
LOG: 0,
|
|
11605
|
-
OUTCOME: 0
|
|
11606
|
-
};
|
|
11607
|
-
}
|
|
11608
|
-
|
|
11609
12178
|
// packages/core/src/exporters/html-exporter.ts
|
|
12179
|
+
init_helpers();
|
|
11610
12180
|
function renderTreeHtml(nodes, ulClass = "tree") {
|
|
11611
12181
|
if (nodes.length === 0) return "";
|
|
11612
12182
|
const parts = [`<ul class="${ulClass}">`];
|
|
@@ -11679,7 +12249,7 @@ function exportHtml(tree, options) {
|
|
|
11679
12249
|
if (includeErrors && errors.length > 0) {
|
|
11680
12250
|
errorsHtml += "<h2>Errors</h2><ul>";
|
|
11681
12251
|
for (const n of errors) {
|
|
11682
|
-
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ?
|
|
12252
|
+
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
|
|
11683
12253
|
n.event.attributes.error.message,
|
|
11684
12254
|
maxLen
|
|
11685
12255
|
) : "";
|
|
@@ -11744,6 +12314,7 @@ ${attrsHtml}
|
|
|
11744
12314
|
}
|
|
11745
12315
|
|
|
11746
12316
|
// packages/core/src/exporters/markdown-exporter.ts
|
|
12317
|
+
init_helpers();
|
|
11747
12318
|
function renderTreeAscii(nodes, indent = "") {
|
|
11748
12319
|
const lines = [];
|
|
11749
12320
|
for (let i = 0; i < nodes.length; i++) {
|
|
@@ -11828,7 +12399,7 @@ function exportMarkdown(tree, options) {
|
|
|
11828
12399
|
lines.push("## Errors");
|
|
11829
12400
|
lines.push("");
|
|
11830
12401
|
for (const n of errors) {
|
|
11831
|
-
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ?
|
|
12402
|
+
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
|
|
11832
12403
|
n.event.attributes.error.message,
|
|
11833
12404
|
maxLen
|
|
11834
12405
|
) : "";
|
|
@@ -11866,6 +12437,7 @@ function exportMarkdown(tree, options) {
|
|
|
11866
12437
|
warnings
|
|
11867
12438
|
};
|
|
11868
12439
|
}
|
|
12440
|
+
init_helpers();
|
|
11869
12441
|
function hexFrom(seed, byteLen) {
|
|
11870
12442
|
return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
11871
12443
|
}
|
|
@@ -11992,6 +12564,9 @@ function exportOpenInference(tree, options) {
|
|
|
11992
12564
|
warnings
|
|
11993
12565
|
};
|
|
11994
12566
|
}
|
|
12567
|
+
|
|
12568
|
+
// packages/core/src/exporters/otlp-json-exporter.ts
|
|
12569
|
+
init_helpers();
|
|
11995
12570
|
function hexFrom2(seed, byteLen) {
|
|
11996
12571
|
return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
11997
12572
|
}
|
|
@@ -12195,7 +12770,11 @@ function validateExportContent(format, content) {
|
|
|
12195
12770
|
return { ok: false, format, errors, warnings };
|
|
12196
12771
|
}
|
|
12197
12772
|
|
|
12773
|
+
// packages/core/src/exporters/index.ts
|
|
12774
|
+
init_helpers();
|
|
12775
|
+
|
|
12198
12776
|
// packages/core/src/exporters/manual-trace-adapter.ts
|
|
12777
|
+
init_helpers();
|
|
12199
12778
|
function stepTypeToInspectKind(t) {
|
|
12200
12779
|
switch (t) {
|
|
12201
12780
|
case "llm":
|
|
@@ -12375,6 +12954,7 @@ function validateExport(result) {
|
|
|
12375
12954
|
}
|
|
12376
12955
|
|
|
12377
12956
|
// packages/core/src/report.ts
|
|
12957
|
+
init_helpers();
|
|
12378
12958
|
init_timeline();
|
|
12379
12959
|
init_what();
|
|
12380
12960
|
init_outcomes();
|
|
@@ -12531,6 +13111,9 @@ ${attrsSection}
|
|
|
12531
13111
|
};
|
|
12532
13112
|
}
|
|
12533
13113
|
|
|
13114
|
+
// packages/core/src/entries/exporters.ts
|
|
13115
|
+
init_helpers();
|
|
13116
|
+
|
|
12534
13117
|
// packages/cli/src/export.ts
|
|
12535
13118
|
function parseRedactionProfile(s) {
|
|
12536
13119
|
const v = (s ?? "local").trim().toLowerCase();
|
|
@@ -12752,6 +13335,7 @@ function manualTraceEventsToComparableRun(events) {
|
|
|
12752
13335
|
}
|
|
12753
13336
|
|
|
12754
13337
|
// packages/core/src/diff/engine.ts
|
|
13338
|
+
init_helpers();
|
|
12755
13339
|
var DEFAULT_THRESHOLD_MS = 0;
|
|
12756
13340
|
function pathSeg(step, index) {
|
|
12757
13341
|
return { index, name: step.name, stepId: step.id };
|
|
@@ -12791,13 +13375,13 @@ function pairSteps(left, right) {
|
|
|
12791
13375
|
return pairs;
|
|
12792
13376
|
}
|
|
12793
13377
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
12794
|
-
const
|
|
13378
|
+
const path31 = buildPath(segments);
|
|
12795
13379
|
if (L.name !== R.name) {
|
|
12796
13380
|
out.push({
|
|
12797
13381
|
kind: "structure",
|
|
12798
13382
|
severity: "warning",
|
|
12799
13383
|
message: "Step name differs",
|
|
12800
|
-
path:
|
|
13384
|
+
path: path31,
|
|
12801
13385
|
left: L.name,
|
|
12802
13386
|
right: R.name
|
|
12803
13387
|
});
|
|
@@ -12807,7 +13391,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12807
13391
|
kind: "step-type",
|
|
12808
13392
|
severity: "warning",
|
|
12809
13393
|
message: "Step type differs",
|
|
12810
|
-
path:
|
|
13394
|
+
path: path31,
|
|
12811
13395
|
left: L.type,
|
|
12812
13396
|
right: R.type
|
|
12813
13397
|
});
|
|
@@ -12817,7 +13401,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12817
13401
|
kind: "step-status",
|
|
12818
13402
|
severity: "warning",
|
|
12819
13403
|
message: "Step status differs",
|
|
12820
|
-
path:
|
|
13404
|
+
path: path31,
|
|
12821
13405
|
left: L.status,
|
|
12822
13406
|
right: R.status
|
|
12823
13407
|
});
|
|
@@ -12829,7 +13413,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12829
13413
|
kind: "error",
|
|
12830
13414
|
severity: "error",
|
|
12831
13415
|
message: "Step error message differs",
|
|
12832
|
-
path:
|
|
13416
|
+
path: path31,
|
|
12833
13417
|
left: le || void 0,
|
|
12834
13418
|
right: re || void 0
|
|
12835
13419
|
});
|
|
@@ -12847,7 +13431,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12847
13431
|
kind: "duration",
|
|
12848
13432
|
severity: "info",
|
|
12849
13433
|
message: "Step duration differs",
|
|
12850
|
-
path:
|
|
13434
|
+
path: path31,
|
|
12851
13435
|
left: ld,
|
|
12852
13436
|
right: rd
|
|
12853
13437
|
});
|
|
@@ -12860,7 +13444,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12860
13444
|
kind: "metadata",
|
|
12861
13445
|
severity: "info",
|
|
12862
13446
|
message: "Step metadata differs",
|
|
12863
|
-
path:
|
|
13447
|
+
path: path31,
|
|
12864
13448
|
left: L.metadata,
|
|
12865
13449
|
right: R.metadata
|
|
12866
13450
|
});
|
|
@@ -12872,7 +13456,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
12872
13456
|
kind: "output",
|
|
12873
13457
|
severity: "info",
|
|
12874
13458
|
message: "Output preview differs",
|
|
12875
|
-
path:
|
|
13459
|
+
path: path31,
|
|
12876
13460
|
left: L.outputPreview,
|
|
12877
13461
|
right: R.outputPreview
|
|
12878
13462
|
});
|
|
@@ -13033,11 +13617,11 @@ function diffRuns(left, right, options) {
|
|
|
13033
13617
|
|
|
13034
13618
|
// packages/core/src/diff/renderer.ts
|
|
13035
13619
|
init_source();
|
|
13036
|
-
function formatPath(
|
|
13037
|
-
if (
|
|
13620
|
+
function formatPath(path31) {
|
|
13621
|
+
if (path31 === void 0 || path31.path.length === 0) {
|
|
13038
13622
|
return "(run)";
|
|
13039
13623
|
}
|
|
13040
|
-
return
|
|
13624
|
+
return path31.path.map((s) => s.name).join(" > ");
|
|
13041
13625
|
}
|
|
13042
13626
|
function formatValue(v, verbose) {
|
|
13043
13627
|
if (v === void 0) return "(undefined)";
|
|
@@ -14158,17 +14742,17 @@ function applyRule(rule, value, replacement) {
|
|
|
14158
14742
|
}
|
|
14159
14743
|
return value;
|
|
14160
14744
|
}
|
|
14161
|
-
function childPath(
|
|
14745
|
+
function childPath(path31, key) {
|
|
14162
14746
|
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
|
|
14163
|
-
return
|
|
14747
|
+
return path31 ? `${path31}.${key}` : key;
|
|
14164
14748
|
}
|
|
14165
|
-
return `${
|
|
14749
|
+
return `${path31 || "$"}[${JSON.stringify(key)}]`;
|
|
14166
14750
|
}
|
|
14167
|
-
function indexPath(
|
|
14168
|
-
return `${
|
|
14751
|
+
function indexPath(path31, index) {
|
|
14752
|
+
return `${path31 || "$"}[${index}]`;
|
|
14169
14753
|
}
|
|
14170
|
-
function makeFinding(
|
|
14171
|
-
return preview === void 0 ? { path:
|
|
14754
|
+
function makeFinding(path31, detector, action, matchKind, severity = "warning", preview) {
|
|
14755
|
+
return preview === void 0 ? { path: path31, detector, action, severity, matchKind } : { path: path31, detector, action, severity, matchKind, preview };
|
|
14172
14756
|
}
|
|
14173
14757
|
function createRedactionProfile(profile = "local") {
|
|
14174
14758
|
switch (profile) {
|
|
@@ -14237,11 +14821,11 @@ var Redactor2 = class {
|
|
|
14237
14821
|
#recordFinding(state, finding) {
|
|
14238
14822
|
if (this.#collectFindings) state.findings.push(finding);
|
|
14239
14823
|
}
|
|
14240
|
-
#redactValue(value, key,
|
|
14824
|
+
#redactValue(value, key, path31, depth, state) {
|
|
14241
14825
|
if (depth > this.#maxDepth) {
|
|
14242
14826
|
this.#recordFinding(
|
|
14243
14827
|
state,
|
|
14244
|
-
makeFinding(
|
|
14828
|
+
makeFinding(path31, "structure.maxDepth", "truncate", "value", "warning")
|
|
14245
14829
|
);
|
|
14246
14830
|
return "[Truncated]";
|
|
14247
14831
|
}
|
|
@@ -14250,19 +14834,19 @@ var Redactor2 = class {
|
|
|
14250
14834
|
if (rule) {
|
|
14251
14835
|
this.#recordFinding(
|
|
14252
14836
|
state,
|
|
14253
|
-
makeFinding(
|
|
14837
|
+
makeFinding(path31, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
14254
14838
|
);
|
|
14255
14839
|
return applyRule(rule, value, this.#replacement);
|
|
14256
14840
|
}
|
|
14257
14841
|
}
|
|
14258
14842
|
for (const detector of this.#detectors) {
|
|
14259
|
-
const detections = detector.detect({ path:
|
|
14843
|
+
const detections = detector.detect({ path: path31, key, value });
|
|
14260
14844
|
for (const detection of detections) {
|
|
14261
14845
|
const action = detection.action ?? "replace";
|
|
14262
14846
|
this.#recordFinding(
|
|
14263
14847
|
state,
|
|
14264
14848
|
makeFinding(
|
|
14265
|
-
|
|
14849
|
+
path31,
|
|
14266
14850
|
detector.id,
|
|
14267
14851
|
action,
|
|
14268
14852
|
detection.matchKind ?? detector.matchKind ?? "custom",
|
|
@@ -14280,7 +14864,7 @@ var Redactor2 = class {
|
|
|
14280
14864
|
const out = [];
|
|
14281
14865
|
state.seen.set(value, out);
|
|
14282
14866
|
value.forEach((item, index) => {
|
|
14283
|
-
out[index] = this.#redactValue(item, void 0, indexPath(
|
|
14867
|
+
out[index] = this.#redactValue(item, void 0, indexPath(path31, index), depth + 1, state);
|
|
14284
14868
|
});
|
|
14285
14869
|
return out;
|
|
14286
14870
|
}
|
|
@@ -14292,7 +14876,7 @@ var Redactor2 = class {
|
|
|
14292
14876
|
out[entryKey] = this.#redactValue(
|
|
14293
14877
|
entryValue,
|
|
14294
14878
|
entryKey,
|
|
14295
|
-
childPath(
|
|
14879
|
+
childPath(path31 === "$" ? "" : path31, entryKey),
|
|
14296
14880
|
depth + 1,
|
|
14297
14881
|
state
|
|
14298
14882
|
);
|
|
@@ -15233,23 +15817,23 @@ function evaluatePromptInjection(text, options = {}) {
|
|
|
15233
15817
|
}
|
|
15234
15818
|
return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
|
|
15235
15819
|
}
|
|
15236
|
-
function validateSchemaField(value, field,
|
|
15820
|
+
function validateSchemaField(value, field, path31, evidence) {
|
|
15237
15821
|
const ruleId = "guardrail.structured-output";
|
|
15238
15822
|
if (field.type) {
|
|
15239
15823
|
const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
15240
15824
|
if (actual !== field.type) {
|
|
15241
|
-
evidence.push({ ruleId, path:
|
|
15825
|
+
evidence.push({ ruleId, path: path31, preview: `expected ${field.type}, got ${actual}` });
|
|
15242
15826
|
return;
|
|
15243
15827
|
}
|
|
15244
15828
|
}
|
|
15245
15829
|
if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
|
|
15246
|
-
evidence.push({ ruleId, path:
|
|
15830
|
+
evidence.push({ ruleId, path: path31, preview: "value not in enum" });
|
|
15247
15831
|
}
|
|
15248
15832
|
if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
|
|
15249
15833
|
const record = value;
|
|
15250
15834
|
for (const key of field.required) {
|
|
15251
15835
|
if (!(key in record)) {
|
|
15252
|
-
evidence.push({ ruleId, path: `${
|
|
15836
|
+
evidence.push({ ruleId, path: `${path31}.${key}`, preview: "missing required key" });
|
|
15253
15837
|
}
|
|
15254
15838
|
}
|
|
15255
15839
|
}
|
|
@@ -15750,10 +16334,10 @@ function printHuman(result) {
|
|
|
15750
16334
|
console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
15751
16335
|
}
|
|
15752
16336
|
for (const finding of result.findings) {
|
|
15753
|
-
const
|
|
16337
|
+
const path31 = finding.evidence[0]?.path;
|
|
15754
16338
|
const run = finding.evidence[0]?.runId;
|
|
15755
16339
|
const runPrefix = run ? `[${run}] ` : "";
|
|
15756
|
-
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${
|
|
16340
|
+
console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path31 ? ` (${path31})` : ""}`);
|
|
15757
16341
|
}
|
|
15758
16342
|
}
|
|
15759
16343
|
function readErrorResult(error) {
|
|
@@ -16294,10 +16878,10 @@ async function evalRun(input3, options = {}) {
|
|
|
16294
16878
|
diagnostics: []
|
|
16295
16879
|
};
|
|
16296
16880
|
}
|
|
16297
|
-
function evidenceForRun(run,
|
|
16298
|
-
return [{ runId: run.runId, ...
|
|
16881
|
+
function evidenceForRun(run, path31) {
|
|
16882
|
+
return [{ runId: run.runId, ...path31 !== void 0 ? { path: path31 } : {} }];
|
|
16299
16883
|
}
|
|
16300
|
-
function evidenceForEvent(event,
|
|
16884
|
+
function evidenceForEvent(event, path31) {
|
|
16301
16885
|
return [
|
|
16302
16886
|
{
|
|
16303
16887
|
runId: event.runId,
|
|
@@ -16305,7 +16889,7 @@ function evidenceForEvent(event, path30) {
|
|
|
16305
16889
|
...event.parentId !== void 0 ? { parentId: event.parentId } : {},
|
|
16306
16890
|
kind: event.kind,
|
|
16307
16891
|
name: event.name,
|
|
16308
|
-
...
|
|
16892
|
+
...path31 !== void 0 ? { path: path31 } : {}
|
|
16309
16893
|
}
|
|
16310
16894
|
];
|
|
16311
16895
|
}
|
|
@@ -16463,9 +17047,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
|
|
|
16463
17047
|
function tokenize(text) {
|
|
16464
17048
|
return [...text.toLowerCase().matchAll(/[a-z0-9][a-z0-9'-]{2,}/g)].map((match) => match[0].replace(/^['-]+|['-]+$/g, "")).filter((token) => token.length > 2 && !STOP_WORDS.has(token));
|
|
16465
17049
|
}
|
|
16466
|
-
function firstEvidence(fields, run,
|
|
17050
|
+
function firstEvidence(fields, run, path31) {
|
|
16467
17051
|
const first = fields[0];
|
|
16468
|
-
return first === void 0 ? evidenceForRun(run,
|
|
17052
|
+
return first === void 0 ? evidenceForRun(run, path31) : evidenceForEvent(first.node.event, first.path);
|
|
16469
17053
|
}
|
|
16470
17054
|
function collectSourceIds(nodes, keys) {
|
|
16471
17055
|
const wanted = keySet(keys);
|
|
@@ -16842,8 +17426,8 @@ function renderEvalMarkdown(result) {
|
|
|
16842
17426
|
if (result.findings.length > 0) {
|
|
16843
17427
|
lines.push("", "## Findings");
|
|
16844
17428
|
for (const finding of result.findings) {
|
|
16845
|
-
const
|
|
16846
|
-
lines.push(`- ${finding.ruleId}: ${finding.message}${
|
|
17429
|
+
const path31 = finding.evidence[0]?.path;
|
|
17430
|
+
lines.push(`- ${finding.ruleId}: ${finding.message}${path31 ? ` (${path31})` : ""}`);
|
|
16847
17431
|
}
|
|
16848
17432
|
}
|
|
16849
17433
|
return `${lines.join("\n")}
|
|
@@ -17036,8 +17620,8 @@ function printHuman2(result) {
|
|
|
17036
17620
|
console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
17037
17621
|
}
|
|
17038
17622
|
for (const finding of result.findings) {
|
|
17039
|
-
const
|
|
17040
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
17623
|
+
const path31 = finding.evidence[0]?.path;
|
|
17624
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path31 ? ` (${path31})` : ""}`);
|
|
17041
17625
|
}
|
|
17042
17626
|
}
|
|
17043
17627
|
function readErrorResult2(error) {
|
|
@@ -17275,8 +17859,8 @@ function printHuman3(result) {
|
|
|
17275
17859
|
console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
17276
17860
|
}
|
|
17277
17861
|
for (const finding of result.findings) {
|
|
17278
|
-
const
|
|
17279
|
-
console.log(`- ${finding.ruleId}: ${finding.message}${
|
|
17862
|
+
const path31 = finding.evidence[0]?.path;
|
|
17863
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path31 ? ` (${path31})` : ""}`);
|
|
17280
17864
|
}
|
|
17281
17865
|
console.log(`Note: ${result.note}`);
|
|
17282
17866
|
}
|
|
@@ -17427,8 +18011,8 @@ function renderCheckSection(result) {
|
|
|
17427
18011
|
`Diagnostics: ${result.diagnostics.length}`
|
|
17428
18012
|
];
|
|
17429
18013
|
for (const finding of result.findings.slice(0, 10)) {
|
|
17430
|
-
const
|
|
17431
|
-
lines.push(`- ${finding.ruleId}: ${finding.message} (${
|
|
18014
|
+
const path31 = finding.evidence[0]?.path ?? "(run)";
|
|
18015
|
+
lines.push(`- ${finding.ruleId}: ${finding.message} (${path31})`);
|
|
17432
18016
|
}
|
|
17433
18017
|
for (const diagnostic7 of result.diagnostics.slice(0, 10)) {
|
|
17434
18018
|
lines.push(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
@@ -19878,6 +20462,88 @@ async function suiteReportCommand(options = {}) {
|
|
|
19878
20462
|
}
|
|
19879
20463
|
}
|
|
19880
20464
|
|
|
20465
|
+
// packages/cli/src/cohort.ts
|
|
20466
|
+
init_advanced();
|
|
20467
|
+
function normalizeMetrics2(raw) {
|
|
20468
|
+
const parsed = parseCohortMetricList(raw);
|
|
20469
|
+
if (parsed.length === 0) return void 0;
|
|
20470
|
+
const allowed = new Set(COHORT_METRIC_IDS);
|
|
20471
|
+
return parsed.filter(
|
|
20472
|
+
(item) => allowed.has(item)
|
|
20473
|
+
);
|
|
20474
|
+
}
|
|
20475
|
+
async function writeArtifacts(result, outputDir) {
|
|
20476
|
+
await promises.mkdir(outputDir, { recursive: true });
|
|
20477
|
+
const jsonPath = path23__default.default.join(outputDir, "cohort-results.json");
|
|
20478
|
+
const markdownPath = path23__default.default.join(outputDir, "cohort-summary.md");
|
|
20479
|
+
const htmlPath = path23__default.default.join(outputDir, "cohort-report.html");
|
|
20480
|
+
await promises.writeFile(jsonPath, `${renderCohortReport(result, { format: "json" })}
|
|
20481
|
+
`, "utf-8");
|
|
20482
|
+
await promises.writeFile(
|
|
20483
|
+
markdownPath,
|
|
20484
|
+
`${renderCohortReport(result, { format: "markdown" })}
|
|
20485
|
+
`,
|
|
20486
|
+
"utf-8"
|
|
20487
|
+
);
|
|
20488
|
+
await promises.writeFile(htmlPath, `${renderCohortReport(result, { format: "html" })}
|
|
20489
|
+
`, "utf-8");
|
|
20490
|
+
return { jsonPath, markdownPath, htmlPath };
|
|
20491
|
+
}
|
|
20492
|
+
async function cohortCommand(options = {}) {
|
|
20493
|
+
try {
|
|
20494
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
20495
|
+
const { runs } = await loadSessionRuns(traceDir);
|
|
20496
|
+
const result = await analyzeCohort(runs, {
|
|
20497
|
+
traceDir,
|
|
20498
|
+
baseline: options.baseline,
|
|
20499
|
+
candidate: options.candidate,
|
|
20500
|
+
cohortKey: options.cohortKey,
|
|
20501
|
+
groupBy: options.groupBy,
|
|
20502
|
+
metrics: normalizeMetrics2(options.metric)
|
|
20503
|
+
});
|
|
20504
|
+
const format = options.format ?? (options.json ? "json" : "markdown");
|
|
20505
|
+
let artifacts;
|
|
20506
|
+
if (options.output !== void 0 && options.output.trim() !== "") {
|
|
20507
|
+
artifacts = await writeArtifacts(result, path23__default.default.resolve(options.output.trim()));
|
|
20508
|
+
}
|
|
20509
|
+
if (options.json || format === "json") {
|
|
20510
|
+
console.log(
|
|
20511
|
+
JSON.stringify(
|
|
20512
|
+
{
|
|
20513
|
+
...result,
|
|
20514
|
+
artifacts: artifacts ?? null
|
|
20515
|
+
},
|
|
20516
|
+
null,
|
|
20517
|
+
2
|
|
20518
|
+
)
|
|
20519
|
+
);
|
|
20520
|
+
} else if (format === "html") {
|
|
20521
|
+
console.log(renderCohortReport(result, { format: "html" }));
|
|
20522
|
+
if (artifacts !== void 0) {
|
|
20523
|
+
console.error(`Artifacts written to ${options.output}`);
|
|
20524
|
+
}
|
|
20525
|
+
} else {
|
|
20526
|
+
console.log(renderCohortReport(result, { format: "markdown" }));
|
|
20527
|
+
if (artifacts !== void 0) {
|
|
20528
|
+
console.log("");
|
|
20529
|
+
console.log(`Artifacts:`);
|
|
20530
|
+
console.log(`- ${artifacts.jsonPath}`);
|
|
20531
|
+
console.log(`- ${artifacts.markdownPath}`);
|
|
20532
|
+
console.log(`- ${artifacts.htmlPath}`);
|
|
20533
|
+
}
|
|
20534
|
+
}
|
|
20535
|
+
if (!result.ok) process.exitCode = 1;
|
|
20536
|
+
} catch (error) {
|
|
20537
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20538
|
+
if (options.json) {
|
|
20539
|
+
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
20540
|
+
} else {
|
|
20541
|
+
console.error(`[AgentInspect] cohort failed: ${message}`);
|
|
20542
|
+
}
|
|
20543
|
+
process.exitCode = 1;
|
|
20544
|
+
}
|
|
20545
|
+
}
|
|
20546
|
+
|
|
19881
20547
|
// packages/cli/src/index.ts
|
|
19882
20548
|
function runCommand(action) {
|
|
19883
20549
|
void action().catch((error) => {
|
|
@@ -20311,6 +20977,12 @@ function createCliProgram() {
|
|
|
20311
20977
|
suiteCmd.command("report").description("Render a report from a prior suite run artifact").option("--input <path>", "suite run JSON artifact").option("--format <format>", "markdown or json", "markdown").option("--json", "print JSON wrapper").action((opts) => {
|
|
20312
20978
|
runCommand(() => suiteReportCommand(opts));
|
|
20313
20979
|
});
|
|
20980
|
+
program.command("cohort").description("Compare baseline/candidate trace cohorts for regressions (v5.1+)").option("--dir <path>", "trace directory").option("--baseline <label>", "baseline cohort label (metadata key)").option("--candidate <label>", "candidate cohort label (metadata key)").option("--cohort-key <key>", "metadata key for cohort labels", "cohort").option("--group-by <spec>", "model | session | group | metadata.<key>", "model").option(
|
|
20981
|
+
"--metric <list>",
|
|
20982
|
+
"comma-separated metrics (errorRate,duration,toolChoice,...)"
|
|
20983
|
+
).option("--format <format>", "markdown, json, or html", "markdown").option("-o, --output <dir>", "write cohort-results.json, cohort-summary.md, cohort-report.html").option("--json", "print deterministic JSON result").action((opts) => {
|
|
20984
|
+
runCommand(() => cohortCommand(opts));
|
|
20985
|
+
});
|
|
20314
20986
|
return program;
|
|
20315
20987
|
}
|
|
20316
20988
|
function isPrimaryModule() {
|