agent-inspect 4.2.0 → 4.3.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.
@@ -4,7 +4,7 @@
4
4
  var crypto = require('crypto');
5
5
  var promises = require('fs/promises');
6
6
  var os = require('os');
7
- var path16 = require('path');
7
+ var path19 = require('path');
8
8
  var async_hooks = require('async_hooks');
9
9
  var process3 = require('process');
10
10
  var tty = require('tty');
@@ -20,7 +20,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
20
20
 
21
21
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
22
22
  var os__default = /*#__PURE__*/_interopDefault(os);
23
- var path16__default = /*#__PURE__*/_interopDefault(path16);
23
+ var path19__default = /*#__PURE__*/_interopDefault(path19);
24
24
  var process3__default = /*#__PURE__*/_interopDefault(process3);
25
25
  var tty__default = /*#__PURE__*/_interopDefault(tty);
26
26
 
@@ -859,7 +859,7 @@ function getDefaultTraceDir() {
859
859
  if (typeof home !== "string" || home.trim() === "") {
860
860
  return FALLBACK_TRACE_DIR;
861
861
  }
862
- return path16__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
862
+ return path19__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
863
863
  } catch {
864
864
  return FALLBACK_TRACE_DIR;
865
865
  }
@@ -867,11 +867,11 @@ function getDefaultTraceDir() {
867
867
  function getTraceFilePath(runId, traceDir) {
868
868
  const baseDir = traceDir ?? getDefaultTraceDir();
869
869
  let safeId = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
870
- safeId = path16__default.default.basename(safeId);
870
+ safeId = path19__default.default.basename(safeId);
871
871
  if (safeId === "" || safeId === "." || safeId === "..") {
872
872
  safeId = "run_unknown";
873
873
  }
874
- return path16__default.default.join(baseDir, `${safeId}.jsonl`);
874
+ return path19__default.default.join(baseDir, `${safeId}.jsonl`);
875
875
  }
876
876
  function formatError(error) {
877
877
  if (error instanceof Error) {
@@ -928,7 +928,7 @@ var init_utils = __esm({
928
928
  init_duration();
929
929
  DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
930
930
  RUNS_DIR_NAME = "runs";
931
- FALLBACK_TRACE_DIR = path16__default.default.join(
931
+ FALLBACK_TRACE_DIR = path19__default.default.join(
932
932
  os__default.default.tmpdir(),
933
933
  "agent-inspect",
934
934
  RUNS_DIR_NAME
@@ -1723,7 +1723,7 @@ var init_trace_directory = __esm({
1723
1723
  this.#dir = resolveTraceDir(options);
1724
1724
  }
1725
1725
  getPath(filename) {
1726
- return filename ? path16__default.default.join(this.#dir, filename) : this.#dir;
1726
+ return filename ? path19__default.default.join(this.#dir, filename) : this.#dir;
1727
1727
  }
1728
1728
  async list() {
1729
1729
  try {
@@ -1752,7 +1752,7 @@ function parseIsoToMs2(value) {
1752
1752
  }
1753
1753
  async function extractMetadata(filePath, _quickScan) {
1754
1754
  const stats = await promises.stat(filePath);
1755
- let runIdFromFile = path16__default.default.basename(filePath);
1755
+ let runIdFromFile = path19__default.default.basename(filePath);
1756
1756
  if (runIdFromFile.endsWith(".jsonl")) {
1757
1757
  runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
1758
1758
  }
@@ -3562,12 +3562,12 @@ function buildCriticalPath(runs, handoffs) {
3562
3562
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
3563
3563
  );
3564
3564
  const ordered = [...runs].sort(compareRuns);
3565
- const path23 = [];
3565
+ const path25 = [];
3566
3566
  const visited = /* @__PURE__ */ new Set();
3567
3567
  const pushRun = (run, confidence, source) => {
3568
3568
  if (visited.has(run.runId)) return;
3569
3569
  visited.add(run.runId);
3570
- path23.push({
3570
+ path25.push({
3571
3571
  runId: run.runId,
3572
3572
  name: run.name,
3573
3573
  startedAt: run.startedAt,
@@ -3592,7 +3592,7 @@ function buildCriticalPath(runs, handoffs) {
3592
3592
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
3593
3593
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
3594
3594
  }
3595
- return path23;
3595
+ return path25;
3596
3596
  }
3597
3597
  function metaRunIdMatches(run, token, runById) {
3598
3598
  const meta2 = extractSessionWorkflowMetadata(run.metadata);
@@ -3732,6 +3732,224 @@ var init_trace_verification = __esm({
3732
3732
  }
3733
3733
  });
3734
3734
 
3735
+ // packages/core/src/bundle/resolve.ts
3736
+ function parseSinceCutoff(since) {
3737
+ const trimmed = since.trim();
3738
+ if (trimmed === "") {
3739
+ throw new Error("--since requires a non-empty duration (e.g. 24h, 7d).");
3740
+ }
3741
+ return Date.now() - parseDuration(trimmed);
3742
+ }
3743
+ function runActivityMs(run) {
3744
+ if (run.startedAt !== void 0 && Number.isFinite(run.startedAt)) return run.startedAt;
3745
+ if (run.endedAt !== void 0 && Number.isFinite(run.endedAt)) return run.endedAt;
3746
+ return void 0;
3747
+ }
3748
+ function runsInSinceWindow(runs, since) {
3749
+ const cutoff = parseSinceCutoff(since);
3750
+ const ids = [];
3751
+ for (const run of runs) {
3752
+ const activity = runActivityMs(run);
3753
+ if (activity !== void 0 && activity >= cutoff) {
3754
+ ids.push(run.runId);
3755
+ }
3756
+ }
3757
+ return ids.sort((a, b) => a.localeCompare(b));
3758
+ }
3759
+ function findSession(index, sessionId) {
3760
+ return index.sessions.find((session) => session.sessionId === sessionId);
3761
+ }
3762
+ function resolveBundleRunIds(index, runs, options) {
3763
+ const runId = options.runId?.trim();
3764
+ const sessionId = options.sessionId?.trim();
3765
+ const since = options.since?.trim();
3766
+ const modes = [runId ? 1 : 0, sessionId ? 1 : 0, since ? 1 : 0].reduce((a, b) => a + b, 0);
3767
+ if (modes === 0) {
3768
+ throw new Error(
3769
+ "bundle requires a run id, --session <sessionId>, or --since <duration>."
3770
+ );
3771
+ }
3772
+ if (modes > 1) {
3773
+ throw new Error(
3774
+ "bundle accepts only one target: a run id, --session, or --since (not combined)."
3775
+ );
3776
+ }
3777
+ if (runId) {
3778
+ const known = runs.some((run) => run.runId === runId);
3779
+ if (!known) {
3780
+ throw new Error(`Run "${runId}" was not found in the trace directory.`);
3781
+ }
3782
+ return { runIds: [runId] };
3783
+ }
3784
+ if (sessionId) {
3785
+ const session = findSession(index, sessionId);
3786
+ if (!session) {
3787
+ throw new Error(`Session "${sessionId}" was not found.`);
3788
+ }
3789
+ if (session.runIds.length === 0) {
3790
+ throw new Error(`Session "${sessionId}" has no runs to bundle.`);
3791
+ }
3792
+ return {
3793
+ runIds: [...session.runIds].sort((a, b) => a.localeCompare(b)),
3794
+ sessionId
3795
+ };
3796
+ }
3797
+ const runIds = runsInSinceWindow(runs, since);
3798
+ if (runIds.length === 0) {
3799
+ throw new Error(`No runs matched --since ${since}.`);
3800
+ }
3801
+ return { runIds, since };
3802
+ }
3803
+ var init_resolve = __esm({
3804
+ "packages/core/src/bundle/resolve.ts"() {
3805
+ init_duration();
3806
+ }
3807
+ });
3808
+
3809
+ // packages/core/src/bundle/safety-status.ts
3810
+ function aggregateBundleSafeStatus(statuses) {
3811
+ if (statuses.length === 0) return "UNKNOWN";
3812
+ if (statuses.some((status) => status === "UNSAFE")) return "UNSAFE";
3813
+ if (statuses.some((status) => status === "UNKNOWN")) return "UNKNOWN";
3814
+ if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
3815
+ return "SAFE";
3816
+ }
3817
+ function toMetadataSafeStatus(status) {
3818
+ if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
3819
+ return status;
3820
+ }
3821
+ function bundleFailsOnSafety(status, allowUnsafe) {
3822
+ if (allowUnsafe) return false;
3823
+ return status === "UNSAFE" || status === "UNKNOWN";
3824
+ }
3825
+ var init_safety_status = __esm({
3826
+ "packages/core/src/bundle/safety-status.ts"() {
3827
+ }
3828
+ });
3829
+
3830
+ // packages/core/src/bundle/manifest.ts
3831
+ function buildBundleMetadata(parts) {
3832
+ const aggregate = aggregateBundleSafeStatus(
3833
+ parts.checks.runs.map((run) => run.status)
3834
+ );
3835
+ return {
3836
+ createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
3837
+ agentInspectVersion: parts.agentInspectVersion,
3838
+ redactionProfile: parts.profile,
3839
+ sourceTraceCount: parts.resolve.runIds.length,
3840
+ runIds: [...parts.resolve.runIds],
3841
+ safeStatus: toMetadataSafeStatus(aggregate),
3842
+ files: [...parts.files].sort((a, b) => a.localeCompare(b)),
3843
+ note: BUNDLE_NOTE,
3844
+ ...parts.resolve.sessionId !== void 0 ? { sessionId: parts.resolve.sessionId } : {},
3845
+ ...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
3846
+ };
3847
+ }
3848
+ function buildPlaceholderArtifact() {
3849
+ return {
3850
+ status: "not_requested",
3851
+ note: PLACEHOLDER_NOTE
3852
+ };
3853
+ }
3854
+ var BUNDLE_NOTE, PLACEHOLDER_NOTE;
3855
+ var init_manifest = __esm({
3856
+ "packages/core/src/bundle/manifest.ts"() {
3857
+ init_safety_status();
3858
+ BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
3859
+ PLACEHOLDER_NOTE = "No eval or performance artifacts were requested for this bundle.";
3860
+ }
3861
+ });
3862
+
3863
+ // packages/core/src/bundle/summary.ts
3864
+ function markdownTable(rows) {
3865
+ const lines = ["| Field | Value |", "| --- | --- |"];
3866
+ for (const [key, value] of rows) {
3867
+ lines.push(`| ${key} | ${value ?? "unknown"} |`);
3868
+ }
3869
+ return lines.join("\n");
3870
+ }
3871
+ function buildBundleSummaryMarkdown(parts) {
3872
+ const { metadata, checks: checks2, redaction } = parts;
3873
+ const lines = [
3874
+ "# AgentInspect trace bundle",
3875
+ "",
3876
+ metadata.note,
3877
+ "",
3878
+ "## Overview",
3879
+ "",
3880
+ markdownTable([
3881
+ ["Created", metadata.createdAt],
3882
+ ["AgentInspect", metadata.agentInspectVersion],
3883
+ ["Redaction profile", metadata.redactionProfile],
3884
+ ["Safe status", metadata.safeStatus],
3885
+ ["Source traces", metadata.sourceTraceCount],
3886
+ ["Runs", metadata.runIds.join(", ")],
3887
+ ...metadata.sessionId ? [["Session", metadata.sessionId]] : [],
3888
+ ...metadata.since ? [["Since", metadata.since]] : []
3889
+ ]),
3890
+ "",
3891
+ "## Safety checks",
3892
+ "",
3893
+ `Aggregate: **${checks2.aggregateStatus}**`,
3894
+ ""
3895
+ ];
3896
+ for (const run of checks2.runs) {
3897
+ lines.push(
3898
+ `- \`${run.runId}\`: ${run.status} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
3899
+ );
3900
+ }
3901
+ lines.push("", "## Redaction", "", `Total findings: ${redaction.totalFindings}`, "");
3902
+ for (const run of redaction.runs) {
3903
+ const detectors = run.detectors.length > 0 ? run.detectors.join(", ") : "none";
3904
+ lines.push(`- \`${run.runId}\`: ${run.findings} finding(s); detectors: ${detectors}`);
3905
+ }
3906
+ lines.push(
3907
+ "",
3908
+ "## Files",
3909
+ "",
3910
+ ...metadata.files.map((file) => `- \`${file}\``),
3911
+ "",
3912
+ "_Review every generated artifact before sharing outside your team._",
3913
+ ""
3914
+ );
3915
+ return lines.join("\n");
3916
+ }
3917
+ var init_summary = __esm({
3918
+ "packages/core/src/bundle/summary.ts"() {
3919
+ }
3920
+ });
3921
+ function normalizeBundleOutputPath(out) {
3922
+ const trimmed = out.trim();
3923
+ if (trimmed === "") {
3924
+ throw new Error("--out requires a non-empty path.");
3925
+ }
3926
+ const resolved = path19__default.default.resolve(trimmed);
3927
+ if (resolved.toLowerCase().endsWith(".zip")) {
3928
+ return resolved.slice(0, -4);
3929
+ }
3930
+ return resolved;
3931
+ }
3932
+ function defaultBundleOutputPath(runIds) {
3933
+ const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
3934
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3935
+ return path19__default.default.resolve(`agent-inspect-bundle-${label}-${stamp}`);
3936
+ }
3937
+ var init_paths = __esm({
3938
+ "packages/core/src/bundle/paths.ts"() {
3939
+ }
3940
+ });
3941
+
3942
+ // packages/core/src/bundle/index.ts
3943
+ var init_bundle = __esm({
3944
+ "packages/core/src/bundle/index.ts"() {
3945
+ init_resolve();
3946
+ init_manifest();
3947
+ init_summary();
3948
+ init_safety_status();
3949
+ init_paths();
3950
+ }
3951
+ });
3952
+
3735
3953
  // packages/core/src/inspect-run.ts
3736
3954
  var init_inspect_run = __esm({
3737
3955
  "packages/core/src/inspect-run.ts"() {
@@ -3775,6 +3993,7 @@ var init_advanced = __esm({
3775
3993
  init_sessions();
3776
3994
  init_trace_verification();
3777
3995
  init_duration();
3996
+ init_bundle();
3778
3997
  init_maybe_inspect_run();
3779
3998
  }
3780
3999
  });
@@ -3839,19 +4058,19 @@ var require_file_uri_to_path = __commonJS({
3839
4058
  var rest = decodeURI(uri.substring(7));
3840
4059
  var firstSlash = rest.indexOf("/");
3841
4060
  var host = rest.substring(0, firstSlash);
3842
- var path23 = rest.substring(firstSlash + 1);
4061
+ var path25 = rest.substring(firstSlash + 1);
3843
4062
  if ("localhost" == host) host = "";
3844
4063
  if (host) {
3845
4064
  host = sep + sep + host;
3846
4065
  }
3847
- path23 = path23.replace(/^(.+)\|/, "$1:");
4066
+ path25 = path25.replace(/^(.+)\|/, "$1:");
3848
4067
  if (sep == "\\") {
3849
- path23 = path23.replace(/\//g, "\\");
4068
+ path25 = path25.replace(/\//g, "\\");
3850
4069
  }
3851
- if (/^.+\:/.test(path23)) ; else {
3852
- path23 = sep + path23;
4070
+ if (/^.+\:/.test(path25)) ; else {
4071
+ path25 = sep + path25;
3853
4072
  }
3854
- return host + path23;
4073
+ return host + path25;
3855
4074
  }
3856
4075
  }
3857
4076
  });
@@ -3860,18 +4079,18 @@ var require_file_uri_to_path = __commonJS({
3860
4079
  var require_bindings = __commonJS({
3861
4080
  "node_modules/.pnpm/bindings@1.5.0/node_modules/bindings/bindings.js"(exports$1, module) {
3862
4081
  var fs = __require("fs");
3863
- var path23 = __require("path");
4082
+ var path25 = __require("path");
3864
4083
  var fileURLToPath2 = require_file_uri_to_path();
3865
- var join = path23.join;
3866
- var dirname = path23.dirname;
3867
- var exists = fs.accessSync && function(path24) {
4084
+ var join = path25.join;
4085
+ var dirname = path25.dirname;
4086
+ var exists = fs.accessSync && function(path26) {
3868
4087
  try {
3869
- fs.accessSync(path24);
4088
+ fs.accessSync(path26);
3870
4089
  } catch (e) {
3871
4090
  return false;
3872
4091
  }
3873
4092
  return true;
3874
- } || fs.existsSync || path23.existsSync;
4093
+ } || fs.existsSync || path25.existsSync;
3875
4094
  var defaults = {
3876
4095
  arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
3877
4096
  compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
@@ -3916,7 +4135,7 @@ var require_bindings = __commonJS({
3916
4135
  if (!opts.module_root) {
3917
4136
  opts.module_root = exports$1.getRoot(exports$1.getFileName());
3918
4137
  }
3919
- if (path23.extname(opts.bindings) != ".node") {
4138
+ if (path25.extname(opts.bindings) != ".node") {
3920
4139
  opts.bindings += ".node";
3921
4140
  }
3922
4141
  var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
@@ -4151,7 +4370,7 @@ var require_pragma = __commonJS({
4151
4370
  var require_backup = __commonJS({
4152
4371
  "node_modules/.pnpm/better-sqlite3@11.10.0/node_modules/better-sqlite3/lib/methods/backup.js"(exports$1, module) {
4153
4372
  var fs = __require("fs");
4154
- var path23 = __require("path");
4373
+ var path25 = __require("path");
4155
4374
  var { promisify } = __require("util");
4156
4375
  var { cppdb } = require_util();
4157
4376
  var fsAccess = promisify(fs.access);
@@ -4167,7 +4386,7 @@ var require_backup = __commonJS({
4167
4386
  if (typeof attachedName !== "string") throw new TypeError('Expected the "attached" option to be a string');
4168
4387
  if (!attachedName) throw new TypeError('The "attached" option cannot be an empty string');
4169
4388
  if (handler != null && typeof handler !== "function") throw new TypeError('Expected the "progress" option to be a function');
4170
- await fsAccess(path23.dirname(filename)).catch(() => {
4389
+ await fsAccess(path25.dirname(filename)).catch(() => {
4171
4390
  throw new TypeError("Cannot save backup because the directory does not exist");
4172
4391
  });
4173
4392
  const isNewFile = await fsAccess(filename).then(() => false, () => true);
@@ -4467,7 +4686,7 @@ var require_inspect = __commonJS({
4467
4686
  var require_database = __commonJS({
4468
4687
  "node_modules/.pnpm/better-sqlite3@11.10.0/node_modules/better-sqlite3/lib/database.js"(exports$1, module) {
4469
4688
  var fs = __require("fs");
4470
- var path23 = __require("path");
4689
+ var path25 = __require("path");
4471
4690
  var util = require_util();
4472
4691
  var SqliteError = require_sqlite_error();
4473
4692
  var DEFAULT_ADDON;
@@ -4503,7 +4722,7 @@ var require_database = __commonJS({
4503
4722
  addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
4504
4723
  } else if (typeof nativeBinding === "string") {
4505
4724
  const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : __require;
4506
- addon = requireFunc(path23.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
4725
+ addon = requireFunc(path25.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
4507
4726
  } else {
4508
4727
  addon = nativeBinding;
4509
4728
  }
@@ -4511,7 +4730,7 @@ var require_database = __commonJS({
4511
4730
  addon.setErrorConstructor(SqliteError);
4512
4731
  addon.isInitialized = true;
4513
4732
  }
4514
- if (!anonymous && !fs.existsSync(path23.dirname(filename))) {
4733
+ if (!anonymous && !fs.existsSync(path25.dirname(filename))) {
4515
4734
  throw new TypeError("Cannot open database because the directory does not exist");
4516
4735
  }
4517
4736
  Object.defineProperties(this, {
@@ -4638,8 +4857,8 @@ GROUP BY session_id;
4638
4857
  }
4639
4858
  });
4640
4859
  function resolveIndexDbPath(traceDir, dbPath) {
4641
- if (dbPath && dbPath.trim() !== "") return path16__default.default.resolve(dbPath);
4642
- return path16__default.default.join(path16__default.default.resolve(traceDir), INDEX_DB_FILENAME);
4860
+ if (dbPath && dbPath.trim() !== "") return path19__default.default.resolve(dbPath);
4861
+ return path19__default.default.join(path19__default.default.resolve(traceDir), INDEX_DB_FILENAME);
4643
4862
  }
4644
4863
  function str(value) {
4645
4864
  return typeof value === "string" && value !== "" ? value : null;
@@ -4731,7 +4950,7 @@ async function buildIndex(options = {}) {
4731
4950
  warnings.push(`index.unreadable: ${file}`);
4732
4951
  }
4733
4952
  }
4734
- await promises.mkdir(path16__default.default.dirname(dbPath), { recursive: true });
4953
+ await promises.mkdir(path19__default.default.dirname(dbPath), { recursive: true });
4735
4954
  await promises.rm(dbPath, { force: true });
4736
4955
  const db = new import_better_sqlite3.default(dbPath);
4737
4956
  let runCount = 0;
@@ -4969,7 +5188,7 @@ var init_src = __esm({
4969
5188
  });
4970
5189
 
4971
5190
  // package.json
4972
- var version = "4.2.0";
5191
+ var version = "4.3.0";
4973
5192
 
4974
5193
  // packages/cli/src/list.ts
4975
5194
  init_advanced();
@@ -5878,7 +6097,7 @@ function findReaderByFormat(format, readers) {
5878
6097
  }
5879
6098
  async function jsonlFilesInDirectory(dirPath) {
5880
6099
  const entries = await promises.readdir(dirPath, { withFileTypes: true });
5881
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path16__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
6100
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path19__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
5882
6101
  }
5883
6102
  async function resolveInput(input3) {
5884
6103
  const cached = resolvedInputCache.get(input3);
@@ -9355,7 +9574,8 @@ function exportOpenInference(tree, options) {
9355
9574
  ];
9356
9575
  const traceId = hexFrom(`trace:${tree.runId}`, 16);
9357
9576
  const includeAttributes = options?.includeAttributes ?? false;
9358
- const maxLen = options?.maxAttributeLength;
9577
+ const maxLen = options?.maxAttributeLength ?? 500;
9578
+ const pretty = options?.pretty ?? true;
9359
9579
  const spans = [];
9360
9580
  for (const n of flattenTree(tree)) {
9361
9581
  const ev = n.event;
@@ -9429,7 +9649,7 @@ function exportOpenInference(tree, options) {
9429
9649
  };
9430
9650
  return {
9431
9651
  format: "openinference",
9432
- content: JSON.stringify(payload, null, 2 ),
9652
+ content: JSON.stringify(payload, null, pretty ? 2 : void 0),
9433
9653
  contentType: "application/json",
9434
9654
  fileExtension: ".openinference.json",
9435
9655
  warnings
@@ -9463,7 +9683,8 @@ function exportOtlpJson(tree, options) {
9463
9683
  ];
9464
9684
  const traceId = hexFrom2(`trace:${tree.runId}`, 16);
9465
9685
  const includeAttributes = options?.includeAttributes ?? false;
9466
- const maxLen = options?.maxAttributeLength;
9686
+ const maxLen = options?.maxAttributeLength ?? 500;
9687
+ const pretty = options?.pretty ?? true;
9467
9688
  const flat = flattenTree(tree);
9468
9689
  const spans = [];
9469
9690
  for (const n of flat) {
@@ -9559,7 +9780,7 @@ function exportOtlpJson(tree, options) {
9559
9780
  };
9560
9781
  return {
9561
9782
  format: "otlp-json",
9562
- content: JSON.stringify(payload, null, 2 ),
9783
+ content: JSON.stringify(payload, null, pretty ? 2 : void 0),
9563
9784
  contentType: "application/json",
9564
9785
  fileExtension: ".otlp.json",
9565
9786
  warnings
@@ -9782,9 +10003,9 @@ function mergeExportDefaults(options) {
9782
10003
  includeMetadata: options.includeMetadata ?? true,
9783
10004
  includeAttributes: options.includeAttributes ?? false,
9784
10005
  includeErrors: options.includeErrors ?? true,
9785
- pretty: options.pretty,
9786
- redacted: options.redacted,
9787
- maxAttributeLength: options.maxAttributeLength,
10006
+ pretty: options.pretty ?? true,
10007
+ redacted: options.redacted ?? true,
10008
+ maxAttributeLength: options.maxAttributeLength ?? 500,
9788
10009
  redactionProfile: options.redactionProfile ?? "local"
9789
10010
  };
9790
10011
  }
@@ -10020,9 +10241,9 @@ Trace directory: ${traceDir}`);
10020
10241
  if (validation !== void 0 && !validation.ok) {
10021
10242
  process.exitCode = 1;
10022
10243
  }
10023
- const outPath = options.output !== void 0 && options.output.trim() !== "" ? path16__default.default.resolve(options.output.trim()) : void 0;
10244
+ const outPath = options.output !== void 0 && options.output.trim() !== "" ? path19__default.default.resolve(options.output.trim()) : void 0;
10024
10245
  if (outPath !== void 0) {
10025
- await promises.mkdir(path16__default.default.dirname(outPath), { recursive: true });
10246
+ await promises.mkdir(path19__default.default.dirname(outPath), { recursive: true });
10026
10247
  await promises.writeFile(outPath, result.content, "utf-8");
10027
10248
  const vlabel = validation !== void 0 ? validation.ok ? "ok" : "failed" : "skipped";
10028
10249
  console.log(`Wrote ${result.fileExtension} export to ${outPath} (validation: ${vlabel})`);
@@ -10202,13 +10423,13 @@ function pairSteps(left, right) {
10202
10423
  return pairs;
10203
10424
  }
10204
10425
  function compareLeafSteps(L, R, segments, opts, out) {
10205
- const path23 = buildPath(segments);
10426
+ const path25 = buildPath(segments);
10206
10427
  if (L.name !== R.name) {
10207
10428
  out.push({
10208
10429
  kind: "structure",
10209
10430
  severity: "warning",
10210
10431
  message: "Step name differs",
10211
- path: path23,
10432
+ path: path25,
10212
10433
  left: L.name,
10213
10434
  right: R.name
10214
10435
  });
@@ -10218,7 +10439,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
10218
10439
  kind: "step-type",
10219
10440
  severity: "warning",
10220
10441
  message: "Step type differs",
10221
- path: path23,
10442
+ path: path25,
10222
10443
  left: L.type,
10223
10444
  right: R.type
10224
10445
  });
@@ -10228,7 +10449,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
10228
10449
  kind: "step-status",
10229
10450
  severity: "warning",
10230
10451
  message: "Step status differs",
10231
- path: path23,
10452
+ path: path25,
10232
10453
  left: L.status,
10233
10454
  right: R.status
10234
10455
  });
@@ -10240,7 +10461,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
10240
10461
  kind: "error",
10241
10462
  severity: "error",
10242
10463
  message: "Step error message differs",
10243
- path: path23,
10464
+ path: path25,
10244
10465
  left: le || void 0,
10245
10466
  right: re || void 0
10246
10467
  });
@@ -10258,7 +10479,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
10258
10479
  kind: "duration",
10259
10480
  severity: "info",
10260
10481
  message: "Step duration differs",
10261
- path: path23,
10482
+ path: path25,
10262
10483
  left: ld,
10263
10484
  right: rd
10264
10485
  });
@@ -10271,7 +10492,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
10271
10492
  kind: "metadata",
10272
10493
  severity: "info",
10273
10494
  message: "Step metadata differs",
10274
- path: path23,
10495
+ path: path25,
10275
10496
  left: L.metadata,
10276
10497
  right: R.metadata
10277
10498
  });
@@ -10283,7 +10504,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
10283
10504
  kind: "output",
10284
10505
  severity: "info",
10285
10506
  message: "Output preview differs",
10286
- path: path23,
10507
+ path: path25,
10287
10508
  left: L.outputPreview,
10288
10509
  right: R.outputPreview
10289
10510
  });
@@ -10444,11 +10665,11 @@ function diffRuns(left, right, options) {
10444
10665
 
10445
10666
  // packages/core/src/diff/renderer.ts
10446
10667
  init_source();
10447
- function formatPath(path23) {
10448
- if (path23 === void 0 || path23.path.length === 0) {
10668
+ function formatPath(path25) {
10669
+ if (path25 === void 0 || path25.path.length === 0) {
10449
10670
  return "(run)";
10450
10671
  }
10451
- return path23.path.map((s) => s.name).join(" > ");
10672
+ return path25.path.map((s) => s.name).join(" > ");
10452
10673
  }
10453
10674
  function formatValue(v, verbose) {
10454
10675
  if (v === void 0) return "(undefined)";
@@ -10835,7 +11056,7 @@ function indexedToMetadata(row, traceDir) {
10835
11056
  endedAt: row.endedAt ?? void 0,
10836
11057
  durationMs: row.durationMs ?? void 0,
10837
11058
  eventCount: 0,
10838
- filePath: path16__default.default.join(traceDir, row.file),
11059
+ filePath: path19__default.default.join(traceDir, row.file),
10839
11060
  fileSize: 0,
10840
11061
  createdAt: new Date(row.mtimeMs)
10841
11062
  };
@@ -10899,7 +11120,7 @@ async function loadSessionIndex(traceDir, options = {}) {
10899
11120
  staleThresholdMs
10900
11121
  });
10901
11122
  }
10902
- function findSession(index, sessionId) {
11123
+ function findSession2(index, sessionId) {
10903
11124
  return index.sessions.find((session) => session.sessionId === sessionId);
10904
11125
  }
10905
11126
  function latestSession(index) {
@@ -10908,12 +11129,12 @@ function latestSession(index) {
10908
11129
  (a, b) => Date.parse(b.lastActivity) - Date.parse(a.lastActivity)
10909
11130
  )[0];
10910
11131
  }
10911
- function parseSinceCutoff(since) {
11132
+ function parseSinceCutoff2(since) {
10912
11133
  if (!since || since.trim() === "") return void 0;
10913
11134
  return Date.now() - parseDuration(since.trim());
10914
11135
  }
10915
11136
  function sessionsInSinceWindow(index, since) {
10916
- const cutoff = parseSinceCutoff(since);
11137
+ const cutoff = parseSinceCutoff2(since);
10917
11138
  if (cutoff === void 0) return index.sessions;
10918
11139
  return index.sessions.filter(
10919
11140
  (session) => Date.parse(session.lastActivity) >= cutoff
@@ -11168,7 +11389,7 @@ async function sessionCommand(sessionId, options = {}) {
11168
11389
  try {
11169
11390
  const traceDir = resolveTraceDir({ dir: options.dir });
11170
11391
  const index = await loadSessionIndex(traceDir);
11171
- const session = findSession(index, id);
11392
+ const session = findSession2(index, id);
11172
11393
  if (!session) {
11173
11394
  console.log(`Session not found: ${id}`);
11174
11395
  console.log(`Trace directory: ${traceDir}`);
@@ -11315,9 +11536,9 @@ async function reportCommand(runId, options = {}) {
11315
11536
  redactionProfile,
11316
11537
  correlation: !options.noCorrelation
11317
11538
  });
11318
- const outPath = options.output !== void 0 && options.output.trim() !== "" ? path16__default.default.resolve(options.output.trim()) : void 0;
11539
+ const outPath = options.output !== void 0 && options.output.trim() !== "" ? path19__default.default.resolve(options.output.trim()) : void 0;
11319
11540
  if (outPath !== void 0) {
11320
- await promises.mkdir(path16__default.default.dirname(outPath), { recursive: true });
11541
+ await promises.mkdir(path19__default.default.dirname(outPath), { recursive: true });
11321
11542
  await promises.writeFile(outPath, result.content, "utf-8");
11322
11543
  console.log(`Wrote ${result.fileExtension} report to ${outPath}`);
11323
11544
  }
@@ -11567,17 +11788,17 @@ function applyRule(rule, value, replacement) {
11567
11788
  }
11568
11789
  return value;
11569
11790
  }
11570
- function childPath(path23, key) {
11791
+ function childPath(path25, key) {
11571
11792
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
11572
- return path23 ? `${path23}.${key}` : key;
11793
+ return path25 ? `${path25}.${key}` : key;
11573
11794
  }
11574
- return `${path23 || "$"}[${JSON.stringify(key)}]`;
11795
+ return `${path25 || "$"}[${JSON.stringify(key)}]`;
11575
11796
  }
11576
- function indexPath(path23, index) {
11577
- return `${path23 || "$"}[${index}]`;
11797
+ function indexPath(path25, index) {
11798
+ return `${path25 || "$"}[${index}]`;
11578
11799
  }
11579
- function makeFinding(path23, detector, action, matchKind, severity = "warning", preview) {
11580
- return preview === void 0 ? { path: path23, detector, action, severity, matchKind } : { path: path23, detector, action, severity, matchKind, preview };
11800
+ function makeFinding(path25, detector, action, matchKind, severity = "warning", preview) {
11801
+ return preview === void 0 ? { path: path25, detector, action, severity, matchKind } : { path: path25, detector, action, severity, matchKind, preview };
11581
11802
  }
11582
11803
  function createRedactionProfile(profile = "local") {
11583
11804
  switch (profile) {
@@ -11646,11 +11867,11 @@ var Redactor2 = class {
11646
11867
  #recordFinding(state, finding) {
11647
11868
  if (this.#collectFindings) state.findings.push(finding);
11648
11869
  }
11649
- #redactValue(value, key, path23, depth, state) {
11870
+ #redactValue(value, key, path25, depth, state) {
11650
11871
  if (depth > this.#maxDepth) {
11651
11872
  this.#recordFinding(
11652
11873
  state,
11653
- makeFinding(path23, "structure.maxDepth", "truncate", "value", "warning")
11874
+ makeFinding(path25, "structure.maxDepth", "truncate", "value", "warning")
11654
11875
  );
11655
11876
  return "[Truncated]";
11656
11877
  }
@@ -11659,19 +11880,19 @@ var Redactor2 = class {
11659
11880
  if (rule) {
11660
11881
  this.#recordFinding(
11661
11882
  state,
11662
- makeFinding(path23, `key.${rule.key}`, actionForRule(rule), "key", "warning")
11883
+ makeFinding(path25, `key.${rule.key}`, actionForRule(rule), "key", "warning")
11663
11884
  );
11664
11885
  return applyRule(rule, value, this.#replacement);
11665
11886
  }
11666
11887
  }
11667
11888
  for (const detector of this.#detectors) {
11668
- const detections = detector.detect({ path: path23, key, value });
11889
+ const detections = detector.detect({ path: path25, key, value });
11669
11890
  for (const detection of detections) {
11670
11891
  const action = detection.action ?? "replace";
11671
11892
  this.#recordFinding(
11672
11893
  state,
11673
11894
  makeFinding(
11674
- path23,
11895
+ path25,
11675
11896
  detector.id,
11676
11897
  action,
11677
11898
  detection.matchKind ?? detector.matchKind ?? "custom",
@@ -11689,7 +11910,7 @@ var Redactor2 = class {
11689
11910
  const out = [];
11690
11911
  state.seen.set(value, out);
11691
11912
  value.forEach((item, index) => {
11692
- out[index] = this.#redactValue(item, void 0, indexPath(path23, index), depth + 1, state);
11913
+ out[index] = this.#redactValue(item, void 0, indexPath(path25, index), depth + 1, state);
11693
11914
  });
11694
11915
  return out;
11695
11916
  }
@@ -11701,7 +11922,7 @@ var Redactor2 = class {
11701
11922
  out[entryKey] = this.#redactValue(
11702
11923
  entryValue,
11703
11924
  entryKey,
11704
- childPath(path23 === "$" ? "" : path23, entryKey),
11925
+ childPath(path25 === "$" ? "" : path25, entryKey),
11705
11926
  depth + 1,
11706
11927
  state
11707
11928
  );
@@ -11830,6 +12051,9 @@ function redactDocument(content, profile) {
11830
12051
  return redactJsonlText(content, profile);
11831
12052
  }
11832
12053
  }
12054
+ function redactTraceContent(content, profile) {
12055
+ return redactDocument(content, profile);
12056
+ }
11833
12057
  async function redactCommand(target, options = {}, stdin = process.stdin) {
11834
12058
  const profile = parseRedactionProfile3(options.profile);
11835
12059
  const source = await contentFromTarget(target, options, stdin);
@@ -12158,14 +12382,14 @@ function uniqueSorted(values) {
12158
12382
  return [...new Set(values)].sort();
12159
12383
  }
12160
12384
  function isWithinDirectory(child, parent) {
12161
- const relative = path16__default.default.relative(parent, child);
12162
- return relative === "" || !relative.startsWith("..") && !path16__default.default.isAbsolute(relative);
12385
+ const relative = path19__default.default.relative(parent, child);
12386
+ return relative === "" || !relative.startsWith("..") && !path19__default.default.isAbsolute(relative);
12163
12387
  }
12164
12388
  async function resolveOutputPath(inputPath, output2, force) {
12165
12389
  if (output2 === void 0 || output2.trim() === "") return void 0;
12166
- const inputAbs = path16__default.default.resolve(inputPath);
12167
- const outputAbs = path16__default.default.resolve(output2.trim());
12168
- const inputDir = path16__default.default.dirname(inputAbs);
12390
+ const inputAbs = path19__default.default.resolve(inputPath);
12391
+ const outputAbs = path19__default.default.resolve(output2.trim());
12392
+ const inputDir = path19__default.default.dirname(inputAbs);
12169
12393
  if (!isWithinDirectory(outputAbs, inputDir)) {
12170
12394
  throw new Error("Refusing to write migrated output outside the input directory.");
12171
12395
  }
@@ -12286,7 +12510,7 @@ async function migrateCommand(input3, options = {}) {
12286
12510
  process.exitCode = 1;
12287
12511
  return;
12288
12512
  }
12289
- const inputPath = path16__default.default.resolve(input3.trim());
12513
+ const inputPath = path19__default.default.resolve(input3.trim());
12290
12514
  const dryRun = options.dryRun === true;
12291
12515
  if (!dryRun && (options.output === void 0 || options.output.trim() === "")) {
12292
12516
  console.error("migrate requires --dry-run or --output <path>.");
@@ -12305,7 +12529,7 @@ async function migrateCommand(input3, options = {}) {
12305
12529
  );
12306
12530
  const result = await buildMigration(inputPath, outputPath);
12307
12531
  if (!dryRun && outputPath !== void 0) {
12308
- await promises.mkdir(path16__default.default.dirname(outputPath), { recursive: true });
12532
+ await promises.mkdir(path19__default.default.dirname(outputPath), { recursive: true });
12309
12533
  await promises.writeFile(outputPath, result.content, "utf-8");
12310
12534
  }
12311
12535
  printSummary2(result, dryRun);
@@ -12584,7 +12808,7 @@ function stripPrefix(name, prefixes) {
12584
12808
  }
12585
12809
  return name;
12586
12810
  }
12587
- function eventEvidence(event, path23) {
12811
+ function eventEvidence(event, path25) {
12588
12812
  return {
12589
12813
  runId: event.runId,
12590
12814
  eventId: event.eventId,
@@ -12594,7 +12818,7 @@ function eventEvidence(event, path23) {
12594
12818
  kind: event.kind,
12595
12819
  name: event.name,
12596
12820
  status: event.status,
12597
- ...path23 ? { path: path23 } : {}
12821
+ ...path25 ? { path: path25 } : {}
12598
12822
  };
12599
12823
  }
12600
12824
  function runEvidence(run) {
@@ -12657,9 +12881,9 @@ function eventEndMs(event) {
12657
12881
  function normalizedKey(value) {
12658
12882
  return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
12659
12883
  }
12660
- function lastPathSegment(path23) {
12661
- const parts = path23.split(".");
12662
- return parts[parts.length - 1] ?? path23;
12884
+ function lastPathSegment(path25) {
12885
+ const parts = path25.split(".");
12886
+ return parts[parts.length - 1] ?? path25;
12663
12887
  }
12664
12888
  function valueType(value) {
12665
12889
  if (Array.isArray(value)) return "array";
@@ -12673,12 +12897,12 @@ function serializedByteLength(value) {
12673
12897
  return void 0;
12674
12898
  }
12675
12899
  }
12676
- function pushValueEntries(entries, event, value, path23, key, depth = 0) {
12677
- entries.push({ event, path: path23, key, value });
12900
+ function pushValueEntries(entries, event, value, path25, key, depth = 0) {
12901
+ entries.push({ event, path: path25, key, value });
12678
12902
  if (depth >= 8) return;
12679
12903
  if (Array.isArray(value)) {
12680
12904
  for (const [index, item] of value.entries()) {
12681
- pushValueEntries(entries, event, item, `${path23}.${index}`, String(index), depth + 1);
12905
+ pushValueEntries(entries, event, item, `${path25}.${index}`, String(index), depth + 1);
12682
12906
  }
12683
12907
  return;
12684
12908
  }
@@ -12688,7 +12912,7 @@ function pushValueEntries(entries, event, value, path23, key, depth = 0) {
12688
12912
  entries,
12689
12913
  event,
12690
12914
  value[nestedKey],
12691
- `${path23}.${nestedKey}`,
12915
+ `${path25}.${nestedKey}`,
12692
12916
  nestedKey,
12693
12917
  depth + 1
12694
12918
  );
@@ -12769,9 +12993,9 @@ function eventDurationMs(event) {
12769
12993
  }
12770
12994
  function treeShape(nodes) {
12771
12995
  const lines = [];
12772
- const visit = (node, path23) => {
12773
- lines.push(`${path23}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
12774
- node.children.forEach((child, index) => visit(child, `${path23}.${index}`));
12996
+ const visit = (node, path25) => {
12997
+ lines.push(`${path25}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
12998
+ node.children.forEach((child, index) => visit(child, `${path25}.${index}`));
12775
12999
  };
12776
13000
  nodes.forEach((node, index) => visit(node, String(index)));
12777
13001
  return lines;
@@ -12820,9 +13044,9 @@ function retrievalShape(context) {
12820
13044
  function guardrailShape(context) {
12821
13045
  return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
12822
13046
  }
12823
- function firstEvidenceForKind(context, kind, path23) {
13047
+ function firstEvidenceForKind(context, kind, path25) {
12824
13048
  const event = context.events.find((candidate) => candidate.kind === kind);
12825
- return event ? [eventEvidence(event, path23)] : runEvidence(context.selectedRun);
13049
+ return event ? [eventEvidence(event, path25)] : runEvidence(context.selectedRun);
12826
13050
  }
12827
13051
  function baselineDiffFinding(message, evidence, expected, actual) {
12828
13052
  return failFinding("baseline.regression", message, evidence, expected, actual);
@@ -13172,13 +13396,13 @@ function createStructureCycleRule() {
13172
13396
  const seenCycles = /* @__PURE__ */ new Set();
13173
13397
  const findings = [];
13174
13398
  for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
13175
- const path23 = [];
13399
+ const path25 = [];
13176
13400
  const seenAt = /* @__PURE__ */ new Map();
13177
13401
  let current = event;
13178
13402
  while (current) {
13179
13403
  const existing = seenAt.get(current.eventId);
13180
13404
  if (existing !== void 0) {
13181
- const cycle = path23.slice(existing);
13405
+ const cycle = path25.slice(existing);
13182
13406
  const key = cycle.map((item) => item.eventId).sort().join("\0");
13183
13407
  if (!seenCycles.has(key)) {
13184
13408
  seenCycles.add(key);
@@ -13194,8 +13418,8 @@ function createStructureCycleRule() {
13194
13418
  }
13195
13419
  break;
13196
13420
  }
13197
- seenAt.set(current.eventId, path23.length);
13198
- path23.push(current);
13421
+ seenAt.set(current.eventId, path25.length);
13422
+ path25.push(current);
13199
13423
  current = current.parentId ? byId.get(current.parentId) : void 0;
13200
13424
  }
13201
13425
  }
@@ -13992,23 +14216,23 @@ function evaluatePromptInjection(text, options = {}) {
13992
14216
  }
13993
14217
  return fail(ruleId, `Matched ${evidence.length} injection pattern(s).`, evidence, "warning");
13994
14218
  }
13995
- function validateSchemaField(value, field, path23, evidence) {
14219
+ function validateSchemaField(value, field, path25, evidence) {
13996
14220
  const ruleId = "guardrail.structured-output";
13997
14221
  if (field.type) {
13998
14222
  const actual = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
13999
14223
  if (actual !== field.type) {
14000
- evidence.push({ ruleId, path: path23, preview: `expected ${field.type}, got ${actual}` });
14224
+ evidence.push({ ruleId, path: path25, preview: `expected ${field.type}, got ${actual}` });
14001
14225
  return;
14002
14226
  }
14003
14227
  }
14004
14228
  if (field.enum && !field.enum.some((item) => Object.is(item, value))) {
14005
- evidence.push({ ruleId, path: path23, preview: "value not in enum" });
14229
+ evidence.push({ ruleId, path: path25, preview: "value not in enum" });
14006
14230
  }
14007
14231
  if (field.type === "object" && field.required && value && typeof value === "object" && !Array.isArray(value)) {
14008
14232
  const record = value;
14009
14233
  for (const key of field.required) {
14010
14234
  if (!(key in record)) {
14011
- evidence.push({ ruleId, path: `${path23}.${key}`, preview: "missing required key" });
14235
+ evidence.push({ ruleId, path: `${path25}.${key}`, preview: "missing required key" });
14012
14236
  }
14013
14237
  }
14014
14238
  }
@@ -14335,7 +14559,7 @@ function asConfig(value) {
14335
14559
  }
14336
14560
  async function loadConfig(configPath) {
14337
14561
  if (configPath === void 0) return {};
14338
- const extension = path16__default.default.extname(configPath);
14562
+ const extension = path19__default.default.extname(configPath);
14339
14563
  if (TS_CONFIG_EXTENSIONS.has(extension)) {
14340
14564
  throw new Error(
14341
14565
  "TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
@@ -14344,7 +14568,7 @@ async function loadConfig(configPath) {
14344
14568
  if (!CONFIG_EXTENSIONS.has(extension)) {
14345
14569
  throw new Error("Unsupported check config extension. Use .json, .js, .mjs, or .cjs.");
14346
14570
  }
14347
- const absolute = path16__default.default.resolve(configPath);
14571
+ const absolute = path19__default.default.resolve(configPath);
14348
14572
  if (extension === ".json") {
14349
14573
  const raw = await promises.readFile(absolute, "utf-8");
14350
14574
  return asConfig(JSON.parse(raw));
@@ -14493,10 +14717,10 @@ function printHuman(result) {
14493
14717
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
14494
14718
  }
14495
14719
  for (const finding of result.findings) {
14496
- const path23 = finding.evidence[0]?.path;
14720
+ const path25 = finding.evidence[0]?.path;
14497
14721
  const run = finding.evidence[0]?.runId;
14498
14722
  const runPrefix = run ? `[${run}] ` : "";
14499
- console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path23 ? ` (${path23})` : ""}`);
14723
+ console.log(`- ${runPrefix}${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
14500
14724
  }
14501
14725
  }
14502
14726
  function readErrorResult(error) {
@@ -14699,7 +14923,7 @@ function createViewerServer(options = {}) {
14699
14923
  return sendJson(res, 200, {
14700
14924
  ok: true,
14701
14925
  readOnly: true,
14702
- traceDir: path16__default.default.resolve(traceDir)
14926
+ traceDir: path19__default.default.resolve(traceDir)
14703
14927
  });
14704
14928
  }
14705
14929
  const td = new TraceDirectory({ dir: traceDir });
@@ -14717,7 +14941,7 @@ function createViewerServer(options = {}) {
14717
14941
  runId: meta2.runId,
14718
14942
  name: meta2.name,
14719
14943
  status: meta2.status,
14720
- file: path16__default.default.basename(meta2.filePath),
14944
+ file: path19__default.default.basename(meta2.filePath),
14721
14945
  startedAt: meta2.startedAt,
14722
14946
  durationMs: meta2.durationMs
14723
14947
  }))
@@ -14832,7 +15056,7 @@ function startViewerServer(options = {}) {
14832
15056
  resolve({
14833
15057
  host,
14834
15058
  port: resolvedPort,
14835
- traceDir: path16__default.default.resolve(traceDir),
15059
+ traceDir: path19__default.default.resolve(traceDir),
14836
15060
  url: `http://${host}:${resolvedPort}`
14837
15061
  });
14838
15062
  });
@@ -15037,10 +15261,10 @@ async function evalRun(input3, options = {}) {
15037
15261
  diagnostics: []
15038
15262
  };
15039
15263
  }
15040
- function evidenceForRun(run, path23) {
15041
- return [{ runId: run.runId, ...path23 !== void 0 ? { path: path23 } : {} }];
15264
+ function evidenceForRun(run, path25) {
15265
+ return [{ runId: run.runId, ...path25 !== void 0 ? { path: path25 } : {} }];
15042
15266
  }
15043
- function evidenceForEvent(event, path23) {
15267
+ function evidenceForEvent(event, path25) {
15044
15268
  return [
15045
15269
  {
15046
15270
  runId: event.runId,
@@ -15048,7 +15272,7 @@ function evidenceForEvent(event, path23) {
15048
15272
  ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
15049
15273
  kind: event.kind,
15050
15274
  name: event.name,
15051
- ...path23 !== void 0 ? { path: path23 } : {}
15275
+ ...path25 !== void 0 ? { path: path25 } : {}
15052
15276
  }
15053
15277
  ];
15054
15278
  }
@@ -15206,9 +15430,9 @@ function collectTextFields(nodes, keys, preferredKinds = []) {
15206
15430
  function tokenize(text) {
15207
15431
  return [...text.toLowerCase().matchAll(/[a-z0-9][a-z0-9'-]{2,}/g)].map((match) => match[0].replace(/^['-]+|['-]+$/g, "")).filter((token) => token.length > 2 && !STOP_WORDS.has(token));
15208
15432
  }
15209
- function firstEvidence(fields, run, path23) {
15433
+ function firstEvidence(fields, run, path25) {
15210
15434
  const first = fields[0];
15211
- return first === void 0 ? evidenceForRun(run, path23) : evidenceForEvent(first.node.event, first.path);
15435
+ return first === void 0 ? evidenceForRun(run, path25) : evidenceForEvent(first.node.event, first.path);
15212
15436
  }
15213
15437
  function collectSourceIds(nodes, keys) {
15214
15438
  const wanted = keySet(keys);
@@ -15585,8 +15809,8 @@ function renderEvalMarkdown(result) {
15585
15809
  if (result.findings.length > 0) {
15586
15810
  lines.push("", "## Findings");
15587
15811
  for (const finding of result.findings) {
15588
- const path23 = finding.evidence[0]?.path;
15589
- lines.push(`- ${finding.ruleId}: ${finding.message}${path23 ? ` (${path23})` : ""}`);
15812
+ const path25 = finding.evidence[0]?.path;
15813
+ lines.push(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
15590
15814
  }
15591
15815
  }
15592
15816
  return `${lines.join("\n")}
@@ -15633,7 +15857,7 @@ function asConfig2(value) {
15633
15857
  }
15634
15858
  async function loadConfig2(configPath) {
15635
15859
  if (configPath === void 0) return {};
15636
- const extension = path16__default.default.extname(configPath);
15860
+ const extension = path19__default.default.extname(configPath);
15637
15861
  if (TS_CONFIG_EXTENSIONS2.has(extension)) {
15638
15862
  throw new Error(
15639
15863
  "TypeScript eval configs require an explicit precompiled JavaScript config or future --config-loader support."
@@ -15642,7 +15866,7 @@ async function loadConfig2(configPath) {
15642
15866
  if (!CONFIG_EXTENSIONS2.has(extension)) {
15643
15867
  throw new Error("Unsupported eval config extension. Use .json, .js, .mjs, or .cjs.");
15644
15868
  }
15645
- const absolute = path16__default.default.resolve(configPath);
15869
+ const absolute = path19__default.default.resolve(configPath);
15646
15870
  if (extension === ".json") {
15647
15871
  const raw = await promises.readFile(absolute, "utf-8");
15648
15872
  return asConfig2(JSON.parse(raw));
@@ -15779,8 +16003,8 @@ function printHuman2(result) {
15779
16003
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
15780
16004
  }
15781
16005
  for (const finding of result.findings) {
15782
- const path23 = finding.evidence[0]?.path;
15783
- console.log(`- ${finding.ruleId}: ${finding.message}${path23 ? ` (${path23})` : ""}`);
16006
+ const path25 = finding.evidence[0]?.path;
16007
+ console.log(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
15784
16008
  }
15785
16009
  }
15786
16010
  function readErrorResult2(error) {
@@ -16018,8 +16242,8 @@ function printHuman3(result) {
16018
16242
  console.log(`- ${diagnostic4.code}: ${diagnostic4.message}`);
16019
16243
  }
16020
16244
  for (const finding of result.findings) {
16021
- const path23 = finding.evidence[0]?.path;
16022
- console.log(`- ${finding.ruleId}: ${finding.message}${path23 ? ` (${path23})` : ""}`);
16245
+ const path25 = finding.evidence[0]?.path;
16246
+ console.log(`- ${finding.ruleId}: ${finding.message}${path25 ? ` (${path25})` : ""}`);
16023
16247
  }
16024
16248
  console.log(`Note: ${result.note}`);
16025
16249
  }
@@ -16065,6 +16289,38 @@ function scanCommand(target, options = {}, stdin = process.stdin) {
16065
16289
  function verifySafeCommand(target, options = {}, stdin = process.stdin) {
16066
16290
  return safetyCommand("verify-safe", target, options, stdin);
16067
16291
  }
16292
+ function assessOpenedTrace(read, options = {}) {
16293
+ try {
16294
+ const rules = buildSafetyRules(options);
16295
+ const checkResult = runTraceChecks(
16296
+ { read },
16297
+ {
16298
+ rules,
16299
+ ...options.runId !== void 0 ? { runId: options.runId } : {},
16300
+ ...options.run !== void 0 ? { runId: options.run } : {}
16301
+ }
16302
+ );
16303
+ const detectorFindings = checkResult.diagnostics.length === 0 ? redactionDetectorFindings(read, checkResult.runId) : [];
16304
+ return resultFromParts({
16305
+ command: "verify-safe",
16306
+ format: checkResult.format,
16307
+ runId: checkResult.runId,
16308
+ findings: [...checkResult.findings, ...detectorFindings],
16309
+ diagnostics: [
16310
+ ...checkResult.diagnostics.map(diagnosticFromCheck),
16311
+ ...warningDiagnostics(read.warnings, read.unsupportedFields)
16312
+ ],
16313
+ warnings: read.warnings,
16314
+ unsupportedFields: read.unsupportedFields
16315
+ });
16316
+ } catch (error) {
16317
+ return messageStartsWithDash(error) ? invalidArgumentResult("verify-safe", error) : readErrorResult3("verify-safe", error);
16318
+ }
16319
+ }
16320
+ function messageStartsWithDash(error) {
16321
+ const message = error instanceof Error ? error.message : String(error);
16322
+ return message.startsWith("--");
16323
+ }
16068
16324
  var NOTE = "Generated locally by AgentInspect. Artifacts are best-effort summaries, not compliance or security certification.";
16069
16325
  var SAFETY_RULES = [
16070
16326
  createSafetyRawContentRule(),
@@ -16092,7 +16348,7 @@ function writeJson3(value) {
16092
16348
  function escapeHtml2(value) {
16093
16349
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
16094
16350
  }
16095
- function markdownTable(rows) {
16351
+ function markdownTable2(rows) {
16096
16352
  const lines = ["| Field | Value |", "| --- | --- |"];
16097
16353
  for (const [key, value] of rows) {
16098
16354
  lines.push(`| ${key} | ${value ?? "unknown"} |`);
@@ -16138,8 +16394,8 @@ function renderCheckSection(result) {
16138
16394
  `Diagnostics: ${result.diagnostics.length}`
16139
16395
  ];
16140
16396
  for (const finding of result.findings.slice(0, 10)) {
16141
- const path23 = finding.evidence[0]?.path ?? "(run)";
16142
- lines.push(`- ${finding.ruleId}: ${finding.message} (${path23})`);
16397
+ const path25 = finding.evidence[0]?.path ?? "(run)";
16398
+ lines.push(`- ${finding.ruleId}: ${finding.message} (${path25})`);
16143
16399
  }
16144
16400
  for (const diagnostic4 of result.diagnostics.slice(0, 10)) {
16145
16401
  lines.push(`- ${diagnostic4.code}: ${diagnostic4.message}`);
@@ -16154,7 +16410,7 @@ function renderMarkdown(trace, check, diff) {
16154
16410
  "",
16155
16411
  "## Trace",
16156
16412
  "",
16157
- markdownTable([
16413
+ markdownTable2([
16158
16414
  ["Format", trace.format],
16159
16415
  ["Run", trace.runId],
16160
16416
  ["Run status", trace.runStatus],
@@ -16217,8 +16473,8 @@ function renderHtml(trace, check, diff) {
16217
16473
  `;
16218
16474
  }
16219
16475
  async function writeArtifact(outputDir, relativePath, content, files) {
16220
- const outPath = path16__default.default.join(outputDir, relativePath);
16221
- await promises.mkdir(path16__default.default.dirname(outPath), { recursive: true });
16476
+ const outPath = path19__default.default.join(outputDir, relativePath);
16477
+ await promises.mkdir(path19__default.default.dirname(outPath), { recursive: true });
16222
16478
  await promises.writeFile(outPath, content, "utf-8");
16223
16479
  files.push(relativePath);
16224
16480
  }
@@ -16234,7 +16490,7 @@ function manifestStatus(check, diff) {
16234
16490
  return "ok";
16235
16491
  }
16236
16492
  async function artifactsCommand(target, options = {}, stdin = process.stdin) {
16237
- const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path16__default.default.resolve(options.outputDir.trim()) : "";
16493
+ const outputDir = options.outputDir !== void 0 && options.outputDir.trim() !== "" ? path19__default.default.resolve(options.outputDir.trim()) : "";
16238
16494
  if (outputDir === "") {
16239
16495
  console.error("--output-dir is required.");
16240
16496
  process.exitCode = 1;
@@ -16300,8 +16556,8 @@ async function artifactsCommand(target, options = {}, stdin = process.stdin) {
16300
16556
  await writeArtifact(outputDir, "report.html", renderHtml(trace, check, diff), files);
16301
16557
  const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
16302
16558
  if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
16303
- await promises.mkdir(path16__default.default.dirname(path16__default.default.resolve(summaryTarget)), { recursive: true });
16304
- await promises.appendFile(path16__default.default.resolve(summaryTarget), `
16559
+ await promises.mkdir(path19__default.default.dirname(path19__default.default.resolve(summaryTarget)), { recursive: true });
16560
+ await promises.appendFile(path19__default.default.resolve(summaryTarget), `
16305
16561
  ${renderMarkdown(trace, check, diff)}`, "utf-8");
16306
16562
  }
16307
16563
  const manifestFiles = [...files, "manifest.json"].sort((a, b) => a.localeCompare(b));
@@ -16320,10 +16576,10 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
16320
16576
  findings: diff?.findings.length ?? 0,
16321
16577
  diagnostics: diff?.diagnostics.length ?? 0
16322
16578
  },
16323
- ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path16__default.default.resolve(summaryTarget) } : {},
16579
+ ...summaryTarget !== void 0 && summaryTarget.trim() !== "" ? { githubSummary: path19__default.default.resolve(summaryTarget) } : {},
16324
16580
  note: NOTE
16325
16581
  };
16326
- await promises.writeFile(path16__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
16582
+ await promises.writeFile(path19__default.default.join(outputDir, "manifest.json"), writeJson3(manifest), "utf-8");
16327
16583
  if (options.json === true) {
16328
16584
  console.log(writeJson3(manifest).trimEnd());
16329
16585
  } else {
@@ -16334,1573 +16590,1901 @@ ${renderMarkdown(trace, check, diff)}`, "utf-8");
16334
16590
  }
16335
16591
  }
16336
16592
  }
16337
- function validateReporterArtifactPath(options) {
16338
- const outputDir = path16__default.default.resolve(options.outputDir);
16339
- const diagnostics = [];
16340
- const rawPath = options.relativePath;
16341
- if (rawPath.length === 0) {
16342
- diagnostics.push({
16343
- code: "artifact_path_empty",
16344
- severity: "error",
16345
- message: "Reporter artifact path must not be empty."
16346
- });
16347
- return { ok: false, outputDir, diagnostics };
16348
- }
16349
- if (rawPath.includes("\0")) {
16350
- diagnostics.push({
16351
- code: "invalid_artifact_path",
16352
- severity: "error",
16353
- message: "Reporter artifact path must not contain null bytes.",
16354
- target: rawPath
16355
- });
16356
- return { ok: false, outputDir, diagnostics };
16357
- }
16358
- if (path16__default.default.isAbsolute(rawPath) || path16__default.default.win32.isAbsolute(rawPath)) {
16359
- diagnostics.push({
16360
- code: "artifact_path_absolute",
16361
- severity: "error",
16362
- message: "Reporter artifact path must be relative.",
16363
- target: rawPath
16364
- });
16365
- return { ok: false, outputDir, diagnostics };
16366
- }
16367
- const normalized = path16__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
16368
- const segments = normalized.split("/");
16369
- if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
16370
- diagnostics.push({
16371
- code: "artifact_path_escape",
16372
- severity: "error",
16373
- message: "Reporter artifact path must stay under the output directory.",
16374
- target: rawPath
16375
- });
16376
- return { ok: false, outputDir, diagnostics };
16377
- }
16378
- const absolutePath = path16__default.default.resolve(outputDir, normalized);
16379
- const relFromOutput = path16__default.default.relative(outputDir, absolutePath);
16380
- if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path16__default.default.isAbsolute(relFromOutput)) {
16381
- diagnostics.push({
16382
- code: "artifact_path_escape",
16383
- severity: "error",
16384
- message: "Reporter artifact path resolved outside the output directory.",
16385
- target: rawPath
16386
- });
16387
- return { ok: false, outputDir, diagnostics };
16388
- }
16593
+
16594
+ // packages/cli/src/bundle.ts
16595
+ init_advanced();
16596
+
16597
+ // packages/core/src/workspace/types.ts
16598
+ var WORKSPACE_SCHEMA_VERSION = "1.0";
16599
+ var WORKSPACE_DIR_NAME = ".agent-inspect";
16600
+ var WORKSPACE_MANIFEST_FILENAME = "workspace.json";
16601
+
16602
+ // packages/core/src/workspace/manifest.ts
16603
+ var DEFAULT_WORKSPACE_LAYOUT = {
16604
+ traceDirs: ["runs"],
16605
+ reportsDir: "reports",
16606
+ artifactsDir: "artifacts",
16607
+ bundlesDir: "bundles",
16608
+ notesDir: "notes"
16609
+ };
16610
+ var DEFAULT_REDACTION_PROFILE = "share";
16611
+ var REDACTION_PROFILES = [
16612
+ "local",
16613
+ "share",
16614
+ "strict"
16615
+ ];
16616
+ var INDEX_TYPES = ["none", "sqlite", "custom"];
16617
+ var MAX_WORKSPACE_MANIFEST_BYTES = 64 * 1024;
16618
+ function createDefaultWorkspaceManifest(options) {
16619
+ const project = typeof options.project === "string" ? options.project.trim() : "";
16620
+ const index = {
16621
+ enabled: options.index?.enabled ?? false,
16622
+ type: options.index?.type ?? "none",
16623
+ ...options.index?.path !== void 0 ? { path: options.index.path } : {}
16624
+ };
16389
16625
  return {
16390
- ok: true,
16391
- outputDir,
16392
- relativePath: normalized,
16393
- absolutePath,
16394
- diagnostics
16626
+ schemaVersion: WORKSPACE_SCHEMA_VERSION,
16627
+ project,
16628
+ createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
16629
+ traceDirs: options.traceDirs ? [...options.traceDirs] : [...DEFAULT_WORKSPACE_LAYOUT.traceDirs],
16630
+ reportsDir: options.reportsDir ?? DEFAULT_WORKSPACE_LAYOUT.reportsDir,
16631
+ artifactsDir: options.artifactsDir ?? DEFAULT_WORKSPACE_LAYOUT.artifactsDir,
16632
+ bundlesDir: options.bundlesDir ?? DEFAULT_WORKSPACE_LAYOUT.bundlesDir,
16633
+ notesDir: options.notesDir ?? DEFAULT_WORKSPACE_LAYOUT.notesDir,
16634
+ redactionProfile: options.redactionProfile ?? DEFAULT_REDACTION_PROFILE,
16635
+ index
16395
16636
  };
16396
16637
  }
16397
-
16398
- // packages/cli/src/ci-summary.ts
16399
- var NOTE2 = "Generated locally by AgentInspect from reporter artifact manifests. Trace contents are not embedded.";
16400
- var MAX_TEXT = 180;
16401
- var FRAMEWORKS = /* @__PURE__ */ new Set(["vitest", "jest", "manual"]);
16402
- var STATUSES = /* @__PURE__ */ new Set(["passed", "failed", "skipped", "todo"]);
16403
- var ARTIFACT_KINDS = /* @__PURE__ */ new Set(["trace", "report", "eval", "redaction", "summary"]);
16404
- var ARTIFACT_FORMATS = /* @__PURE__ */ new Set(["json", "jsonl", "md", "html"]);
16405
- var REDACTION_PROFILES = /* @__PURE__ */ new Set(["local", "share", "strict"]);
16406
- function stable6(value) {
16407
- if (Array.isArray(value)) return value.map(stable6);
16408
- if (value === null || typeof value !== "object") return value;
16409
- const record = value;
16410
- return Object.fromEntries(
16411
- Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable6(record[key])])
16412
- );
16413
- }
16414
- function writeJson4(value) {
16415
- return `${JSON.stringify(stable6(value), null, 2)}
16416
- `;
16417
- }
16418
- function isObject(value) {
16638
+ function isPlainObject(value) {
16419
16639
  return typeof value === "object" && value !== null && !Array.isArray(value);
16420
16640
  }
16421
- function readString(value, label) {
16422
- if (typeof value !== "string" || value.trim() === "") {
16423
- throw new Error(`${label} must be a non-empty string.`);
16424
- }
16425
- return safeText(value);
16641
+ function isSafeRelativeWorkspacePath(p) {
16642
+ if (typeof p !== "string") return false;
16643
+ const trimmed = p.trim();
16644
+ if (trimmed === "") return false;
16645
+ if (trimmed.startsWith("/") || trimmed.startsWith("\\")) return false;
16646
+ if (/^[a-zA-Z]:/.test(trimmed)) return false;
16647
+ const segments = trimmed.split(/[/\\]+/);
16648
+ return !segments.some((seg) => seg === "..");
16426
16649
  }
16427
- function readOptionalString(value) {
16428
- return typeof value === "string" && value.trim() !== "" ? safeText(value) : void 0;
16429
- }
16430
- function readFramework(value) {
16431
- const framework = readString(value, "manifest.framework");
16432
- if (!FRAMEWORKS.has(framework)) {
16433
- throw new Error(`Unsupported reporter framework: ${framework}.`);
16650
+ function validateDirField(value, field, errors) {
16651
+ if (typeof value !== "string" || value.trim() === "") {
16652
+ errors.push(`${field} must be a non-empty string`);
16653
+ return;
16434
16654
  }
16435
- return framework;
16436
- }
16437
- function readStatus(value) {
16438
- const status = readString(value, "result.status");
16439
- if (!STATUSES.has(status)) {
16440
- throw new Error(`Unsupported reporter test status: ${status}.`);
16655
+ if (!isSafeRelativeWorkspacePath(value)) {
16656
+ errors.push(
16657
+ `${field} must be a relative path inside the workspace (no absolute paths or ".." traversal)`
16658
+ );
16441
16659
  }
16442
- return status;
16443
16660
  }
16444
- function readArtifact(value, index) {
16445
- if (!isObject(value)) throw new Error(`manifest.artifacts[${index}] must be an object.`);
16446
- const kind = readString(value.kind, `manifest.artifacts[${index}].kind`);
16447
- const format = readString(value.format, `manifest.artifacts[${index}].format`);
16448
- const redactionProfile = readString(
16449
- value.redactionProfile,
16450
- `manifest.artifacts[${index}].redactionProfile`
16451
- );
16452
- if (!ARTIFACT_KINDS.has(kind)) throw new Error(`Unsupported artifact kind: ${kind}.`);
16453
- if (!ARTIFACT_FORMATS.has(format)) throw new Error(`Unsupported artifact format: ${format}.`);
16454
- if (!REDACTION_PROFILES.has(redactionProfile)) {
16455
- throw new Error(`Unsupported artifact redaction profile: ${redactionProfile}.`);
16661
+ function validateIndex(value, errors) {
16662
+ if (!isPlainObject(value)) {
16663
+ errors.push("index must be an object");
16664
+ return void 0;
16456
16665
  }
16457
- const artifactPath = readString(value.path, `manifest.artifacts[${index}].path`);
16458
- const pathCheck = validateReporterArtifactPath({
16459
- outputDir: process.cwd(),
16460
- relativePath: artifactPath
16461
- });
16462
- if (!pathCheck.ok || pathCheck.relativePath === void 0) {
16463
- throw new Error(`Unsafe reporter artifact path: ${artifactPath}.`);
16666
+ if (typeof value.enabled !== "boolean") {
16667
+ errors.push("index.enabled must be a boolean");
16464
16668
  }
16669
+ if (!INDEX_TYPES.includes(value.type)) {
16670
+ errors.push(`index.type must be one of: ${INDEX_TYPES.join(", ")}`);
16671
+ }
16672
+ if (value.path !== void 0 && !isSafeRelativeWorkspacePath(value.path)) {
16673
+ errors.push(
16674
+ 'index.path must be a relative path inside the workspace (no absolute paths or ".." traversal)'
16675
+ );
16676
+ }
16677
+ if (errors.length > 0) return void 0;
16465
16678
  return {
16466
- kind,
16467
- path: pathCheck.relativePath,
16468
- format,
16469
- redactionProfile
16470
- };
16471
- }
16472
- function readArtifacts(value, label) {
16473
- if (value === void 0) return [];
16474
- if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
16475
- return value.map((item, index) => readArtifact(item, index));
16476
- }
16477
- function readDiagnosticsCount(value, label) {
16478
- if (value === void 0) return 0;
16479
- if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
16480
- return value.length;
16481
- }
16482
- function readResult(value, index) {
16483
- if (!isObject(value)) throw new Error(`manifest.results[${index}] must be an object.`);
16484
- const file = readOptionalString(value.file);
16485
- const tracePath = readOptionalString(value.tracePath);
16486
- return {
16487
- testId: readString(value.testId, `manifest.results[${index}].testId`),
16488
- name: readString(value.name, `manifest.results[${index}].name`),
16489
- ...file === void 0 ? {} : { file },
16490
- status: readStatus(value.status),
16491
- ...tracePath === void 0 ? {} : { tracePath },
16492
- artifacts: readArtifacts(value.artifacts, `manifest.results[${index}].artifacts`),
16493
- diagnostics: readDiagnosticsCount(
16494
- value.diagnostics,
16495
- `manifest.results[${index}].diagnostics`
16496
- )
16679
+ enabled: value.enabled,
16680
+ type: value.type,
16681
+ ...value.path !== void 0 ? { path: value.path } : {}
16497
16682
  };
16498
16683
  }
16499
- function readManifestDocument(value) {
16500
- if (!isObject(value)) throw new Error("Reporter manifest file must contain a JSON object.");
16501
- const candidate = isObject(value.manifest) ? value.manifest : value;
16502
- if (!isObject(candidate)) throw new Error("Reporter manifest must be a JSON object.");
16503
- const schemaVersion = readString(candidate.schemaVersion, "manifest.schemaVersion");
16504
- if (schemaVersion !== "0.1") {
16505
- throw new Error(`Unsupported reporter manifest schemaVersion: ${schemaVersion}.`);
16684
+ function validateWorkspaceManifest(input3) {
16685
+ const errors = [];
16686
+ const warnings = [];
16687
+ if (!isPlainObject(input3)) {
16688
+ return { ok: false, errors: ["manifest must be an object"], warnings };
16506
16689
  }
16507
- if (!Array.isArray(candidate.results)) {
16508
- throw new Error("manifest.results must be an array.");
16690
+ if (input3.schemaVersion !== WORKSPACE_SCHEMA_VERSION) {
16691
+ errors.push(
16692
+ `schemaVersion must be "${WORKSPACE_SCHEMA_VERSION}" (received ${JSON.stringify(
16693
+ input3.schemaVersion
16694
+ )})`
16695
+ );
16696
+ }
16697
+ if (typeof input3.project !== "string" || input3.project.trim() === "") {
16698
+ errors.push("project must be a non-empty string");
16699
+ }
16700
+ if (typeof input3.createdAt !== "string" || input3.createdAt.trim() === "") {
16701
+ errors.push("createdAt must be a non-empty ISO-8601 string");
16702
+ } else if (Number.isNaN(Date.parse(input3.createdAt))) {
16703
+ errors.push("createdAt must be a valid ISO-8601 date string");
16704
+ }
16705
+ if (!Array.isArray(input3.traceDirs) || input3.traceDirs.length === 0) {
16706
+ errors.push("traceDirs must be a non-empty array");
16707
+ } else {
16708
+ input3.traceDirs.forEach((dir, i) => {
16709
+ if (typeof dir !== "string" || dir.trim() === "") {
16710
+ errors.push(`traceDirs[${i}] must be a non-empty string`);
16711
+ } else if (!isSafeRelativeWorkspacePath(dir)) {
16712
+ errors.push(
16713
+ `traceDirs[${i}] must be a relative path inside the workspace (no absolute paths or ".." traversal)`
16714
+ );
16715
+ }
16716
+ });
16717
+ }
16718
+ validateDirField(input3.reportsDir, "reportsDir", errors);
16719
+ validateDirField(input3.artifactsDir, "artifactsDir", errors);
16720
+ validateDirField(input3.bundlesDir, "bundlesDir", errors);
16721
+ validateDirField(input3.notesDir, "notesDir", errors);
16722
+ if (!REDACTION_PROFILES.includes(input3.redactionProfile)) {
16723
+ errors.push(`redactionProfile must be one of: ${REDACTION_PROFILES.join(", ")}`);
16724
+ }
16725
+ const indexErrors = [];
16726
+ const index = validateIndex(input3.index, indexErrors);
16727
+ errors.push(...indexErrors);
16728
+ if (index && index.type !== "none" && !index.enabled) {
16729
+ warnings.push(`index.type is "${index.type}" but index.enabled is false`);
16730
+ }
16731
+ if (errors.length > 0 || index === void 0) {
16732
+ return { ok: false, errors, warnings };
16509
16733
  }
16510
- const artifacts = readArtifacts(candidate.artifacts, "manifest.artifacts");
16511
- const results = candidate.results.map((item, index) => readResult(item, index));
16512
- const diagnostics = readDiagnosticsCount(candidate.diagnostics, "manifest.diagnostics");
16513
16734
  const manifest = {
16514
- framework: readFramework(candidate.framework),
16515
- generatedAt: readString(candidate.generatedAt, "manifest.generatedAt"),
16516
- results,
16517
- artifacts
16518
- };
16519
- return {
16520
- packageName: readOptionalString(value.package),
16521
- manifest,
16522
- diagnostics
16735
+ schemaVersion: WORKSPACE_SCHEMA_VERSION,
16736
+ project: input3.project.trim(),
16737
+ createdAt: input3.createdAt,
16738
+ traceDirs: input3.traceDirs.map((d) => d.trim()),
16739
+ reportsDir: input3.reportsDir.trim(),
16740
+ artifactsDir: input3.artifactsDir.trim(),
16741
+ bundlesDir: input3.bundlesDir.trim(),
16742
+ notesDir: input3.notesDir.trim(),
16743
+ redactionProfile: input3.redactionProfile,
16744
+ index
16523
16745
  };
16746
+ return { ok: true, manifest, errors, warnings };
16524
16747
  }
16525
- function cwdRelative(filePath) {
16526
- const relative = path16__default.default.relative(process.cwd(), path16__default.default.resolve(filePath)).replace(/\\/g, "/");
16527
- if (relative === "" || relative.startsWith("../") || path16__default.default.isAbsolute(relative)) {
16528
- return path16__default.default.basename(filePath);
16748
+ function parseWorkspaceManifest(json) {
16749
+ const warnings = [];
16750
+ if (typeof json !== "string") {
16751
+ return { ok: false, errors: ["manifest input must be a string"], warnings };
16529
16752
  }
16530
- return relative;
16753
+ if (json.length > MAX_WORKSPACE_MANIFEST_BYTES) {
16754
+ return {
16755
+ ok: false,
16756
+ errors: [
16757
+ `manifest exceeds maximum size of ${MAX_WORKSPACE_MANIFEST_BYTES} bytes`
16758
+ ],
16759
+ warnings
16760
+ };
16761
+ }
16762
+ let parsed;
16763
+ try {
16764
+ parsed = JSON.parse(json);
16765
+ } catch {
16766
+ return { ok: false, errors: ["manifest is not valid JSON"], warnings };
16767
+ }
16768
+ return validateWorkspaceManifest(parsed);
16531
16769
  }
16532
- async function readReporterManifest(filePath) {
16533
- const absolute = path16__default.default.resolve(filePath);
16534
- const raw = await promises.readFile(absolute, "utf-8");
16535
- const document = readManifestDocument(JSON.parse(raw));
16536
- const manifest = document.manifest;
16537
- const results = manifest.results.map((result) => ({
16538
- testId: safeText(result.testId),
16539
- name: safeText(result.name),
16540
- ...result.file === void 0 ? {} : { file: safeText(path16__default.default.basename(result.file)) },
16541
- status: result.status,
16542
- ...result.tracePath === void 0 ? {} : { tracePath: safeText(path16__default.default.basename(result.tracePath)) },
16543
- artifacts: result.artifacts,
16544
- diagnostics: result.diagnostics
16545
- }));
16546
- return {
16547
- ...document.packageName === void 0 ? {} : { packageName: document.packageName },
16548
- manifestFile: cwdRelative(absolute),
16549
- framework: manifest.framework,
16550
- generatedAt: manifest.generatedAt,
16551
- results,
16552
- artifacts: manifest.artifacts,
16553
- diagnostics: document.diagnostics
16554
- };
16770
+ function serializeWorkspaceManifest(manifest) {
16771
+ return `${JSON.stringify(manifest, null, 2)}
16772
+ `;
16555
16773
  }
16556
- function summarize3(manifests) {
16557
- const summary = {
16558
- manifests: manifests.length,
16559
- tests: 0,
16560
- failed: 0,
16561
- passed: 0,
16562
- skipped: 0,
16563
- todo: 0,
16564
- artifacts: 0,
16565
- diagnostics: 0
16566
- };
16567
- for (const manifest of manifests) {
16568
- summary.artifacts += manifest.artifacts.length;
16569
- summary.diagnostics += manifest.diagnostics;
16570
- for (const result of manifest.results) {
16571
- summary.tests += 1;
16572
- if (result.status === "failed") summary.failed += 1;
16573
- else if (result.status === "passed") summary.passed += 1;
16574
- else if (result.status === "skipped") summary.skipped += 1;
16575
- else summary.todo += 1;
16576
- summary.artifacts += result.artifacts.length;
16577
- summary.diagnostics += result.diagnostics;
16578
- }
16579
- }
16774
+ var INDEX_DIR_NAME = "index";
16775
+ function resolveWorkspaceLocation(cwd = process.cwd()) {
16776
+ const projectRoot = path19__default.default.resolve(cwd);
16777
+ const workspaceDir = path19__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
16580
16778
  return {
16581
- status: summary.failed > 0 ? "failed" : summary.diagnostics > 0 ? "warning" : "ok",
16582
- manifests,
16583
- summary,
16584
- note: NOTE2
16779
+ projectRoot,
16780
+ workspaceDir,
16781
+ manifestPath: path19__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
16585
16782
  };
16586
16783
  }
16587
- function markdownCell(value) {
16588
- return safeText(String(value ?? "unknown")).replaceAll("|", "\\|").replace(/\r?\n/g, " ");
16784
+ function resolveInsideWorkspace(workspaceDir, relative) {
16785
+ const base = path19__default.default.resolve(workspaceDir);
16786
+ const resolved = path19__default.default.resolve(base, relative);
16787
+ const rel = path19__default.default.relative(base, resolved);
16788
+ if (rel === "" || rel === "." || !rel.startsWith("..") && !path19__default.default.isAbsolute(rel)) {
16789
+ return resolved;
16790
+ }
16791
+ throw new Error(
16792
+ `Workspace path "${relative}" resolves outside the workspace directory`
16793
+ );
16589
16794
  }
16590
- function renderMarkdown2(result) {
16591
- const lines = [
16592
- "# AgentInspect CI Summary",
16593
- "",
16594
- NOTE2,
16595
- "",
16596
- "| Field | Value |",
16597
- "| --- | --- |",
16598
- `| Status | ${result.status} |`,
16599
- `| Manifests | ${result.summary.manifests} |`,
16600
- `| Tests | ${result.summary.tests} |`,
16601
- `| Failed | ${result.summary.failed} |`,
16602
- `| Passed | ${result.summary.passed} |`,
16603
- `| Skipped | ${result.summary.skipped} |`,
16604
- `| Todo | ${result.summary.todo} |`,
16605
- `| Artifacts | ${result.summary.artifacts} |`,
16606
- `| Diagnostics | ${result.summary.diagnostics} |`,
16607
- "",
16608
- "## Tests",
16609
- "",
16610
- "| Framework | Status | Test | File | Trace | Artifacts |",
16611
- "| --- | --- | --- | --- | --- | --- |"
16612
- ];
16613
- const rows = result.manifests.flatMap(
16614
- (manifest) => manifest.results.map((test) => ({
16615
- framework: manifest.framework,
16616
- test
16617
- }))
16618
- );
16619
- if (rows.length === 0) {
16620
- lines.push("| unknown | unknown | No tests found | unknown | unknown | 0 |");
16621
- } else {
16622
- for (const row of rows) {
16623
- lines.push(
16624
- `| ${markdownCell(row.framework)} | ${markdownCell(row.test.status)} | ${markdownCell(row.test.name)} | ${markdownCell(row.test.file)} | ${markdownCell(row.test.tracePath)} | ${row.test.artifacts.length} |`
16625
- );
16626
- }
16627
- }
16628
- lines.push("", "## Manifests", "", "| Framework | File | Generated | Artifacts |", "| --- | --- | --- | --- |");
16629
- for (const manifest of result.manifests) {
16630
- lines.push(
16631
- `| ${markdownCell(manifest.framework)} | ${markdownCell(manifest.manifestFile)} | ${markdownCell(manifest.generatedAt)} | ${manifest.artifacts.length} |`
16632
- );
16795
+ async function pathExists(p) {
16796
+ try {
16797
+ await promises.access(p);
16798
+ return true;
16799
+ } catch {
16800
+ return false;
16633
16801
  }
16634
- lines.push("", "## Artifacts", "", "| Framework | Kind | Path | Format | Profile |", "| --- | --- | --- | --- | --- |");
16635
- const artifacts = result.manifests.flatMap(
16636
- (manifest) => manifest.artifacts.map((artifact) => ({ framework: manifest.framework, artifact }))
16637
- );
16638
- if (artifacts.length === 0) {
16639
- lines.push("| unknown | unknown | No artifacts found | unknown | unknown |");
16640
- } else {
16641
- for (const row of artifacts) {
16642
- lines.push(
16643
- `| ${markdownCell(row.framework)} | ${markdownCell(row.artifact.kind)} | ${markdownCell(row.artifact.path)} | ${markdownCell(row.artifact.format)} | ${markdownCell(row.artifact.redactionProfile)} |`
16644
- );
16645
- }
16802
+ }
16803
+ async function isWritable(p) {
16804
+ try {
16805
+ await promises.access(p, fs.constants.W_OK);
16806
+ return true;
16807
+ } catch {
16808
+ return false;
16646
16809
  }
16647
- lines.push("");
16648
- return `${lines.join("\n")}
16649
- `;
16650
16810
  }
16651
- function safeText(value) {
16652
- const compact = value.replace(/\s+/g, " ").trim();
16653
- const redacted = compact.replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(
16654
- /\b(?:api[_-]?key|authorization|token|secret|password)\s*[:=]\s*[^,\s]+/gi,
16655
- "$1=[REDACTED]"
16656
- );
16657
- if (redacted.length <= MAX_TEXT) return redacted;
16658
- return `${redacted.slice(0, MAX_TEXT - 12)}...[truncated]`;
16811
+ async function listJsonl(dir) {
16812
+ try {
16813
+ const entries = await promises.readdir(dir);
16814
+ return entries.filter((f) => f.endsWith(".jsonl"));
16815
+ } catch {
16816
+ return [];
16817
+ }
16659
16818
  }
16660
- async function ciSummaryCommand(manifestPaths, options = {}) {
16661
- if (manifestPaths.length === 0) {
16662
- console.error("At least one reporter manifest path is required.");
16663
- process.exitCode = 1;
16664
- return;
16819
+ async function countFiles(dir) {
16820
+ try {
16821
+ const entries = await promises.readdir(dir, { withFileTypes: true });
16822
+ return entries.filter((e) => e.isFile()).length;
16823
+ } catch {
16824
+ return 0;
16665
16825
  }
16666
- let result;
16826
+ }
16827
+ async function readWorkspaceManifestFile(location) {
16828
+ let raw;
16667
16829
  try {
16668
- const manifests = [];
16669
- for (const manifestPath of manifestPaths) {
16670
- manifests.push(await readReporterManifest(manifestPath));
16671
- }
16672
- manifests.sort((a, b) => a.manifestFile.localeCompare(b.manifestFile));
16673
- result = summarize3(manifests);
16674
- } catch (error) {
16675
- const message = error instanceof Error ? error.message : String(error);
16676
- console.error(`[AgentInspect] ci-summary failed: ${message}`);
16677
- process.exitCode = 1;
16678
- return;
16830
+ raw = await promises.readFile(location.manifestPath, "utf-8");
16831
+ } catch {
16832
+ return { exists: false, ok: false, errors: ["workspace.json not found"], warnings: [] };
16679
16833
  }
16680
- const markdown = renderMarkdown2(result);
16681
- const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path16__default.default.resolve(options.output.trim()) : void 0;
16682
- if (outputPath !== void 0) {
16683
- await promises.mkdir(path16__default.default.dirname(outputPath), { recursive: true });
16684
- await promises.writeFile(outputPath, markdown, "utf-8");
16834
+ const parsed = parseWorkspaceManifest(raw);
16835
+ return {
16836
+ exists: true,
16837
+ ok: parsed.ok,
16838
+ ...parsed.manifest ? { manifest: parsed.manifest } : {},
16839
+ errors: parsed.errors,
16840
+ warnings: parsed.warnings
16841
+ };
16842
+ }
16843
+ async function createWorkspace(options = {}) {
16844
+ const location = resolveWorkspaceLocation(options.cwd);
16845
+ const dryRun = options.dryRun === true;
16846
+ const existing = await readWorkspaceManifestFile(location);
16847
+ const topLevelTraces = await listJsonl(location.workspaceDir);
16848
+ const detectedExistingTraces = topLevelTraces.length > 0;
16849
+ let manifest;
16850
+ let created;
16851
+ let adopted;
16852
+ if (existing.exists && existing.ok && existing.manifest) {
16853
+ manifest = existing.manifest;
16854
+ created = false;
16855
+ adopted = true;
16856
+ } else {
16857
+ const project = options.project?.trim() || path19__default.default.basename(location.projectRoot) || "workspace";
16858
+ const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
16859
+ manifest = createDefaultWorkspaceManifest({
16860
+ project,
16861
+ traceDirs,
16862
+ ...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {}
16863
+ });
16864
+ created = true;
16865
+ adopted = detectedExistingTraces || existing.exists && !existing.ok;
16685
16866
  }
16686
- const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
16687
- if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
16688
- const summaryPath = path16__default.default.resolve(summaryTarget);
16689
- await promises.mkdir(path16__default.default.dirname(summaryPath), { recursive: true });
16690
- await promises.appendFile(summaryPath, `
16691
- ${markdown}`, "utf-8");
16867
+ const relDirs = uniqueDirs([
16868
+ ...manifest.traceDirs.filter((d) => d !== "."),
16869
+ manifest.reportsDir,
16870
+ manifest.artifactsDir,
16871
+ manifest.bundlesDir,
16872
+ manifest.notesDir,
16873
+ INDEX_DIR_NAME
16874
+ ]);
16875
+ const createdDirs = [];
16876
+ for (const rel of relDirs) {
16877
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
16878
+ if (await pathExists(abs)) continue;
16879
+ createdDirs.push(rel);
16880
+ if (!dryRun) await promises.mkdir(abs, { recursive: true });
16692
16881
  }
16693
- if (options.json === true) {
16694
- console.log(writeJson4(result).trimEnd());
16695
- } else if (outputPath !== void 0) {
16696
- console.log(`Wrote AgentInspect CI summary to ${outputPath}`);
16697
- console.log(`Status: ${result.status}`);
16698
- } else {
16699
- console.log(markdown.trimEnd());
16882
+ if (!dryRun && created) {
16883
+ await promises.mkdir(location.workspaceDir, { recursive: true });
16884
+ await promises.writeFile(location.manifestPath, serializeWorkspaceManifest(manifest), "utf-8");
16700
16885
  }
16886
+ return {
16887
+ location,
16888
+ manifest,
16889
+ created,
16890
+ adopted,
16891
+ createdDirs,
16892
+ detectedExistingTraces,
16893
+ dryRun
16894
+ };
16701
16895
  }
16702
- var CONFIG_FILE = "agent-inspect.config.ts";
16703
- var TRACE_DIR = ".agent-inspect";
16704
- var GITKEEP = ".agent-inspect/.gitkeep";
16705
- function normalizeFramework(value) {
16706
- const raw = (value ?? "custom").trim();
16707
- if (raw === "ai-sdk" || raw === "openai-agents" || raw === "langchain" || raw === "custom") {
16708
- return raw;
16896
+ function uniqueDirs(dirs) {
16897
+ const seen = /* @__PURE__ */ new Set();
16898
+ const out = [];
16899
+ for (const d of dirs) {
16900
+ const t = d.trim();
16901
+ if (t === "" || t === "." || seen.has(t)) continue;
16902
+ seen.add(t);
16903
+ out.push(t);
16709
16904
  }
16710
- throw new Error(
16711
- "Unsupported --framework value. Use ai-sdk, openai-agents, langchain, or custom."
16712
- );
16905
+ return out;
16713
16906
  }
16714
- function configTemplate(framework) {
16715
- const base = `/**
16716
- * AgentInspect local config (metadata-only capture by default).
16717
- * See https://github.com/rajudandigam/agent-inspect/blob/main/docs/SAFE-TRACE-SHARING.md
16718
- */
16719
- export const agentInspectConfig = {
16720
- traceDir: ".agent-inspect",
16721
- enabled: process.env.AGENT_INSPECT !== "0",
16722
- redactionProfile: "local" as const,
16723
- };
16724
- `;
16725
- if (framework === "custom") return base;
16726
- return `${base}
16727
- export const framework = "${framework}" as const;
16728
- `;
16907
+ async function getWorkspaceStatus(location, manifest) {
16908
+ let traceFiles = 0;
16909
+ for (const rel of manifest.traceDirs) {
16910
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
16911
+ traceFiles += (await listJsonl(abs)).length;
16912
+ }
16913
+ const reports = await countFiles(
16914
+ resolveInsideWorkspace(location.workspaceDir, manifest.reportsDir)
16915
+ );
16916
+ const artifacts = await countFiles(
16917
+ resolveInsideWorkspace(location.workspaceDir, manifest.artifactsDir)
16918
+ );
16919
+ const bundles = await countFiles(
16920
+ resolveInsideWorkspace(location.workspaceDir, manifest.bundlesDir)
16921
+ );
16922
+ const notes = await countFiles(
16923
+ resolveInsideWorkspace(location.workspaceDir, manifest.notesDir)
16924
+ );
16925
+ const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
16926
+ return {
16927
+ project: manifest.project,
16928
+ traceFiles,
16929
+ reports,
16930
+ artifacts,
16931
+ bundles,
16932
+ notes,
16933
+ index: {
16934
+ enabled: manifest.index.enabled,
16935
+ type: manifest.index.type,
16936
+ exists: await pathExists(indexPath2)
16937
+ }
16938
+ };
16729
16939
  }
16730
- function demoTemplate(framework) {
16731
- switch (framework) {
16732
- case "ai-sdk":
16733
- return `/**
16734
- * AI SDK starter \u2014 metadata-only telemetry (no real model calls in this demo).
16735
- * Install: npm install agent-inspect @agent-inspect/ai-sdk ai
16736
- */
16737
- import { inspectRun, step } from "agent-inspect";
16738
-
16739
- async function main() {
16740
- await inspectRun("ai-sdk-demo", async () => {
16741
- await step.tool("mock-generate", async () => ({ text: "ok" }));
16742
- }, { traceDir: ".agent-inspect", silent: true });
16743
- console.log("Trace written to .agent-inspect/");
16940
+ async function doctorWorkspace(location) {
16941
+ const checks2 = [];
16942
+ const manifestResult = await readWorkspaceManifestFile(location);
16943
+ if (!manifestResult.exists) {
16944
+ checks2.push({
16945
+ id: "manifest",
16946
+ status: "fail",
16947
+ message: "workspace.json not found (run `agent-inspect workspace init`)"
16948
+ });
16949
+ return { ok: false, checks: checks2 };
16950
+ }
16951
+ if (!manifestResult.ok || !manifestResult.manifest) {
16952
+ checks2.push({
16953
+ id: "manifest",
16954
+ status: "fail",
16955
+ message: `workspace.json is invalid: ${manifestResult.errors.join("; ")}`
16956
+ });
16957
+ return { ok: false, checks: checks2 };
16958
+ }
16959
+ const manifest = manifestResult.manifest;
16960
+ checks2.push({ id: "manifest", status: "pass", message: "workspace.json is valid" });
16961
+ for (const warning of manifestResult.warnings) {
16962
+ checks2.push({ id: "manifest-warning", status: "warn", message: warning });
16963
+ }
16964
+ const dirFields = [
16965
+ ...manifest.traceDirs.filter((d) => d !== ".").map((d, i) => [`traceDir[${i}]`, d]),
16966
+ ["reportsDir", manifest.reportsDir],
16967
+ ["artifactsDir", manifest.artifactsDir],
16968
+ ["bundlesDir", manifest.bundlesDir],
16969
+ ["notesDir", manifest.notesDir]
16970
+ ];
16971
+ for (const [id, rel] of dirFields) {
16972
+ let abs;
16973
+ try {
16974
+ abs = resolveInsideWorkspace(location.workspaceDir, rel);
16975
+ } catch (error) {
16976
+ checks2.push({
16977
+ id,
16978
+ status: "fail",
16979
+ message: error instanceof Error ? error.message : String(error)
16980
+ });
16981
+ continue;
16982
+ }
16983
+ if (!await pathExists(abs)) {
16984
+ checks2.push({ id, status: "warn", message: `${rel}/ does not exist yet` });
16985
+ } else if (!await isWritable(abs)) {
16986
+ checks2.push({ id, status: "fail", message: `${rel}/ is not writable` });
16987
+ } else {
16988
+ checks2.push({ id, status: "pass", message: `${rel}/ is present and writable` });
16989
+ }
16990
+ }
16991
+ let newestTraceMtime = 0;
16992
+ for (const rel of manifest.traceDirs) {
16993
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
16994
+ for (const file of await listJsonl(abs)) {
16995
+ try {
16996
+ const s = await promises.stat(path19__default.default.join(abs, file));
16997
+ newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
16998
+ } catch {
16999
+ checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
17000
+ }
17001
+ }
17002
+ }
17003
+ if (manifest.index.enabled) {
17004
+ const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
17005
+ if (!await pathExists(indexPath2)) {
17006
+ checks2.push({ id: "index", status: "warn", message: "index enabled but not built" });
17007
+ } else {
17008
+ try {
17009
+ const s = await promises.stat(indexPath2);
17010
+ if (newestTraceMtime > s.mtimeMs) {
17011
+ checks2.push({ id: "index", status: "warn", message: "index is stale (traces are newer)" });
17012
+ } else {
17013
+ checks2.push({ id: "index", status: "pass", message: "index is present" });
17014
+ }
17015
+ } catch {
17016
+ checks2.push({ id: "index", status: "warn", message: "cannot stat index" });
17017
+ }
17018
+ }
17019
+ }
17020
+ const ok = !checks2.some((c) => c.status === "fail");
17021
+ return { ok, checks: checks2 };
16744
17022
  }
16745
-
16746
- main().catch((error) => {
16747
- console.error(error);
16748
- process.exitCode = 1;
16749
- });
16750
- `;
16751
- case "openai-agents":
16752
- return `/**
16753
- * OpenAI Agents starter \u2014 use @agent-inspect/openai-agents for local-only processors.
16754
- * This demo uses manual steps (no API keys).
16755
- */
16756
- import { inspectRun, step } from "agent-inspect";
16757
-
16758
- async function main() {
16759
- await inspectRun("openai-agents-demo", async () => {
16760
- await step.tool("mock-agent-run", async () => "ok");
16761
- }, { traceDir: ".agent-inspect", silent: true });
16762
- console.log("Trace written to .agent-inspect/");
17023
+ async function cleanWorkspace(location, manifest, options = {}) {
17024
+ const dryRun = options.confirm !== true;
17025
+ const targets = uniqueDirs([
17026
+ manifest.reportsDir,
17027
+ manifest.artifactsDir,
17028
+ manifest.bundlesDir,
17029
+ manifest.index.path ?? INDEX_DIR_NAME
17030
+ ]);
17031
+ const removed = [];
17032
+ for (const rel of targets) {
17033
+ const abs = resolveInsideWorkspace(location.workspaceDir, rel);
17034
+ let entries;
17035
+ try {
17036
+ entries = await promises.readdir(abs);
17037
+ } catch {
17038
+ continue;
17039
+ }
17040
+ for (const entry of entries) {
17041
+ const relPath = `${rel}/${entry}`;
17042
+ removed.push(relPath);
17043
+ if (!dryRun) {
17044
+ await promises.rm(path19__default.default.join(abs, entry), { recursive: true, force: true });
17045
+ }
17046
+ }
17047
+ }
17048
+ return { dryRun, removed };
16763
17049
  }
16764
17050
 
16765
- main().catch((error) => {
16766
- console.error(error);
16767
- process.exitCode = 1;
16768
- });
16769
- `;
16770
- case "langchain":
16771
- return `/**
16772
- * LangChain starter \u2014 wire @agent-inspect/langchain callbacks in your app.
16773
- * This demo uses manual steps (no API keys).
16774
- */
16775
- import { inspectRun, step } from "agent-inspect";
16776
-
16777
- async function main() {
16778
- await inspectRun("langchain-demo", async () => {
16779
- await step.tool("mock-chain", async () => "ok");
16780
- }, { traceDir: ".agent-inspect", silent: true });
16781
- console.log("Trace written to .agent-inspect/");
17051
+ // packages/cli/src/bundle.ts
17052
+ var BUNDLE_NOTE2 = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification.";
17053
+ function stable6(value) {
17054
+ if (Array.isArray(value)) return value.map(stable6);
17055
+ if (value === null || typeof value !== "object") return value;
17056
+ const record = value;
17057
+ return Object.fromEntries(
17058
+ Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable6(record[key])])
17059
+ );
16782
17060
  }
16783
-
16784
- main().catch((error) => {
16785
- console.error(error);
16786
- process.exitCode = 1;
16787
- });
17061
+ function writeJson4(value) {
17062
+ return `${JSON.stringify(stable6(value), null, 2)}
16788
17063
  `;
16789
- default:
16790
- return `import { observe } from "agent-inspect";
16791
-
16792
- class DemoAgent {
16793
- async run(input: { question: string }) {
16794
- return { answer: \`Echo: \${input.question}\` };
16795
- }
16796
17064
  }
16797
-
16798
- const agent = observe(new DemoAgent(), {
16799
- traceDir: ".agent-inspect",
16800
- silent: true,
16801
- });
16802
-
16803
- await agent.run({ question: "hello" });
16804
- console.log("Trace written to .agent-inspect/");
16805
- `;
17065
+ function parseBundleProfile(value) {
17066
+ if (value === void 0 || value === "local" || value === "share" || value === "strict") {
17067
+ return value ?? "share";
16806
17068
  }
17069
+ throw new Error(`Unsupported --profile "${value}". Use local, share, or strict.`);
16807
17070
  }
16808
- function githubWorkflowTemplate() {
16809
- return `name: AgentInspect artifacts
16810
-
16811
- on:
16812
- workflow_dispatch:
16813
- push:
16814
- branches: [main]
16815
-
16816
- jobs:
16817
- trace-artifacts:
16818
- runs-on: ubuntu-latest
16819
- steps:
16820
- - uses: actions/checkout@v4
16821
- - uses: actions/setup-node@v4
16822
- with:
16823
- node-version: "22"
16824
- - run: npm ci
16825
- - run: npm test
16826
- - name: Upload AgentInspect traces
16827
- if: always()
16828
- uses: actions/upload-artifact@v4
16829
- with:
16830
- name: agent-inspect-traces
16831
- path: .agent-inspect/**/*.jsonl
16832
- if-no-files-found: ignore
16833
- `;
17071
+ function toReportProfile(profile) {
17072
+ return profile;
16834
17073
  }
16835
- async function planInit(options = {}) {
16836
- const framework = normalizeFramework(options.framework);
16837
- const cwd = path16__default.default.resolve(options.cwd ?? process.cwd());
16838
- const demoPath = framework === "custom" ? path16__default.default.join("examples", "agent-inspect-demo.mjs") : path16__default.default.join("examples", `agent-inspect-${framework}-demo.mjs`);
16839
- const candidates = [
16840
- { rel: CONFIG_FILE, content: configTemplate(framework) },
16841
- { rel: GITKEEP, content: "" },
16842
- { rel: demoPath, content: demoTemplate(framework) }
16843
- ];
16844
- if (options.ci === "github") {
16845
- candidates.push({
16846
- rel: ".github/workflows/agent-inspect-artifacts.yml",
16847
- content: githubWorkflowTemplate()
16848
- });
17074
+ function escapeHtml3(value) {
17075
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
17076
+ }
17077
+ function safetyStatusFromAssess(status) {
17078
+ if (status === "SAFE" || status === "SAFE WITH WARNINGS" || status === "UNSAFE" || status === "UNKNOWN") {
17079
+ return status;
16849
17080
  }
16850
- const files = [];
16851
- for (const candidate of candidates) {
16852
- const abs = path16__default.default.join(cwd, candidate.rel);
17081
+ return "UNKNOWN";
17082
+ }
17083
+ async function resolveOutputDir(options, runIds, cwd) {
17084
+ if (options.out !== void 0 && options.out.trim() !== "") {
17085
+ const normalized = normalizeBundleOutputPath(options.out);
16853
17086
  try {
16854
- await promises.access(abs);
16855
- files.push({
16856
- path: candidate.rel,
16857
- action: "skip",
16858
- reason: "file already exists"
16859
- });
16860
- } catch (error) {
16861
- if (error.code !== "ENOENT") throw error;
16862
- files.push({ path: candidate.rel, action: "create" });
17087
+ const location = resolveWorkspaceLocation(cwd);
17088
+ const manifest = await readWorkspaceManifestFile(location);
17089
+ if (manifest.ok && manifest.manifest) {
17090
+ const rel = path19__default.default.relative(location.workspaceDir, normalized);
17091
+ if (!rel.startsWith("..") && !path19__default.default.isAbsolute(rel)) {
17092
+ return resolveInsideWorkspace(location.workspaceDir, rel);
17093
+ }
17094
+ }
17095
+ } catch {
16863
17096
  }
17097
+ return normalized;
16864
17098
  }
16865
- return { framework, ...options.ci ? { ci: options.ci } : {}, files };
16866
- }
16867
- async function writePlannedFiles(plan, cwd, options) {
16868
- const written = [];
16869
- for (const entry of plan.files) {
16870
- if (entry.action === "skip") {
16871
- continue;
16872
- }
16873
- const abs = path16__default.default.join(cwd, entry.path);
16874
- if (options.dryRun) {
16875
- written.push(entry.path);
16876
- continue;
17099
+ try {
17100
+ const location = resolveWorkspaceLocation(cwd);
17101
+ const manifest = await readWorkspaceManifestFile(location);
17102
+ if (manifest.ok && manifest.manifest) {
17103
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
17104
+ const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
17105
+ return resolveInsideWorkspace(
17106
+ location.workspaceDir,
17107
+ path19__default.default.join(manifest.manifest.bundlesDir, `bundle-${label}-${stamp}`)
17108
+ );
16877
17109
  }
16878
- await promises.mkdir(path16__default.default.dirname(abs), { recursive: true });
16879
- const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
16880
- await promises.writeFile(abs, content, "utf-8");
16881
- written.push(entry.path);
17110
+ } catch {
16882
17111
  }
16883
- return written;
17112
+ return defaultBundleOutputPath(runIds);
16884
17113
  }
16885
- async function initCommand(options = {}) {
16886
- const cwd = path16__default.default.resolve(options.cwd ?? process.cwd());
16887
- try {
16888
- const plan = await planInit({ ...options, cwd });
16889
- const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
16890
- const skipped = plan.files.filter((file) => file.action === "skip");
17114
+ async function writeBundleFile(outputDir, relativePath, content, files) {
17115
+ const outPath = path19__default.default.join(outputDir, relativePath);
17116
+ await promises.mkdir(path19__default.default.dirname(outPath), { recursive: true });
17117
+ await promises.writeFile(outPath, content, "utf-8");
17118
+ files.push(relativePath);
17119
+ }
17120
+ function renderBundleIndexHtml(parts) {
17121
+ const links = parts.runIds.map(
17122
+ (runId) => `<li><a href="assets/runs/${escapeHtml3(runId)}.html">${escapeHtml3(runId)}</a></li>`
17123
+ ).join("");
17124
+ const sections = parts.runIds.map((runId) => {
17125
+ const html = parts.reports.get(runId) ?? "";
17126
+ return `<section id="run-${escapeHtml3(runId)}"><h2>${escapeHtml3(runId)}</h2>${html}</section>`;
17127
+ }).join("\n");
17128
+ return `<!doctype html>
17129
+ <html lang="en">
17130
+ <head>
17131
+ <meta charset="utf-8"/>
17132
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
17133
+ <title>AgentInspect Bundle</title>
17134
+ <style>
17135
+ body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}
17136
+ a{color:#0366d6}
17137
+ section{border-top:1px solid #ddd;margin-top:1.5rem;padding-top:1rem}
17138
+ </style>
17139
+ </head>
17140
+ <body>
17141
+ <h1>AgentInspect trace bundle</h1>
17142
+ <p>${escapeHtml3(BUNDLE_NOTE2)}</p>
17143
+ <h2>Runs</h2>
17144
+ <ul>${links}</ul>
17145
+ ${sections}
17146
+ </body>
17147
+ </html>
17148
+ `;
17149
+ }
17150
+ function extractHtmlBody(content) {
17151
+ const match = content.match(/<body[^>]*>([\s\S]*)<\/body>/i);
17152
+ return match?.[1]?.trim() ?? content;
17153
+ }
17154
+ async function bundleCommand(runIdArg, options = {}) {
17155
+ const profile = parseBundleProfile(options.profile);
17156
+ const traceDir = resolveTraceDir({ dir: options.dir });
17157
+ const cwd = process.cwd();
17158
+ let resolveResult;
17159
+ try {
17160
+ const { runs } = await loadSessionRuns(traceDir);
17161
+ const staleThresholdMs = options.staleAfter && options.staleAfter.trim() !== "" ? parseDuration(options.staleAfter.trim()) : void 0;
17162
+ const index = buildSessionIndex(runs, {
17163
+ correlateByGroupId: options.correlateGroup === true,
17164
+ staleThresholdMs
17165
+ });
17166
+ resolveResult = resolveBundleRunIds(index, runs, {
17167
+ ...runIdArg !== void 0 && runIdArg.trim() !== "" ? { runId: runIdArg.trim() } : {},
17168
+ ...options.session ? { sessionId: options.session } : {},
17169
+ ...options.since ? { since: options.since } : {}
17170
+ });
17171
+ } catch (error) {
17172
+ const message = error instanceof Error ? error.message : String(error);
16891
17173
  if (options.json) {
16892
- const payload = {
16893
- ok: true,
16894
- version,
16895
- framework: plan.framework,
16896
- ci: plan.ci ?? null,
16897
- dryRun: options.dryRun === true,
16898
- planned: plan.files,
16899
- wouldWrite: options.dryRun ? toWrite : void 0
16900
- };
16901
- console.log(JSON.stringify(payload, null, 2));
16902
- if (options.dryRun) return;
17174
+ console.log(writeJson4({ ok: false, error: message }).trimEnd());
17175
+ } else {
17176
+ console.error(`[AgentInspect] bundle failed: ${message}`);
16903
17177
  }
16904
- const written = await writePlannedFiles(plan, cwd, options);
16905
- if (!options.json) {
16906
- console.log("AgentInspect init");
16907
- console.log(`Framework: ${plan.framework}`);
16908
- console.log(`Trace directory: ${TRACE_DIR}/`);
16909
- if (options.dryRun) {
16910
- console.log("Dry run \u2014 would create:");
16911
- for (const file of toWrite) console.log(`- ${file}`);
16912
- for (const file of skipped) {
16913
- console.log(`- ${file.path} (skip: ${file.reason ?? "exists"})`);
16914
- }
16915
- return;
17178
+ process.exitCode = 1;
17179
+ return;
17180
+ }
17181
+ const outputDir = await resolveOutputDir(options, resolveResult.runIds, cwd);
17182
+ const files = [];
17183
+ const checkRuns = [];
17184
+ const redactionRuns = [];
17185
+ const htmlByRun = /* @__PURE__ */ new Map();
17186
+ const redactedJsonlByRun = /* @__PURE__ */ new Map();
17187
+ let combinedJsonl = "";
17188
+ for (const runId of resolveResult.runIds) {
17189
+ const tracePath = getTraceFilePath(runId, traceDir);
17190
+ let rawContent;
17191
+ let sourceMtimeMs;
17192
+ try {
17193
+ rawContent = await promises.readFile(tracePath, "utf-8");
17194
+ sourceMtimeMs = (await promises.stat(tracePath)).mtimeMs;
17195
+ } catch (error) {
17196
+ const message = error instanceof Error ? error.message : String(error);
17197
+ if (options.json) {
17198
+ console.log(writeJson4({ ok: false, error: message, runId }).trimEnd());
17199
+ } else {
17200
+ console.error(`[AgentInspect] bundle failed: ${message}`);
16916
17201
  }
16917
- console.log("Created:");
16918
- for (const file of written) console.log(`- ${file}`);
16919
- for (const file of skipped) {
16920
- console.log(`- ${file.path} (skipped: ${file.reason ?? "exists"})`);
17202
+ process.exitCode = 1;
17203
+ return;
17204
+ }
17205
+ let read;
17206
+ try {
17207
+ read = await openTrace(
17208
+ { type: "file", path: tracePath },
17209
+ { format: "agent-inspect-jsonl" }
17210
+ );
17211
+ } catch (error) {
17212
+ const message = error instanceof Error ? error.message : String(error);
17213
+ if (options.json) {
17214
+ console.log(writeJson4({ ok: false, error: message, runId }).trimEnd());
17215
+ } else {
17216
+ console.error(`[AgentInspect] bundle failed: ${message}`);
16921
17217
  }
16922
- console.log("\nNext: run your demo, then `npx agent-inspect list --dir .agent-inspect`");
16923
- console.log("No dependencies were installed. Add packages manually when ready.");
17218
+ process.exitCode = 1;
17219
+ return;
16924
17220
  }
16925
- } catch (error) {
16926
- const msg = error instanceof Error ? error.message : String(error);
17221
+ const safety = assessOpenedTrace(read, { run: runId });
17222
+ const status = safetyStatusFromAssess(safety.status);
17223
+ checkRuns.push({
17224
+ runId,
17225
+ status,
17226
+ errors: safety.summary.errors,
17227
+ warnings: safety.summary.warnings,
17228
+ findings: safety.summary.findings
17229
+ });
17230
+ const redacted = redactTraceContent(rawContent, toReportProfile(profile));
17231
+ redactedJsonlByRun.set(runId, redacted.content);
17232
+ combinedJsonl += redacted.content.endsWith("\n") ? redacted.content : `${redacted.content}
17233
+ `;
17234
+ const detectors = [
17235
+ ...new Set(redacted.findings.map((finding) => finding.detector))
17236
+ ].sort((a, b) => a.localeCompare(b));
17237
+ redactionRuns.push({
17238
+ runId,
17239
+ findings: redacted.findings.length,
17240
+ detectors
17241
+ });
17242
+ const selected = read.runs.find((run) => run.runId === runId);
17243
+ const runReport = exportRunTree(selected ?? read.runs[0], {
17244
+ format: "html",
17245
+ redacted: true,
17246
+ redactionProfile: toReportProfile(profile)
17247
+ });
17248
+ htmlByRun.set(runId, extractHtmlBody(runReport.content));
17249
+ const afterMtime = (await promises.stat(tracePath)).mtimeMs;
17250
+ if (afterMtime !== sourceMtimeMs) {
17251
+ const message = `Source trace "${runId}" was modified during bundle creation.`;
17252
+ if (options.json) {
17253
+ console.log(writeJson4({ ok: false, error: message }).trimEnd());
17254
+ } else {
17255
+ console.error(`[AgentInspect] bundle failed: ${message}`);
17256
+ }
17257
+ process.exitCode = 1;
17258
+ return;
17259
+ }
17260
+ }
17261
+ const checks2 = {
17262
+ aggregateStatus: aggregateBundleSafeStatus(checkRuns.map((run) => run.status)),
17263
+ runs: checkRuns
17264
+ };
17265
+ if (bundleFailsOnSafety(checks2.aggregateStatus, options.allowUnsafe === true)) {
16927
17266
  if (options.json) {
16928
- console.log(JSON.stringify({ ok: false, error: msg }, null, 2));
17267
+ console.log(
17268
+ writeJson4({
17269
+ ok: false,
17270
+ error: `Bundle safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`,
17271
+ checks: checks2
17272
+ }).trimEnd()
17273
+ );
16929
17274
  } else {
16930
- console.error(`[AgentInspect] init failed: ${msg}`);
17275
+ console.error(
17276
+ `[AgentInspect] bundle refused: safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`
17277
+ );
16931
17278
  }
16932
17279
  process.exitCode = 1;
17280
+ return;
16933
17281
  }
16934
- }
16935
- var OPTIONAL_PACKAGES = {
16936
- custom: [],
16937
- "ai-sdk": ["@agent-inspect/ai-sdk"],
16938
- "openai-agents": ["@agent-inspect/openai-agents"],
16939
- langchain: ["@agent-inspect/langchain"]
16940
- };
16941
- function nodeVersionCheck() {
16942
- const major = Number(process3__default.default.versions.node.split(".")[0]);
16943
- if (Number.isNaN(major) || major < 20) {
16944
- return {
16945
- id: "node-version",
16946
- status: "fail",
16947
- message: `Node ${process3__default.default.versions.node} is below the supported minimum (20).`,
16948
- remediation: "Upgrade to Node 20 LTS or newer.",
16949
- evidence: process3__default.default.versions.node
16950
- };
16951
- }
16952
- return {
16953
- id: "node-version",
16954
- status: "pass",
16955
- message: `Node ${process3__default.default.versions.node} meets the minimum (>=20).`,
16956
- evidence: process3__default.default.versions.node
17282
+ const redactionReport = {
17283
+ profile,
17284
+ totalFindings: redactionRuns.reduce((sum, run) => sum + run.findings, 0),
17285
+ runs: redactionRuns
16957
17286
  };
16958
- }
16959
- function envCheck(name, optional = true) {
16960
- const value = process3__default.default.env[name];
16961
- if (value === void 0 || value.trim() === "") {
16962
- return {
16963
- id: `env-${name.toLowerCase()}`,
16964
- status: optional ? "skipped" : "warn",
16965
- message: `${name} is not set.`,
16966
- remediation: optional ? void 0 : `Set ${name}=1 to enable tracing.`
16967
- };
17287
+ await promises.mkdir(outputDir, { recursive: true });
17288
+ for (const runId of resolveResult.runIds) {
17289
+ const jsonl = redactedJsonlByRun.get(runId) ?? "";
17290
+ await writeBundleFile(outputDir, `assets/runs/${runId}.jsonl`, jsonl, files);
17291
+ const html = htmlByRun.get(runId) ?? "";
17292
+ await writeBundleFile(
17293
+ outputDir,
17294
+ `assets/runs/${runId}.html`,
17295
+ runReportWrap(html, runId),
17296
+ files
17297
+ );
16968
17298
  }
16969
- return {
16970
- id: `env-${name.toLowerCase()}`,
16971
- status: "pass",
16972
- message: `${name} is set.`,
16973
- evidence: value
16974
- };
16975
- }
16976
- async function traceDirWritable(traceDir) {
16977
- const resolved = path16__default.default.resolve(traceDir);
16978
- try {
16979
- await promises.mkdir(resolved, { recursive: true });
16980
- await promises.access(resolved, promises.constants.W_OK);
16981
- return {
16982
- id: "trace-dir-writable",
16983
- status: "pass",
16984
- message: `Trace directory is writable: ${resolved}`,
16985
- evidence: resolved
16986
- };
16987
- } catch (error) {
16988
- return {
16989
- id: "trace-dir-writable",
16990
- status: "fail",
16991
- message: `Trace directory is not writable: ${resolved}`,
16992
- remediation: "Create the directory or set AGENT_INSPECT_TRACE_DIR to a writable path.",
16993
- evidence: error instanceof Error ? error.message : String(error)
16994
- };
17299
+ const primaryRunId = resolveResult.runIds[0];
17300
+ const traceHtml = resolveResult.runIds.length === 1 ? runReportWrap(htmlByRun.get(primaryRunId) ?? "", primaryRunId) : renderBundleIndexHtml({ runIds: resolveResult.runIds, reports: htmlByRun });
17301
+ await writeBundleFile(outputDir, "trace.jsonl", combinedJsonl, files);
17302
+ await writeBundleFile(outputDir, "trace.html", traceHtml, files);
17303
+ await writeBundleFile(
17304
+ outputDir,
17305
+ "check-results.json",
17306
+ writeJson4(checks2),
17307
+ files
17308
+ );
17309
+ await writeBundleFile(
17310
+ outputDir,
17311
+ "eval-results.json",
17312
+ writeJson4(buildPlaceholderArtifact()),
17313
+ files
17314
+ );
17315
+ await writeBundleFile(
17316
+ outputDir,
17317
+ "redaction-report.json",
17318
+ writeJson4(redactionReport),
17319
+ files
17320
+ );
17321
+ await writeBundleFile(
17322
+ outputDir,
17323
+ "performance-summary.json",
17324
+ writeJson4(buildPlaceholderArtifact()),
17325
+ files
17326
+ );
17327
+ const metadata = buildBundleMetadata({
17328
+ agentInspectVersion: version,
17329
+ profile,
17330
+ resolve: resolveResult,
17331
+ checks: checks2,
17332
+ files: [...files]
17333
+ });
17334
+ await writeBundleFile(outputDir, "metadata.json", writeJson4(metadata), files);
17335
+ await writeBundleFile(
17336
+ outputDir,
17337
+ "summary.md",
17338
+ buildBundleSummaryMarkdown({ metadata, checks: checks2, redaction: redactionReport }),
17339
+ files
17340
+ );
17341
+ metadata.files = [...files].sort((a, b) => a.localeCompare(b));
17342
+ await promises.writeFile(path19__default.default.join(outputDir, "metadata.json"), writeJson4(metadata), "utf-8");
17343
+ if (options.json) {
17344
+ console.log(
17345
+ writeJson4({
17346
+ ok: true,
17347
+ outputDir,
17348
+ metadata,
17349
+ checks: checks2,
17350
+ redaction: redactionReport
17351
+ }).trimEnd()
17352
+ );
17353
+ return;
16995
17354
  }
17355
+ console.log(`Bundle written to ${outputDir}`);
17356
+ console.log(`Safe status: ${metadata.safeStatus}`);
17357
+ console.log(`Runs: ${resolveResult.runIds.join(", ")}`);
17358
+ console.log(`Files: ${metadata.files.length}`);
16996
17359
  }
16997
- function resolvePackage(cwd, name) {
16998
- const require2 = module$1.createRequire(path16__default.default.join(cwd, "package.json"));
16999
- try {
17000
- const pkgPath = require2.resolve(`${name}/package.json`);
17001
- const pkg = require2(pkgPath);
17002
- return { ok: true, version: pkg.version };
17003
- } catch {
17004
- return { ok: false };
17005
- }
17360
+ function runReportWrap(body, runId) {
17361
+ return `<!doctype html>
17362
+ <html lang="en">
17363
+ <head>
17364
+ <meta charset="utf-8"/>
17365
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
17366
+ <title>AgentInspect \u2014 ${escapeHtml3(runId)}</title>
17367
+ <style>body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}</style>
17368
+ </head>
17369
+ <body>
17370
+ ${body}
17371
+ </body>
17372
+ </html>
17373
+ `;
17006
17374
  }
17007
- function importSmoke(cwd) {
17008
- const results = [];
17009
- const require2 = module$1.createRequire(path16__default.default.join(cwd, "package.json"));
17010
- try {
17011
- require2.resolve("agent-inspect");
17012
- results.push({
17013
- id: "import-agent-inspect",
17014
- status: "pass",
17015
- message: "agent-inspect resolves from the current project."
17375
+ function validateReporterArtifactPath(options) {
17376
+ const outputDir = path19__default.default.resolve(options.outputDir);
17377
+ const diagnostics = [];
17378
+ const rawPath = options.relativePath;
17379
+ if (rawPath.length === 0) {
17380
+ diagnostics.push({
17381
+ code: "artifact_path_empty",
17382
+ severity: "error",
17383
+ message: "Reporter artifact path must not be empty."
17016
17384
  });
17017
- } catch {
17018
- results.push({
17019
- id: "import-agent-inspect",
17020
- status: "warn",
17021
- message: "agent-inspect is not installed in the current project.",
17022
- remediation: "Run npm install agent-inspect (or pnpm add agent-inspect)."
17385
+ return { ok: false, outputDir, diagnostics };
17386
+ }
17387
+ if (rawPath.includes("\0")) {
17388
+ diagnostics.push({
17389
+ code: "invalid_artifact_path",
17390
+ severity: "error",
17391
+ message: "Reporter artifact path must not contain null bytes.",
17392
+ target: rawPath
17023
17393
  });
17394
+ return { ok: false, outputDir, diagnostics };
17024
17395
  }
17025
- try {
17026
- require2.resolve("agent-inspect/package.json");
17027
- results.push({
17028
- id: "import-agent-inspect-cjs",
17029
- status: "pass",
17030
- message: "CJS resolution for agent-inspect succeeded."
17396
+ if (path19__default.default.isAbsolute(rawPath) || path19__default.default.win32.isAbsolute(rawPath)) {
17397
+ diagnostics.push({
17398
+ code: "artifact_path_absolute",
17399
+ severity: "error",
17400
+ message: "Reporter artifact path must be relative.",
17401
+ target: rawPath
17031
17402
  });
17032
- } catch {
17033
- results.push({
17034
- id: "import-agent-inspect-cjs",
17035
- status: "skipped",
17036
- message: "CJS resolution check skipped (package not installed locally)."
17403
+ return { ok: false, outputDir, diagnostics };
17404
+ }
17405
+ const normalized = path19__default.default.posix.normalize(rawPath.replace(/\\/g, "/"));
17406
+ const segments = normalized.split("/");
17407
+ if (normalized === "." || normalized.startsWith("../") || segments.some((segment) => segment === "..")) {
17408
+ diagnostics.push({
17409
+ code: "artifact_path_escape",
17410
+ severity: "error",
17411
+ message: "Reporter artifact path must stay under the output directory.",
17412
+ target: rawPath
17037
17413
  });
17414
+ return { ok: false, outputDir, diagnostics };
17038
17415
  }
17039
- return results;
17040
- }
17041
- function optionalPackageChecks(cwd, framework) {
17042
- const packages = framework !== void 0 ? OPTIONAL_PACKAGES[framework] : [
17043
- "@agent-inspect/ai-sdk",
17044
- "@agent-inspect/openai-agents",
17045
- "@agent-inspect/langchain",
17046
- "@agent-inspect/redact",
17047
- "@agent-inspect/eval"
17048
- ];
17049
- return packages.map((name) => {
17050
- const resolved = resolvePackage(cwd, name);
17051
- if (!resolved.ok) {
17052
- return {
17053
- id: `optional-package-${name}`,
17054
- status: framework !== void 0 ? "warn" : "skipped",
17055
- message: `${name} is not installed.`,
17056
- remediation: framework !== void 0 ? `npm install ${name}` : void 0
17057
- };
17058
- }
17059
- return {
17060
- id: `optional-package-${name}`,
17061
- status: "pass",
17062
- message: `${name} is available.`,
17063
- evidence: resolved.version
17064
- };
17065
- });
17066
- }
17067
- function versionMismatchCheck(cwd) {
17068
- const root = resolvePackage(cwd, "agent-inspect");
17069
- if (!root.ok || root.version === void 0) {
17070
- return {
17071
- id: "version-alignment",
17072
- status: "skipped",
17073
- message: "Skipped version alignment \u2014 agent-inspect not installed locally."
17074
- };
17075
- }
17076
- if (root.version !== version) {
17077
- return {
17078
- id: "version-alignment",
17079
- status: "warn",
17080
- message: `CLI version ${version} differs from local agent-inspect@${root.version}.`,
17081
- remediation: "Align versions with npm install agent-inspect@latest",
17082
- evidence: `cli=${version};local=${root.version}`
17083
- };
17416
+ const absolutePath = path19__default.default.resolve(outputDir, normalized);
17417
+ const relFromOutput = path19__default.default.relative(outputDir, absolutePath);
17418
+ if (relFromOutput.length === 0 || relFromOutput.startsWith("..") || path19__default.default.isAbsolute(relFromOutput)) {
17419
+ diagnostics.push({
17420
+ code: "artifact_path_escape",
17421
+ severity: "error",
17422
+ message: "Reporter artifact path resolved outside the output directory.",
17423
+ target: rawPath
17424
+ });
17425
+ return { ok: false, outputDir, diagnostics };
17084
17426
  }
17085
17427
  return {
17086
- id: "version-alignment",
17087
- status: "pass",
17088
- message: `CLI and local agent-inspect are aligned at ${version}.`
17428
+ ok: true,
17429
+ outputDir,
17430
+ relativePath: normalized,
17431
+ absolutePath,
17432
+ diagnostics
17089
17433
  };
17090
17434
  }
17091
- async function runDoctorChecks(options = {}) {
17092
- const cwd = path16__default.default.resolve(options.cwd ?? process3__default.default.cwd());
17093
- const traceDir = options.traceDir?.trim() || process3__default.default.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
17094
- const checks2 = [
17095
- nodeVersionCheck(),
17096
- {
17097
- id: "cli-version",
17098
- status: "pass",
17099
- message: `agent-inspect CLI ${version}`,
17100
- evidence: version
17101
- },
17102
- await traceDirWritable(traceDir),
17103
- envCheck("AGENT_INSPECT"),
17104
- envCheck("AGENT_INSPECT_TRACE_DIR"),
17105
- versionMismatchCheck(cwd)
17106
- ];
17107
- if (options.checkImports !== false) {
17108
- checks2.push(...importSmoke(cwd));
17109
- }
17110
- checks2.push(...optionalPackageChecks(cwd, options.framework));
17111
- return checks2.sort((a, b) => a.id.localeCompare(b.id));
17435
+
17436
+ // packages/cli/src/ci-summary.ts
17437
+ var NOTE2 = "Generated locally by AgentInspect from reporter artifact manifests. Trace contents are not embedded.";
17438
+ var MAX_TEXT = 180;
17439
+ var FRAMEWORKS = /* @__PURE__ */ new Set(["vitest", "jest", "manual"]);
17440
+ var STATUSES = /* @__PURE__ */ new Set(["passed", "failed", "skipped", "todo"]);
17441
+ var ARTIFACT_KINDS = /* @__PURE__ */ new Set(["trace", "report", "eval", "redaction", "summary"]);
17442
+ var ARTIFACT_FORMATS = /* @__PURE__ */ new Set(["json", "jsonl", "md", "html"]);
17443
+ var REDACTION_PROFILES2 = /* @__PURE__ */ new Set(["local", "share", "strict"]);
17444
+ function stable7(value) {
17445
+ if (Array.isArray(value)) return value.map(stable7);
17446
+ if (value === null || typeof value !== "object") return value;
17447
+ const record = value;
17448
+ return Object.fromEntries(
17449
+ Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable7(record[key])])
17450
+ );
17112
17451
  }
17113
- async function doctorCommand(options = {}) {
17114
- const checks2 = await runDoctorChecks(options);
17115
- const failed = checks2.filter((check) => check.status === "fail").length;
17116
- const warned = checks2.filter((check) => check.status === "warn").length;
17117
- if (options.json) {
17118
- console.log(
17119
- JSON.stringify(
17120
- {
17121
- ok: failed === 0,
17122
- version,
17123
- summary: { pass: checks2.filter((c) => c.status === "pass").length, warn: warned, fail: failed },
17124
- checks: checks2
17125
- },
17126
- null,
17127
- 2
17128
- )
17129
- );
17130
- if (failed > 0) process3__default.default.exitCode = 1;
17131
- return;
17132
- }
17133
- console.log("AgentInspect doctor");
17134
- for (const check of checks2) {
17135
- const tag = check.status.toUpperCase();
17136
- console.log(`[${tag}] ${check.id}: ${check.message}`);
17137
- if (check.remediation) console.log(` \u2192 ${check.remediation}`);
17138
- }
17139
- console.log(`
17140
- Summary: ${failed} failed, ${warned} warnings`);
17141
- if (failed > 0) process3__default.default.exitCode = 1;
17452
+ function writeJson5(value) {
17453
+ return `${JSON.stringify(stable7(value), null, 2)}
17454
+ `;
17142
17455
  }
17143
-
17144
- // packages/adapter-sdk/src/indexer.ts
17145
- init_advanced();
17146
- function defineIndexer(indexer) {
17147
- if (!indexer.id.trim()) throw new Error("indexer id is required");
17148
- return indexer;
17456
+ function isObject(value) {
17457
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17149
17458
  }
17150
- async function indexIsStale(snapshot, traceDir) {
17151
- const builtMs = Date.parse(snapshot.builtAt);
17152
- if (Number.isNaN(builtMs)) return true;
17153
- const td = new TraceDirectory({ dir: traceDir });
17154
- const files = await td.list();
17155
- for (const file of files) {
17156
- const stats = await td.getFileStats(file);
17157
- if (stats.mtimeMs > builtMs) return true;
17459
+ function readString(value, label) {
17460
+ if (typeof value !== "string" || value.trim() === "") {
17461
+ throw new Error(`${label} must be a non-empty string.`);
17158
17462
  }
17159
- return false;
17160
- }
17161
- function createTraceDirectoryIndexer() {
17162
- return defineIndexer({
17163
- id: "trace-directory-metadata",
17164
- async rebuild(traceDir, options = {}) {
17165
- const warnings = [];
17166
- const td = new TraceDirectory({ dir: traceDir });
17167
- const files = await td.list();
17168
- const maxEntries = options.maxEntries ?? 1e4;
17169
- if (files.length > maxEntries) {
17170
- warnings.push(
17171
- `indexer.truncated: trace directory has ${files.length} files; indexing first ${maxEntries}`
17172
- );
17173
- }
17174
- const slice = files.slice(0, maxEntries);
17175
- const metas = await loadTraceMetadataList(
17176
- traceDir,
17177
- slice,
17178
- (fileName) => td.getPath(fileName)
17179
- );
17180
- const entries = metas.map((meta2) => ({
17181
- runId: meta2.runId,
17182
- path: meta2.filePath,
17183
- name: meta2.name,
17184
- startedAt: meta2.startedAt,
17185
- status: meta2.status
17186
- })).sort((a, b) => a.runId.localeCompare(b.runId));
17187
- if (entries.length < slice.length) {
17188
- warnings.push(
17189
- `indexer.partial: indexed ${entries.length} of ${slice.length} trace files`
17190
- );
17191
- }
17192
- return {
17193
- traceDir,
17194
- builtAt: (/* @__PURE__ */ new Date()).toISOString(),
17195
- entries,
17196
- warnings
17197
- };
17198
- }
17199
- });
17463
+ return safeText(value);
17200
17464
  }
17201
-
17202
- // packages/cli/src/index-cmd.ts
17203
- init_advanced();
17204
- var INDEX_FILENAME = ".agent-inspect-index.json";
17205
- function traceIndexPath(traceDir) {
17206
- return path16__default.default.join(traceDir, INDEX_FILENAME);
17465
+ function readOptionalString(value) {
17466
+ return typeof value === "string" && value.trim() !== "" ? safeText(value) : void 0;
17207
17467
  }
17208
- function parseMaxEntries(raw) {
17209
- if (raw === void 0 || raw.trim() === "") return void 0;
17210
- const parsed = Number.parseInt(raw, 10);
17211
- if (!Number.isFinite(parsed) || parsed <= 0) {
17212
- throw new Error("--max-entries must be a positive integer.");
17468
+ function readFramework(value) {
17469
+ const framework = readString(value, "manifest.framework");
17470
+ if (!FRAMEWORKS.has(framework)) {
17471
+ throw new Error(`Unsupported reporter framework: ${framework}.`);
17213
17472
  }
17214
- return parsed;
17473
+ return framework;
17215
17474
  }
17216
- async function readSnapshot(indexPath2) {
17217
- try {
17218
- const raw = await promises.readFile(indexPath2, "utf8");
17219
- return JSON.parse(raw);
17220
- } catch {
17221
- return void 0;
17475
+ function readStatus(value) {
17476
+ const status = readString(value, "result.status");
17477
+ if (!STATUSES.has(status)) {
17478
+ throw new Error(`Unsupported reporter test status: ${status}.`);
17222
17479
  }
17480
+ return status;
17223
17481
  }
17224
- async function indexBuildCommand(options = {}) {
17225
- try {
17226
- const traceDir = resolveTraceDir({ dir: options.dir });
17227
- await promises.mkdir(traceDir, { recursive: true });
17228
- const indexer = createTraceDirectoryIndexer();
17229
- const snapshot = await indexer.rebuild(traceDir, {
17230
- maxEntries: parseMaxEntries(options.maxEntries)
17231
- });
17232
- const indexPath2 = traceIndexPath(traceDir);
17233
- await promises.writeFile(indexPath2, `${JSON.stringify(snapshot, null, 2)}
17234
- `, "utf8");
17235
- if (options.json) {
17236
- console.log(JSON.stringify({ ok: true, indexPath: indexPath2, ...snapshot }, null, 2));
17237
- return;
17238
- }
17239
- console.log(`Built trace index: ${indexPath2}`);
17240
- console.log(`Entries: ${snapshot.entries.length}`);
17241
- if (snapshot.warnings.length > 0) {
17242
- for (const warning of snapshot.warnings) {
17243
- console.log(`warning: ${warning}`);
17244
- }
17245
- }
17246
- } catch (e) {
17247
- const msg = e instanceof Error ? e.message : String(e);
17248
- console.error(`[AgentInspect] index build failed: ${msg}`);
17249
- process.exitCode = 1;
17482
+ function readArtifact(value, index) {
17483
+ if (!isObject(value)) throw new Error(`manifest.artifacts[${index}] must be an object.`);
17484
+ const kind = readString(value.kind, `manifest.artifacts[${index}].kind`);
17485
+ const format = readString(value.format, `manifest.artifacts[${index}].format`);
17486
+ const redactionProfile = readString(
17487
+ value.redactionProfile,
17488
+ `manifest.artifacts[${index}].redactionProfile`
17489
+ );
17490
+ if (!ARTIFACT_KINDS.has(kind)) throw new Error(`Unsupported artifact kind: ${kind}.`);
17491
+ if (!ARTIFACT_FORMATS.has(format)) throw new Error(`Unsupported artifact format: ${format}.`);
17492
+ if (!REDACTION_PROFILES2.has(redactionProfile)) {
17493
+ throw new Error(`Unsupported artifact redaction profile: ${redactionProfile}.`);
17250
17494
  }
17251
- }
17252
- async function indexStatusCommand(options = {}) {
17253
- try {
17254
- const traceDir = resolveTraceDir({ dir: options.dir });
17255
- const indexPath2 = traceIndexPath(traceDir);
17256
- const snapshot = await readSnapshot(indexPath2);
17257
- if (!snapshot) {
17258
- const payload2 = { ok: true, exists: false, indexPath: indexPath2, traceDir, stale: true };
17259
- if (options.json) {
17260
- console.log(JSON.stringify(payload2, null, 2));
17261
- } else {
17262
- console.log(`No index at ${indexPath2}`);
17263
- console.log("Run: agent-inspect index build");
17264
- }
17265
- return;
17495
+ const artifactPath = readString(value.path, `manifest.artifacts[${index}].path`);
17496
+ const pathCheck = validateReporterArtifactPath({
17497
+ outputDir: process.cwd(),
17498
+ relativePath: artifactPath
17499
+ });
17500
+ if (!pathCheck.ok || pathCheck.relativePath === void 0) {
17501
+ throw new Error(`Unsafe reporter artifact path: ${artifactPath}.`);
17502
+ }
17503
+ return {
17504
+ kind,
17505
+ path: pathCheck.relativePath,
17506
+ format,
17507
+ redactionProfile
17508
+ };
17509
+ }
17510
+ function readArtifacts(value, label) {
17511
+ if (value === void 0) return [];
17512
+ if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
17513
+ return value.map((item, index) => readArtifact(item, index));
17514
+ }
17515
+ function readDiagnosticsCount(value, label) {
17516
+ if (value === void 0) return 0;
17517
+ if (!Array.isArray(value)) throw new Error(`${label} must be an array.`);
17518
+ return value.length;
17519
+ }
17520
+ function readResult(value, index) {
17521
+ if (!isObject(value)) throw new Error(`manifest.results[${index}] must be an object.`);
17522
+ const file = readOptionalString(value.file);
17523
+ const tracePath = readOptionalString(value.tracePath);
17524
+ return {
17525
+ testId: readString(value.testId, `manifest.results[${index}].testId`),
17526
+ name: readString(value.name, `manifest.results[${index}].name`),
17527
+ ...file === void 0 ? {} : { file },
17528
+ status: readStatus(value.status),
17529
+ ...tracePath === void 0 ? {} : { tracePath },
17530
+ artifacts: readArtifacts(value.artifacts, `manifest.results[${index}].artifacts`),
17531
+ diagnostics: readDiagnosticsCount(
17532
+ value.diagnostics,
17533
+ `manifest.results[${index}].diagnostics`
17534
+ )
17535
+ };
17536
+ }
17537
+ function readManifestDocument(value) {
17538
+ if (!isObject(value)) throw new Error("Reporter manifest file must contain a JSON object.");
17539
+ const candidate = isObject(value.manifest) ? value.manifest : value;
17540
+ if (!isObject(candidate)) throw new Error("Reporter manifest must be a JSON object.");
17541
+ const schemaVersion = readString(candidate.schemaVersion, "manifest.schemaVersion");
17542
+ if (schemaVersion !== "0.1") {
17543
+ throw new Error(`Unsupported reporter manifest schemaVersion: ${schemaVersion}.`);
17544
+ }
17545
+ if (!Array.isArray(candidate.results)) {
17546
+ throw new Error("manifest.results must be an array.");
17547
+ }
17548
+ const artifacts = readArtifacts(candidate.artifacts, "manifest.artifacts");
17549
+ const results = candidate.results.map((item, index) => readResult(item, index));
17550
+ const diagnostics = readDiagnosticsCount(candidate.diagnostics, "manifest.diagnostics");
17551
+ const manifest = {
17552
+ framework: readFramework(candidate.framework),
17553
+ generatedAt: readString(candidate.generatedAt, "manifest.generatedAt"),
17554
+ results,
17555
+ artifacts
17556
+ };
17557
+ return {
17558
+ packageName: readOptionalString(value.package),
17559
+ manifest,
17560
+ diagnostics
17561
+ };
17562
+ }
17563
+ function cwdRelative(filePath) {
17564
+ const relative = path19__default.default.relative(process.cwd(), path19__default.default.resolve(filePath)).replace(/\\/g, "/");
17565
+ if (relative === "" || relative.startsWith("../") || path19__default.default.isAbsolute(relative)) {
17566
+ return path19__default.default.basename(filePath);
17567
+ }
17568
+ return relative;
17569
+ }
17570
+ async function readReporterManifest(filePath) {
17571
+ const absolute = path19__default.default.resolve(filePath);
17572
+ const raw = await promises.readFile(absolute, "utf-8");
17573
+ const document = readManifestDocument(JSON.parse(raw));
17574
+ const manifest = document.manifest;
17575
+ const results = manifest.results.map((result) => ({
17576
+ testId: safeText(result.testId),
17577
+ name: safeText(result.name),
17578
+ ...result.file === void 0 ? {} : { file: safeText(path19__default.default.basename(result.file)) },
17579
+ status: result.status,
17580
+ ...result.tracePath === void 0 ? {} : { tracePath: safeText(path19__default.default.basename(result.tracePath)) },
17581
+ artifacts: result.artifacts,
17582
+ diagnostics: result.diagnostics
17583
+ }));
17584
+ return {
17585
+ ...document.packageName === void 0 ? {} : { packageName: document.packageName },
17586
+ manifestFile: cwdRelative(absolute),
17587
+ framework: manifest.framework,
17588
+ generatedAt: manifest.generatedAt,
17589
+ results,
17590
+ artifacts: manifest.artifacts,
17591
+ diagnostics: document.diagnostics
17592
+ };
17593
+ }
17594
+ function summarize3(manifests) {
17595
+ const summary = {
17596
+ manifests: manifests.length,
17597
+ tests: 0,
17598
+ failed: 0,
17599
+ passed: 0,
17600
+ skipped: 0,
17601
+ todo: 0,
17602
+ artifacts: 0,
17603
+ diagnostics: 0
17604
+ };
17605
+ for (const manifest of manifests) {
17606
+ summary.artifacts += manifest.artifacts.length;
17607
+ summary.diagnostics += manifest.diagnostics;
17608
+ for (const result of manifest.results) {
17609
+ summary.tests += 1;
17610
+ if (result.status === "failed") summary.failed += 1;
17611
+ else if (result.status === "passed") summary.passed += 1;
17612
+ else if (result.status === "skipped") summary.skipped += 1;
17613
+ else summary.todo += 1;
17614
+ summary.artifacts += result.artifacts.length;
17615
+ summary.diagnostics += result.diagnostics;
17266
17616
  }
17267
- const stale = await indexIsStale(snapshot, traceDir);
17268
- const payload = {
17269
- ok: true,
17270
- exists: true,
17271
- indexPath: indexPath2,
17272
- traceDir,
17273
- stale,
17274
- builtAt: snapshot.builtAt,
17275
- entries: snapshot.entries.length,
17276
- warnings: snapshot.warnings
17277
- };
17278
- if (options.json) {
17279
- console.log(JSON.stringify(payload, null, 2));
17280
- return;
17617
+ }
17618
+ return {
17619
+ status: summary.failed > 0 ? "failed" : summary.diagnostics > 0 ? "warning" : "ok",
17620
+ manifests,
17621
+ summary,
17622
+ note: NOTE2
17623
+ };
17624
+ }
17625
+ function markdownCell(value) {
17626
+ return safeText(String(value ?? "unknown")).replaceAll("|", "\\|").replace(/\r?\n/g, " ");
17627
+ }
17628
+ function renderMarkdown2(result) {
17629
+ const lines = [
17630
+ "# AgentInspect CI Summary",
17631
+ "",
17632
+ NOTE2,
17633
+ "",
17634
+ "| Field | Value |",
17635
+ "| --- | --- |",
17636
+ `| Status | ${result.status} |`,
17637
+ `| Manifests | ${result.summary.manifests} |`,
17638
+ `| Tests | ${result.summary.tests} |`,
17639
+ `| Failed | ${result.summary.failed} |`,
17640
+ `| Passed | ${result.summary.passed} |`,
17641
+ `| Skipped | ${result.summary.skipped} |`,
17642
+ `| Todo | ${result.summary.todo} |`,
17643
+ `| Artifacts | ${result.summary.artifacts} |`,
17644
+ `| Diagnostics | ${result.summary.diagnostics} |`,
17645
+ "",
17646
+ "## Tests",
17647
+ "",
17648
+ "| Framework | Status | Test | File | Trace | Artifacts |",
17649
+ "| --- | --- | --- | --- | --- | --- |"
17650
+ ];
17651
+ const rows = result.manifests.flatMap(
17652
+ (manifest) => manifest.results.map((test) => ({
17653
+ framework: manifest.framework,
17654
+ test
17655
+ }))
17656
+ );
17657
+ if (rows.length === 0) {
17658
+ lines.push("| unknown | unknown | No tests found | unknown | unknown | 0 |");
17659
+ } else {
17660
+ for (const row of rows) {
17661
+ lines.push(
17662
+ `| ${markdownCell(row.framework)} | ${markdownCell(row.test.status)} | ${markdownCell(row.test.name)} | ${markdownCell(row.test.file)} | ${markdownCell(row.test.tracePath)} | ${row.test.artifacts.length} |`
17663
+ );
17281
17664
  }
17282
- console.log(`Index: ${indexPath2}`);
17283
- console.log(`Built: ${snapshot.builtAt}`);
17284
- console.log(`Entries: ${snapshot.entries.length}`);
17285
- console.log(`Stale: ${stale ? "yes" : "no"}`);
17286
- if (snapshot.warnings.length > 0) {
17287
- for (const warning of snapshot.warnings) {
17288
- console.log(`warning: ${warning}`);
17289
- }
17665
+ }
17666
+ lines.push("", "## Manifests", "", "| Framework | File | Generated | Artifacts |", "| --- | --- | --- | --- |");
17667
+ for (const manifest of result.manifests) {
17668
+ lines.push(
17669
+ `| ${markdownCell(manifest.framework)} | ${markdownCell(manifest.manifestFile)} | ${markdownCell(manifest.generatedAt)} | ${manifest.artifacts.length} |`
17670
+ );
17671
+ }
17672
+ lines.push("", "## Artifacts", "", "| Framework | Kind | Path | Format | Profile |", "| --- | --- | --- | --- | --- |");
17673
+ const artifacts = result.manifests.flatMap(
17674
+ (manifest) => manifest.artifacts.map((artifact) => ({ framework: manifest.framework, artifact }))
17675
+ );
17676
+ if (artifacts.length === 0) {
17677
+ lines.push("| unknown | unknown | No artifacts found | unknown | unknown |");
17678
+ } else {
17679
+ for (const row of artifacts) {
17680
+ lines.push(
17681
+ `| ${markdownCell(row.framework)} | ${markdownCell(row.artifact.kind)} | ${markdownCell(row.artifact.path)} | ${markdownCell(row.artifact.format)} | ${markdownCell(row.artifact.redactionProfile)} |`
17682
+ );
17290
17683
  }
17291
- } catch (e) {
17292
- const msg = e instanceof Error ? e.message : String(e);
17293
- console.error(`[AgentInspect] index status failed: ${msg}`);
17294
- process.exitCode = 1;
17295
17684
  }
17685
+ lines.push("");
17686
+ return `${lines.join("\n")}
17687
+ `;
17296
17688
  }
17297
- async function indexCleanCommand(options = {}) {
17689
+ function safeText(value) {
17690
+ const compact = value.replace(/\s+/g, " ").trim();
17691
+ const redacted = compact.replace(/\bsk-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(
17692
+ /\b(?:api[_-]?key|authorization|token|secret|password)\s*[:=]\s*[^,\s]+/gi,
17693
+ "$1=[REDACTED]"
17694
+ );
17695
+ if (redacted.length <= MAX_TEXT) return redacted;
17696
+ return `${redacted.slice(0, MAX_TEXT - 12)}...[truncated]`;
17697
+ }
17698
+ async function ciSummaryCommand(manifestPaths, options = {}) {
17699
+ if (manifestPaths.length === 0) {
17700
+ console.error("At least one reporter manifest path is required.");
17701
+ process.exitCode = 1;
17702
+ return;
17703
+ }
17704
+ let result;
17298
17705
  try {
17299
- const traceDir = resolveTraceDir({ dir: options.dir });
17300
- const indexPath2 = traceIndexPath(traceDir);
17301
- await promises.rm(indexPath2, { force: true });
17302
- if (options.json) {
17303
- console.log(JSON.stringify({ ok: true, removed: indexPath2 }, null, 2));
17304
- return;
17706
+ const manifests = [];
17707
+ for (const manifestPath of manifestPaths) {
17708
+ manifests.push(await readReporterManifest(manifestPath));
17305
17709
  }
17306
- console.log(`Removed index: ${indexPath2}`);
17307
- } catch (e) {
17308
- const msg = e instanceof Error ? e.message : String(e);
17309
- console.error(`[AgentInspect] index clean failed: ${msg}`);
17710
+ manifests.sort((a, b) => a.manifestFile.localeCompare(b.manifestFile));
17711
+ result = summarize3(manifests);
17712
+ } catch (error) {
17713
+ const message = error instanceof Error ? error.message : String(error);
17714
+ console.error(`[AgentInspect] ci-summary failed: ${message}`);
17310
17715
  process.exitCode = 1;
17716
+ return;
17717
+ }
17718
+ const markdown = renderMarkdown2(result);
17719
+ const outputPath = options.output !== void 0 && options.output.trim() !== "" ? path19__default.default.resolve(options.output.trim()) : void 0;
17720
+ if (outputPath !== void 0) {
17721
+ await promises.mkdir(path19__default.default.dirname(outputPath), { recursive: true });
17722
+ await promises.writeFile(outputPath, markdown, "utf-8");
17311
17723
  }
17724
+ const summaryTarget = options.githubSummary ?? process.env.GITHUB_STEP_SUMMARY;
17725
+ if (summaryTarget !== void 0 && summaryTarget.trim() !== "") {
17726
+ const summaryPath = path19__default.default.resolve(summaryTarget);
17727
+ await promises.mkdir(path19__default.default.dirname(summaryPath), { recursive: true });
17728
+ await promises.appendFile(summaryPath, `
17729
+ ${markdown}`, "utf-8");
17730
+ }
17731
+ if (options.json === true) {
17732
+ console.log(writeJson5(result).trimEnd());
17733
+ } else if (outputPath !== void 0) {
17734
+ console.log(`Wrote AgentInspect CI summary to ${outputPath}`);
17735
+ console.log(`Status: ${result.status}`);
17736
+ } else {
17737
+ console.log(markdown.trimEnd());
17738
+ }
17739
+ }
17740
+ var CONFIG_FILE = "agent-inspect.config.ts";
17741
+ var TRACE_DIR = ".agent-inspect";
17742
+ var GITKEEP = ".agent-inspect/.gitkeep";
17743
+ function normalizeFramework(value) {
17744
+ const raw = (value ?? "custom").trim();
17745
+ if (raw === "ai-sdk" || raw === "openai-agents" || raw === "langchain" || raw === "custom") {
17746
+ return raw;
17747
+ }
17748
+ throw new Error(
17749
+ "Unsupported --framework value. Use ai-sdk, openai-agents, langchain, or custom."
17750
+ );
17751
+ }
17752
+ function configTemplate(framework) {
17753
+ const base = `/**
17754
+ * AgentInspect local config (metadata-only capture by default).
17755
+ * See https://github.com/rajudandigam/agent-inspect/blob/main/docs/SAFE-TRACE-SHARING.md
17756
+ */
17757
+ export const agentInspectConfig = {
17758
+ traceDir: ".agent-inspect",
17759
+ enabled: process.env.AGENT_INSPECT !== "0",
17760
+ redactionProfile: "local" as const,
17761
+ };
17762
+ `;
17763
+ if (framework === "custom") return base;
17764
+ return `${base}
17765
+ export const framework = "${framework}" as const;
17766
+ `;
17312
17767
  }
17768
+ function demoTemplate(framework) {
17769
+ switch (framework) {
17770
+ case "ai-sdk":
17771
+ return `/**
17772
+ * AI SDK starter \u2014 metadata-only telemetry (no real model calls in this demo).
17773
+ * Install: npm install agent-inspect @agent-inspect/ai-sdk ai
17774
+ */
17775
+ import { inspectRun, step } from "agent-inspect";
17313
17776
 
17314
- // packages/cli/src/index-sqlite-cmd.ts
17315
- init_advanced();
17316
- var PACKAGE = "@agent-inspect/index-sqlite";
17317
- function isModuleNotFound3(e) {
17318
- return e !== null && typeof e === "object" && "code" in e && (e.code === "ERR_MODULE_NOT_FOUND" || e.code === "MODULE_NOT_FOUND");
17777
+ async function main() {
17778
+ await inspectRun("ai-sdk-demo", async () => {
17779
+ await step.tool("mock-generate", async () => ({ text: "ok" }));
17780
+ }, { traceDir: ".agent-inspect", silent: true });
17781
+ console.log("Trace written to .agent-inspect/");
17319
17782
  }
17320
- async function loadIndexSqlite() {
17321
- try {
17322
- return await Promise.resolve().then(() => (init_src(), src_exports));
17323
- } catch (e) {
17324
- if (isModuleNotFound3(e)) {
17325
- console.error(
17326
- `The optional SQLite index is not installed. Run: npm install ${PACKAGE}`
17327
- );
17328
- process.exitCode = 1;
17329
- return null;
17783
+
17784
+ main().catch((error) => {
17785
+ console.error(error);
17786
+ process.exitCode = 1;
17787
+ });
17788
+ `;
17789
+ case "openai-agents":
17790
+ return `/**
17791
+ * OpenAI Agents starter \u2014 use @agent-inspect/openai-agents for local-only processors.
17792
+ * This demo uses manual steps (no API keys).
17793
+ */
17794
+ import { inspectRun, step } from "agent-inspect";
17795
+
17796
+ async function main() {
17797
+ await inspectRun("openai-agents-demo", async () => {
17798
+ await step.tool("mock-agent-run", async () => "ok");
17799
+ }, { traceDir: ".agent-inspect", silent: true });
17800
+ console.log("Trace written to .agent-inspect/");
17801
+ }
17802
+
17803
+ main().catch((error) => {
17804
+ console.error(error);
17805
+ process.exitCode = 1;
17806
+ });
17807
+ `;
17808
+ case "langchain":
17809
+ return `/**
17810
+ * LangChain starter \u2014 wire @agent-inspect/langchain callbacks in your app.
17811
+ * This demo uses manual steps (no API keys).
17812
+ */
17813
+ import { inspectRun, step } from "agent-inspect";
17814
+
17815
+ async function main() {
17816
+ await inspectRun("langchain-demo", async () => {
17817
+ await step.tool("mock-chain", async () => "ok");
17818
+ }, { traceDir: ".agent-inspect", silent: true });
17819
+ console.log("Trace written to .agent-inspect/");
17820
+ }
17821
+
17822
+ main().catch((error) => {
17823
+ console.error(error);
17824
+ process.exitCode = 1;
17825
+ });
17826
+ `;
17827
+ default:
17828
+ return `import { observe } from "agent-inspect";
17829
+
17830
+ class DemoAgent {
17831
+ async run(input: { question: string }) {
17832
+ return { answer: \`Echo: \${input.question}\` };
17833
+ }
17834
+ }
17835
+
17836
+ const agent = observe(new DemoAgent(), {
17837
+ traceDir: ".agent-inspect",
17838
+ silent: true,
17839
+ });
17840
+
17841
+ await agent.run({ question: "hello" });
17842
+ console.log("Trace written to .agent-inspect/");
17843
+ `;
17844
+ }
17845
+ }
17846
+ function githubWorkflowTemplate() {
17847
+ return `name: AgentInspect artifacts
17848
+
17849
+ on:
17850
+ workflow_dispatch:
17851
+ push:
17852
+ branches: [main]
17853
+
17854
+ jobs:
17855
+ trace-artifacts:
17856
+ runs-on: ubuntu-latest
17857
+ steps:
17858
+ - uses: actions/checkout@v4
17859
+ - uses: actions/setup-node@v4
17860
+ with:
17861
+ node-version: "22"
17862
+ - run: npm ci
17863
+ - run: npm test
17864
+ - name: Upload AgentInspect traces
17865
+ if: always()
17866
+ uses: actions/upload-artifact@v4
17867
+ with:
17868
+ name: agent-inspect-traces
17869
+ path: .agent-inspect/**/*.jsonl
17870
+ if-no-files-found: ignore
17871
+ `;
17872
+ }
17873
+ async function planInit(options = {}) {
17874
+ const framework = normalizeFramework(options.framework);
17875
+ const cwd = path19__default.default.resolve(options.cwd ?? process.cwd());
17876
+ const demoPath = framework === "custom" ? path19__default.default.join("examples", "agent-inspect-demo.mjs") : path19__default.default.join("examples", `agent-inspect-${framework}-demo.mjs`);
17877
+ const candidates = [
17878
+ { rel: CONFIG_FILE, content: configTemplate(framework) },
17879
+ { rel: GITKEEP, content: "" },
17880
+ { rel: demoPath, content: demoTemplate(framework) }
17881
+ ];
17882
+ if (options.ci === "github") {
17883
+ candidates.push({
17884
+ rel: ".github/workflows/agent-inspect-artifacts.yml",
17885
+ content: githubWorkflowTemplate()
17886
+ });
17887
+ }
17888
+ const files = [];
17889
+ for (const candidate of candidates) {
17890
+ const abs = path19__default.default.join(cwd, candidate.rel);
17891
+ try {
17892
+ await promises.access(abs);
17893
+ files.push({
17894
+ path: candidate.rel,
17895
+ action: "skip",
17896
+ reason: "file already exists"
17897
+ });
17898
+ } catch (error) {
17899
+ if (error.code !== "ENOENT") throw error;
17900
+ files.push({ path: candidate.rel, action: "create" });
17330
17901
  }
17331
- const msg = e instanceof Error ? e.message : String(e);
17332
- console.error(`[AgentInspect] failed to load ${PACKAGE}: ${msg}`);
17333
- process.exitCode = 1;
17334
- return null;
17335
17902
  }
17903
+ return { framework, ...options.ci ? { ci: options.ci } : {}, files };
17336
17904
  }
17337
- function parsePositiveInt(raw, flag) {
17338
- if (raw === void 0 || raw.trim() === "") return void 0;
17339
- const parsed = Number.parseInt(raw, 10);
17340
- if (!Number.isFinite(parsed) || parsed <= 0) {
17341
- throw new Error(`${flag} must be a positive integer.`);
17905
+ async function writePlannedFiles(plan, cwd, options) {
17906
+ const written = [];
17907
+ for (const entry of plan.files) {
17908
+ if (entry.action === "skip") {
17909
+ continue;
17910
+ }
17911
+ const abs = path19__default.default.join(cwd, entry.path);
17912
+ if (options.dryRun) {
17913
+ written.push(entry.path);
17914
+ continue;
17915
+ }
17916
+ await promises.mkdir(path19__default.default.dirname(abs), { recursive: true });
17917
+ const content = entry.path === CONFIG_FILE ? configTemplate(plan.framework) : entry.path === GITKEEP ? "" : entry.path.endsWith(".yml") ? githubWorkflowTemplate() : demoTemplate(plan.framework);
17918
+ await promises.writeFile(abs, content, "utf-8");
17919
+ written.push(entry.path);
17342
17920
  }
17343
- return parsed;
17921
+ return written;
17344
17922
  }
17345
- async function newestTraceMtimeMs2(traceDir) {
17346
- let newest = 0;
17923
+ async function initCommand(options = {}) {
17924
+ const cwd = path19__default.default.resolve(options.cwd ?? process.cwd());
17347
17925
  try {
17348
- const files = await promises.readdir(traceDir);
17349
- for (const file of files) {
17350
- if (!file.endsWith(".jsonl")) continue;
17351
- try {
17352
- const s = await promises.stat(path16__default.default.join(traceDir, file));
17353
- if (s.mtimeMs > newest) newest = s.mtimeMs;
17354
- } catch {
17926
+ const plan = await planInit({ ...options, cwd });
17927
+ const toWrite = plan.files.filter((file) => file.action === "create").map((f) => f.path);
17928
+ const skipped = plan.files.filter((file) => file.action === "skip");
17929
+ if (options.json) {
17930
+ const payload = {
17931
+ ok: true,
17932
+ version,
17933
+ framework: plan.framework,
17934
+ ci: plan.ci ?? null,
17935
+ dryRun: options.dryRun === true,
17936
+ planned: plan.files,
17937
+ wouldWrite: options.dryRun ? toWrite : void 0
17938
+ };
17939
+ console.log(JSON.stringify(payload, null, 2));
17940
+ if (options.dryRun) return;
17941
+ }
17942
+ const written = await writePlannedFiles(plan, cwd, options);
17943
+ if (!options.json) {
17944
+ console.log("AgentInspect init");
17945
+ console.log(`Framework: ${plan.framework}`);
17946
+ console.log(`Trace directory: ${TRACE_DIR}/`);
17947
+ if (options.dryRun) {
17948
+ console.log("Dry run \u2014 would create:");
17949
+ for (const file of toWrite) console.log(`- ${file}`);
17950
+ for (const file of skipped) {
17951
+ console.log(`- ${file.path} (skip: ${file.reason ?? "exists"})`);
17952
+ }
17953
+ return;
17954
+ }
17955
+ console.log("Created:");
17956
+ for (const file of written) console.log(`- ${file}`);
17957
+ for (const file of skipped) {
17958
+ console.log(`- ${file.path} (skipped: ${file.reason ?? "exists"})`);
17355
17959
  }
17960
+ console.log("\nNext: run your demo, then `npx agent-inspect list --dir .agent-inspect`");
17961
+ console.log("No dependencies were installed. Add packages manually when ready.");
17356
17962
  }
17357
- } catch {
17358
- }
17359
- return newest;
17360
- }
17361
- async function indexSqliteBuildCommand(options = {}) {
17362
- const mod = await loadIndexSqlite();
17363
- if (!mod) return;
17364
- const result = await mod.buildIndex({
17365
- traceDir: options.dir,
17366
- maxRuns: parsePositiveInt(options.maxRuns, "--max-runs")
17367
- });
17368
- if (options.json) {
17369
- console.log(JSON.stringify({ ok: true, ...result }, null, 2));
17370
- return;
17371
- }
17372
- console.log(`Built SQLite index: ${result.dbPath}`);
17373
- console.log(`Runs: ${result.runs} Steps: ${result.steps} Errors: ${result.errors}`);
17374
- for (const warning of result.warnings) console.log(`warning: ${warning}`);
17375
- }
17376
- async function indexSqliteStatusCommand(options = {}) {
17377
- const mod = await loadIndexSqlite();
17378
- if (!mod) return;
17379
- const traceDir = resolveTraceDir({ dir: options.dir });
17380
- const dbPath = mod.resolveIndexDbPath(traceDir);
17381
- const status = mod.indexStatus(dbPath);
17382
- const stale = mod.isIndexStale(dbPath, await newestTraceMtimeMs2(traceDir));
17383
- if (options.json) {
17384
- console.log(JSON.stringify({ ok: true, traceDir, stale, ...status }, null, 2));
17385
- return;
17386
- }
17387
- if (!status.exists) {
17388
- console.log(`No SQLite index at ${dbPath}`);
17389
- console.log("Run: agent-inspect index sqlite build");
17390
- return;
17391
- }
17392
- console.log(`Index: ${status.dbPath}`);
17393
- console.log(`Healthy: ${status.healthy ? "yes" : "no"}`);
17394
- console.log(`Built: ${status.builtAt ?? "unknown"}`);
17395
- console.log(`Runs: ${status.runs} Steps: ${status.steps}`);
17396
- console.log(`Stale: ${stale ? "yes" : "no"}`);
17397
- }
17398
- async function indexSqliteQueryCommand(options = {}) {
17399
- const mod = await loadIndexSqlite();
17400
- if (!mod) return;
17401
- const traceDir = resolveTraceDir({ dir: options.dir });
17402
- const dbPath = mod.resolveIndexDbPath(traceDir);
17403
- const status = mod.indexStatus(dbPath);
17404
- if (!status.exists || !status.healthy) {
17963
+ } catch (error) {
17964
+ const msg = error instanceof Error ? error.message : String(error);
17405
17965
  if (options.json) {
17406
- console.log(JSON.stringify({ ok: false, reason: "index-missing", dbPath }, null, 2));
17966
+ console.log(JSON.stringify({ ok: false, error: msg }, null, 2));
17407
17967
  } else {
17408
- console.log("No usable SQLite index. Run: agent-inspect index sqlite build");
17968
+ console.error(`[AgentInspect] init failed: ${msg}`);
17409
17969
  }
17410
17970
  process.exitCode = 1;
17411
- return;
17412
- }
17413
- const rows = mod.queryRuns(dbPath, {
17414
- status: options.status,
17415
- sessionId: options.session,
17416
- name: options.name,
17417
- kind: options.kind,
17418
- tool: options.tool,
17419
- limit: parsePositiveInt(options.limit, "--limit")
17420
- });
17421
- if (options.json) {
17422
- console.log(JSON.stringify({ ok: true, count: rows.length, runs: rows }, null, 2));
17423
- return;
17424
- }
17425
- if (rows.length === 0) {
17426
- console.log("No matching runs.");
17427
- return;
17428
- }
17429
- for (const run of rows) {
17430
- const parts = [
17431
- run.runId,
17432
- run.status ?? "unknown",
17433
- run.name ?? "",
17434
- run.durationMs != null ? `${run.durationMs}ms` : ""
17435
- ].filter((p) => p !== "");
17436
- console.log(parts.join(" "));
17437
- }
17438
- }
17439
- async function indexSqliteCleanCommand(options = {}) {
17440
- const mod = await loadIndexSqlite();
17441
- if (!mod) return;
17442
- const traceDir = resolveTraceDir({ dir: options.dir });
17443
- const dbPath = mod.resolveIndexDbPath(traceDir);
17444
- await mod.cleanIndex(dbPath);
17445
- if (options.json) {
17446
- console.log(JSON.stringify({ ok: true, removed: dbPath }, null, 2));
17447
- return;
17448
17971
  }
17449
- console.log(`Removed SQLite index: ${dbPath}`);
17450
17972
  }
17451
-
17452
- // packages/core/src/workspace/types.ts
17453
- var WORKSPACE_SCHEMA_VERSION = "1.0";
17454
- var WORKSPACE_DIR_NAME = ".agent-inspect";
17455
- var WORKSPACE_MANIFEST_FILENAME = "workspace.json";
17456
-
17457
- // packages/core/src/workspace/manifest.ts
17458
- var DEFAULT_WORKSPACE_LAYOUT = {
17459
- traceDirs: ["runs"],
17460
- reportsDir: "reports",
17461
- artifactsDir: "artifacts",
17462
- bundlesDir: "bundles",
17463
- notesDir: "notes"
17973
+ var OPTIONAL_PACKAGES = {
17974
+ custom: [],
17975
+ "ai-sdk": ["@agent-inspect/ai-sdk"],
17976
+ "openai-agents": ["@agent-inspect/openai-agents"],
17977
+ langchain: ["@agent-inspect/langchain"]
17464
17978
  };
17465
- var DEFAULT_REDACTION_PROFILE = "share";
17466
- var REDACTION_PROFILES2 = [
17467
- "local",
17468
- "share",
17469
- "strict"
17470
- ];
17471
- var INDEX_TYPES = ["none", "sqlite", "custom"];
17472
- var MAX_WORKSPACE_MANIFEST_BYTES = 64 * 1024;
17473
- function createDefaultWorkspaceManifest(options) {
17474
- const project = typeof options.project === "string" ? options.project.trim() : "";
17475
- const index = {
17476
- enabled: options.index?.enabled ?? false,
17477
- type: options.index?.type ?? "none",
17478
- ...options.index?.path !== void 0 ? { path: options.index.path } : {}
17479
- };
17480
- return {
17481
- schemaVersion: WORKSPACE_SCHEMA_VERSION,
17482
- project,
17483
- createdAt: options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
17484
- traceDirs: options.traceDirs ? [...options.traceDirs] : [...DEFAULT_WORKSPACE_LAYOUT.traceDirs],
17485
- reportsDir: options.reportsDir ?? DEFAULT_WORKSPACE_LAYOUT.reportsDir,
17486
- artifactsDir: options.artifactsDir ?? DEFAULT_WORKSPACE_LAYOUT.artifactsDir,
17487
- bundlesDir: options.bundlesDir ?? DEFAULT_WORKSPACE_LAYOUT.bundlesDir,
17488
- notesDir: options.notesDir ?? DEFAULT_WORKSPACE_LAYOUT.notesDir,
17489
- redactionProfile: options.redactionProfile ?? DEFAULT_REDACTION_PROFILE,
17490
- index
17491
- };
17492
- }
17493
- function isPlainObject(value) {
17494
- return typeof value === "object" && value !== null && !Array.isArray(value);
17495
- }
17496
- function isSafeRelativeWorkspacePath(p) {
17497
- if (typeof p !== "string") return false;
17498
- const trimmed = p.trim();
17499
- if (trimmed === "") return false;
17500
- if (trimmed.startsWith("/") || trimmed.startsWith("\\")) return false;
17501
- if (/^[a-zA-Z]:/.test(trimmed)) return false;
17502
- const segments = trimmed.split(/[/\\]+/);
17503
- return !segments.some((seg) => seg === "..");
17504
- }
17505
- function validateDirField(value, field, errors) {
17506
- if (typeof value !== "string" || value.trim() === "") {
17507
- errors.push(`${field} must be a non-empty string`);
17508
- return;
17509
- }
17510
- if (!isSafeRelativeWorkspacePath(value)) {
17511
- errors.push(
17512
- `${field} must be a relative path inside the workspace (no absolute paths or ".." traversal)`
17513
- );
17979
+ function nodeVersionCheck() {
17980
+ const major = Number(process3__default.default.versions.node.split(".")[0]);
17981
+ if (Number.isNaN(major) || major < 20) {
17982
+ return {
17983
+ id: "node-version",
17984
+ status: "fail",
17985
+ message: `Node ${process3__default.default.versions.node} is below the supported minimum (20).`,
17986
+ remediation: "Upgrade to Node 20 LTS or newer.",
17987
+ evidence: process3__default.default.versions.node
17988
+ };
17514
17989
  }
17990
+ return {
17991
+ id: "node-version",
17992
+ status: "pass",
17993
+ message: `Node ${process3__default.default.versions.node} meets the minimum (>=20).`,
17994
+ evidence: process3__default.default.versions.node
17995
+ };
17515
17996
  }
17516
- function validateIndex(value, errors) {
17517
- if (!isPlainObject(value)) {
17518
- errors.push("index must be an object");
17519
- return void 0;
17520
- }
17521
- if (typeof value.enabled !== "boolean") {
17522
- errors.push("index.enabled must be a boolean");
17523
- }
17524
- if (!INDEX_TYPES.includes(value.type)) {
17525
- errors.push(`index.type must be one of: ${INDEX_TYPES.join(", ")}`);
17526
- }
17527
- if (value.path !== void 0 && !isSafeRelativeWorkspacePath(value.path)) {
17528
- errors.push(
17529
- 'index.path must be a relative path inside the workspace (no absolute paths or ".." traversal)'
17530
- );
17997
+ function envCheck(name, optional = true) {
17998
+ const value = process3__default.default.env[name];
17999
+ if (value === void 0 || value.trim() === "") {
18000
+ return {
18001
+ id: `env-${name.toLowerCase()}`,
18002
+ status: optional ? "skipped" : "warn",
18003
+ message: `${name} is not set.`,
18004
+ remediation: optional ? void 0 : `Set ${name}=1 to enable tracing.`
18005
+ };
17531
18006
  }
17532
- if (errors.length > 0) return void 0;
17533
18007
  return {
17534
- enabled: value.enabled,
17535
- type: value.type,
17536
- ...value.path !== void 0 ? { path: value.path } : {}
18008
+ id: `env-${name.toLowerCase()}`,
18009
+ status: "pass",
18010
+ message: `${name} is set.`,
18011
+ evidence: value
17537
18012
  };
17538
18013
  }
17539
- function validateWorkspaceManifest(input3) {
17540
- const errors = [];
17541
- const warnings = [];
17542
- if (!isPlainObject(input3)) {
17543
- return { ok: false, errors: ["manifest must be an object"], warnings };
17544
- }
17545
- if (input3.schemaVersion !== WORKSPACE_SCHEMA_VERSION) {
17546
- errors.push(
17547
- `schemaVersion must be "${WORKSPACE_SCHEMA_VERSION}" (received ${JSON.stringify(
17548
- input3.schemaVersion
17549
- )})`
17550
- );
17551
- }
17552
- if (typeof input3.project !== "string" || input3.project.trim() === "") {
17553
- errors.push("project must be a non-empty string");
18014
+ async function traceDirWritable(traceDir) {
18015
+ const resolved = path19__default.default.resolve(traceDir);
18016
+ try {
18017
+ await promises.mkdir(resolved, { recursive: true });
18018
+ await promises.access(resolved, promises.constants.W_OK);
18019
+ return {
18020
+ id: "trace-dir-writable",
18021
+ status: "pass",
18022
+ message: `Trace directory is writable: ${resolved}`,
18023
+ evidence: resolved
18024
+ };
18025
+ } catch (error) {
18026
+ return {
18027
+ id: "trace-dir-writable",
18028
+ status: "fail",
18029
+ message: `Trace directory is not writable: ${resolved}`,
18030
+ remediation: "Create the directory or set AGENT_INSPECT_TRACE_DIR to a writable path.",
18031
+ evidence: error instanceof Error ? error.message : String(error)
18032
+ };
17554
18033
  }
17555
- if (typeof input3.createdAt !== "string" || input3.createdAt.trim() === "") {
17556
- errors.push("createdAt must be a non-empty ISO-8601 string");
17557
- } else if (Number.isNaN(Date.parse(input3.createdAt))) {
17558
- errors.push("createdAt must be a valid ISO-8601 date string");
18034
+ }
18035
+ function resolvePackage(cwd, name) {
18036
+ const require2 = module$1.createRequire(path19__default.default.join(cwd, "package.json"));
18037
+ try {
18038
+ const pkgPath = require2.resolve(`${name}/package.json`);
18039
+ const pkg = require2(pkgPath);
18040
+ return { ok: true, version: pkg.version };
18041
+ } catch {
18042
+ return { ok: false };
17559
18043
  }
17560
- if (!Array.isArray(input3.traceDirs) || input3.traceDirs.length === 0) {
17561
- errors.push("traceDirs must be a non-empty array");
17562
- } else {
17563
- input3.traceDirs.forEach((dir, i) => {
17564
- if (typeof dir !== "string" || dir.trim() === "") {
17565
- errors.push(`traceDirs[${i}] must be a non-empty string`);
17566
- } else if (!isSafeRelativeWorkspacePath(dir)) {
17567
- errors.push(
17568
- `traceDirs[${i}] must be a relative path inside the workspace (no absolute paths or ".." traversal)`
17569
- );
17570
- }
18044
+ }
18045
+ function importSmoke(cwd) {
18046
+ const results = [];
18047
+ const require2 = module$1.createRequire(path19__default.default.join(cwd, "package.json"));
18048
+ try {
18049
+ require2.resolve("agent-inspect");
18050
+ results.push({
18051
+ id: "import-agent-inspect",
18052
+ status: "pass",
18053
+ message: "agent-inspect resolves from the current project."
18054
+ });
18055
+ } catch {
18056
+ results.push({
18057
+ id: "import-agent-inspect",
18058
+ status: "warn",
18059
+ message: "agent-inspect is not installed in the current project.",
18060
+ remediation: "Run npm install agent-inspect (or pnpm add agent-inspect)."
17571
18061
  });
17572
18062
  }
17573
- validateDirField(input3.reportsDir, "reportsDir", errors);
17574
- validateDirField(input3.artifactsDir, "artifactsDir", errors);
17575
- validateDirField(input3.bundlesDir, "bundlesDir", errors);
17576
- validateDirField(input3.notesDir, "notesDir", errors);
17577
- if (!REDACTION_PROFILES2.includes(input3.redactionProfile)) {
17578
- errors.push(`redactionProfile must be one of: ${REDACTION_PROFILES2.join(", ")}`);
18063
+ try {
18064
+ require2.resolve("agent-inspect/package.json");
18065
+ results.push({
18066
+ id: "import-agent-inspect-cjs",
18067
+ status: "pass",
18068
+ message: "CJS resolution for agent-inspect succeeded."
18069
+ });
18070
+ } catch {
18071
+ results.push({
18072
+ id: "import-agent-inspect-cjs",
18073
+ status: "skipped",
18074
+ message: "CJS resolution check skipped (package not installed locally)."
18075
+ });
17579
18076
  }
17580
- const indexErrors = [];
17581
- const index = validateIndex(input3.index, indexErrors);
17582
- errors.push(...indexErrors);
17583
- if (index && index.type !== "none" && !index.enabled) {
17584
- warnings.push(`index.type is "${index.type}" but index.enabled is false`);
18077
+ return results;
18078
+ }
18079
+ function optionalPackageChecks(cwd, framework) {
18080
+ const packages = framework !== void 0 ? OPTIONAL_PACKAGES[framework] : [
18081
+ "@agent-inspect/ai-sdk",
18082
+ "@agent-inspect/openai-agents",
18083
+ "@agent-inspect/langchain",
18084
+ "@agent-inspect/redact",
18085
+ "@agent-inspect/eval"
18086
+ ];
18087
+ return packages.map((name) => {
18088
+ const resolved = resolvePackage(cwd, name);
18089
+ if (!resolved.ok) {
18090
+ return {
18091
+ id: `optional-package-${name}`,
18092
+ status: framework !== void 0 ? "warn" : "skipped",
18093
+ message: `${name} is not installed.`,
18094
+ remediation: framework !== void 0 ? `npm install ${name}` : void 0
18095
+ };
18096
+ }
18097
+ return {
18098
+ id: `optional-package-${name}`,
18099
+ status: "pass",
18100
+ message: `${name} is available.`,
18101
+ evidence: resolved.version
18102
+ };
18103
+ });
18104
+ }
18105
+ function versionMismatchCheck(cwd) {
18106
+ const root = resolvePackage(cwd, "agent-inspect");
18107
+ if (!root.ok || root.version === void 0) {
18108
+ return {
18109
+ id: "version-alignment",
18110
+ status: "skipped",
18111
+ message: "Skipped version alignment \u2014 agent-inspect not installed locally."
18112
+ };
17585
18113
  }
17586
- if (errors.length > 0 || index === void 0) {
17587
- return { ok: false, errors, warnings };
18114
+ if (root.version !== version) {
18115
+ return {
18116
+ id: "version-alignment",
18117
+ status: "warn",
18118
+ message: `CLI version ${version} differs from local agent-inspect@${root.version}.`,
18119
+ remediation: "Align versions with npm install agent-inspect@latest",
18120
+ evidence: `cli=${version};local=${root.version}`
18121
+ };
17588
18122
  }
17589
- const manifest = {
17590
- schemaVersion: WORKSPACE_SCHEMA_VERSION,
17591
- project: input3.project.trim(),
17592
- createdAt: input3.createdAt,
17593
- traceDirs: input3.traceDirs.map((d) => d.trim()),
17594
- reportsDir: input3.reportsDir.trim(),
17595
- artifactsDir: input3.artifactsDir.trim(),
17596
- bundlesDir: input3.bundlesDir.trim(),
17597
- notesDir: input3.notesDir.trim(),
17598
- redactionProfile: input3.redactionProfile,
17599
- index
18123
+ return {
18124
+ id: "version-alignment",
18125
+ status: "pass",
18126
+ message: `CLI and local agent-inspect are aligned at ${version}.`
17600
18127
  };
17601
- return { ok: true, manifest, errors, warnings };
17602
18128
  }
17603
- function parseWorkspaceManifest(json) {
17604
- const warnings = [];
17605
- if (typeof json !== "string") {
17606
- return { ok: false, errors: ["manifest input must be a string"], warnings };
18129
+ async function runDoctorChecks(options = {}) {
18130
+ const cwd = path19__default.default.resolve(options.cwd ?? process3__default.default.cwd());
18131
+ const traceDir = options.traceDir?.trim() || process3__default.default.env.AGENT_INSPECT_TRACE_DIR?.trim() || ".agent-inspect";
18132
+ const checks2 = [
18133
+ nodeVersionCheck(),
18134
+ {
18135
+ id: "cli-version",
18136
+ status: "pass",
18137
+ message: `agent-inspect CLI ${version}`,
18138
+ evidence: version
18139
+ },
18140
+ await traceDirWritable(traceDir),
18141
+ envCheck("AGENT_INSPECT"),
18142
+ envCheck("AGENT_INSPECT_TRACE_DIR"),
18143
+ versionMismatchCheck(cwd)
18144
+ ];
18145
+ if (options.checkImports !== false) {
18146
+ checks2.push(...importSmoke(cwd));
17607
18147
  }
17608
- if (json.length > MAX_WORKSPACE_MANIFEST_BYTES) {
17609
- return {
17610
- ok: false,
17611
- errors: [
17612
- `manifest exceeds maximum size of ${MAX_WORKSPACE_MANIFEST_BYTES} bytes`
17613
- ],
17614
- warnings
17615
- };
18148
+ checks2.push(...optionalPackageChecks(cwd, options.framework));
18149
+ return checks2.sort((a, b) => a.id.localeCompare(b.id));
18150
+ }
18151
+ async function doctorCommand(options = {}) {
18152
+ const checks2 = await runDoctorChecks(options);
18153
+ const failed = checks2.filter((check) => check.status === "fail").length;
18154
+ const warned = checks2.filter((check) => check.status === "warn").length;
18155
+ if (options.json) {
18156
+ console.log(
18157
+ JSON.stringify(
18158
+ {
18159
+ ok: failed === 0,
18160
+ version,
18161
+ summary: { pass: checks2.filter((c) => c.status === "pass").length, warn: warned, fail: failed },
18162
+ checks: checks2
18163
+ },
18164
+ null,
18165
+ 2
18166
+ )
18167
+ );
18168
+ if (failed > 0) process3__default.default.exitCode = 1;
18169
+ return;
17616
18170
  }
17617
- let parsed;
17618
- try {
17619
- parsed = JSON.parse(json);
17620
- } catch {
17621
- return { ok: false, errors: ["manifest is not valid JSON"], warnings };
18171
+ console.log("AgentInspect doctor");
18172
+ for (const check of checks2) {
18173
+ const tag = check.status.toUpperCase();
18174
+ console.log(`[${tag}] ${check.id}: ${check.message}`);
18175
+ if (check.remediation) console.log(` \u2192 ${check.remediation}`);
17622
18176
  }
17623
- return validateWorkspaceManifest(parsed);
17624
- }
17625
- function serializeWorkspaceManifest(manifest) {
17626
- return `${JSON.stringify(manifest, null, 2)}
17627
- `;
18177
+ console.log(`
18178
+ Summary: ${failed} failed, ${warned} warnings`);
18179
+ if (failed > 0) process3__default.default.exitCode = 1;
17628
18180
  }
17629
- var INDEX_DIR_NAME = "index";
17630
- function resolveWorkspaceLocation(cwd = process.cwd()) {
17631
- const projectRoot = path16__default.default.resolve(cwd);
17632
- const workspaceDir = path16__default.default.join(projectRoot, WORKSPACE_DIR_NAME);
17633
- return {
17634
- projectRoot,
17635
- workspaceDir,
17636
- manifestPath: path16__default.default.join(workspaceDir, WORKSPACE_MANIFEST_FILENAME)
17637
- };
18181
+
18182
+ // packages/adapter-sdk/src/indexer.ts
18183
+ init_advanced();
18184
+ function defineIndexer(indexer) {
18185
+ if (!indexer.id.trim()) throw new Error("indexer id is required");
18186
+ return indexer;
17638
18187
  }
17639
- function resolveInsideWorkspace(workspaceDir, relative) {
17640
- const base = path16__default.default.resolve(workspaceDir);
17641
- const resolved = path16__default.default.resolve(base, relative);
17642
- const rel = path16__default.default.relative(base, resolved);
17643
- if (rel === "" || rel === "." || !rel.startsWith("..") && !path16__default.default.isAbsolute(rel)) {
17644
- return resolved;
18188
+ async function indexIsStale(snapshot, traceDir) {
18189
+ const builtMs = Date.parse(snapshot.builtAt);
18190
+ if (Number.isNaN(builtMs)) return true;
18191
+ const td = new TraceDirectory({ dir: traceDir });
18192
+ const files = await td.list();
18193
+ for (const file of files) {
18194
+ const stats = await td.getFileStats(file);
18195
+ if (stats.mtimeMs > builtMs) return true;
17645
18196
  }
17646
- throw new Error(
17647
- `Workspace path "${relative}" resolves outside the workspace directory`
17648
- );
18197
+ return false;
17649
18198
  }
17650
- async function pathExists(p) {
17651
- try {
17652
- await promises.access(p);
17653
- return true;
17654
- } catch {
17655
- return false;
18199
+ function createTraceDirectoryIndexer() {
18200
+ return defineIndexer({
18201
+ id: "trace-directory-metadata",
18202
+ async rebuild(traceDir, options = {}) {
18203
+ const warnings = [];
18204
+ const td = new TraceDirectory({ dir: traceDir });
18205
+ const files = await td.list();
18206
+ const maxEntries = options.maxEntries ?? 1e4;
18207
+ if (files.length > maxEntries) {
18208
+ warnings.push(
18209
+ `indexer.truncated: trace directory has ${files.length} files; indexing first ${maxEntries}`
18210
+ );
18211
+ }
18212
+ const slice = files.slice(0, maxEntries);
18213
+ const metas = await loadTraceMetadataList(
18214
+ traceDir,
18215
+ slice,
18216
+ (fileName) => td.getPath(fileName)
18217
+ );
18218
+ const entries = metas.map((meta2) => ({
18219
+ runId: meta2.runId,
18220
+ path: meta2.filePath,
18221
+ name: meta2.name,
18222
+ startedAt: meta2.startedAt,
18223
+ status: meta2.status
18224
+ })).sort((a, b) => a.runId.localeCompare(b.runId));
18225
+ if (entries.length < slice.length) {
18226
+ warnings.push(
18227
+ `indexer.partial: indexed ${entries.length} of ${slice.length} trace files`
18228
+ );
18229
+ }
18230
+ return {
18231
+ traceDir,
18232
+ builtAt: (/* @__PURE__ */ new Date()).toISOString(),
18233
+ entries,
18234
+ warnings
18235
+ };
18236
+ }
18237
+ });
18238
+ }
18239
+
18240
+ // packages/cli/src/index-cmd.ts
18241
+ init_advanced();
18242
+ var INDEX_FILENAME = ".agent-inspect-index.json";
18243
+ function traceIndexPath(traceDir) {
18244
+ return path19__default.default.join(traceDir, INDEX_FILENAME);
18245
+ }
18246
+ function parseMaxEntries(raw) {
18247
+ if (raw === void 0 || raw.trim() === "") return void 0;
18248
+ const parsed = Number.parseInt(raw, 10);
18249
+ if (!Number.isFinite(parsed) || parsed <= 0) {
18250
+ throw new Error("--max-entries must be a positive integer.");
17656
18251
  }
18252
+ return parsed;
17657
18253
  }
17658
- async function isWritable(p) {
18254
+ async function readSnapshot(indexPath2) {
17659
18255
  try {
17660
- await promises.access(p, fs.constants.W_OK);
17661
- return true;
18256
+ const raw = await promises.readFile(indexPath2, "utf8");
18257
+ return JSON.parse(raw);
17662
18258
  } catch {
17663
- return false;
18259
+ return void 0;
17664
18260
  }
17665
18261
  }
17666
- async function listJsonl(dir) {
18262
+ async function indexBuildCommand(options = {}) {
17667
18263
  try {
17668
- const entries = await promises.readdir(dir);
17669
- return entries.filter((f) => f.endsWith(".jsonl"));
17670
- } catch {
17671
- return [];
18264
+ const traceDir = resolveTraceDir({ dir: options.dir });
18265
+ await promises.mkdir(traceDir, { recursive: true });
18266
+ const indexer = createTraceDirectoryIndexer();
18267
+ const snapshot = await indexer.rebuild(traceDir, {
18268
+ maxEntries: parseMaxEntries(options.maxEntries)
18269
+ });
18270
+ const indexPath2 = traceIndexPath(traceDir);
18271
+ await promises.writeFile(indexPath2, `${JSON.stringify(snapshot, null, 2)}
18272
+ `, "utf8");
18273
+ if (options.json) {
18274
+ console.log(JSON.stringify({ ok: true, indexPath: indexPath2, ...snapshot }, null, 2));
18275
+ return;
18276
+ }
18277
+ console.log(`Built trace index: ${indexPath2}`);
18278
+ console.log(`Entries: ${snapshot.entries.length}`);
18279
+ if (snapshot.warnings.length > 0) {
18280
+ for (const warning of snapshot.warnings) {
18281
+ console.log(`warning: ${warning}`);
18282
+ }
18283
+ }
18284
+ } catch (e) {
18285
+ const msg = e instanceof Error ? e.message : String(e);
18286
+ console.error(`[AgentInspect] index build failed: ${msg}`);
18287
+ process.exitCode = 1;
17672
18288
  }
17673
18289
  }
17674
- async function countFiles(dir) {
18290
+ async function indexStatusCommand(options = {}) {
17675
18291
  try {
17676
- const entries = await promises.readdir(dir, { withFileTypes: true });
17677
- return entries.filter((e) => e.isFile()).length;
17678
- } catch {
17679
- return 0;
18292
+ const traceDir = resolveTraceDir({ dir: options.dir });
18293
+ const indexPath2 = traceIndexPath(traceDir);
18294
+ const snapshot = await readSnapshot(indexPath2);
18295
+ if (!snapshot) {
18296
+ const payload2 = { ok: true, exists: false, indexPath: indexPath2, traceDir, stale: true };
18297
+ if (options.json) {
18298
+ console.log(JSON.stringify(payload2, null, 2));
18299
+ } else {
18300
+ console.log(`No index at ${indexPath2}`);
18301
+ console.log("Run: agent-inspect index build");
18302
+ }
18303
+ return;
18304
+ }
18305
+ const stale = await indexIsStale(snapshot, traceDir);
18306
+ const payload = {
18307
+ ok: true,
18308
+ exists: true,
18309
+ indexPath: indexPath2,
18310
+ traceDir,
18311
+ stale,
18312
+ builtAt: snapshot.builtAt,
18313
+ entries: snapshot.entries.length,
18314
+ warnings: snapshot.warnings
18315
+ };
18316
+ if (options.json) {
18317
+ console.log(JSON.stringify(payload, null, 2));
18318
+ return;
18319
+ }
18320
+ console.log(`Index: ${indexPath2}`);
18321
+ console.log(`Built: ${snapshot.builtAt}`);
18322
+ console.log(`Entries: ${snapshot.entries.length}`);
18323
+ console.log(`Stale: ${stale ? "yes" : "no"}`);
18324
+ if (snapshot.warnings.length > 0) {
18325
+ for (const warning of snapshot.warnings) {
18326
+ console.log(`warning: ${warning}`);
18327
+ }
18328
+ }
18329
+ } catch (e) {
18330
+ const msg = e instanceof Error ? e.message : String(e);
18331
+ console.error(`[AgentInspect] index status failed: ${msg}`);
18332
+ process.exitCode = 1;
17680
18333
  }
17681
18334
  }
17682
- async function readWorkspaceManifestFile(location) {
17683
- let raw;
18335
+ async function indexCleanCommand(options = {}) {
17684
18336
  try {
17685
- raw = await promises.readFile(location.manifestPath, "utf-8");
17686
- } catch {
17687
- return { exists: false, ok: false, errors: ["workspace.json not found"], warnings: [] };
18337
+ const traceDir = resolveTraceDir({ dir: options.dir });
18338
+ const indexPath2 = traceIndexPath(traceDir);
18339
+ await promises.rm(indexPath2, { force: true });
18340
+ if (options.json) {
18341
+ console.log(JSON.stringify({ ok: true, removed: indexPath2 }, null, 2));
18342
+ return;
18343
+ }
18344
+ console.log(`Removed index: ${indexPath2}`);
18345
+ } catch (e) {
18346
+ const msg = e instanceof Error ? e.message : String(e);
18347
+ console.error(`[AgentInspect] index clean failed: ${msg}`);
18348
+ process.exitCode = 1;
17688
18349
  }
17689
- const parsed = parseWorkspaceManifest(raw);
17690
- return {
17691
- exists: true,
17692
- ok: parsed.ok,
17693
- ...parsed.manifest ? { manifest: parsed.manifest } : {},
17694
- errors: parsed.errors,
17695
- warnings: parsed.warnings
17696
- };
17697
18350
  }
17698
- async function createWorkspace(options = {}) {
17699
- const location = resolveWorkspaceLocation(options.cwd);
17700
- const dryRun = options.dryRun === true;
17701
- const existing = await readWorkspaceManifestFile(location);
17702
- const topLevelTraces = await listJsonl(location.workspaceDir);
17703
- const detectedExistingTraces = topLevelTraces.length > 0;
17704
- let manifest;
17705
- let created;
17706
- let adopted;
17707
- if (existing.exists && existing.ok && existing.manifest) {
17708
- manifest = existing.manifest;
17709
- created = false;
17710
- adopted = true;
17711
- } else {
17712
- const project = options.project?.trim() || path16__default.default.basename(location.projectRoot) || "workspace";
17713
- const traceDirs = detectedExistingTraces ? ["runs", "."] : ["runs"];
17714
- manifest = createDefaultWorkspaceManifest({
17715
- project,
17716
- traceDirs,
17717
- ...options.redactionProfile ? { redactionProfile: options.redactionProfile } : {}
17718
- });
17719
- created = true;
17720
- adopted = detectedExistingTraces || existing.exists && !existing.ok;
17721
- }
17722
- const relDirs = uniqueDirs([
17723
- ...manifest.traceDirs.filter((d) => d !== "."),
17724
- manifest.reportsDir,
17725
- manifest.artifactsDir,
17726
- manifest.bundlesDir,
17727
- manifest.notesDir,
17728
- INDEX_DIR_NAME
17729
- ]);
17730
- const createdDirs = [];
17731
- for (const rel of relDirs) {
17732
- const abs = resolveInsideWorkspace(location.workspaceDir, rel);
17733
- if (await pathExists(abs)) continue;
17734
- createdDirs.push(rel);
17735
- if (!dryRun) await promises.mkdir(abs, { recursive: true });
17736
- }
17737
- if (!dryRun && created) {
17738
- await promises.mkdir(location.workspaceDir, { recursive: true });
17739
- await promises.writeFile(location.manifestPath, serializeWorkspaceManifest(manifest), "utf-8");
17740
- }
17741
- return {
17742
- location,
17743
- manifest,
17744
- created,
17745
- adopted,
17746
- createdDirs,
17747
- detectedExistingTraces,
17748
- dryRun
17749
- };
18351
+
18352
+ // packages/cli/src/index-sqlite-cmd.ts
18353
+ init_advanced();
18354
+ var PACKAGE = "@agent-inspect/index-sqlite";
18355
+ function isModuleNotFound3(e) {
18356
+ return e !== null && typeof e === "object" && "code" in e && (e.code === "ERR_MODULE_NOT_FOUND" || e.code === "MODULE_NOT_FOUND");
17750
18357
  }
17751
- function uniqueDirs(dirs) {
17752
- const seen = /* @__PURE__ */ new Set();
17753
- const out = [];
17754
- for (const d of dirs) {
17755
- const t = d.trim();
17756
- if (t === "" || t === "." || seen.has(t)) continue;
17757
- seen.add(t);
17758
- out.push(t);
18358
+ async function loadIndexSqlite() {
18359
+ try {
18360
+ return await Promise.resolve().then(() => (init_src(), src_exports));
18361
+ } catch (e) {
18362
+ if (isModuleNotFound3(e)) {
18363
+ console.error(
18364
+ `The optional SQLite index is not installed. Run: npm install ${PACKAGE}`
18365
+ );
18366
+ process.exitCode = 1;
18367
+ return null;
18368
+ }
18369
+ const msg = e instanceof Error ? e.message : String(e);
18370
+ console.error(`[AgentInspect] failed to load ${PACKAGE}: ${msg}`);
18371
+ process.exitCode = 1;
18372
+ return null;
17759
18373
  }
17760
- return out;
17761
18374
  }
17762
- async function getWorkspaceStatus(location, manifest) {
17763
- let traceFiles = 0;
17764
- for (const rel of manifest.traceDirs) {
17765
- const abs = resolveInsideWorkspace(location.workspaceDir, rel);
17766
- traceFiles += (await listJsonl(abs)).length;
18375
+ function parsePositiveInt(raw, flag) {
18376
+ if (raw === void 0 || raw.trim() === "") return void 0;
18377
+ const parsed = Number.parseInt(raw, 10);
18378
+ if (!Number.isFinite(parsed) || parsed <= 0) {
18379
+ throw new Error(`${flag} must be a positive integer.`);
17767
18380
  }
17768
- const reports = await countFiles(
17769
- resolveInsideWorkspace(location.workspaceDir, manifest.reportsDir)
17770
- );
17771
- const artifacts = await countFiles(
17772
- resolveInsideWorkspace(location.workspaceDir, manifest.artifactsDir)
17773
- );
17774
- const bundles = await countFiles(
17775
- resolveInsideWorkspace(location.workspaceDir, manifest.bundlesDir)
17776
- );
17777
- const notes = await countFiles(
17778
- resolveInsideWorkspace(location.workspaceDir, manifest.notesDir)
17779
- );
17780
- const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
17781
- return {
17782
- project: manifest.project,
17783
- traceFiles,
17784
- reports,
17785
- artifacts,
17786
- bundles,
17787
- notes,
17788
- index: {
17789
- enabled: manifest.index.enabled,
17790
- type: manifest.index.type,
17791
- exists: await pathExists(indexPath2)
18381
+ return parsed;
18382
+ }
18383
+ async function newestTraceMtimeMs2(traceDir) {
18384
+ let newest = 0;
18385
+ try {
18386
+ const files = await promises.readdir(traceDir);
18387
+ for (const file of files) {
18388
+ if (!file.endsWith(".jsonl")) continue;
18389
+ try {
18390
+ const s = await promises.stat(path19__default.default.join(traceDir, file));
18391
+ if (s.mtimeMs > newest) newest = s.mtimeMs;
18392
+ } catch {
18393
+ }
17792
18394
  }
17793
- };
18395
+ } catch {
18396
+ }
18397
+ return newest;
17794
18398
  }
17795
- async function doctorWorkspace(location) {
17796
- const checks2 = [];
17797
- const manifestResult = await readWorkspaceManifestFile(location);
17798
- if (!manifestResult.exists) {
17799
- checks2.push({
17800
- id: "manifest",
17801
- status: "fail",
17802
- message: "workspace.json not found (run `agent-inspect workspace init`)"
17803
- });
17804
- return { ok: false, checks: checks2 };
18399
+ async function indexSqliteBuildCommand(options = {}) {
18400
+ const mod = await loadIndexSqlite();
18401
+ if (!mod) return;
18402
+ const result = await mod.buildIndex({
18403
+ traceDir: options.dir,
18404
+ maxRuns: parsePositiveInt(options.maxRuns, "--max-runs")
18405
+ });
18406
+ if (options.json) {
18407
+ console.log(JSON.stringify({ ok: true, ...result }, null, 2));
18408
+ return;
17805
18409
  }
17806
- if (!manifestResult.ok || !manifestResult.manifest) {
17807
- checks2.push({
17808
- id: "manifest",
17809
- status: "fail",
17810
- message: `workspace.json is invalid: ${manifestResult.errors.join("; ")}`
17811
- });
17812
- return { ok: false, checks: checks2 };
18410
+ console.log(`Built SQLite index: ${result.dbPath}`);
18411
+ console.log(`Runs: ${result.runs} Steps: ${result.steps} Errors: ${result.errors}`);
18412
+ for (const warning of result.warnings) console.log(`warning: ${warning}`);
18413
+ }
18414
+ async function indexSqliteStatusCommand(options = {}) {
18415
+ const mod = await loadIndexSqlite();
18416
+ if (!mod) return;
18417
+ const traceDir = resolveTraceDir({ dir: options.dir });
18418
+ const dbPath = mod.resolveIndexDbPath(traceDir);
18419
+ const status = mod.indexStatus(dbPath);
18420
+ const stale = mod.isIndexStale(dbPath, await newestTraceMtimeMs2(traceDir));
18421
+ if (options.json) {
18422
+ console.log(JSON.stringify({ ok: true, traceDir, stale, ...status }, null, 2));
18423
+ return;
17813
18424
  }
17814
- const manifest = manifestResult.manifest;
17815
- checks2.push({ id: "manifest", status: "pass", message: "workspace.json is valid" });
17816
- for (const warning of manifestResult.warnings) {
17817
- checks2.push({ id: "manifest-warning", status: "warn", message: warning });
18425
+ if (!status.exists) {
18426
+ console.log(`No SQLite index at ${dbPath}`);
18427
+ console.log("Run: agent-inspect index sqlite build");
18428
+ return;
17818
18429
  }
17819
- const dirFields = [
17820
- ...manifest.traceDirs.filter((d) => d !== ".").map((d, i) => [`traceDir[${i}]`, d]),
17821
- ["reportsDir", manifest.reportsDir],
17822
- ["artifactsDir", manifest.artifactsDir],
17823
- ["bundlesDir", manifest.bundlesDir],
17824
- ["notesDir", manifest.notesDir]
17825
- ];
17826
- for (const [id, rel] of dirFields) {
17827
- let abs;
17828
- try {
17829
- abs = resolveInsideWorkspace(location.workspaceDir, rel);
17830
- } catch (error) {
17831
- checks2.push({
17832
- id,
17833
- status: "fail",
17834
- message: error instanceof Error ? error.message : String(error)
17835
- });
17836
- continue;
17837
- }
17838
- if (!await pathExists(abs)) {
17839
- checks2.push({ id, status: "warn", message: `${rel}/ does not exist yet` });
17840
- } else if (!await isWritable(abs)) {
17841
- checks2.push({ id, status: "fail", message: `${rel}/ is not writable` });
18430
+ console.log(`Index: ${status.dbPath}`);
18431
+ console.log(`Healthy: ${status.healthy ? "yes" : "no"}`);
18432
+ console.log(`Built: ${status.builtAt ?? "unknown"}`);
18433
+ console.log(`Runs: ${status.runs} Steps: ${status.steps}`);
18434
+ console.log(`Stale: ${stale ? "yes" : "no"}`);
18435
+ }
18436
+ async function indexSqliteQueryCommand(options = {}) {
18437
+ const mod = await loadIndexSqlite();
18438
+ if (!mod) return;
18439
+ const traceDir = resolveTraceDir({ dir: options.dir });
18440
+ const dbPath = mod.resolveIndexDbPath(traceDir);
18441
+ const status = mod.indexStatus(dbPath);
18442
+ if (!status.exists || !status.healthy) {
18443
+ if (options.json) {
18444
+ console.log(JSON.stringify({ ok: false, reason: "index-missing", dbPath }, null, 2));
17842
18445
  } else {
17843
- checks2.push({ id, status: "pass", message: `${rel}/ is present and writable` });
18446
+ console.log("No usable SQLite index. Run: agent-inspect index sqlite build");
17844
18447
  }
18448
+ process.exitCode = 1;
18449
+ return;
17845
18450
  }
17846
- let newestTraceMtime = 0;
17847
- for (const rel of manifest.traceDirs) {
17848
- const abs = resolveInsideWorkspace(location.workspaceDir, rel);
17849
- for (const file of await listJsonl(abs)) {
17850
- try {
17851
- const s = await promises.stat(path16__default.default.join(abs, file));
17852
- newestTraceMtime = Math.max(newestTraceMtime, s.mtimeMs);
17853
- } catch {
17854
- checks2.push({ id: "trace-readability", status: "warn", message: `cannot stat ${rel}/${file}` });
17855
- }
17856
- }
18451
+ const rows = mod.queryRuns(dbPath, {
18452
+ status: options.status,
18453
+ sessionId: options.session,
18454
+ name: options.name,
18455
+ kind: options.kind,
18456
+ tool: options.tool,
18457
+ limit: parsePositiveInt(options.limit, "--limit")
18458
+ });
18459
+ if (options.json) {
18460
+ console.log(JSON.stringify({ ok: true, count: rows.length, runs: rows }, null, 2));
18461
+ return;
17857
18462
  }
17858
- if (manifest.index.enabled) {
17859
- const indexPath2 = manifest.index.path ? resolveInsideWorkspace(location.workspaceDir, manifest.index.path) : resolveInsideWorkspace(location.workspaceDir, INDEX_DIR_NAME);
17860
- if (!await pathExists(indexPath2)) {
17861
- checks2.push({ id: "index", status: "warn", message: "index enabled but not built" });
17862
- } else {
17863
- try {
17864
- const s = await promises.stat(indexPath2);
17865
- if (newestTraceMtime > s.mtimeMs) {
17866
- checks2.push({ id: "index", status: "warn", message: "index is stale (traces are newer)" });
17867
- } else {
17868
- checks2.push({ id: "index", status: "pass", message: "index is present" });
17869
- }
17870
- } catch {
17871
- checks2.push({ id: "index", status: "warn", message: "cannot stat index" });
17872
- }
17873
- }
18463
+ if (rows.length === 0) {
18464
+ console.log("No matching runs.");
18465
+ return;
18466
+ }
18467
+ for (const run of rows) {
18468
+ const parts = [
18469
+ run.runId,
18470
+ run.status ?? "unknown",
18471
+ run.name ?? "",
18472
+ run.durationMs != null ? `${run.durationMs}ms` : ""
18473
+ ].filter((p) => p !== "");
18474
+ console.log(parts.join(" "));
17874
18475
  }
17875
- const ok = !checks2.some((c) => c.status === "fail");
17876
- return { ok, checks: checks2 };
17877
18476
  }
17878
- async function cleanWorkspace(location, manifest, options = {}) {
17879
- const dryRun = options.confirm !== true;
17880
- const targets = uniqueDirs([
17881
- manifest.reportsDir,
17882
- manifest.artifactsDir,
17883
- manifest.bundlesDir,
17884
- manifest.index.path ?? INDEX_DIR_NAME
17885
- ]);
17886
- const removed = [];
17887
- for (const rel of targets) {
17888
- const abs = resolveInsideWorkspace(location.workspaceDir, rel);
17889
- let entries;
17890
- try {
17891
- entries = await promises.readdir(abs);
17892
- } catch {
17893
- continue;
17894
- }
17895
- for (const entry of entries) {
17896
- const relPath = `${rel}/${entry}`;
17897
- removed.push(relPath);
17898
- if (!dryRun) {
17899
- await promises.rm(path16__default.default.join(abs, entry), { recursive: true, force: true });
17900
- }
17901
- }
18477
+ async function indexSqliteCleanCommand(options = {}) {
18478
+ const mod = await loadIndexSqlite();
18479
+ if (!mod) return;
18480
+ const traceDir = resolveTraceDir({ dir: options.dir });
18481
+ const dbPath = mod.resolveIndexDbPath(traceDir);
18482
+ await mod.cleanIndex(dbPath);
18483
+ if (options.json) {
18484
+ console.log(JSON.stringify({ ok: true, removed: dbPath }, null, 2));
18485
+ return;
17902
18486
  }
17903
- return { dryRun, removed };
18487
+ console.log(`Removed SQLite index: ${dbPath}`);
17904
18488
  }
17905
18489
 
17906
18490
  // packages/cli/src/workspace.ts
@@ -18252,6 +18836,11 @@ function createCliProgram() {
18252
18836
  ).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--baseline <trace-path-or-run-id>", "optional baseline trace for diff artifacts").option("--baseline-run <run-id>", "select a run from the baseline trace").option("--github-summary <path>", "append a safe summary to this file, e.g. GITHUB_STEP_SUMMARY").option("--json", "print deterministic JSON manifest").action((target, opts) => {
18253
18837
  runCommand(() => artifactsCommand(target, opts));
18254
18838
  });
18839
+ program.command("bundle").description("Create a share-safe offline trace bundle (local folder)").argument("[run-id]", "run id to bundle (optional with --session or --since)").option("--dir <path>", "trace directory for run/session lookup").option("--session <session-id>", "bundle all runs in a session").option("--since <duration>", "bundle runs with activity since a duration (e.g. 24h)").addOption(
18840
+ new commander.Option("--profile <profile>", "redaction profile for exported copies").choices(["local", "share", "strict"]).default("share")
18841
+ ).option("--out <path>", "output directory (folder; .zip suffix is stripped)").option("--allow-unsafe", "write bundle even when verify-safe reports UNSAFE").option("--json", "print deterministic JSON manifest").action((runId, opts) => {
18842
+ runCommand(() => bundleCommand(runId, opts));
18843
+ });
18255
18844
  program.command("ci-summary").description("Summarize local reporter artifact manifests for CI").argument("<manifest...>", "reporter artifact manifest JSON files").option("-o, --output <path>", "write Markdown summary to a local file").option("--github-summary <path>", "append Markdown summary to this local file, e.g. GITHUB_STEP_SUMMARY").option("--json", "print deterministic JSON summary").action((manifest, opts) => {
18256
18845
  runCommand(() => ciSummaryCommand(manifest, opts));
18257
18846
  });
@@ -18456,9 +19045,9 @@ function isPrimaryModule() {
18456
19045
  if (!entry) return false;
18457
19046
  const selfPath = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
18458
19047
  try {
18459
- return fs.realpathSync(path16__default.default.resolve(entry)) === fs.realpathSync(path16__default.default.resolve(selfPath));
19048
+ return fs.realpathSync(path19__default.default.resolve(entry)) === fs.realpathSync(path19__default.default.resolve(selfPath));
18460
19049
  } catch {
18461
- return path16__default.default.resolve(entry) === path16__default.default.resolve(selfPath);
19050
+ return path19__default.default.resolve(entry) === path19__default.default.resolve(selfPath);
18462
19051
  }
18463
19052
  }
18464
19053
  if (isPrimaryModule()) {