agent-inspect 4.3.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/docs/API.md +3 -0
  3. package/docs/CLI.md +37 -0
  4. package/docs/SCHEMA.md +27 -1
  5. package/package.json +1 -1
  6. package/packages/cli/dist/chunk-TB45H3QT.mjs +7881 -0
  7. package/packages/cli/dist/chunk-TB45H3QT.mjs.map +1 -0
  8. package/packages/cli/dist/index.cjs +10461 -9188
  9. package/packages/cli/dist/index.cjs.map +1 -1
  10. package/packages/cli/dist/index.mjs +2619 -5688
  11. package/packages/cli/dist/index.mjs.map +1 -1
  12. package/packages/cli/dist/{src-YFMPWEIS.mjs → src-ZRFSYML6.mjs} +3 -3
  13. package/packages/cli/dist/{src-YFMPWEIS.mjs.map → src-ZRFSYML6.mjs.map} +1 -1
  14. package/packages/core/dist/advanced.cjs +3410 -21
  15. package/packages/core/dist/advanced.cjs.map +1 -1
  16. package/packages/core/dist/advanced.d.cts +147 -9
  17. package/packages/core/dist/advanced.d.ts +147 -9
  18. package/packages/core/dist/advanced.mjs +603 -20
  19. package/packages/core/dist/advanced.mjs.map +1 -1
  20. package/packages/core/dist/checks.cjs +117 -18
  21. package/packages/core/dist/checks.cjs.map +1 -1
  22. package/packages/core/dist/checks.d.cts +11 -3
  23. package/packages/core/dist/checks.d.ts +11 -3
  24. package/packages/core/dist/checks.mjs +7 -1612
  25. package/packages/core/dist/checks.mjs.map +1 -1
  26. package/packages/core/dist/{chunk-2ZXEMWGK.mjs → chunk-3USJVBLA.mjs} +3 -3
  27. package/packages/core/dist/{chunk-2ZXEMWGK.mjs.map → chunk-3USJVBLA.mjs.map} +1 -1
  28. package/packages/core/dist/{chunk-HY7H3CQM.mjs → chunk-CAWPF22J.mjs} +4 -3
  29. package/packages/core/dist/chunk-CAWPF22J.mjs.map +1 -0
  30. package/packages/core/dist/{chunk-XUNJMAPD.mjs → chunk-KNYL56KZ.mjs} +38 -3
  31. package/packages/core/dist/chunk-KNYL56KZ.mjs.map +1 -0
  32. package/packages/core/dist/{chunk-IZBJAZGF.mjs → chunk-P366CCL2.mjs} +34 -3
  33. package/packages/core/dist/chunk-P366CCL2.mjs.map +1 -0
  34. package/packages/core/dist/chunk-QLYOCCEF.mjs +1650 -0
  35. package/packages/core/dist/chunk-QLYOCCEF.mjs.map +1 -0
  36. package/packages/core/dist/{chunk-4JVGFNQH.mjs → chunk-ULCGIRTC.mjs} +95 -8
  37. package/packages/core/dist/chunk-ULCGIRTC.mjs.map +1 -0
  38. package/packages/core/dist/chunk-VFO76UH3.mjs +228 -0
  39. package/packages/core/dist/chunk-VFO76UH3.mjs.map +1 -0
  40. package/packages/core/dist/{chunk-NQZPZ2FN.mjs → chunk-WRE4CNPF.mjs} +6 -3
  41. package/packages/core/dist/chunk-WRE4CNPF.mjs.map +1 -0
  42. package/packages/core/dist/chunk-X2FLDF7M.mjs +1581 -0
  43. package/packages/core/dist/chunk-X2FLDF7M.mjs.map +1 -0
  44. package/packages/core/dist/{chunk-A4V2H3YU.mjs → chunk-ZWA5RWMV.mjs} +40 -4
  45. package/packages/core/dist/chunk-ZWA5RWMV.mjs.map +1 -0
  46. package/packages/core/dist/{context-DjY-jvQk.d.cts → context-BbfkMYjz.d.cts} +5 -3
  47. package/packages/core/dist/{context-BDZmi53V.d.ts → context-Cp8NRbBX.d.ts} +5 -3
  48. package/packages/core/dist/diff.cjs.map +1 -1
  49. package/packages/core/dist/diff.d.cts +3 -3
  50. package/packages/core/dist/diff.d.ts +3 -3
  51. package/packages/core/dist/diff.mjs +1 -1
  52. package/packages/core/dist/exporters.cjs +137 -1
  53. package/packages/core/dist/exporters.cjs.map +1 -1
  54. package/packages/core/dist/exporters.d.cts +6 -3
  55. package/packages/core/dist/exporters.d.ts +6 -3
  56. package/packages/core/dist/exporters.mjs +71 -6
  57. package/packages/core/dist/exporters.mjs.map +1 -1
  58. package/packages/core/dist/index.cjs +181 -6
  59. package/packages/core/dist/index.cjs.map +1 -1
  60. package/packages/core/dist/index.d.cts +14 -7
  61. package/packages/core/dist/index.d.ts +14 -7
  62. package/packages/core/dist/index.mjs +51 -7
  63. package/packages/core/dist/index.mjs.map +1 -1
  64. package/packages/core/dist/{inspect-event-CYAV7Hxm.d.cts → inspect-event-IqwOHbGo.d.cts} +1 -1
  65. package/packages/core/dist/{inspect-event-CYAV7Hxm.d.ts → inspect-event-IqwOHbGo.d.ts} +1 -1
  66. package/packages/core/dist/{log-config-BNQ9UTmP.d.cts → log-config-CW8w5YQ9.d.cts} +1 -1
  67. package/packages/core/dist/{log-config-D3Yp1SZN.d.ts → log-config-SRA4xTp8.d.ts} +1 -1
  68. package/packages/core/dist/logs.d.cts +3 -3
  69. package/packages/core/dist/logs.d.ts +3 -3
  70. package/packages/core/dist/{persisted-inspect-event-BLvb0jSX.d.ts → persisted-inspect-event-DruwqARZ.d.ts} +1 -1
  71. package/packages/core/dist/{persisted-inspect-event-D5SjBz9b.d.cts → persisted-inspect-event-Gd-W8vB7.d.cts} +1 -1
  72. package/packages/core/dist/persisted.cjs +67 -1
  73. package/packages/core/dist/persisted.cjs.map +1 -1
  74. package/packages/core/dist/persisted.d.cts +5 -5
  75. package/packages/core/dist/persisted.d.ts +5 -5
  76. package/packages/core/dist/persisted.mjs +3 -3
  77. package/packages/core/dist/readers.cjs +72 -3
  78. package/packages/core/dist/readers.cjs.map +1 -1
  79. package/packages/core/dist/readers.d.cts +2 -2
  80. package/packages/core/dist/readers.d.ts +2 -2
  81. package/packages/core/dist/readers.mjs +4 -1580
  82. package/packages/core/dist/readers.mjs.map +1 -1
  83. package/packages/core/dist/{types-CQun9LW6.d.ts → types-B1X87dUW.d.cts} +16 -2
  84. package/packages/core/dist/{types-C-llnPH0.d.cts → types-BVkjSd0q.d.ts} +16 -2
  85. package/packages/core/dist/types-CvsKGweN.d.cts +12 -0
  86. package/packages/core/dist/types-CvsKGweN.d.ts +12 -0
  87. package/packages/core/dist/writers.cjs +2 -1
  88. package/packages/core/dist/writers.cjs.map +1 -1
  89. package/packages/core/dist/writers.d.cts +2 -2
  90. package/packages/core/dist/writers.d.ts +2 -2
  91. package/packages/core/dist/writers.mjs +3 -4
  92. package/packages/core/dist/writers.mjs.map +1 -1
  93. package/packages/cli/dist/chunk-BS5LSKZ3.mjs +0 -3671
  94. package/packages/cli/dist/chunk-BS5LSKZ3.mjs.map +0 -1
  95. package/packages/core/dist/chunk-4JVGFNQH.mjs.map +0 -1
  96. package/packages/core/dist/chunk-A4V2H3YU.mjs.map +0 -1
  97. package/packages/core/dist/chunk-EDTQHZPM.mjs +0 -88
  98. package/packages/core/dist/chunk-EDTQHZPM.mjs.map +0 -1
  99. package/packages/core/dist/chunk-HY7H3CQM.mjs.map +0 -1
  100. package/packages/core/dist/chunk-IZBJAZGF.mjs.map +0 -1
  101. package/packages/core/dist/chunk-NQZPZ2FN.mjs.map +0 -1
  102. package/packages/core/dist/chunk-XUNJMAPD.mjs.map +0 -1
