@agent-inspect/viewer 6.9.0 → 6.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +862 -851
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +861 -850
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createServer } from 'http';
|
|
2
|
-
import
|
|
2
|
+
import path15 from 'path';
|
|
3
3
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
4
|
import 'crypto';
|
|
5
5
|
import { readFile, stat, readdir, access } from 'fs/promises';
|
|
@@ -518,7 +518,7 @@ function persistedInspectEventsToTraceEvents(events, options) {
|
|
|
518
518
|
}
|
|
519
519
|
var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
|
|
520
520
|
var RUNS_DIR_NAME = "runs";
|
|
521
|
-
var FALLBACK_TRACE_DIR =
|
|
521
|
+
var FALLBACK_TRACE_DIR = path15.join(
|
|
522
522
|
os.tmpdir(),
|
|
523
523
|
"agent-inspect",
|
|
524
524
|
RUNS_DIR_NAME
|
|
@@ -533,7 +533,7 @@ function getDefaultTraceDir() {
|
|
|
533
533
|
if (typeof home !== "string" || home.trim() === "") {
|
|
534
534
|
return FALLBACK_TRACE_DIR;
|
|
535
535
|
}
|
|
536
|
-
return
|
|
536
|
+
return path15.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
|
|
537
537
|
} catch {
|
|
538
538
|
return FALLBACK_TRACE_DIR;
|
|
539
539
|
}
|
|
@@ -541,11 +541,11 @@ function getDefaultTraceDir() {
|
|
|
541
541
|
function getTraceFilePath(runId, traceDir) {
|
|
542
542
|
const baseDir = traceDir ?? getDefaultTraceDir();
|
|
543
543
|
let safeId = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
|
|
544
|
-
safeId =
|
|
544
|
+
safeId = path15.basename(safeId);
|
|
545
545
|
if (safeId === "" || safeId === "." || safeId === "..") {
|
|
546
546
|
safeId = "run_unknown";
|
|
547
547
|
}
|
|
548
|
-
return
|
|
548
|
+
return path15.join(baseDir, `${safeId}.jsonl`);
|
|
549
549
|
}
|
|
550
550
|
function formatError(error) {
|
|
551
551
|
if (error instanceof Error) {
|
|
@@ -823,7 +823,7 @@ var TraceDirectory = class {
|
|
|
823
823
|
this.#dir = resolveTraceDir(options);
|
|
824
824
|
}
|
|
825
825
|
getPath(filename) {
|
|
826
|
-
return filename ?
|
|
826
|
+
return filename ? path15.join(this.#dir, filename) : this.#dir;
|
|
827
827
|
}
|
|
828
828
|
async list() {
|
|
829
829
|
try {
|
|
@@ -850,7 +850,7 @@ function parseIsoToMs2(value) {
|
|
|
850
850
|
}
|
|
851
851
|
async function extractMetadata(filePath, _quickScan) {
|
|
852
852
|
const stats = await stat(filePath);
|
|
853
|
-
let runIdFromFile =
|
|
853
|
+
let runIdFromFile = path15.basename(filePath);
|
|
854
854
|
if (runIdFromFile.endsWith(".jsonl")) {
|
|
855
855
|
runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
|
|
856
856
|
}
|
|
@@ -1460,12 +1460,12 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
1460
1460
|
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
1461
1461
|
);
|
|
1462
1462
|
const ordered = [...runs].sort(compareRuns);
|
|
1463
|
-
const
|
|
1463
|
+
const path18 = [];
|
|
1464
1464
|
const visited = /* @__PURE__ */ new Set();
|
|
1465
1465
|
const pushRun = (run, confidence, source) => {
|
|
1466
1466
|
if (visited.has(run.runId)) return;
|
|
1467
1467
|
visited.add(run.runId);
|
|
1468
|
-
|
|
1468
|
+
path18.push({
|
|
1469
1469
|
runId: run.runId,
|
|
1470
1470
|
name: run.name,
|
|
1471
1471
|
startedAt: run.startedAt,
|
|
@@ -1490,7 +1490,7 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
1490
1490
|
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
1491
1491
|
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
1492
1492
|
}
|
|
1493
|
-
return
|
|
1493
|
+
return path18;
|
|
1494
1494
|
}
|
|
1495
1495
|
function metaRunIdMatches(run, token, runById) {
|
|
1496
1496
|
const meta = extractSessionWorkflowMetadata(run.metadata);
|
|
@@ -1567,524 +1567,915 @@ function buildSessionIndex(inputRuns, options = {}) {
|
|
|
1567
1567
|
};
|
|
1568
1568
|
}
|
|
1569
1569
|
|
|
1570
|
-
// packages/core/src/
|
|
1571
|
-
|
|
1572
|
-
"
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
return { code, message, severity, ...{} };
|
|
1579
|
-
}
|
|
1580
|
-
function asString(value, label) {
|
|
1581
|
-
if (value === void 0) return void 0;
|
|
1582
|
-
if (typeof value !== "string" || value.trim() === "") {
|
|
1583
|
-
throw new Error(`${label} must be a non-empty string.`);
|
|
1570
|
+
// packages/core/src/exporters/helpers.ts
|
|
1571
|
+
function sortKeysDeep(input) {
|
|
1572
|
+
if (input === null || typeof input !== "object") return input;
|
|
1573
|
+
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
1574
|
+
const o = input;
|
|
1575
|
+
const out = {};
|
|
1576
|
+
for (const k of Object.keys(o).sort()) {
|
|
1577
|
+
out[k] = sortKeysDeep(o[k]);
|
|
1584
1578
|
}
|
|
1585
|
-
return
|
|
1579
|
+
return out;
|
|
1586
1580
|
}
|
|
1587
|
-
function
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
throw new Error(`${label} must be an array of strings.`);
|
|
1591
|
-
}
|
|
1592
|
-
return value;
|
|
1581
|
+
function stableJson(value, pretty) {
|
|
1582
|
+
const sorted = sortKeysDeep(value);
|
|
1583
|
+
return JSON.stringify(sorted);
|
|
1593
1584
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
return
|
|
1585
|
+
|
|
1586
|
+
// packages/core/src/diff/comparable.ts
|
|
1587
|
+
function extractOutputPreview(meta) {
|
|
1588
|
+
if (meta === void 0) return void 0;
|
|
1589
|
+
if ("outputPreview" in meta) return meta.outputPreview;
|
|
1590
|
+
if ("resultPreview" in meta) return meta.resultPreview;
|
|
1591
|
+
return void 0;
|
|
1600
1592
|
}
|
|
1601
|
-
function
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
diagnostics.push(
|
|
1605
|
-
diagnostic("AI_SUITE_CONFIG_INVALID", `cases[${index}] must be an object.`)
|
|
1606
|
-
);
|
|
1607
|
-
return { diagnostics };
|
|
1608
|
-
}
|
|
1609
|
-
const raw = value;
|
|
1610
|
-
try {
|
|
1611
|
-
const id = asString(raw.id, `cases[${index}].id`);
|
|
1612
|
-
if (id === void 0) {
|
|
1613
|
-
diagnostics.push(
|
|
1614
|
-
diagnostic("AI_SUITE_CONFIG_INVALID", `cases[${index}].id is required.`)
|
|
1615
|
-
);
|
|
1616
|
-
return { diagnostics };
|
|
1617
|
-
}
|
|
1618
|
-
const trace = asString(raw.trace, `cases[${index}].trace`);
|
|
1619
|
-
const runId = asString(raw.runId, `cases[${index}].runId`);
|
|
1620
|
-
const input = asString(raw.input, `cases[${index}].input`);
|
|
1621
|
-
return {
|
|
1622
|
-
caseConfig: {
|
|
1623
|
-
id,
|
|
1624
|
-
...trace !== void 0 ? { trace } : {},
|
|
1625
|
-
...runId !== void 0 ? { runId } : {},
|
|
1626
|
-
...input !== void 0 ? { input } : {},
|
|
1627
|
-
...asStringArray(raw.requireTools, `cases[${index}].requireTools`) !== void 0 ? { requireTools: asStringArray(raw.requireTools, `cases[${index}].requireTools`) } : {},
|
|
1628
|
-
...asStringArray(raw.forbidTools, `cases[${index}].forbidTools`) !== void 0 ? { forbidTools: asStringArray(raw.forbidTools, `cases[${index}].forbidTools`) } : {},
|
|
1629
|
-
...asPositiveNumber(raw.maxDurationMs, `cases[${index}].maxDurationMs`) !== void 0 ? {
|
|
1630
|
-
maxDurationMs: asPositiveNumber(
|
|
1631
|
-
raw.maxDurationMs,
|
|
1632
|
-
`cases[${index}].maxDurationMs`
|
|
1633
|
-
)
|
|
1634
|
-
} : {},
|
|
1635
|
-
...asStringArray(
|
|
1636
|
-
raw.expectedObservations,
|
|
1637
|
-
`cases[${index}].expectedObservations`
|
|
1638
|
-
) !== void 0 ? {
|
|
1639
|
-
expectedObservations: asStringArray(
|
|
1640
|
-
raw.expectedObservations,
|
|
1641
|
-
`cases[${index}].expectedObservations`
|
|
1642
|
-
)
|
|
1643
|
-
} : {}
|
|
1644
|
-
},
|
|
1645
|
-
diagnostics
|
|
1646
|
-
};
|
|
1647
|
-
} catch (error) {
|
|
1648
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1649
|
-
diagnostics.push(diagnostic("AI_SUITE_CONFIG_INVALID", message));
|
|
1650
|
-
return { diagnostics };
|
|
1651
|
-
}
|
|
1593
|
+
function mapStepStatus(s) {
|
|
1594
|
+
if (s === void 0) return "running";
|
|
1595
|
+
return s;
|
|
1652
1596
|
}
|
|
1653
|
-
function
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
const raw = value;
|
|
1658
|
-
const name = asString(raw.name, "name");
|
|
1659
|
-
const traces = asString(raw.traces, "traces");
|
|
1660
|
-
if (name === void 0) throw new Error("name is required.");
|
|
1661
|
-
if (traces === void 0) throw new Error("traces is required.");
|
|
1662
|
-
if (!Array.isArray(raw.cases) || raw.cases.length === 0) {
|
|
1663
|
-
throw new Error("cases must be a non-empty array.");
|
|
1597
|
+
function manualTraceEventsToComparableRun(events) {
|
|
1598
|
+
const started = events.find((e) => e.event === "run_started");
|
|
1599
|
+
if (!started || started.event !== "run_started") {
|
|
1600
|
+
throw new Error("Invalid trace: missing run_started");
|
|
1664
1601
|
}
|
|
1665
|
-
const
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1602
|
+
const rs = started;
|
|
1603
|
+
const runId = rs.runId;
|
|
1604
|
+
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
1605
|
+
const lastCompleted = completedAll[completedAll.length - 1];
|
|
1606
|
+
let runStatus;
|
|
1607
|
+
if (lastCompleted === void 0) runStatus = "running";
|
|
1608
|
+
else runStatus = lastCompleted.status;
|
|
1609
|
+
const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
1610
|
+
const steps = /* @__PURE__ */ new Map();
|
|
1611
|
+
let order = 0;
|
|
1612
|
+
for (const e of events) {
|
|
1613
|
+
if (e.event !== "step_started") continue;
|
|
1614
|
+
const s = e;
|
|
1615
|
+
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
1616
|
+
steps.set(s.stepId, {
|
|
1617
|
+
id: s.stepId,
|
|
1618
|
+
parentId: s.parentId,
|
|
1619
|
+
name: s.name,
|
|
1620
|
+
type: s.type,
|
|
1621
|
+
order: order++,
|
|
1622
|
+
timestamp: s.timestamp,
|
|
1623
|
+
metadata: meta
|
|
1624
|
+
});
|
|
1672
1625
|
}
|
|
1673
|
-
const
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1626
|
+
for (const e of events) {
|
|
1627
|
+
if (e.event !== "step_completed") continue;
|
|
1628
|
+
const acc = steps.get(e.stepId);
|
|
1629
|
+
if (!acc) continue;
|
|
1630
|
+
acc.status = e.status;
|
|
1631
|
+
acc.durationMs = e.durationMs;
|
|
1632
|
+
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
1633
|
+
const extra = e;
|
|
1634
|
+
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
1635
|
+
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
1677
1636
|
}
|
|
1678
|
-
ids.add(suiteCase.id);
|
|
1679
1637
|
}
|
|
1680
|
-
const
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
if (raw.checks !== void 0) {
|
|
1686
|
-
if (typeof raw.checks !== "object" || Array.isArray(raw.checks)) {
|
|
1687
|
-
throw new Error("checks must be an object.");
|
|
1638
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
1639
|
+
for (const acc of steps.values()) {
|
|
1640
|
+
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
1641
|
+
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
1642
|
+
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
1688
1643
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
throw new Error("eval must be an object.");
|
|
1644
|
+
const outputPreview = extractOutputPreview(meta);
|
|
1645
|
+
if (meta !== void 0 && ("outputPreview" in meta || "resultPreview" in meta)) {
|
|
1646
|
+
delete meta.outputPreview;
|
|
1647
|
+
delete meta.resultPreview;
|
|
1694
1648
|
}
|
|
1695
|
-
|
|
1649
|
+
const sc = {
|
|
1650
|
+
id: acc.id,
|
|
1651
|
+
name: acc.name,
|
|
1652
|
+
type: acc.type,
|
|
1653
|
+
status: mapStepStatus(acc.status),
|
|
1654
|
+
durationMs: acc.durationMs,
|
|
1655
|
+
error: acc.errorMsg,
|
|
1656
|
+
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
1657
|
+
outputPreview,
|
|
1658
|
+
children: []
|
|
1659
|
+
};
|
|
1660
|
+
nodes.set(acc.id, sc);
|
|
1696
1661
|
}
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1662
|
+
const roots = [];
|
|
1663
|
+
const sortByOrder = (a, b) => {
|
|
1664
|
+
const oa = steps.get(a.id)?.order ?? 0;
|
|
1665
|
+
const ob = steps.get(b.id)?.order ?? 0;
|
|
1666
|
+
return oa - ob;
|
|
1667
|
+
};
|
|
1668
|
+
for (const acc of steps.values()) {
|
|
1669
|
+
const node = nodes.get(acc.id);
|
|
1670
|
+
if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
|
|
1671
|
+
nodes.get(acc.parentId).children.push(node);
|
|
1672
|
+
} else {
|
|
1673
|
+
roots.push(node);
|
|
1700
1674
|
}
|
|
1701
|
-
const outputDir = asString(
|
|
1702
|
-
raw.artifacts.outputDir,
|
|
1703
|
-
"artifacts.outputDir"
|
|
1704
|
-
);
|
|
1705
|
-
config.artifacts = outputDir !== void 0 ? { outputDir } : {};
|
|
1706
|
-
}
|
|
1707
|
-
if (raw.baseline !== void 0) {
|
|
1708
|
-
const baseline = asString(raw.baseline, "baseline");
|
|
1709
|
-
if (baseline !== void 0) config.baseline = baseline;
|
|
1710
1675
|
}
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1676
|
+
roots.sort(sortByOrder);
|
|
1677
|
+
for (const n of nodes.values()) {
|
|
1678
|
+
n.children.sort(sortByOrder);
|
|
1714
1679
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1680
|
+
return {
|
|
1681
|
+
runId,
|
|
1682
|
+
name: rs.name,
|
|
1683
|
+
status: runStatus,
|
|
1684
|
+
durationMs,
|
|
1685
|
+
steps: roots
|
|
1686
|
+
};
|
|
1717
1687
|
}
|
|
1718
1688
|
|
|
1719
|
-
// packages/core/src/
|
|
1720
|
-
var
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
return { code, message, severity: "error" };
|
|
1689
|
+
// packages/core/src/diff/engine.ts
|
|
1690
|
+
var DEFAULT_THRESHOLD_MS = 0;
|
|
1691
|
+
function pathSeg(step, index) {
|
|
1692
|
+
return { index, name: step.name, stepId: step.id };
|
|
1724
1693
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
await access(filePath);
|
|
1728
|
-
return true;
|
|
1729
|
-
} catch {
|
|
1730
|
-
return false;
|
|
1731
|
-
}
|
|
1694
|
+
function buildPath(segments) {
|
|
1695
|
+
return { path: [...segments] };
|
|
1732
1696
|
}
|
|
1733
|
-
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1697
|
+
function pairSteps(left, right) {
|
|
1698
|
+
const usedRight = /* @__PURE__ */ new Set();
|
|
1699
|
+
const pairs = [];
|
|
1700
|
+
for (let i = 0; i < left.length; i++) {
|
|
1701
|
+
const L = left[i];
|
|
1702
|
+
let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
|
|
1703
|
+
if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
|
|
1704
|
+
const cand = right[i];
|
|
1705
|
+
if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
|
|
1706
|
+
R = cand;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
if (R === void 0) {
|
|
1710
|
+
R = right.find(
|
|
1711
|
+
(r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
if (R !== void 0) {
|
|
1715
|
+
usedRight.add(R.id);
|
|
1716
|
+
pairs.push([L, R]);
|
|
1717
|
+
} else {
|
|
1718
|
+
pairs.push([L, void 0]);
|
|
1719
|
+
}
|
|
1741
1720
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1721
|
+
for (const R of right) {
|
|
1722
|
+
if (!usedRight.has(R.id)) {
|
|
1723
|
+
pairs.push([void 0, R]);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
return pairs;
|
|
1745
1727
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1728
|
+
function compareLeafSteps(L, R, segments, opts, out) {
|
|
1729
|
+
const path18 = buildPath(segments);
|
|
1730
|
+
if (L.name !== R.name) {
|
|
1731
|
+
out.push({
|
|
1732
|
+
kind: "structure",
|
|
1733
|
+
severity: "warning",
|
|
1734
|
+
message: "Step name differs",
|
|
1735
|
+
path: path18,
|
|
1736
|
+
left: L.name,
|
|
1737
|
+
right: R.name
|
|
1754
1738
|
});
|
|
1755
1739
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1740
|
+
if ((L.type ?? "") !== (R.type ?? "")) {
|
|
1741
|
+
out.push({
|
|
1742
|
+
kind: "step-type",
|
|
1743
|
+
severity: "warning",
|
|
1744
|
+
message: "Step type differs",
|
|
1745
|
+
path: path18,
|
|
1746
|
+
left: L.type,
|
|
1747
|
+
right: R.type
|
|
1761
1748
|
});
|
|
1762
1749
|
}
|
|
1763
|
-
if (
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1750
|
+
if ((L.status ?? "") !== (R.status ?? "")) {
|
|
1751
|
+
out.push({
|
|
1752
|
+
kind: "step-status",
|
|
1753
|
+
severity: "warning",
|
|
1754
|
+
message: "Step status differs",
|
|
1755
|
+
path: path18,
|
|
1756
|
+
left: L.status,
|
|
1757
|
+
right: R.status
|
|
1767
1758
|
});
|
|
1768
1759
|
}
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
config,
|
|
1780
|
-
configPath,
|
|
1781
|
-
configDir: path13.dirname(configPath)
|
|
1782
|
-
};
|
|
1783
|
-
} catch (error) {
|
|
1784
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1785
|
-
throw Object.assign(new Error(message), {
|
|
1786
|
-
diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
|
|
1760
|
+
const le = L.error ?? "";
|
|
1761
|
+
const re = R.error ?? "";
|
|
1762
|
+
if (le !== re) {
|
|
1763
|
+
out.push({
|
|
1764
|
+
kind: "error",
|
|
1765
|
+
severity: "error",
|
|
1766
|
+
message: "Step error message differs",
|
|
1767
|
+
path: path18,
|
|
1768
|
+
left: le || void 0,
|
|
1769
|
+
right: re || void 0
|
|
1787
1770
|
});
|
|
1788
1771
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1772
|
+
if (!opts.ignoreDuration) {
|
|
1773
|
+
const ld = L.durationMs;
|
|
1774
|
+
const rd = R.durationMs;
|
|
1775
|
+
const th = opts.durationThresholdMs;
|
|
1776
|
+
let differs = false;
|
|
1777
|
+
if (ld === void 0 && rd === void 0) differs = false;
|
|
1778
|
+
else if (ld === void 0 || rd === void 0) differs = true;
|
|
1779
|
+
else differs = Math.abs(ld - rd) > th;
|
|
1780
|
+
if (differs) {
|
|
1781
|
+
out.push({
|
|
1782
|
+
kind: "duration",
|
|
1783
|
+
severity: "info",
|
|
1784
|
+
message: "Step duration differs",
|
|
1785
|
+
path: path18,
|
|
1786
|
+
left: ld,
|
|
1787
|
+
right: rd
|
|
1788
|
+
});
|
|
1803
1789
|
}
|
|
1804
|
-
return {
|
|
1805
|
-
caseId: suiteCase.id,
|
|
1806
|
-
tracePath,
|
|
1807
|
-
missing: true,
|
|
1808
|
-
reason: `trace file not found: ${suiteCase.trace}`
|
|
1809
|
-
};
|
|
1810
1790
|
}
|
|
1811
|
-
const
|
|
1812
|
-
const
|
|
1813
|
-
if (
|
|
1814
|
-
|
|
1791
|
+
const lm = stableJson(L.metadata ?? {});
|
|
1792
|
+
const rm2 = stableJson(R.metadata ?? {});
|
|
1793
|
+
if (lm !== rm2) {
|
|
1794
|
+
out.push({
|
|
1795
|
+
kind: "metadata",
|
|
1796
|
+
severity: "info",
|
|
1797
|
+
message: "Step metadata differs",
|
|
1798
|
+
path: path18,
|
|
1799
|
+
left: L.metadata,
|
|
1800
|
+
right: R.metadata
|
|
1801
|
+
});
|
|
1815
1802
|
}
|
|
1816
|
-
const
|
|
1817
|
-
|
|
1818
|
-
|
|
1803
|
+
const lo = stableJson(L.outputPreview ?? null);
|
|
1804
|
+
const ro = stableJson(R.outputPreview ?? null);
|
|
1805
|
+
if (lo !== ro) {
|
|
1806
|
+
out.push({
|
|
1807
|
+
kind: "output",
|
|
1808
|
+
severity: "info",
|
|
1809
|
+
message: "Output preview differs",
|
|
1810
|
+
path: path18,
|
|
1811
|
+
left: L.outputPreview,
|
|
1812
|
+
right: R.outputPreview
|
|
1813
|
+
});
|
|
1819
1814
|
}
|
|
1820
|
-
return {
|
|
1821
|
-
caseId: suiteCase.id,
|
|
1822
|
-
runId: runKey,
|
|
1823
|
-
missing: true,
|
|
1824
|
-
reason: `no trace found for run id "${runKey}" under ${options.tracesDir}`
|
|
1825
|
-
};
|
|
1826
1815
|
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1816
|
+
function compareRecursive(L, R, segments, opts, out) {
|
|
1817
|
+
compareLeafSteps(L, R, segments, opts, out);
|
|
1818
|
+
const pairs = pairSteps(L.children, R.children);
|
|
1819
|
+
let ci = 0;
|
|
1820
|
+
for (const [lch, rch] of pairs) {
|
|
1821
|
+
if (lch !== void 0 && rch !== void 0) {
|
|
1822
|
+
compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
|
|
1823
|
+
} else if (lch !== void 0) {
|
|
1824
|
+
out.push({
|
|
1825
|
+
kind: "step-removed",
|
|
1826
|
+
severity: "warning",
|
|
1827
|
+
message: `Step only in left run: ${lch.name}`,
|
|
1828
|
+
path: buildPath([...segments, pathSeg(lch, ci)]),
|
|
1829
|
+
left: lch.id,
|
|
1830
|
+
right: void 0
|
|
1831
|
+
});
|
|
1832
|
+
} else if (rch !== void 0) {
|
|
1833
|
+
out.push({
|
|
1834
|
+
kind: "step-added",
|
|
1835
|
+
severity: "warning",
|
|
1836
|
+
message: `Step only in right run: ${rch.name}`,
|
|
1837
|
+
path: buildPath([...segments, pathSeg(rch, ci)]),
|
|
1838
|
+
left: void 0,
|
|
1839
|
+
right: rch.id
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
ci += 1;
|
|
1843
|
+
}
|
|
1841
1844
|
}
|
|
1842
|
-
function
|
|
1845
|
+
function mergeDiffDefaults(options) {
|
|
1843
1846
|
return {
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1847
|
+
ignoreDuration: false,
|
|
1848
|
+
durationThresholdMs: DEFAULT_THRESHOLD_MS,
|
|
1849
|
+
focus: "all",
|
|
1850
|
+
check: "all"
|
|
1848
1851
|
};
|
|
1849
1852
|
}
|
|
1850
|
-
function
|
|
1851
|
-
return
|
|
1852
|
-
passed: 0,
|
|
1853
|
-
failed: 0,
|
|
1854
|
-
warnings: 0,
|
|
1855
|
-
errors: 0
|
|
1856
|
-
};
|
|
1853
|
+
function kindMatchesFilter(kind, merged) {
|
|
1854
|
+
return true;
|
|
1857
1855
|
}
|
|
1858
|
-
function
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
...selectedRun ? { runId: selectedRun.runId } : {},
|
|
1864
|
-
summary: {
|
|
1865
|
-
...emptySummary(),
|
|
1866
|
-
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
1867
|
-
},
|
|
1868
|
-
findings: [],
|
|
1869
|
-
diagnostics: [...diagnostics]
|
|
1856
|
+
function diffRuns(left, right, options) {
|
|
1857
|
+
const merged = mergeDiffDefaults();
|
|
1858
|
+
const opts = {
|
|
1859
|
+
ignoreDuration: merged.ignoreDuration,
|
|
1860
|
+
durationThresholdMs: merged.durationThresholdMs
|
|
1870
1861
|
};
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
const nodesByEventId = /* @__PURE__ */ new Map();
|
|
1881
|
-
const childrenByParentId = /* @__PURE__ */ new Map();
|
|
1882
|
-
for (const node of nodes) {
|
|
1883
|
-
nodesByEventId.set(node.event.eventId, node);
|
|
1884
|
-
const parentId = node.event.parentId;
|
|
1885
|
-
if (parentId) {
|
|
1886
|
-
const children = childrenByParentId.get(parentId) ?? [];
|
|
1887
|
-
children.push(node);
|
|
1888
|
-
childrenByParentId.set(parentId, children);
|
|
1889
|
-
}
|
|
1862
|
+
const raw = [];
|
|
1863
|
+
if ((left.status ?? "") !== (right.status ?? "")) {
|
|
1864
|
+
raw.push({
|
|
1865
|
+
kind: "run-status",
|
|
1866
|
+
severity: "warning",
|
|
1867
|
+
message: "Run completion status differs",
|
|
1868
|
+
left: left.status,
|
|
1869
|
+
right: right.status
|
|
1870
|
+
});
|
|
1890
1871
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
diagnostics: [
|
|
1908
|
-
diagnostic3(
|
|
1909
|
-
"AI_CHECK_INVALID_ARGUMENTS",
|
|
1910
|
-
`Selected run ${input.selectedRun.runId} does not match requested run ${runId}.`
|
|
1911
|
-
)
|
|
1912
|
-
]
|
|
1913
|
-
};
|
|
1872
|
+
{
|
|
1873
|
+
const ld = left.durationMs;
|
|
1874
|
+
const rd = right.durationMs;
|
|
1875
|
+
const th = merged.durationThresholdMs;
|
|
1876
|
+
let differs = false;
|
|
1877
|
+
if (ld === void 0 && rd === void 0) differs = false;
|
|
1878
|
+
else if (ld === void 0 || rd === void 0) differs = true;
|
|
1879
|
+
else differs = Math.abs(ld - rd) > th;
|
|
1880
|
+
if (differs) {
|
|
1881
|
+
raw.push({
|
|
1882
|
+
kind: "duration",
|
|
1883
|
+
severity: "info",
|
|
1884
|
+
message: "Run duration differs",
|
|
1885
|
+
left: ld,
|
|
1886
|
+
right: rd
|
|
1887
|
+
});
|
|
1914
1888
|
}
|
|
1915
|
-
return { run: input.selectedRun, diagnostics: [] };
|
|
1916
1889
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1890
|
+
const pairs = pairSteps(left.steps, right.steps);
|
|
1891
|
+
let idx = 0;
|
|
1892
|
+
for (const [ls, rs] of pairs) {
|
|
1893
|
+
if (ls !== void 0 && rs !== void 0) {
|
|
1894
|
+
compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
|
|
1895
|
+
idx += 1;
|
|
1896
|
+
} else if (ls !== void 0) {
|
|
1897
|
+
raw.push({
|
|
1898
|
+
kind: "step-removed",
|
|
1899
|
+
severity: "warning",
|
|
1900
|
+
message: `Step only in left run: ${ls.name}`,
|
|
1901
|
+
path: buildPath([pathSeg(ls, idx)]),
|
|
1902
|
+
left: ls.id,
|
|
1903
|
+
right: void 0
|
|
1904
|
+
});
|
|
1905
|
+
idx += 1;
|
|
1906
|
+
} else if (rs !== void 0) {
|
|
1907
|
+
raw.push({
|
|
1908
|
+
kind: "step-added",
|
|
1909
|
+
severity: "warning",
|
|
1910
|
+
message: `Step only in right run: ${rs.name}`,
|
|
1911
|
+
path: buildPath([pathSeg(rs, idx)]),
|
|
1912
|
+
left: void 0,
|
|
1913
|
+
right: rs.id
|
|
1914
|
+
});
|
|
1915
|
+
idx += 1;
|
|
1925
1916
|
}
|
|
1926
|
-
return { run, diagnostics: [] };
|
|
1927
|
-
}
|
|
1928
|
-
if (input.read.runs.length === 1) {
|
|
1929
|
-
return { run: input.read.runs[0], diagnostics: [] };
|
|
1930
1917
|
}
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1918
|
+
const differences = raw.filter((d) => kindMatchesFilter(d.kind));
|
|
1919
|
+
let errors = 0;
|
|
1920
|
+
let warnings = 0;
|
|
1921
|
+
let info = 0;
|
|
1922
|
+
for (const d of differences) {
|
|
1923
|
+
if (d.severity === "error") errors += 1;
|
|
1924
|
+
else if (d.severity === "warning") warnings += 1;
|
|
1925
|
+
else info += 1;
|
|
1937
1926
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1927
|
+
const firstVisible = differences[0];
|
|
1928
|
+
const firstDivergence = firstVisible !== void 0 ? {
|
|
1929
|
+
kind: "first-divergence",
|
|
1930
|
+
severity: firstVisible.severity,
|
|
1931
|
+
message: `First divergence: ${firstVisible.message}`,
|
|
1932
|
+
path: firstVisible.path,
|
|
1933
|
+
left: firstVisible.left,
|
|
1934
|
+
right: firstVisible.right
|
|
1935
|
+
} : void 0;
|
|
1936
|
+
const summary = {
|
|
1937
|
+
leftRunId: left.runId,
|
|
1938
|
+
rightRunId: right.runId,
|
|
1939
|
+
totalDifferences: differences.length,
|
|
1940
|
+
errors,
|
|
1941
|
+
warnings,
|
|
1942
|
+
info,
|
|
1943
|
+
firstDivergence
|
|
1945
1944
|
};
|
|
1945
|
+
return { summary, differences };
|
|
1946
1946
|
}
|
|
1947
|
-
|
|
1947
|
+
|
|
1948
|
+
// packages/core/src/evidence/zip.ts
|
|
1949
|
+
(() => {
|
|
1950
|
+
const table = new Uint32Array(256);
|
|
1951
|
+
for (let n = 0; n < 256; n += 1) {
|
|
1952
|
+
let c = n;
|
|
1953
|
+
for (let k = 0; k < 8; k += 1) {
|
|
1954
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
1955
|
+
}
|
|
1956
|
+
table[n] = c >>> 0;
|
|
1957
|
+
}
|
|
1958
|
+
return table;
|
|
1959
|
+
})();
|
|
1960
|
+
|
|
1961
|
+
// packages/core/src/suite/types.ts
|
|
1962
|
+
var DEFAULT_SUITE_CONFIG_NAMES = [
|
|
1963
|
+
"agent-inspect.suite.json",
|
|
1964
|
+
"agent-inspect.suite.js",
|
|
1965
|
+
"agent-inspect.suite.mjs",
|
|
1966
|
+
"agent-inspect.suite.cjs"
|
|
1967
|
+
];
|
|
1968
|
+
function diagnostic(code, message, severity = "error", caseId) {
|
|
1969
|
+
return { code, message, severity, ...{} };
|
|
1970
|
+
}
|
|
1971
|
+
function asString(value, label) {
|
|
1972
|
+
if (value === void 0) return void 0;
|
|
1973
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
1974
|
+
throw new Error(`${label} must be a non-empty string.`);
|
|
1975
|
+
}
|
|
1976
|
+
return value.trim();
|
|
1977
|
+
}
|
|
1978
|
+
function asStringArray(value, label) {
|
|
1979
|
+
if (value === void 0) return void 0;
|
|
1980
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
1981
|
+
throw new Error(`${label} must be an array of strings.`);
|
|
1982
|
+
}
|
|
1983
|
+
return value;
|
|
1984
|
+
}
|
|
1985
|
+
function asPositiveNumber(value, label) {
|
|
1986
|
+
if (value === void 0) return void 0;
|
|
1987
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
1988
|
+
throw new Error(`${label} must be a non-negative number.`);
|
|
1989
|
+
}
|
|
1990
|
+
return value;
|
|
1991
|
+
}
|
|
1992
|
+
function validateCaseConfig(value, index) {
|
|
1948
1993
|
const diagnostics = [];
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1994
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1995
|
+
diagnostics.push(
|
|
1996
|
+
diagnostic("AI_SUITE_CONFIG_INVALID", `cases[${index}] must be an object.`)
|
|
1997
|
+
);
|
|
1998
|
+
return { diagnostics };
|
|
1999
|
+
}
|
|
2000
|
+
const raw = value;
|
|
2001
|
+
try {
|
|
2002
|
+
const id = asString(raw.id, `cases[${index}].id`);
|
|
2003
|
+
if (id === void 0) {
|
|
1952
2004
|
diagnostics.push(
|
|
1953
|
-
|
|
2005
|
+
diagnostic("AI_SUITE_CONFIG_INVALID", `cases[${index}].id is required.`)
|
|
1954
2006
|
);
|
|
1955
|
-
|
|
1956
|
-
}
|
|
1957
|
-
byId.set(rule.id, rule);
|
|
1958
|
-
}
|
|
1959
|
-
if (selectedIds && selectedIds.length > 0) {
|
|
1960
|
-
const selected = new Set(selectedIds);
|
|
1961
|
-
for (const id of selected) {
|
|
1962
|
-
if (!byId.has(id)) {
|
|
1963
|
-
diagnostics.push(
|
|
1964
|
-
diagnostic3("AI_CHECK_INVALID_CONFIG", `Unknown trace check rule id: ${id}.`, id)
|
|
1965
|
-
);
|
|
1966
|
-
}
|
|
2007
|
+
return { diagnostics };
|
|
1967
2008
|
}
|
|
2009
|
+
const trace = asString(raw.trace, `cases[${index}].trace`);
|
|
2010
|
+
const runId = asString(raw.runId, `cases[${index}].runId`);
|
|
2011
|
+
const input = asString(raw.input, `cases[${index}].input`);
|
|
1968
2012
|
return {
|
|
1969
|
-
|
|
2013
|
+
caseConfig: {
|
|
2014
|
+
id,
|
|
2015
|
+
...trace !== void 0 ? { trace } : {},
|
|
2016
|
+
...runId !== void 0 ? { runId } : {},
|
|
2017
|
+
...input !== void 0 ? { input } : {},
|
|
2018
|
+
...asStringArray(raw.requireTools, `cases[${index}].requireTools`) !== void 0 ? { requireTools: asStringArray(raw.requireTools, `cases[${index}].requireTools`) } : {},
|
|
2019
|
+
...asStringArray(raw.forbidTools, `cases[${index}].forbidTools`) !== void 0 ? { forbidTools: asStringArray(raw.forbidTools, `cases[${index}].forbidTools`) } : {},
|
|
2020
|
+
...asPositiveNumber(raw.maxDurationMs, `cases[${index}].maxDurationMs`) !== void 0 ? {
|
|
2021
|
+
maxDurationMs: asPositiveNumber(
|
|
2022
|
+
raw.maxDurationMs,
|
|
2023
|
+
`cases[${index}].maxDurationMs`
|
|
2024
|
+
)
|
|
2025
|
+
} : {},
|
|
2026
|
+
...asStringArray(
|
|
2027
|
+
raw.expectedObservations,
|
|
2028
|
+
`cases[${index}].expectedObservations`
|
|
2029
|
+
) !== void 0 ? {
|
|
2030
|
+
expectedObservations: asStringArray(
|
|
2031
|
+
raw.expectedObservations,
|
|
2032
|
+
`cases[${index}].expectedObservations`
|
|
2033
|
+
)
|
|
2034
|
+
} : {}
|
|
2035
|
+
},
|
|
1970
2036
|
diagnostics
|
|
1971
2037
|
};
|
|
2038
|
+
} catch (error) {
|
|
2039
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2040
|
+
diagnostics.push(diagnostic("AI_SUITE_CONFIG_INVALID", message));
|
|
2041
|
+
return { diagnostics };
|
|
1972
2042
|
}
|
|
1973
|
-
return { rules: [...byId.values()].sort(compareRules), diagnostics };
|
|
1974
|
-
}
|
|
1975
|
-
function compareRules(a, b) {
|
|
1976
|
-
return a.id.localeCompare(b.id);
|
|
1977
|
-
}
|
|
1978
|
-
function eventTimestamp(finding, eventById) {
|
|
1979
|
-
const eventId = finding.evidence[0]?.eventId;
|
|
1980
|
-
return eventId ? eventById.get(eventId)?.timestamp ?? "" : "";
|
|
1981
2043
|
}
|
|
1982
|
-
function
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
2044
|
+
function normalizeSuiteConfig(value) {
|
|
2045
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
2046
|
+
throw new Error("Suite config must export an object.");
|
|
2047
|
+
}
|
|
2048
|
+
const raw = value;
|
|
2049
|
+
const name = asString(raw.name, "name");
|
|
2050
|
+
const traces = asString(raw.traces, "traces");
|
|
2051
|
+
if (name === void 0) throw new Error("name is required.");
|
|
2052
|
+
if (traces === void 0) throw new Error("traces is required.");
|
|
2053
|
+
if (!Array.isArray(raw.cases) || raw.cases.length === 0) {
|
|
2054
|
+
throw new Error("cases must be a non-empty array.");
|
|
2055
|
+
}
|
|
2056
|
+
const cases = [];
|
|
2057
|
+
for (let index = 0; index < raw.cases.length; index += 1) {
|
|
2058
|
+
const { caseConfig, diagnostics } = validateCaseConfig(raw.cases[index], index);
|
|
2059
|
+
if (diagnostics.length > 0) {
|
|
2060
|
+
throw new Error(diagnostics.map((item) => item.message).join("; "));
|
|
1986
2061
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
2062
|
+
if (caseConfig !== void 0) cases.push(caseConfig);
|
|
2063
|
+
}
|
|
2064
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2065
|
+
for (const suiteCase of cases) {
|
|
2066
|
+
if (ids.has(suiteCase.id)) {
|
|
2067
|
+
throw new Error(`Duplicate case id "${suiteCase.id}".`);
|
|
1991
2068
|
}
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2069
|
+
ids.add(suiteCase.id);
|
|
2070
|
+
}
|
|
2071
|
+
const redactionProfile = raw.redactionProfile === "local" || raw.redactionProfile === "share" || raw.redactionProfile === "strict" ? raw.redactionProfile : void 0;
|
|
2072
|
+
if (raw.redactionProfile !== void 0 && redactionProfile === void 0) {
|
|
2073
|
+
throw new Error('redactionProfile must be "local", "share", or "strict".');
|
|
2074
|
+
}
|
|
2075
|
+
const config = { name, traces, cases };
|
|
2076
|
+
if (raw.checks !== void 0) {
|
|
2077
|
+
if (typeof raw.checks !== "object" || Array.isArray(raw.checks)) {
|
|
2078
|
+
throw new Error("checks must be an object.");
|
|
2079
|
+
}
|
|
2080
|
+
config.checks = raw.checks;
|
|
2081
|
+
}
|
|
2082
|
+
if (raw.eval !== void 0) {
|
|
2083
|
+
if (typeof raw.eval !== "object" || Array.isArray(raw.eval)) {
|
|
2084
|
+
throw new Error("eval must be an object.");
|
|
2085
|
+
}
|
|
2086
|
+
config.eval = raw.eval;
|
|
2087
|
+
}
|
|
2088
|
+
if (raw.artifacts !== void 0) {
|
|
2089
|
+
if (typeof raw.artifacts !== "object" || Array.isArray(raw.artifacts)) {
|
|
2090
|
+
throw new Error("artifacts must be an object.");
|
|
2091
|
+
}
|
|
2092
|
+
const outputDir = asString(
|
|
2093
|
+
raw.artifacts.outputDir,
|
|
2094
|
+
"artifacts.outputDir"
|
|
2095
|
+
);
|
|
2096
|
+
config.artifacts = outputDir !== void 0 ? { outputDir } : {};
|
|
2097
|
+
}
|
|
2098
|
+
if (raw.baseline !== void 0) {
|
|
2099
|
+
const baseline = asString(raw.baseline, "baseline");
|
|
2100
|
+
if (baseline !== void 0) config.baseline = baseline;
|
|
2101
|
+
}
|
|
2102
|
+
if (raw.candidate !== void 0) {
|
|
2103
|
+
const candidate = asString(raw.candidate, "candidate");
|
|
2104
|
+
if (candidate !== void 0) config.candidate = candidate;
|
|
2105
|
+
}
|
|
2106
|
+
if (redactionProfile !== void 0) config.redactionProfile = redactionProfile;
|
|
2107
|
+
return config;
|
|
2000
2108
|
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
...finding.expected !== void 0 ? { expected: finding.expected } : {},
|
|
2008
|
-
...finding.actual !== void 0 ? { actual: finding.actual } : {},
|
|
2009
|
-
evidence: [...finding.evidence ?? []],
|
|
2010
|
-
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
2011
|
-
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
2012
|
-
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
2013
|
-
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
2014
|
-
};
|
|
2109
|
+
|
|
2110
|
+
// packages/core/src/suite/load.ts
|
|
2111
|
+
var CONFIG_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
|
|
2112
|
+
var TS_CONFIG_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".mts", ".cts"]);
|
|
2113
|
+
function diagnostic2(code, message) {
|
|
2114
|
+
return { code, message, severity: "error" };
|
|
2015
2115
|
}
|
|
2016
|
-
function
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
2024
|
-
).length,
|
|
2025
|
-
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
2026
|
-
};
|
|
2116
|
+
async function fileExists(filePath) {
|
|
2117
|
+
try {
|
|
2118
|
+
await access(filePath);
|
|
2119
|
+
return true;
|
|
2120
|
+
} catch {
|
|
2121
|
+
return false;
|
|
2122
|
+
}
|
|
2027
2123
|
}
|
|
2028
|
-
function
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2124
|
+
async function resolveSuiteConfigPath(options = {}) {
|
|
2125
|
+
const cwd = path15.resolve(options.cwd ?? process.cwd());
|
|
2126
|
+
if (options.configPath !== void 0 && options.configPath.trim() !== "") {
|
|
2127
|
+
return path15.resolve(cwd, options.configPath.trim());
|
|
2032
2128
|
}
|
|
2033
|
-
|
|
2129
|
+
for (const name of DEFAULT_SUITE_CONFIG_NAMES) {
|
|
2130
|
+
const candidate = path15.join(cwd, name);
|
|
2131
|
+
if (await fileExists(candidate)) return candidate;
|
|
2132
|
+
}
|
|
2133
|
+
throw new Error(
|
|
2134
|
+
`No suite config found. Create one with \`agent-inspect suite init\` or pass --config.`
|
|
2135
|
+
);
|
|
2034
2136
|
}
|
|
2035
|
-
function
|
|
2036
|
-
|
|
2037
|
-
|
|
2137
|
+
async function loadSuiteConfig(options = {}) {
|
|
2138
|
+
let configPath;
|
|
2139
|
+
try {
|
|
2140
|
+
configPath = await resolveSuiteConfigPath(options);
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2143
|
+
throw Object.assign(new Error(message), {
|
|
2144
|
+
diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
const extension = path15.extname(configPath);
|
|
2148
|
+
if (TS_CONFIG_EXTENSIONS.has(extension)) {
|
|
2149
|
+
const message = "TypeScript suite configs require an explicit precompiled JavaScript config or future --config-loader support.";
|
|
2150
|
+
throw Object.assign(new Error(message), {
|
|
2151
|
+
diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
if (!CONFIG_EXTENSIONS.has(extension)) {
|
|
2155
|
+
const message = "Unsupported suite config extension. Use .json, .js, .mjs, or .cjs.";
|
|
2156
|
+
throw Object.assign(new Error(message), {
|
|
2157
|
+
diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
|
|
2158
|
+
});
|
|
2159
|
+
}
|
|
2160
|
+
try {
|
|
2161
|
+
let raw;
|
|
2162
|
+
if (extension === ".json") {
|
|
2163
|
+
raw = JSON.parse(await readFile(configPath, "utf-8"));
|
|
2164
|
+
} else {
|
|
2165
|
+
const mod = await import(pathToFileURL(configPath).href);
|
|
2166
|
+
raw = "default" in mod ? mod.default : mod;
|
|
2167
|
+
}
|
|
2168
|
+
const config = normalizeSuiteConfig(raw);
|
|
2169
|
+
return {
|
|
2170
|
+
config,
|
|
2171
|
+
configPath,
|
|
2172
|
+
configDir: path15.dirname(configPath)
|
|
2173
|
+
};
|
|
2174
|
+
} catch (error) {
|
|
2175
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2176
|
+
throw Object.assign(new Error(message), {
|
|
2177
|
+
diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
async function exists(filePath) {
|
|
2182
|
+
try {
|
|
2183
|
+
await access(filePath);
|
|
2184
|
+
return true;
|
|
2185
|
+
} catch {
|
|
2186
|
+
return false;
|
|
2038
2187
|
}
|
|
2039
|
-
return name;
|
|
2040
2188
|
}
|
|
2041
|
-
function
|
|
2189
|
+
async function resolveSuiteCaseTrace(suiteCase, options) {
|
|
2190
|
+
if (suiteCase.trace !== void 0) {
|
|
2191
|
+
const tracePath = path15.resolve(options.configDir, suiteCase.trace);
|
|
2192
|
+
if (await exists(tracePath)) {
|
|
2193
|
+
return { caseId: suiteCase.id, tracePath, missing: false };
|
|
2194
|
+
}
|
|
2195
|
+
return {
|
|
2196
|
+
caseId: suiteCase.id,
|
|
2197
|
+
tracePath,
|
|
2198
|
+
missing: true,
|
|
2199
|
+
reason: `trace file not found: ${suiteCase.trace}`
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
const runKey = suiteCase.runId ?? suiteCase.id;
|
|
2203
|
+
const directPath = getTraceFilePath(runKey, options.tracesDir);
|
|
2204
|
+
if (await exists(directPath)) {
|
|
2205
|
+
return { caseId: suiteCase.id, tracePath: directPath, runId: runKey, missing: false };
|
|
2206
|
+
}
|
|
2207
|
+
const nestedPath = path15.join(options.tracesDir, `${path15.basename(runKey)}.jsonl`);
|
|
2208
|
+
if (await exists(nestedPath)) {
|
|
2209
|
+
return { caseId: suiteCase.id, tracePath: nestedPath, runId: runKey, missing: false };
|
|
2210
|
+
}
|
|
2042
2211
|
return {
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
spanId: event.trace?.spanId,
|
|
2048
|
-
kind: event.kind,
|
|
2049
|
-
name: event.name,
|
|
2050
|
-
status: event.status,
|
|
2051
|
-
...path16 ? { path: path16 } : {}
|
|
2212
|
+
caseId: suiteCase.id,
|
|
2213
|
+
runId: runKey,
|
|
2214
|
+
missing: true,
|
|
2215
|
+
reason: `no trace found for run id "${runKey}" under ${options.tracesDir}`
|
|
2052
2216
|
};
|
|
2053
2217
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2218
|
+
|
|
2219
|
+
// packages/core/src/checks/index.ts
|
|
2220
|
+
var SEVERITY_RANK = {
|
|
2221
|
+
error: 0,
|
|
2222
|
+
warning: 1,
|
|
2223
|
+
info: 2
|
|
2224
|
+
};
|
|
2225
|
+
var STATUS_RANK = {
|
|
2226
|
+
fail: 0,
|
|
2227
|
+
warning: 1,
|
|
2228
|
+
pass: 2
|
|
2229
|
+
};
|
|
2230
|
+
function compareStrings(a, b) {
|
|
2231
|
+
return (a ?? "").localeCompare(b ?? "");
|
|
2056
2232
|
}
|
|
2057
|
-
function
|
|
2233
|
+
function diagnostic3(code, message, ruleId) {
|
|
2058
2234
|
return {
|
|
2059
|
-
|
|
2060
|
-
severity: meta?.severity ?? "error",
|
|
2061
|
-
status: meta?.status ?? "fail",
|
|
2235
|
+
code,
|
|
2062
2236
|
message,
|
|
2063
|
-
|
|
2064
|
-
...
|
|
2065
|
-
evidence: [...evidence],
|
|
2066
|
-
...{},
|
|
2067
|
-
...{},
|
|
2068
|
-
...{},
|
|
2069
|
-
...{}
|
|
2237
|
+
severity: "error",
|
|
2238
|
+
...ruleId ? { ruleId } : {}
|
|
2070
2239
|
};
|
|
2071
2240
|
}
|
|
2072
|
-
function
|
|
2073
|
-
return
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2241
|
+
function emptySummary() {
|
|
2242
|
+
return {
|
|
2243
|
+
passed: 0,
|
|
2244
|
+
failed: 0,
|
|
2245
|
+
warnings: 0,
|
|
2246
|
+
errors: 0
|
|
2247
|
+
};
|
|
2077
2248
|
}
|
|
2078
|
-
function
|
|
2079
|
-
return
|
|
2249
|
+
function errorResult(input, diagnostics, selectedRun) {
|
|
2250
|
+
return {
|
|
2251
|
+
ok: false,
|
|
2252
|
+
status: "error",
|
|
2253
|
+
format: input.read.format,
|
|
2254
|
+
...selectedRun ? { runId: selectedRun.runId } : {},
|
|
2255
|
+
summary: {
|
|
2256
|
+
...emptySummary(),
|
|
2257
|
+
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
2258
|
+
},
|
|
2259
|
+
findings: [],
|
|
2260
|
+
diagnostics: [...diagnostics]
|
|
2261
|
+
};
|
|
2080
2262
|
}
|
|
2081
|
-
function
|
|
2082
|
-
return
|
|
2263
|
+
function flattenNodes(nodes) {
|
|
2264
|
+
return nodes.flatMap((node) => [node, ...flattenNodes(node.children)]);
|
|
2083
2265
|
}
|
|
2084
|
-
function
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
);
|
|
2266
|
+
function buildFacts(input, selectedRun) {
|
|
2267
|
+
const scopedRuns = selectedRun ? [selectedRun] : input.read.runs;
|
|
2268
|
+
const scopedRunIds = new Set(scopedRuns.map((run) => run.runId));
|
|
2269
|
+
const scopedEvents = selectedRun === void 0 ? input.read.events : input.read.events.filter((event) => scopedRunIds.has(event.runId));
|
|
2270
|
+
const nodes = flattenNodes(scopedRuns.flatMap((run) => run.children));
|
|
2271
|
+
const nodesByEventId = /* @__PURE__ */ new Map();
|
|
2272
|
+
const childrenByParentId = /* @__PURE__ */ new Map();
|
|
2273
|
+
for (const node of nodes) {
|
|
2274
|
+
nodesByEventId.set(node.event.eventId, node);
|
|
2275
|
+
const parentId = node.event.parentId;
|
|
2276
|
+
if (parentId) {
|
|
2277
|
+
const children = childrenByParentId.get(parentId) ?? [];
|
|
2278
|
+
children.push(node);
|
|
2279
|
+
childrenByParentId.set(parentId, children);
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
return {
|
|
2283
|
+
format: input.read.format,
|
|
2284
|
+
runs: Object.freeze([...input.read.runs]),
|
|
2285
|
+
events: Object.freeze([...scopedEvents]),
|
|
2286
|
+
readerWarnings: Object.freeze([...input.read.warnings]),
|
|
2287
|
+
unsupportedFields: Object.freeze([...input.read.unsupportedFields]),
|
|
2288
|
+
sourceFiles: Object.freeze([...input.read.sourceFiles]),
|
|
2289
|
+
nodesByEventId,
|
|
2290
|
+
childrenByParentId,
|
|
2291
|
+
rootNodes: Object.freeze(scopedRuns.flatMap((run) => run.children))
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
function resolveSelectedRun(input, runId) {
|
|
2295
|
+
if (input.selectedRun) {
|
|
2296
|
+
if (runId && input.selectedRun.runId !== runId) {
|
|
2297
|
+
return {
|
|
2298
|
+
diagnostics: [
|
|
2299
|
+
diagnostic3(
|
|
2300
|
+
"AI_CHECK_INVALID_ARGUMENTS",
|
|
2301
|
+
`Selected run ${input.selectedRun.runId} does not match requested run ${runId}.`
|
|
2302
|
+
)
|
|
2303
|
+
]
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
return { run: input.selectedRun, diagnostics: [] };
|
|
2307
|
+
}
|
|
2308
|
+
if (runId) {
|
|
2309
|
+
const run = input.read.runs.find((candidate) => candidate.runId === runId);
|
|
2310
|
+
if (!run) {
|
|
2311
|
+
return {
|
|
2312
|
+
diagnostics: [
|
|
2313
|
+
diagnostic3("AI_CHECK_RUN_SELECTION_REQUIRED", `Run not found: ${runId}.`)
|
|
2314
|
+
]
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2317
|
+
return { run, diagnostics: [] };
|
|
2318
|
+
}
|
|
2319
|
+
if (input.read.runs.length === 1) {
|
|
2320
|
+
return { run: input.read.runs[0], diagnostics: [] };
|
|
2321
|
+
}
|
|
2322
|
+
if (input.read.runs.length === 0) {
|
|
2323
|
+
return {
|
|
2324
|
+
diagnostics: [
|
|
2325
|
+
diagnostic3("AI_CHECK_RUN_SELECTION_REQUIRED", "No runs are available for checks.")
|
|
2326
|
+
]
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
return {
|
|
2330
|
+
diagnostics: [
|
|
2331
|
+
diagnostic3(
|
|
2332
|
+
"AI_CHECK_RUN_SELECTION_REQUIRED",
|
|
2333
|
+
"Multiple runs are available; select a run before executing checks."
|
|
2334
|
+
)
|
|
2335
|
+
]
|
|
2336
|
+
};
|
|
2337
|
+
}
|
|
2338
|
+
function selectRules(rules, selectedIds) {
|
|
2339
|
+
const diagnostics = [];
|
|
2340
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2341
|
+
for (const rule of rules) {
|
|
2342
|
+
if (byId.has(rule.id)) {
|
|
2343
|
+
diagnostics.push(
|
|
2344
|
+
diagnostic3("AI_CHECK_INVALID_CONFIG", `Duplicate trace check rule id: ${rule.id}.`, rule.id)
|
|
2345
|
+
);
|
|
2346
|
+
continue;
|
|
2347
|
+
}
|
|
2348
|
+
byId.set(rule.id, rule);
|
|
2349
|
+
}
|
|
2350
|
+
if (selectedIds && selectedIds.length > 0) {
|
|
2351
|
+
const selected = new Set(selectedIds);
|
|
2352
|
+
for (const id of selected) {
|
|
2353
|
+
if (!byId.has(id)) {
|
|
2354
|
+
diagnostics.push(
|
|
2355
|
+
diagnostic3("AI_CHECK_INVALID_CONFIG", `Unknown trace check rule id: ${id}.`, id)
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
return {
|
|
2360
|
+
rules: [...byId.values()].filter((rule) => selected.has(rule.id)).sort(compareRules),
|
|
2361
|
+
diagnostics
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
return { rules: [...byId.values()].sort(compareRules), diagnostics };
|
|
2365
|
+
}
|
|
2366
|
+
function compareRules(a, b) {
|
|
2367
|
+
return a.id.localeCompare(b.id);
|
|
2368
|
+
}
|
|
2369
|
+
function eventTimestamp(finding, eventById) {
|
|
2370
|
+
const eventId = finding.evidence[0]?.eventId;
|
|
2371
|
+
return eventId ? eventById.get(eventId)?.timestamp ?? "" : "";
|
|
2372
|
+
}
|
|
2373
|
+
function compareFindings(eventById) {
|
|
2374
|
+
return (a, b) => {
|
|
2375
|
+
if (SEVERITY_RANK[a.severity] !== SEVERITY_RANK[b.severity]) {
|
|
2376
|
+
return SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
|
|
2377
|
+
}
|
|
2378
|
+
const byRule = a.ruleId.localeCompare(b.ruleId);
|
|
2379
|
+
if (byRule !== 0) return byRule;
|
|
2380
|
+
if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
|
|
2381
|
+
return STATUS_RANK[a.status] - STATUS_RANK[b.status];
|
|
2382
|
+
}
|
|
2383
|
+
const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
|
|
2384
|
+
if (byRun !== 0) return byRun;
|
|
2385
|
+
const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
|
|
2386
|
+
if (byTime !== 0) return byTime;
|
|
2387
|
+
const byEvent = compareStrings(a.evidence[0]?.eventId, b.evidence[0]?.eventId);
|
|
2388
|
+
if (byEvent !== 0) return byEvent;
|
|
2389
|
+
return compareStrings(a.evidence[0]?.path, b.evidence[0]?.path);
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
function normalizeFinding(rule, finding) {
|
|
2393
|
+
return {
|
|
2394
|
+
ruleId: finding.ruleId || rule.id,
|
|
2395
|
+
severity: finding.severity ?? rule.defaultSeverity,
|
|
2396
|
+
status: finding.status,
|
|
2397
|
+
message: finding.message,
|
|
2398
|
+
...finding.expected !== void 0 ? { expected: finding.expected } : {},
|
|
2399
|
+
...finding.actual !== void 0 ? { actual: finding.actual } : {},
|
|
2400
|
+
evidence: [...finding.evidence ?? []],
|
|
2401
|
+
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
2402
|
+
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
2403
|
+
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
2404
|
+
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
function summarize(findings, diagnostics) {
|
|
2408
|
+
return {
|
|
2409
|
+
passed: findings.filter((finding) => finding.status === "pass").length,
|
|
2410
|
+
failed: findings.filter(
|
|
2411
|
+
(finding) => finding.status === "fail" && finding.severity === "error"
|
|
2412
|
+
).length,
|
|
2413
|
+
warnings: findings.filter(
|
|
2414
|
+
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
2415
|
+
).length,
|
|
2416
|
+
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
function stringAttr(event, keys) {
|
|
2420
|
+
for (const key of keys) {
|
|
2421
|
+
const value = event.attributes?.[key];
|
|
2422
|
+
if (typeof value === "string" && value.trim() !== "") return value;
|
|
2423
|
+
}
|
|
2424
|
+
return void 0;
|
|
2425
|
+
}
|
|
2426
|
+
function stripPrefix(name, prefixes) {
|
|
2427
|
+
for (const prefix of prefixes) {
|
|
2428
|
+
if (name.startsWith(prefix)) return name.slice(prefix.length);
|
|
2429
|
+
}
|
|
2430
|
+
return name;
|
|
2431
|
+
}
|
|
2432
|
+
function eventEvidence(event, path18) {
|
|
2433
|
+
return {
|
|
2434
|
+
runId: event.runId,
|
|
2435
|
+
eventId: event.eventId,
|
|
2436
|
+
parentId: event.parentId,
|
|
2437
|
+
traceId: event.trace?.traceId,
|
|
2438
|
+
spanId: event.trace?.spanId,
|
|
2439
|
+
kind: event.kind,
|
|
2440
|
+
name: event.name,
|
|
2441
|
+
status: event.status,
|
|
2442
|
+
...path18 ? { path: path18 } : {}
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
function runEvidence(run) {
|
|
2446
|
+
return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
|
|
2447
|
+
}
|
|
2448
|
+
function failFinding(ruleId, message, evidence, expected, actual, meta) {
|
|
2449
|
+
return {
|
|
2450
|
+
ruleId,
|
|
2451
|
+
severity: meta?.severity ?? "error",
|
|
2452
|
+
status: meta?.status ?? "fail",
|
|
2453
|
+
message,
|
|
2454
|
+
...expected !== void 0 ? { expected } : {},
|
|
2455
|
+
...actual !== void 0 ? { actual } : {},
|
|
2456
|
+
evidence: [...evidence],
|
|
2457
|
+
...{},
|
|
2458
|
+
...{},
|
|
2459
|
+
...{},
|
|
2460
|
+
...{}
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
function toolName(event) {
|
|
2464
|
+
return stringAttr(event, ["toolName", "tool"]) ?? stripPrefix(event.name, ["tool:", "function:", "mcp-tools:"]);
|
|
2465
|
+
}
|
|
2466
|
+
function llmModel(event) {
|
|
2467
|
+
return stringAttr(event, ["model", "modelId", "responseModelId", "modelName", "model_name"]) ?? stripPrefix(event.name, ["llm:", "generation:", "transcription:", "speech:"]);
|
|
2468
|
+
}
|
|
2469
|
+
function llmProvider(event) {
|
|
2470
|
+
return stringAttr(event, ["provider", "providerName", "provider_name"]);
|
|
2471
|
+
}
|
|
2472
|
+
function llmFinishReason(event) {
|
|
2473
|
+
return stringAttr(event, ["finishReason", "rawFinishReason", "finish_reason"]);
|
|
2474
|
+
}
|
|
2475
|
+
function finishedEvents(context, kind) {
|
|
2476
|
+
return context.events.filter(
|
|
2477
|
+
(event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
|
|
2478
|
+
);
|
|
2088
2479
|
}
|
|
2089
2480
|
function createRunStatusRule(options = {}) {
|
|
2090
2481
|
const expected = options.expected ?? "ok";
|
|
@@ -2972,7 +3363,7 @@ function findReaderByFormat(format, readers) {
|
|
|
2972
3363
|
}
|
|
2973
3364
|
async function jsonlFilesInDirectory(dirPath) {
|
|
2974
3365
|
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
2975
|
-
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) =>
|
|
3366
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path15.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
2976
3367
|
}
|
|
2977
3368
|
async function resolveInput(input) {
|
|
2978
3369
|
const cached = resolvedInputCache.get(input);
|
|
@@ -4592,7 +4983,7 @@ async function runSuiteCase(suiteCase, config, options) {
|
|
|
4592
4983
|
async function runSuite(options = {}) {
|
|
4593
4984
|
const startedAt = new Date(options.nowMs ?? Date.now()).toISOString();
|
|
4594
4985
|
const { config, configPath, configDir } = await loadSuiteConfig(options);
|
|
4595
|
-
const tracesDir =
|
|
4986
|
+
const tracesDir = path15.resolve(configDir, config.traces);
|
|
4596
4987
|
const cases = [];
|
|
4597
4988
|
const diagnostics = [];
|
|
4598
4989
|
for (const suiteCase of config.cases) {
|
|
@@ -4627,22 +5018,6 @@ async function runSuite(options = {}) {
|
|
|
4627
5018
|
};
|
|
4628
5019
|
}
|
|
4629
5020
|
|
|
4630
|
-
// packages/core/src/exporters/helpers.ts
|
|
4631
|
-
function sortKeysDeep(input) {
|
|
4632
|
-
if (input === null || typeof input !== "object") return input;
|
|
4633
|
-
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
4634
|
-
const o = input;
|
|
4635
|
-
const out = {};
|
|
4636
|
-
for (const k of Object.keys(o).sort()) {
|
|
4637
|
-
out[k] = sortKeysDeep(o[k]);
|
|
4638
|
-
}
|
|
4639
|
-
return out;
|
|
4640
|
-
}
|
|
4641
|
-
function stableJson(value, pretty) {
|
|
4642
|
-
const sorted = sortKeysDeep(value);
|
|
4643
|
-
return JSON.stringify(sorted);
|
|
4644
|
-
}
|
|
4645
|
-
|
|
4646
5021
|
// packages/viewer/src/html.ts
|
|
4647
5022
|
var viewerIndexHtml = `<!DOCTYPE html>
|
|
4648
5023
|
<html lang="en">
|
|
@@ -4732,370 +5107,6 @@ var viewerIndexHtml = `<!DOCTYPE html>
|
|
|
4732
5107
|
</body>
|
|
4733
5108
|
</html>
|
|
4734
5109
|
`;
|
|
4735
|
-
|
|
4736
|
-
// packages/core/src/diff/comparable.ts
|
|
4737
|
-
function extractOutputPreview(meta) {
|
|
4738
|
-
if (meta === void 0) return void 0;
|
|
4739
|
-
if ("outputPreview" in meta) return meta.outputPreview;
|
|
4740
|
-
if ("resultPreview" in meta) return meta.resultPreview;
|
|
4741
|
-
return void 0;
|
|
4742
|
-
}
|
|
4743
|
-
function mapStepStatus(s) {
|
|
4744
|
-
if (s === void 0) return "running";
|
|
4745
|
-
return s;
|
|
4746
|
-
}
|
|
4747
|
-
function manualTraceEventsToComparableRun(events) {
|
|
4748
|
-
const started = events.find((e) => e.event === "run_started");
|
|
4749
|
-
if (!started || started.event !== "run_started") {
|
|
4750
|
-
throw new Error("Invalid trace: missing run_started");
|
|
4751
|
-
}
|
|
4752
|
-
const rs = started;
|
|
4753
|
-
const runId = rs.runId;
|
|
4754
|
-
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
4755
|
-
const lastCompleted = completedAll[completedAll.length - 1];
|
|
4756
|
-
let runStatus;
|
|
4757
|
-
if (lastCompleted === void 0) runStatus = "running";
|
|
4758
|
-
else runStatus = lastCompleted.status;
|
|
4759
|
-
const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
4760
|
-
const steps = /* @__PURE__ */ new Map();
|
|
4761
|
-
let order = 0;
|
|
4762
|
-
for (const e of events) {
|
|
4763
|
-
if (e.event !== "step_started") continue;
|
|
4764
|
-
const s = e;
|
|
4765
|
-
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
4766
|
-
steps.set(s.stepId, {
|
|
4767
|
-
id: s.stepId,
|
|
4768
|
-
parentId: s.parentId,
|
|
4769
|
-
name: s.name,
|
|
4770
|
-
type: s.type,
|
|
4771
|
-
order: order++,
|
|
4772
|
-
timestamp: s.timestamp,
|
|
4773
|
-
metadata: meta
|
|
4774
|
-
});
|
|
4775
|
-
}
|
|
4776
|
-
for (const e of events) {
|
|
4777
|
-
if (e.event !== "step_completed") continue;
|
|
4778
|
-
const acc = steps.get(e.stepId);
|
|
4779
|
-
if (!acc) continue;
|
|
4780
|
-
acc.status = e.status;
|
|
4781
|
-
acc.durationMs = e.durationMs;
|
|
4782
|
-
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
4783
|
-
const extra = e;
|
|
4784
|
-
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
4785
|
-
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
4786
|
-
}
|
|
4787
|
-
}
|
|
4788
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
4789
|
-
for (const acc of steps.values()) {
|
|
4790
|
-
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
4791
|
-
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
4792
|
-
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
4793
|
-
}
|
|
4794
|
-
const outputPreview = extractOutputPreview(meta);
|
|
4795
|
-
if (meta !== void 0 && ("outputPreview" in meta || "resultPreview" in meta)) {
|
|
4796
|
-
delete meta.outputPreview;
|
|
4797
|
-
delete meta.resultPreview;
|
|
4798
|
-
}
|
|
4799
|
-
const sc = {
|
|
4800
|
-
id: acc.id,
|
|
4801
|
-
name: acc.name,
|
|
4802
|
-
type: acc.type,
|
|
4803
|
-
status: mapStepStatus(acc.status),
|
|
4804
|
-
durationMs: acc.durationMs,
|
|
4805
|
-
error: acc.errorMsg,
|
|
4806
|
-
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
4807
|
-
outputPreview,
|
|
4808
|
-
children: []
|
|
4809
|
-
};
|
|
4810
|
-
nodes.set(acc.id, sc);
|
|
4811
|
-
}
|
|
4812
|
-
const roots = [];
|
|
4813
|
-
const sortByOrder = (a, b) => {
|
|
4814
|
-
const oa = steps.get(a.id)?.order ?? 0;
|
|
4815
|
-
const ob = steps.get(b.id)?.order ?? 0;
|
|
4816
|
-
return oa - ob;
|
|
4817
|
-
};
|
|
4818
|
-
for (const acc of steps.values()) {
|
|
4819
|
-
const node = nodes.get(acc.id);
|
|
4820
|
-
if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
|
|
4821
|
-
nodes.get(acc.parentId).children.push(node);
|
|
4822
|
-
} else {
|
|
4823
|
-
roots.push(node);
|
|
4824
|
-
}
|
|
4825
|
-
}
|
|
4826
|
-
roots.sort(sortByOrder);
|
|
4827
|
-
for (const n of nodes.values()) {
|
|
4828
|
-
n.children.sort(sortByOrder);
|
|
4829
|
-
}
|
|
4830
|
-
return {
|
|
4831
|
-
runId,
|
|
4832
|
-
name: rs.name,
|
|
4833
|
-
status: runStatus,
|
|
4834
|
-
durationMs,
|
|
4835
|
-
steps: roots
|
|
4836
|
-
};
|
|
4837
|
-
}
|
|
4838
|
-
|
|
4839
|
-
// packages/core/src/diff/engine.ts
|
|
4840
|
-
var DEFAULT_THRESHOLD_MS = 0;
|
|
4841
|
-
function pathSeg(step, index) {
|
|
4842
|
-
return { index, name: step.name, stepId: step.id };
|
|
4843
|
-
}
|
|
4844
|
-
function buildPath(segments) {
|
|
4845
|
-
return { path: [...segments] };
|
|
4846
|
-
}
|
|
4847
|
-
function pairSteps(left, right) {
|
|
4848
|
-
const usedRight = /* @__PURE__ */ new Set();
|
|
4849
|
-
const pairs = [];
|
|
4850
|
-
for (let i = 0; i < left.length; i++) {
|
|
4851
|
-
const L = left[i];
|
|
4852
|
-
let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
|
|
4853
|
-
if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
|
|
4854
|
-
const cand = right[i];
|
|
4855
|
-
if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
|
|
4856
|
-
R = cand;
|
|
4857
|
-
}
|
|
4858
|
-
}
|
|
4859
|
-
if (R === void 0) {
|
|
4860
|
-
R = right.find(
|
|
4861
|
-
(r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
|
|
4862
|
-
);
|
|
4863
|
-
}
|
|
4864
|
-
if (R !== void 0) {
|
|
4865
|
-
usedRight.add(R.id);
|
|
4866
|
-
pairs.push([L, R]);
|
|
4867
|
-
} else {
|
|
4868
|
-
pairs.push([L, void 0]);
|
|
4869
|
-
}
|
|
4870
|
-
}
|
|
4871
|
-
for (const R of right) {
|
|
4872
|
-
if (!usedRight.has(R.id)) {
|
|
4873
|
-
pairs.push([void 0, R]);
|
|
4874
|
-
}
|
|
4875
|
-
}
|
|
4876
|
-
return pairs;
|
|
4877
|
-
}
|
|
4878
|
-
function compareLeafSteps(L, R, segments, opts, out) {
|
|
4879
|
-
const path16 = buildPath(segments);
|
|
4880
|
-
if (L.name !== R.name) {
|
|
4881
|
-
out.push({
|
|
4882
|
-
kind: "structure",
|
|
4883
|
-
severity: "warning",
|
|
4884
|
-
message: "Step name differs",
|
|
4885
|
-
path: path16,
|
|
4886
|
-
left: L.name,
|
|
4887
|
-
right: R.name
|
|
4888
|
-
});
|
|
4889
|
-
}
|
|
4890
|
-
if ((L.type ?? "") !== (R.type ?? "")) {
|
|
4891
|
-
out.push({
|
|
4892
|
-
kind: "step-type",
|
|
4893
|
-
severity: "warning",
|
|
4894
|
-
message: "Step type differs",
|
|
4895
|
-
path: path16,
|
|
4896
|
-
left: L.type,
|
|
4897
|
-
right: R.type
|
|
4898
|
-
});
|
|
4899
|
-
}
|
|
4900
|
-
if ((L.status ?? "") !== (R.status ?? "")) {
|
|
4901
|
-
out.push({
|
|
4902
|
-
kind: "step-status",
|
|
4903
|
-
severity: "warning",
|
|
4904
|
-
message: "Step status differs",
|
|
4905
|
-
path: path16,
|
|
4906
|
-
left: L.status,
|
|
4907
|
-
right: R.status
|
|
4908
|
-
});
|
|
4909
|
-
}
|
|
4910
|
-
const le = L.error ?? "";
|
|
4911
|
-
const re = R.error ?? "";
|
|
4912
|
-
if (le !== re) {
|
|
4913
|
-
out.push({
|
|
4914
|
-
kind: "error",
|
|
4915
|
-
severity: "error",
|
|
4916
|
-
message: "Step error message differs",
|
|
4917
|
-
path: path16,
|
|
4918
|
-
left: le || void 0,
|
|
4919
|
-
right: re || void 0
|
|
4920
|
-
});
|
|
4921
|
-
}
|
|
4922
|
-
if (!opts.ignoreDuration) {
|
|
4923
|
-
const ld = L.durationMs;
|
|
4924
|
-
const rd = R.durationMs;
|
|
4925
|
-
const th = opts.durationThresholdMs;
|
|
4926
|
-
let differs = false;
|
|
4927
|
-
if (ld === void 0 && rd === void 0) differs = false;
|
|
4928
|
-
else if (ld === void 0 || rd === void 0) differs = true;
|
|
4929
|
-
else differs = Math.abs(ld - rd) > th;
|
|
4930
|
-
if (differs) {
|
|
4931
|
-
out.push({
|
|
4932
|
-
kind: "duration",
|
|
4933
|
-
severity: "info",
|
|
4934
|
-
message: "Step duration differs",
|
|
4935
|
-
path: path16,
|
|
4936
|
-
left: ld,
|
|
4937
|
-
right: rd
|
|
4938
|
-
});
|
|
4939
|
-
}
|
|
4940
|
-
}
|
|
4941
|
-
const lm = stableJson(L.metadata ?? {});
|
|
4942
|
-
const rm2 = stableJson(R.metadata ?? {});
|
|
4943
|
-
if (lm !== rm2) {
|
|
4944
|
-
out.push({
|
|
4945
|
-
kind: "metadata",
|
|
4946
|
-
severity: "info",
|
|
4947
|
-
message: "Step metadata differs",
|
|
4948
|
-
path: path16,
|
|
4949
|
-
left: L.metadata,
|
|
4950
|
-
right: R.metadata
|
|
4951
|
-
});
|
|
4952
|
-
}
|
|
4953
|
-
const lo = stableJson(L.outputPreview ?? null);
|
|
4954
|
-
const ro = stableJson(R.outputPreview ?? null);
|
|
4955
|
-
if (lo !== ro) {
|
|
4956
|
-
out.push({
|
|
4957
|
-
kind: "output",
|
|
4958
|
-
severity: "info",
|
|
4959
|
-
message: "Output preview differs",
|
|
4960
|
-
path: path16,
|
|
4961
|
-
left: L.outputPreview,
|
|
4962
|
-
right: R.outputPreview
|
|
4963
|
-
});
|
|
4964
|
-
}
|
|
4965
|
-
}
|
|
4966
|
-
function compareRecursive(L, R, segments, opts, out) {
|
|
4967
|
-
compareLeafSteps(L, R, segments, opts, out);
|
|
4968
|
-
const pairs = pairSteps(L.children, R.children);
|
|
4969
|
-
let ci = 0;
|
|
4970
|
-
for (const [lch, rch] of pairs) {
|
|
4971
|
-
if (lch !== void 0 && rch !== void 0) {
|
|
4972
|
-
compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
|
|
4973
|
-
} else if (lch !== void 0) {
|
|
4974
|
-
out.push({
|
|
4975
|
-
kind: "step-removed",
|
|
4976
|
-
severity: "warning",
|
|
4977
|
-
message: `Step only in left run: ${lch.name}`,
|
|
4978
|
-
path: buildPath([...segments, pathSeg(lch, ci)]),
|
|
4979
|
-
left: lch.id,
|
|
4980
|
-
right: void 0
|
|
4981
|
-
});
|
|
4982
|
-
} else if (rch !== void 0) {
|
|
4983
|
-
out.push({
|
|
4984
|
-
kind: "step-added",
|
|
4985
|
-
severity: "warning",
|
|
4986
|
-
message: `Step only in right run: ${rch.name}`,
|
|
4987
|
-
path: buildPath([...segments, pathSeg(rch, ci)]),
|
|
4988
|
-
left: void 0,
|
|
4989
|
-
right: rch.id
|
|
4990
|
-
});
|
|
4991
|
-
}
|
|
4992
|
-
ci += 1;
|
|
4993
|
-
}
|
|
4994
|
-
}
|
|
4995
|
-
function mergeDiffDefaults(options) {
|
|
4996
|
-
return {
|
|
4997
|
-
ignoreDuration: false,
|
|
4998
|
-
durationThresholdMs: DEFAULT_THRESHOLD_MS,
|
|
4999
|
-
focus: "all",
|
|
5000
|
-
check: "all"
|
|
5001
|
-
};
|
|
5002
|
-
}
|
|
5003
|
-
function kindMatchesFilter(kind, merged) {
|
|
5004
|
-
return true;
|
|
5005
|
-
}
|
|
5006
|
-
function diffRuns(left, right, options) {
|
|
5007
|
-
const merged = mergeDiffDefaults();
|
|
5008
|
-
const opts = {
|
|
5009
|
-
ignoreDuration: merged.ignoreDuration,
|
|
5010
|
-
durationThresholdMs: merged.durationThresholdMs
|
|
5011
|
-
};
|
|
5012
|
-
const raw = [];
|
|
5013
|
-
if ((left.status ?? "") !== (right.status ?? "")) {
|
|
5014
|
-
raw.push({
|
|
5015
|
-
kind: "run-status",
|
|
5016
|
-
severity: "warning",
|
|
5017
|
-
message: "Run completion status differs",
|
|
5018
|
-
left: left.status,
|
|
5019
|
-
right: right.status
|
|
5020
|
-
});
|
|
5021
|
-
}
|
|
5022
|
-
{
|
|
5023
|
-
const ld = left.durationMs;
|
|
5024
|
-
const rd = right.durationMs;
|
|
5025
|
-
const th = merged.durationThresholdMs;
|
|
5026
|
-
let differs = false;
|
|
5027
|
-
if (ld === void 0 && rd === void 0) differs = false;
|
|
5028
|
-
else if (ld === void 0 || rd === void 0) differs = true;
|
|
5029
|
-
else differs = Math.abs(ld - rd) > th;
|
|
5030
|
-
if (differs) {
|
|
5031
|
-
raw.push({
|
|
5032
|
-
kind: "duration",
|
|
5033
|
-
severity: "info",
|
|
5034
|
-
message: "Run duration differs",
|
|
5035
|
-
left: ld,
|
|
5036
|
-
right: rd
|
|
5037
|
-
});
|
|
5038
|
-
}
|
|
5039
|
-
}
|
|
5040
|
-
const pairs = pairSteps(left.steps, right.steps);
|
|
5041
|
-
let idx = 0;
|
|
5042
|
-
for (const [ls, rs] of pairs) {
|
|
5043
|
-
if (ls !== void 0 && rs !== void 0) {
|
|
5044
|
-
compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
|
|
5045
|
-
idx += 1;
|
|
5046
|
-
} else if (ls !== void 0) {
|
|
5047
|
-
raw.push({
|
|
5048
|
-
kind: "step-removed",
|
|
5049
|
-
severity: "warning",
|
|
5050
|
-
message: `Step only in left run: ${ls.name}`,
|
|
5051
|
-
path: buildPath([pathSeg(ls, idx)]),
|
|
5052
|
-
left: ls.id,
|
|
5053
|
-
right: void 0
|
|
5054
|
-
});
|
|
5055
|
-
idx += 1;
|
|
5056
|
-
} else if (rs !== void 0) {
|
|
5057
|
-
raw.push({
|
|
5058
|
-
kind: "step-added",
|
|
5059
|
-
severity: "warning",
|
|
5060
|
-
message: `Step only in right run: ${rs.name}`,
|
|
5061
|
-
path: buildPath([pathSeg(rs, idx)]),
|
|
5062
|
-
left: void 0,
|
|
5063
|
-
right: rs.id
|
|
5064
|
-
});
|
|
5065
|
-
idx += 1;
|
|
5066
|
-
}
|
|
5067
|
-
}
|
|
5068
|
-
const differences = raw.filter((d) => kindMatchesFilter(d.kind));
|
|
5069
|
-
let errors = 0;
|
|
5070
|
-
let warnings = 0;
|
|
5071
|
-
let info = 0;
|
|
5072
|
-
for (const d of differences) {
|
|
5073
|
-
if (d.severity === "error") errors += 1;
|
|
5074
|
-
else if (d.severity === "warning") warnings += 1;
|
|
5075
|
-
else info += 1;
|
|
5076
|
-
}
|
|
5077
|
-
const firstVisible = differences[0];
|
|
5078
|
-
const firstDivergence = firstVisible !== void 0 ? {
|
|
5079
|
-
kind: "first-divergence",
|
|
5080
|
-
severity: firstVisible.severity,
|
|
5081
|
-
message: `First divergence: ${firstVisible.message}`,
|
|
5082
|
-
path: firstVisible.path,
|
|
5083
|
-
left: firstVisible.left,
|
|
5084
|
-
right: firstVisible.right
|
|
5085
|
-
} : void 0;
|
|
5086
|
-
const summary = {
|
|
5087
|
-
leftRunId: left.runId,
|
|
5088
|
-
rightRunId: right.runId,
|
|
5089
|
-
totalDifferences: differences.length,
|
|
5090
|
-
errors,
|
|
5091
|
-
warnings,
|
|
5092
|
-
info,
|
|
5093
|
-
firstDivergence
|
|
5094
|
-
};
|
|
5095
|
-
return { summary, differences };
|
|
5096
|
-
}
|
|
5097
|
-
|
|
5098
|
-
// packages/viewer/src/suite-data.ts
|
|
5099
5110
|
async function enrichCase(suiteCase, baselineTracePath) {
|
|
5100
5111
|
const base = {
|
|
5101
5112
|
id: suiteCase.id,
|
|
@@ -5155,7 +5166,7 @@ async function loadSuiteViewerData(options) {
|
|
|
5155
5166
|
for (const suiteCase of result.cases) {
|
|
5156
5167
|
cases.push(await enrichCase(suiteCase, baselinePath));
|
|
5157
5168
|
}
|
|
5158
|
-
const artifactsDir =
|
|
5169
|
+
const artifactsDir = path15.join(path15.dirname(result.configPath), ".agent-inspect/suite-runs");
|
|
5159
5170
|
return {
|
|
5160
5171
|
suiteName: result.suiteName,
|
|
5161
5172
|
configPath: result.configPath,
|
|
@@ -5316,19 +5327,19 @@ function parseWorkspaceManifest(json) {
|
|
|
5316
5327
|
}
|
|
5317
5328
|
var INDEX_DIR_NAME = "index";
|
|
5318
5329
|
function resolveWorkspaceLocation(cwd = process.cwd()) {
|
|
5319
|
-
const projectRoot =
|
|
5320
|
-
const workspaceDir =
|
|
5330
|
+
const projectRoot = path15.resolve(cwd);
|
|
5331
|
+
const workspaceDir = path15.join(projectRoot, WORKSPACE_DIR_NAME);
|
|
5321
5332
|
return {
|
|
5322
5333
|
projectRoot,
|
|
5323
5334
|
workspaceDir,
|
|
5324
|
-
manifestPath:
|
|
5335
|
+
manifestPath: path15.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
|
|
5325
5336
|
};
|
|
5326
5337
|
}
|
|
5327
5338
|
function resolveInsideWorkspace(workspaceDir, relative) {
|
|
5328
|
-
const base =
|
|
5329
|
-
const resolved =
|
|
5330
|
-
const rel =
|
|
5331
|
-
if (rel === "" || rel === "." || !rel.startsWith("..") && !
|
|
5339
|
+
const base = path15.resolve(workspaceDir);
|
|
5340
|
+
const resolved = path15.resolve(base, relative);
|
|
5341
|
+
const rel = path15.relative(base, resolved);
|
|
5342
|
+
if (rel === "" || rel === "." || !rel.startsWith("..") && !path15.isAbsolute(rel)) {
|
|
5332
5343
|
return resolved;
|
|
5333
5344
|
}
|
|
5334
5345
|
throw new Error(
|
|
@@ -5472,7 +5483,7 @@ async function doctorWorkspace(location) {
|
|
|
5472
5483
|
const abs = resolveInsideWorkspace(location.workspaceDir, rel);
|
|
5473
5484
|
for (const file of await listJsonl(abs)) {
|
|
5474
5485
|
try {
|
|
5475
|
-
const s = await stat(
|
|
5486
|
+
const s = await stat(path15.join(abs, file));
|
|
5476
5487
|
newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
|
|
5477
5488
|
} catch {
|
|
5478
5489
|
checks.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
|
|
@@ -5502,7 +5513,7 @@ async function doctorWorkspace(location) {
|
|
|
5502
5513
|
|
|
5503
5514
|
// packages/viewer/src/workspace-data.ts
|
|
5504
5515
|
async function loadWorkspaceViewerData(options) {
|
|
5505
|
-
const cwd =
|
|
5516
|
+
const cwd = path15.resolve(options.cwd ?? process.cwd());
|
|
5506
5517
|
const location = resolveWorkspaceLocation(cwd);
|
|
5507
5518
|
const manifestRead = await readWorkspaceManifestFile(location);
|
|
5508
5519
|
if (!manifestRead.ok || manifestRead.manifest === void 0) {
|
|
@@ -5515,7 +5526,7 @@ async function loadWorkspaceViewerData(options) {
|
|
|
5515
5526
|
const runs = [];
|
|
5516
5527
|
for (const rel of manifestRead.manifest.traceDirs) {
|
|
5517
5528
|
const traceDir = resolveTraceDir({
|
|
5518
|
-
dir:
|
|
5529
|
+
dir: path15.join(location.workspaceDir, rel)
|
|
5519
5530
|
});
|
|
5520
5531
|
const td = new TraceDirectory({ dir: traceDir });
|
|
5521
5532
|
const files = await td.list();
|
|
@@ -5529,7 +5540,7 @@ async function loadWorkspaceViewerData(options) {
|
|
|
5529
5540
|
runId: meta.runId,
|
|
5530
5541
|
...meta.name !== void 0 ? { name: meta.name } : {},
|
|
5531
5542
|
status: meta.status,
|
|
5532
|
-
file:
|
|
5543
|
+
file: path15.basename(meta.filePath)
|
|
5533
5544
|
});
|
|
5534
5545
|
}
|
|
5535
5546
|
}
|
|
@@ -5540,8 +5551,8 @@ async function loadWorkspaceViewerData(options) {
|
|
|
5540
5551
|
doctor,
|
|
5541
5552
|
runs,
|
|
5542
5553
|
bundleDirs: [
|
|
5543
|
-
|
|
5544
|
-
|
|
5554
|
+
path15.join(location.workspaceDir, manifestRead.manifest.bundlesDir),
|
|
5555
|
+
path15.join(location.workspaceDir, manifestRead.manifest.artifactsDir)
|
|
5545
5556
|
]
|
|
5546
5557
|
};
|
|
5547
5558
|
}
|
|
@@ -5604,7 +5615,7 @@ function createViewerServer(options = {}) {
|
|
|
5604
5615
|
ok: true,
|
|
5605
5616
|
readOnly: true,
|
|
5606
5617
|
mode,
|
|
5607
|
-
traceDir:
|
|
5618
|
+
traceDir: path15.resolve(traceDir)
|
|
5608
5619
|
});
|
|
5609
5620
|
}
|
|
5610
5621
|
if (pathname === "/api/suite" && mode === "suite") {
|
|
@@ -5633,7 +5644,7 @@ function createViewerServer(options = {}) {
|
|
|
5633
5644
|
runId: meta.runId,
|
|
5634
5645
|
name: meta.name,
|
|
5635
5646
|
status: meta.status,
|
|
5636
|
-
file:
|
|
5647
|
+
file: path15.basename(meta.filePath),
|
|
5637
5648
|
startedAt: meta.startedAt,
|
|
5638
5649
|
durationMs: meta.durationMs
|
|
5639
5650
|
}))
|
|
@@ -5750,7 +5761,7 @@ function startViewerServer(options = {}) {
|
|
|
5750
5761
|
resolve({
|
|
5751
5762
|
host,
|
|
5752
5763
|
port: resolvedPort,
|
|
5753
|
-
traceDir:
|
|
5764
|
+
traceDir: path15.resolve(traceDir),
|
|
5754
5765
|
url: `http://${host}:${resolvedPort}/${modeQuery}`,
|
|
5755
5766
|
mode
|
|
5756
5767
|
});
|