@@ -1,20 +1,25 @@
1
- export { MAX_TERMINAL_DEPTH, MAX_TERMINAL_NAME_LENGTH, TERMINAL_INDENT, TraceDirectory, createInspector, createInspectorRuntime, formatTerminalName, getCurrentContext, getCurrentCorrelationMetadata, getCurrentDepth, getCurrentRunId, getCurrentRunName, getCurrentStepId, getIndent, getParentStepId, getTraceDirFromContext, getTraceSafetyFromContext, hasActiveContext, isAgentInspectEnabled, isSilentContext, maybeInspectRun, printError, printFailedAt, printRunComplete, printRunStart, printStepComplete, printStepStart, renderErrorLine, renderRunSummary, renderStepLine, resolveTraceDir, runWithContext, runWithStepContext } from './chunk-A4V2H3YU.mjs';
2
- import { buildRunSummary, extractMetadata } from './chunk-2ZXEMWGK.mjs';
3
- export { buildRunSummary, buildRunTimeline, buildRunWhatSummary, extractMetadata, renderRunWhat, renderTimeline } from './chunk-2ZXEMWGK.mjs';
1
+ import { runTraceChecks, createRunStatusRule, createToolUsageRule, createRunDurationRule, createLlmUsageRule, createObservedOutcomeRule } from './chunk-QLYOCCEF.mjs';
2
+ export { MAX_TERMINAL_DEPTH, MAX_TERMINAL_NAME_LENGTH, TERMINAL_INDENT, TraceDirectory, createInspector, createInspectorRuntime, formatTerminalName, getCurrentContext, getCurrentCorrelationMetadata, getCurrentDepth, getCurrentRunId, getCurrentRunName, getCurrentStepId, getIndent, getParentStepId, getTraceDirFromContext, getTraceSafetyFromContext, hasActiveContext, isAgentInspectEnabled, isSilentContext, maybeInspectRun, printError, printFailedAt, printRunComplete, printRunStart, printStepComplete, printStepStart, renderErrorLine, renderRunSummary, renderStepLine, resolveTraceDir, runWithContext, runWithStepContext } from './chunk-ZWA5RWMV.mjs';
3
+ import { buildRunSummary, extractMetadata } from './chunk-3USJVBLA.mjs';
4
+ export { buildRunSummary, buildRunTimeline, buildRunWhatSummary, extractMetadata, renderRunWhat, renderTimeline } from './chunk-3USJVBLA.mjs';
5
+ import { parseObservationFilter, extractOutcomesFromTraceEvents, extractOutcomesFromPersistedEvents } from './chunk-VFO76UH3.mjs';
4
6
  import './chunk-BT7CATSD.mjs';
5
- import { readTraceEventsFromFile } from './chunk-4JVGFNQH.mjs';
6
- export { DEFAULT_MAX_EVENT_BYTES, DEFAULT_MAX_METADATA_VALUE_LENGTH, DEFAULT_MAX_PREVIEW_LENGTH, getRunIdFromTraceFileName, initializeTraceFile, listTraceFiles, prepareMetadataForDisk, prepareTraceEventForDisk, readTraceEvents, readTraceFile, resolveTraceSafetyOptions, serializeEvent, validateEvent, writeTraceEvent } from './chunk-4JVGFNQH.mjs';
7
- import { resolveRedactionProfile } from './chunk-EDTQHZPM.mjs';
8
- export { resolveRedactionProfile } from './chunk-EDTQHZPM.mjs';
7
+ import { resolveRedactionProfile, readTraceEventsFromFile } from './chunk-ULCGIRTC.mjs';
8
+ export { DEFAULT_MAX_EVENT_BYTES, DEFAULT_MAX_METADATA_VALUE_LENGTH, DEFAULT_MAX_PREVIEW_LENGTH, getRunIdFromTraceFileName, initializeTraceFile, listTraceFiles, prepareMetadataForDisk, prepareTraceEventForDisk, readTraceEvents, readTraceFile, resolveRedactionProfile, resolveTraceSafetyOptions, serializeEvent, validateEvent, writeTraceEvent } from './chunk-ULCGIRTC.mjs';
9
9
  import { Redactor } from './chunk-VU6O5QAH.mjs';
10
+ import { openTrace } from './chunk-X2FLDF7M.mjs';
11
+ import './chunk-KNYL56KZ.mjs';
10
12
  import './chunk-S4YWKV4G.mjs';
11
- import { parseDuration, formatDuration, isTraceEvent } from './chunk-NQZPZ2FN.mjs';
12
- export { DEFAULT_TRACE_DIR_NAME, FALLBACK_TRACE_DIR, MAX_NAME_LENGTH, RUNS_DIR_NAME, createRunId, createStepId, ensureTraceDir, formatDuration, formatError, formatTimestamp, getDefaultTraceDir, getTraceFilePath, isStepStatus, isStepType, isTraceEvent, parseDuration, parseTraceJsonl, truncateName, unknownTraceFormatMessage, warn } from './chunk-NQZPZ2FN.mjs';
13
- import './chunk-IZBJAZGF.mjs';
13
+ import './chunk-E5F2LQCX.mjs';
14
+ import { parseDuration, formatDuration, isTraceEvent, getTraceFilePath } from './chunk-WRE4CNPF.mjs';
15
+ export { DEFAULT_TRACE_DIR_NAME, FALLBACK_TRACE_DIR, MAX_NAME_LENGTH, RUNS_DIR_NAME, createRunId, createStepId, ensureTraceDir, formatDuration, formatError, formatTimestamp, getDefaultTraceDir, getTraceFilePath, isStepStatus, isStepType, isTraceEvent, parseDuration, parseTraceJsonl, truncateName, unknownTraceFormatMessage, warn } from './chunk-WRE4CNPF.mjs';
16
+ import './chunk-P366CCL2.mjs';
14
17
  import './chunk-7TGZLWEE.mjs';
15
18
  import { createReadStream } from 'fs';
16
19
  import { createInterface } from 'readline';
17
- import path from 'path';
20
+ import path3 from 'path';
21
+ import { access, readFile } from 'fs/promises';
22
+ import { pathToFileURL } from 'url';
18
23
 
19
24
  // packages/core/src/trace-filter.ts
20
25
  function toLower(s) {
@@ -434,8 +439,9 @@ async function searchTraces(metas, options) {
434
439
  }
435
440
  const limit = options.limit ?? 50;
436
441
  const sessionId = options.session?.trim();
442
+ const observationStatus = parseObservationFilter(options.observation);
437
443
  const hasContentFilter = Boolean(
438
- options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
444
+ options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus
439
445
  );
440
446
  const results = [];
441
447
  const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
@@ -480,6 +486,22 @@ async function searchTraces(metas, options) {
480
486
  statusFilter: options.status
481
487
  });
482
488
  results.push(...stepMatches);
489
+ if (observationStatus) {
490
+ const outcomes = extractOutcomesFromTraceEvents(events);
491
+ const matched = outcomes.filter((outcome) => outcome.status === observationStatus);
492
+ for (const outcome of matched) {
493
+ results.push({
494
+ runId: m.runId,
495
+ runName: m.name,
496
+ runStatus: m.status,
497
+ stepName: outcome.name,
498
+ timestamp: outcome.observedAt,
499
+ matchReason: `observation status=${outcome.status}`,
500
+ matchedFields: ["outcome.status", "outcome.name"],
501
+ filePath: m.filePath
502
+ });
503
+ }
504
+ }
483
505
  }
484
506
  results.sort((a, b) => {
485
507
  const ta = a.timestamp ?? 0;
@@ -868,9 +890,10 @@ function buildActivitySummary(index, options = {}) {
868
890
  entries
869
891
  };
870
892
  }
871
- function renderActivitySummaryHuman(summary) {
893
+ function renderActivitySummaryHuman(summary, options) {
872
894
  const lines = [];
873
- const todayStart = /* @__PURE__ */ new Date();
895
+ const nowMs = options?.nowMs ?? Date.now();
896
+ const todayStart = new Date(nowMs);
874
897
  todayStart.setHours(0, 0, 0, 0);
875
898
  const todayMs = todayStart.getTime();
876
899
  const today = summary.entries.filter(
@@ -1301,12 +1324,12 @@ function buildCriticalPath(runs, handoffs) {
1301
1324
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
1302
1325
  );
1303
1326
  const ordered = [...runs].sort(compareRuns);
1304
- const path2 = [];
1327
+ const path6 = [];
1305
1328
  const visited = /* @__PURE__ */ new Set();
1306
1329
  const pushRun = (run, confidence, source) => {
1307
1330
  if (visited.has(run.runId)) return;
1308
1331
  visited.add(run.runId);
1309
- path2.push({
1332
+ path6.push({
1310
1333
  runId: run.runId,
1311
1334
  name: run.name,
1312
1335
  startedAt: run.startedAt,
@@ -1331,7 +1354,7 @@ function buildCriticalPath(runs, handoffs) {
1331
1354
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
1332
1355
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
1333
1356
  }
1334
- return path2;
1357
+ return path6;
1335
1358
  }
1336
1359
  function metaRunIdMatches(run, token, runById) {
1337
1360
  const meta = extractSessionWorkflowMetadata(run.metadata);
@@ -1623,7 +1646,7 @@ function normalizeBundleOutputPath(out) {
1623
1646
  if (trimmed === "") {
1624
1647
  throw new Error("--out requires a non-empty path.");
1625
1648
  }
1626
- const resolved = path.resolve(trimmed);
1649
+ const resolved = path3.resolve(trimmed);
1627
1650
  if (resolved.toLowerCase().endsWith(".zip")) {
1628
1651
  return resolved.slice(0, -4);
1629
1652
  }
@@ -1632,9 +1655,569 @@ function normalizeBundleOutputPath(out) {
1632
1655
  function defaultBundleOutputPath(runIds) {
1633
1656
  const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
1634
1657
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1635
- return path.resolve(`agent-inspect-bundle-${label}-${stamp}`);
1658
+ return path3.resolve(`agent-inspect-bundle-${label}-${stamp}`);
1659
+ }
1660
+
1661
+ // packages/core/src/suite/types.ts
1662
+ var DEFAULT_SUITE_CONFIG_NAMES = [
1663
+ "agent-inspect.suite.json",
1664
+ "agent-inspect.suite.js",
1665
+ "agent-inspect.suite.mjs",
1666
+ "agent-inspect.suite.cjs"
1667
+ ];
1668
+ var DEFAULT_SUITE_ARTIFACTS_DIR = ".agent-inspect/suite-runs";
1669
+ function diagnostic(code, message, severity = "error", caseId) {
1670
+ return { code, message, severity, ...caseId !== void 0 ? { caseId } : {} };
1671
+ }
1672
+ function asString(value, label) {
1673
+ if (value === void 0) return void 0;
1674
+ if (typeof value !== "string" || value.trim() === "") {
1675
+ throw new Error(`${label} must be a non-empty string.`);
1676
+ }
1677
+ return value.trim();
1678
+ }
1679
+ function asStringArray(value, label) {
1680
+ if (value === void 0) return void 0;
1681
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
1682
+ throw new Error(`${label} must be an array of strings.`);
1683
+ }
1684
+ return value;
1685
+ }
1686
+ function asPositiveNumber(value, label) {
1687
+ if (value === void 0) return void 0;
1688
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
1689
+ throw new Error(`${label} must be a non-negative number.`);
1690
+ }
1691
+ return value;
1692
+ }
1693
+ function validateCaseConfig(value, index) {
1694
+ const diagnostics = [];
1695
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1696
+ diagnostics.push(
1697
+ diagnostic("AI_SUITE_CONFIG_INVALID", `cases[${index}] must be an object.`)
1698
+ );
1699
+ return { diagnostics };
1700
+ }
1701
+ const raw = value;
1702
+ try {
1703
+ const id = asString(raw.id, `cases[${index}].id`);
1704
+ if (id === void 0) {
1705
+ diagnostics.push(
1706
+ diagnostic("AI_SUITE_CONFIG_INVALID", `cases[${index}].id is required.`)
1707
+ );
1708
+ return { diagnostics };
1709
+ }
1710
+ const trace = asString(raw.trace, `cases[${index}].trace`);
1711
+ const runId = asString(raw.runId, `cases[${index}].runId`);
1712
+ const input = asString(raw.input, `cases[${index}].input`);
1713
+ return {
1714
+ caseConfig: {
1715
+ id,
1716
+ ...trace !== void 0 ? { trace } : {},
1717
+ ...runId !== void 0 ? { runId } : {},
1718
+ ...input !== void 0 ? { input } : {},
1719
+ ...asStringArray(raw.requireTools, `cases[${index}].requireTools`) !== void 0 ? { requireTools: asStringArray(raw.requireTools, `cases[${index}].requireTools`) } : {},
1720
+ ...asStringArray(raw.forbidTools, `cases[${index}].forbidTools`) !== void 0 ? { forbidTools: asStringArray(raw.forbidTools, `cases[${index}].forbidTools`) } : {},
1721
+ ...asPositiveNumber(raw.maxDurationMs, `cases[${index}].maxDurationMs`) !== void 0 ? {
1722
+ maxDurationMs: asPositiveNumber(
1723
+ raw.maxDurationMs,
1724
+ `cases[${index}].maxDurationMs`
1725
+ )
1726
+ } : {},
1727
+ ...asStringArray(
1728
+ raw.expectedObservations,
1729
+ `cases[${index}].expectedObservations`
1730
+ ) !== void 0 ? {
1731
+ expectedObservations: asStringArray(
1732
+ raw.expectedObservations,
1733
+ `cases[${index}].expectedObservations`
1734
+ )
1735
+ } : {}
1736
+ },
1737
+ diagnostics
1738
+ };
1739
+ } catch (error) {
1740
+ const message = error instanceof Error ? error.message : String(error);
1741
+ diagnostics.push(diagnostic("AI_SUITE_CONFIG_INVALID", message));
1742
+ return { diagnostics };
1743
+ }
1744
+ }
1745
+ function normalizeSuiteConfig(value) {
1746
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1747
+ throw new Error("Suite config must export an object.");
1748
+ }
1749
+ const raw = value;
1750
+ const name = asString(raw.name, "name");
1751
+ const traces = asString(raw.traces, "traces");
1752
+ if (name === void 0) throw new Error("name is required.");
1753
+ if (traces === void 0) throw new Error("traces is required.");
1754
+ if (!Array.isArray(raw.cases) || raw.cases.length === 0) {
1755
+ throw new Error("cases must be a non-empty array.");
1756
+ }
1757
+ const cases = [];
1758
+ for (let index = 0; index < raw.cases.length; index += 1) {
1759
+ const { caseConfig, diagnostics } = validateCaseConfig(raw.cases[index], index);
1760
+ if (diagnostics.length > 0) {
1761
+ throw new Error(diagnostics.map((item) => item.message).join("; "));
1762
+ }
1763
+ if (caseConfig !== void 0) cases.push(caseConfig);
1764
+ }
1765
+ const ids = /* @__PURE__ */ new Set();
1766
+ for (const suiteCase of cases) {
1767
+ if (ids.has(suiteCase.id)) {
1768
+ throw new Error(`Duplicate case id "${suiteCase.id}".`);
1769
+ }
1770
+ ids.add(suiteCase.id);
1771
+ }
1772
+ const redactionProfile = raw.redactionProfile === "local" || raw.redactionProfile === "share" || raw.redactionProfile === "strict" ? raw.redactionProfile : void 0;
1773
+ if (raw.redactionProfile !== void 0 && redactionProfile === void 0) {
1774
+ throw new Error('redactionProfile must be "local", "share", or "strict".');
1775
+ }
1776
+ const config = { name, traces, cases };
1777
+ if (raw.checks !== void 0) {
1778
+ if (typeof raw.checks !== "object" || Array.isArray(raw.checks)) {
1779
+ throw new Error("checks must be an object.");
1780
+ }
1781
+ config.checks = raw.checks;
1782
+ }
1783
+ if (raw.eval !== void 0) {
1784
+ if (typeof raw.eval !== "object" || Array.isArray(raw.eval)) {
1785
+ throw new Error("eval must be an object.");
1786
+ }
1787
+ config.eval = raw.eval;
1788
+ }
1789
+ if (raw.artifacts !== void 0) {
1790
+ if (typeof raw.artifacts !== "object" || Array.isArray(raw.artifacts)) {
1791
+ throw new Error("artifacts must be an object.");
1792
+ }
1793
+ const outputDir = asString(
1794
+ raw.artifacts.outputDir,
1795
+ "artifacts.outputDir"
1796
+ );
1797
+ config.artifacts = outputDir !== void 0 ? { outputDir } : {};
1798
+ }
1799
+ if (raw.baseline !== void 0) {
1800
+ const baseline = asString(raw.baseline, "baseline");
1801
+ if (baseline !== void 0) config.baseline = baseline;
1802
+ }
1803
+ if (raw.candidate !== void 0) {
1804
+ const candidate = asString(raw.candidate, "candidate");
1805
+ if (candidate !== void 0) config.candidate = candidate;
1806
+ }
1807
+ if (redactionProfile !== void 0) config.redactionProfile = redactionProfile;
1808
+ return config;
1809
+ }
1810
+ async function validateSuiteConfig(config, options) {
1811
+ const diagnostics = [];
1812
+ const tracesDir = path3.resolve(options.configDir, config.traces);
1813
+ try {
1814
+ await access(tracesDir);
1815
+ } catch {
1816
+ diagnostics.push(
1817
+ diagnostic("AI_SUITE_CONFIG_INVALID", `traces directory not found: ${tracesDir}`)
1818
+ );
1819
+ }
1820
+ for (const suiteCase of config.cases) {
1821
+ if (suiteCase.input !== void 0) {
1822
+ const inputPath = path3.resolve(options.configDir, suiteCase.input);
1823
+ try {
1824
+ await access(inputPath);
1825
+ } catch {
1826
+ diagnostics.push(
1827
+ diagnostic(
1828
+ "AI_SUITE_CONFIG_INVALID",
1829
+ `input fixture not found: ${suiteCase.input}`,
1830
+ "warning",
1831
+ suiteCase.id
1832
+ )
1833
+ );
1834
+ }
1835
+ }
1836
+ }
1837
+ return { ok: diagnostics.every((item) => item.severity !== "error"), diagnostics };
1838
+ }
1839
+
1840
+ // packages/core/src/suite/load.ts
1841
+ var CONFIG_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
1842
+ var TS_CONFIG_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".mts", ".cts"]);
1843
+ function diagnostic2(code, message) {
1844
+ return { code, message, severity: "error" };
1845
+ }
1846
+ async function fileExists(filePath) {
1847
+ try {
1848
+ await access(filePath);
1849
+ return true;
1850
+ } catch {
1851
+ return false;
1852
+ }
1853
+ }
1854
+ async function resolveSuiteConfigPath(options = {}) {
1855
+ const cwd = path3.resolve(options.cwd ?? process.cwd());
1856
+ if (options.configPath !== void 0 && options.configPath.trim() !== "") {
1857
+ return path3.resolve(cwd, options.configPath.trim());
1858
+ }
1859
+ for (const name of DEFAULT_SUITE_CONFIG_NAMES) {
1860
+ const candidate = path3.join(cwd, name);
1861
+ if (await fileExists(candidate)) return candidate;
1862
+ }
1863
+ throw new Error(
1864
+ `No suite config found. Create one with \`agent-inspect suite init\` or pass --config.`
1865
+ );
1866
+ }
1867
+ async function loadSuiteConfig(options = {}) {
1868
+ let configPath;
1869
+ try {
1870
+ configPath = await resolveSuiteConfigPath(options);
1871
+ } catch (error) {
1872
+ const message = error instanceof Error ? error.message : String(error);
1873
+ throw Object.assign(new Error(message), {
1874
+ diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
1875
+ });
1876
+ }
1877
+ const extension = path3.extname(configPath);
1878
+ if (TS_CONFIG_EXTENSIONS.has(extension)) {
1879
+ const message = "TypeScript suite configs require an explicit precompiled JavaScript config or future --config-loader support.";
1880
+ throw Object.assign(new Error(message), {
1881
+ diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
1882
+ });
1883
+ }
1884
+ if (!CONFIG_EXTENSIONS.has(extension)) {
1885
+ const message = "Unsupported suite config extension. Use .json, .js, .mjs, or .cjs.";
1886
+ throw Object.assign(new Error(message), {
1887
+ diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
1888
+ });
1889
+ }
1890
+ try {
1891
+ let raw;
1892
+ if (extension === ".json") {
1893
+ raw = JSON.parse(await readFile(configPath, "utf-8"));
1894
+ } else {
1895
+ const mod = await import(pathToFileURL(configPath).href);
1896
+ raw = "default" in mod ? mod.default : mod;
1897
+ }
1898
+ const config = normalizeSuiteConfig(raw);
1899
+ return {
1900
+ config,
1901
+ configPath,
1902
+ configDir: path3.dirname(configPath)
1903
+ };
1904
+ } catch (error) {
1905
+ const message = error instanceof Error ? error.message : String(error);
1906
+ throw Object.assign(new Error(message), {
1907
+ diagnostics: [diagnostic2("AI_SUITE_CONFIG_LOAD_FAILED", message)]
1908
+ });
1909
+ }
1910
+ }
1911
+ function defaultSuiteConfigTemplate() {
1912
+ return {
1913
+ name: "my-agent-suite",
1914
+ traces: "./.agent-inspect",
1915
+ cases: [
1916
+ {
1917
+ id: "example-case",
1918
+ runId: "example-run",
1919
+ requireTools: ["searchDocs"],
1920
+ maxDurationMs: 3e4,
1921
+ expectedObservations: ["answerReady"]
1922
+ }
1923
+ ],
1924
+ checks: {
1925
+ select: ["run.status"]
1926
+ },
1927
+ redactionProfile: "local",
1928
+ artifacts: {
1929
+ outputDir: ".agent-inspect/suite-runs"
1930
+ }
1931
+ };
1932
+ }
1933
+ async function exists(filePath) {
1934
+ try {
1935
+ await access(filePath);
1936
+ return true;
1937
+ } catch {
1938
+ return false;
1939
+ }
1940
+ }
1941
+ async function resolveSuiteCaseTrace(suiteCase, options) {
1942
+ if (suiteCase.trace !== void 0) {
1943
+ const tracePath = path3.resolve(options.configDir, suiteCase.trace);
1944
+ if (await exists(tracePath)) {
1945
+ return { caseId: suiteCase.id, tracePath, missing: false };
1946
+ }
1947
+ return {
1948
+ caseId: suiteCase.id,
1949
+ tracePath,
1950
+ missing: true,
1951
+ reason: `trace file not found: ${suiteCase.trace}`
1952
+ };
1953
+ }
1954
+ const runKey = suiteCase.runId ?? suiteCase.id;
1955
+ const directPath = getTraceFilePath(runKey, options.tracesDir);
1956
+ if (await exists(directPath)) {
1957
+ return { caseId: suiteCase.id, tracePath: directPath, runId: runKey, missing: false };
1958
+ }
1959
+ const nestedPath = path3.join(options.tracesDir, `${path3.basename(runKey)}.jsonl`);
1960
+ if (await exists(nestedPath)) {
1961
+ return { caseId: suiteCase.id, tracePath: nestedPath, runId: runKey, missing: false };
1962
+ }
1963
+ return {
1964
+ caseId: suiteCase.id,
1965
+ runId: runKey,
1966
+ missing: true,
1967
+ reason: `no trace found for run id "${runKey}" under ${options.tracesDir}`
1968
+ };
1969
+ }
1970
+ function diagnostic3(code, message, severity = "error", caseId) {
1971
+ return { code, message, severity, ...caseId !== void 0 ? { caseId } : {} };
1972
+ }
1973
+ function buildCaseRules(suiteCase, config) {
1974
+ const rules = [];
1975
+ const select = new Set(config.checks?.select ?? []);
1976
+ if (select.has("run.status")) {
1977
+ rules.push(createRunStatusRule());
1978
+ }
1979
+ const requiredTools = [
1980
+ ...config.checks?.tool?.required ?? [],
1981
+ ...suiteCase.requireTools ?? []
1982
+ ];
1983
+ const forbiddenTools = [
1984
+ ...config.checks?.tool?.forbidden ?? [],
1985
+ ...suiteCase.forbidTools ?? []
1986
+ ];
1987
+ if (requiredTools.length > 0 || forbiddenTools.length > 0) {
1988
+ rules.push(
1989
+ createToolUsageRule({
1990
+ required: requiredTools.length > 0 ? requiredTools : void 0,
1991
+ forbidden: forbiddenTools.length > 0 ? forbiddenTools : void 0
1992
+ })
1993
+ );
1994
+ select.add("tool.usage");
1995
+ }
1996
+ const maxDurationMs = suiteCase.maxDurationMs ?? config.checks?.run?.maxDurationMs ?? config.eval?.maxDurationMs;
1997
+ if (maxDurationMs !== void 0) {
1998
+ rules.push(createRunDurationRule({ maxDurationMs }));
1999
+ select.add("run.duration");
2000
+ }
2001
+ const llm = config.checks?.llm;
2002
+ if (llm?.allowedModels !== void 0 || llm?.maxTotalTokens !== void 0) {
2003
+ rules.push(createLlmUsageRule(llm));
2004
+ select.add("llm.usage");
2005
+ }
2006
+ if (select.has("outcome.status")) {
2007
+ rules.push(createObservedOutcomeRule({ failOn: ["failed"] }));
2008
+ }
2009
+ return { rules, select: [...select] };
2010
+ }
2011
+ function outcomesFromRead(read) {
2012
+ const persistedOutcomes = extractOutcomesFromPersistedEvents(
2013
+ read.events.filter((event) => event.kind === "OUTCOME")
2014
+ );
2015
+ if (persistedOutcomes.length > 0) return persistedOutcomes;
2016
+ const traceEvents = [];
2017
+ for (const event of read.events) {
2018
+ const legacy = event;
2019
+ if (typeof legacy === "object" && legacy !== null && "event" in legacy && legacy.event === "outcome_observed") {
2020
+ traceEvents.push(legacy);
2021
+ }
2022
+ }
2023
+ return traceEvents.length > 0 ? extractOutcomesFromTraceEvents(traceEvents) : [];
2024
+ }
2025
+ function validateExpectedObservations(suiteCase, read) {
2026
+ const expected = suiteCase.expectedObservations ?? [];
2027
+ if (expected.length === 0) return { ok: true, diagnostics: [] };
2028
+ const outcomes = outcomesFromRead(read);
2029
+ const diagnostics = [];
2030
+ for (const name of expected) {
2031
+ const match = outcomes.find((outcome) => outcome.name === name);
2032
+ if (!match) {
2033
+ diagnostics.push(
2034
+ diagnostic3(
2035
+ "AI_SUITE_CASE_OBSERVATION_FAILED",
2036
+ `Expected observation "${name}" was not recorded.`,
2037
+ "error",
2038
+ suiteCase.id
2039
+ )
2040
+ );
2041
+ continue;
2042
+ }
2043
+ if (match.status !== "passed") {
2044
+ diagnostics.push(
2045
+ diagnostic3(
2046
+ "AI_SUITE_CASE_OBSERVATION_FAILED",
2047
+ `Observation "${name}" has status "${match.status}", expected "passed".`,
2048
+ "error",
2049
+ suiteCase.id
2050
+ )
2051
+ );
2052
+ }
2053
+ }
2054
+ return { ok: diagnostics.length === 0, diagnostics };
2055
+ }
2056
+ async function runSuiteCase(suiteCase, config, options) {
2057
+ const resolved = await resolveSuiteCaseTrace(suiteCase, options);
2058
+ if (resolved.missing || resolved.tracePath === void 0) {
2059
+ return {
2060
+ id: suiteCase.id,
2061
+ status: "skipped",
2062
+ ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
2063
+ message: resolved.reason,
2064
+ diagnostics: [
2065
+ diagnostic3(
2066
+ "AI_SUITE_CASE_TRACE_MISSING",
2067
+ resolved.reason ?? "Trace not found.",
2068
+ "warning",
2069
+ suiteCase.id
2070
+ )
2071
+ ]
2072
+ };
2073
+ }
2074
+ let read;
2075
+ try {
2076
+ read = await openTrace({ type: "file", path: resolved.tracePath });
2077
+ } catch (error) {
2078
+ const message = error instanceof Error ? error.message : String(error);
2079
+ return {
2080
+ id: suiteCase.id,
2081
+ status: "error",
2082
+ tracePath: resolved.tracePath,
2083
+ ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
2084
+ message,
2085
+ diagnostics: [
2086
+ diagnostic3("AI_SUITE_TRACE_UNREADABLE", message, "error", suiteCase.id)
2087
+ ]
2088
+ };
2089
+ }
2090
+ const { rules, select } = buildCaseRules(suiteCase, config);
2091
+ const checkResult = rules.length > 0 ? runTraceChecks({ read }, { rules, select }) : {
2092
+ ok: true,
2093
+ status: "pass",
2094
+ format: read.format,
2095
+ findings: [],
2096
+ diagnostics: []
2097
+ };
2098
+ const observationResult = validateExpectedObservations(suiteCase, read);
2099
+ const diagnostics = [
2100
+ ...checkResult.diagnostics.map(
2101
+ (item) => diagnostic3(
2102
+ "AI_SUITE_CASE_CHECK_FAILED",
2103
+ item.message,
2104
+ item.severity,
2105
+ suiteCase.id
2106
+ )
2107
+ ),
2108
+ ...checkResult.findings.filter((finding) => finding.status === "fail").map(
2109
+ (finding) => diagnostic3(
2110
+ "AI_SUITE_CASE_CHECK_FAILED",
2111
+ finding.message,
2112
+ finding.severity,
2113
+ suiteCase.id
2114
+ )
2115
+ ),
2116
+ ...observationResult.diagnostics
2117
+ ];
2118
+ const checkOk = checkResult.ok;
2119
+ const observationsOk = observationResult.ok;
2120
+ const ok = checkOk && observationsOk;
2121
+ const status = ok ? "pass" : checkResult.status === "error" ? "error" : "fail";
2122
+ return {
2123
+ id: suiteCase.id,
2124
+ status,
2125
+ tracePath: resolved.tracePath,
2126
+ ...resolved.runId !== void 0 ? { runId: resolved.runId } : {},
2127
+ checkOk,
2128
+ observationsOk,
2129
+ diagnostics,
2130
+ ...ok ? {} : {
2131
+ message: diagnostics.map((item) => item.message).join("; ") || checkResult.findings.map((item) => item.message).join("; ")
2132
+ }
2133
+ };
2134
+ }
2135
+ async function runSuite(options = {}) {
2136
+ const startedAt = new Date(options.nowMs ?? Date.now()).toISOString();
2137
+ const { config, configPath, configDir } = await loadSuiteConfig(options);
2138
+ const tracesDir = path3.resolve(configDir, config.traces);
2139
+ const cases = [];
2140
+ const diagnostics = [];
2141
+ for (const suiteCase of config.cases) {
2142
+ cases.push(
2143
+ await runSuiteCase(suiteCase, config, {
2144
+ configDir,
2145
+ tracesDir
2146
+ })
2147
+ );
2148
+ }
2149
+ const summary = {
2150
+ passed: cases.filter((item) => item.status === "pass").length,
2151
+ failed: cases.filter((item) => item.status === "fail").length,
2152
+ errors: cases.filter((item) => item.status === "error").length,
2153
+ skipped: cases.filter((item) => item.status === "skipped").length
2154
+ };
2155
+ const finishedAt = new Date(options.nowMs ?? Date.now()).toISOString();
2156
+ const ok = summary.failed === 0 && summary.errors === 0;
2157
+ const status = summary.errors > 0 ? "error" : summary.failed > 0 || !ok ? "fail" : "pass";
2158
+ return {
2159
+ ok,
2160
+ status,
2161
+ suiteName: config.name,
2162
+ configPath,
2163
+ tracesDir,
2164
+ startedAt,
2165
+ finishedAt,
2166
+ summary,
2167
+ cases,
2168
+ diagnostics
2169
+ };
2170
+ }
2171
+
2172
+ // packages/core/src/suite/report.ts
2173
+ function statusLabel(status) {
2174
+ return status.toUpperCase();
2175
+ }
2176
+ function renderSuiteReportMarkdown(result) {
2177
+ const lines = [];
2178
+ lines.push(`# Suite: ${result.suiteName}`);
2179
+ lines.push("");
2180
+ lines.push(`Status: **${statusLabel(result.status)}**`);
2181
+ lines.push(`Config: \`${result.configPath}\``);
2182
+ lines.push(`Traces: \`${result.tracesDir}\``);
2183
+ lines.push(`Started: ${result.startedAt}`);
2184
+ lines.push(`Finished: ${result.finishedAt}`);
2185
+ lines.push("");
2186
+ lines.push("## Summary");
2187
+ lines.push(`- Passed: ${result.summary.passed}`);
2188
+ lines.push(`- Failed: ${result.summary.failed}`);
2189
+ lines.push(`- Errors: ${result.summary.errors}`);
2190
+ lines.push(`- Skipped: ${result.summary.skipped}`);
2191
+ lines.push("");
2192
+ lines.push("## Cases");
2193
+ for (const suiteCase of result.cases) {
2194
+ lines.push(`### ${suiteCase.id} \u2014 ${statusLabel(suiteCase.status)}`);
2195
+ if (suiteCase.tracePath !== void 0) {
2196
+ lines.push(`- Trace: \`${suiteCase.tracePath}\``);
2197
+ }
2198
+ if (suiteCase.runId !== void 0) {
2199
+ lines.push(`- Run: \`${suiteCase.runId}\``);
2200
+ }
2201
+ if (suiteCase.message !== void 0 && suiteCase.message.trim() !== "") {
2202
+ lines.push(`- ${suiteCase.message}`);
2203
+ }
2204
+ if (suiteCase.diagnostics.length > 0) {
2205
+ for (const item of suiteCase.diagnostics) {
2206
+ lines.push(`- [${item.severity}] ${item.message}`);
2207
+ }
2208
+ }
2209
+ lines.push("");
2210
+ }
2211
+ return lines.join("\n").trimEnd();
2212
+ }
2213
+ function renderSuiteReport(result, options = {}) {
2214
+ const format = options.format ?? "markdown";
2215
+ if (format === "json") {
2216
+ return JSON.stringify(result, null, 2);
2217
+ }
2218
+ return renderSuiteReportMarkdown(result);
1636
2219
  }
1637
2220
 
1638
- export { SESSION_WORKFLOW_KEYS, aggregateBundleSafeStatus, aggregateSessionCheckResults, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildLocalExplanation, buildPlaceholderArtifact, buildSessionIndex, buildTraceStats, bundleFailsOnSafety, defaultBundleOutputPath, deriveSessionStatus, enrichSessionRunRecord, enrichSessionSummary, extractSessionWorkflowMetadata, filterMetasBySessionScope, filterTraces, groupSessionCohorts, isAgentInspectTrace, loadSessionRunRecords, loadTraceMetadataList, normalizeBundleOutputPath, parseDurationFilter, renderActivitySummaryHuman, renderTraceStats, resolveBundleRunIds, searchTraces, sessionKeyForRun, toMetadataSafeStatus, traceMetasToSessionRunRecords };
2221
+ export { DEFAULT_SUITE_ARTIFACTS_DIR, DEFAULT_SUITE_CONFIG_NAMES, SESSION_WORKFLOW_KEYS, aggregateBundleSafeStatus, aggregateSessionCheckResults, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildLocalExplanation, buildPlaceholderArtifact, buildSessionIndex, buildTraceStats, bundleFailsOnSafety, defaultBundleOutputPath, defaultSuiteConfigTemplate, deriveSessionStatus, enrichSessionRunRecord, enrichSessionSummary, extractSessionWorkflowMetadata, filterMetasBySessionScope, filterTraces, groupSessionCohorts, isAgentInspectTrace, loadSessionRunRecords, loadSuiteConfig, loadTraceMetadataList, normalizeBundleOutputPath, normalizeSuiteConfig, parseDurationFilter, renderActivitySummaryHuman, renderSuiteReport, renderSuiteReportMarkdown, renderTraceStats, resolveBundleRunIds, resolveSuiteCaseTrace, resolveSuiteConfigPath, runSuite, searchTraces, sessionKeyForRun, toMetadataSafeStatus, traceMetasToSessionRunRecords, validateSuiteConfig };
1639
2222
  //# sourceMappingURL=advanced.mjs.map
1640
2223
  //# sourceMappingURL=advanced.mjs.map