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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8a21bce: Add share-safe offline trace bundles (`agent-inspect bundle`) with automatic verify-safe, default share redaction profile, session/since targeting, and folder output for PR-ready evidence.
8
+
3
9
  ## 4.2.0
4
10
 
5
11
  ### Minor Changes
package/docs/CLI.md CHANGED
@@ -37,6 +37,7 @@ Core commands:
37
37
  - `scan` — best-effort local safety scan for trace capture risks
38
38
  - `verify-safe` — best-effort local trace safety verification
39
39
  - `artifacts` — create safe local CI trace artifact bundles and optional step summaries
40
+ - `bundle` — create share-safe offline trace bundles (redacted copies + verify-safe)
40
41
  - `ci-summary` — summarize local reporter artifact manifests for CI
41
42
  - `diff` — compare two manual traces (local, read-only)
42
43
  - `timeline` — chronological view of one run (local JSONL)
@@ -860,6 +861,38 @@ agent-inspect index sqlite clean [--dir <path>] [--json]
860
861
 
861
862
  If `@agent-inspect/index-sqlite` is not installed, these subcommands print a short install hint and exit non-zero.
862
863
 
864
+ ### 6.24 `bundle`
865
+
866
+ Create a **share-safe offline trace bundle** as a local folder (v4.3+). Bundles are derived copies: original traces are read-only and never mutated. Automatic `verify-safe` runs before write; the command fails on `UNSAFE` unless `--allow-unsafe`. See [BUNDLES.md](BUNDLES.md).
867
+
868
+ ```bash
869
+ agent-inspect bundle <run-id> [options]
870
+ agent-inspect bundle --session <session-id> [options]
871
+ agent-inspect bundle --since <duration> [options]
872
+ ```
873
+
874
+ Options:
875
+
876
+ - `--dir <path>` — trace directory
877
+ - `--session <session-id>` — bundle all runs in a session
878
+ - `--since <duration>` — bundle runs with activity since a window (e.g. `24h`, `7d`)
879
+ - `--profile <profile>` — `local`, `share` (default), or `strict` redaction for exported copies
880
+ - `--out <path>` — output directory; `.zip` suffix is stripped (folder-first)
881
+ - `--allow-unsafe` — write bundle even when verify-safe reports UNSAFE
882
+ - `--json` — print deterministic JSON manifest
883
+
884
+ Output includes `trace.html`, `trace.jsonl`, `summary.md`, `metadata.json`, `check-results.json`, `redaction-report.json`, and `assets/runs/` mirrors for multi-run bundles.
885
+
886
+ Examples:
887
+
888
+ ```bash
889
+ npx agent-inspect bundle minimal-success --dir fixtures/traces --out ./bundle-out
890
+ npx agent-inspect bundle --session sess-handoff-001 --dir ./.agent-inspect --profile strict
891
+ npx agent-inspect bundle --since 24h --dir ./.agent-inspect --json
892
+ ```
893
+
894
+ Recipe: [shareable-bundle-basic](../examples/recipes/shareable-bundle-basic/README.md).
895
+
863
896
  ## 7. Optional TUI behavior
864
897
 
865
898
  `view --tui` delegates to `@agent-inspect/tui` and requires an interactive terminal. If the package is not installed, the CLI prints a short install hint.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-inspect",
3
- "version": "4.2.0",
3
+ "version": "4.3.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Trace, check, and safely share TypeScript AI-agent runs locally — no account, no upload, metadata-only by default",
@@ -3268,12 +3268,12 @@ function buildCriticalPath(runs, handoffs) {
3268
3268
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
3269
3269
  );
3270
3270
  const ordered = [...runs].sort(compareRuns);
3271
- const path5 = [];
3271
+ const path6 = [];
3272
3272
  const visited = /* @__PURE__ */ new Set();
3273
3273
  const pushRun = (run, confidence, source) => {
3274
3274
  if (visited.has(run.runId)) return;
3275
3275
  visited.add(run.runId);
3276
- path5.push({
3276
+ path6.push({
3277
3277
  runId: run.runId,
3278
3278
  name: run.name,
3279
3279
  startedAt: run.startedAt,
@@ -3298,7 +3298,7 @@ function buildCriticalPath(runs, handoffs) {
3298
3298
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
3299
3299
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
3300
3300
  }
3301
- return path5;
3301
+ return path6;
3302
3302
  }
3303
3303
  function metaRunIdMatches(run, token, runById) {
3304
3304
  const meta = extractSessionWorkflowMetadata(run.metadata);
@@ -3482,6 +3482,190 @@ async function isAgentInspectTrace(filePath) {
3482
3482
  }
3483
3483
  }
3484
3484
 
3485
- export { Redactor, TraceDirectory, __commonJS, __require, __toESM, aggregateSessionCheckResults, applyProfileMetadataCaps, buildActivitySummary, buildLocalExplanation, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, enrichSessionRunRecord, extractCorrelationMetadata, extractMetadata, filterMetasBySessionScope, filterTraces, formatDuration2 as formatDuration, formatTimestamp, getIndent, getTraceFilePath, isAgentInspectTrace, isPersistedInspectEvent, loadSessionRunRecords, loadTraceMetadataList, nanoid, parseDuration, parseDurationFilter, parseTraceJsonl, persistedInspectEventsToTraceEvents, renderActivitySummaryHuman, renderErrorLine, renderRunWhat, renderStepLine, renderTimeline, renderTraceStats, resolveRedactionProfile, resolveTraceDir, searchTraces, source_default, truncateName, truncateStringForProfile, validateEvent };
3486
- //# sourceMappingURL=chunk-5VSPJEZ7.mjs.map
3487
- //# sourceMappingURL=chunk-5VSPJEZ7.mjs.map
3485
+ // packages/core/src/bundle/resolve.ts
3486
+ function parseSinceCutoff(since) {
3487
+ const trimmed = since.trim();
3488
+ if (trimmed === "") {
3489
+ throw new Error("--since requires a non-empty duration (e.g. 24h, 7d).");
3490
+ }
3491
+ return Date.now() - parseDuration(trimmed);
3492
+ }
3493
+ function runActivityMs(run) {
3494
+ if (run.startedAt !== void 0 && Number.isFinite(run.startedAt)) return run.startedAt;
3495
+ if (run.endedAt !== void 0 && Number.isFinite(run.endedAt)) return run.endedAt;
3496
+ return void 0;
3497
+ }
3498
+ function runsInSinceWindow(runs, since) {
3499
+ const cutoff = parseSinceCutoff(since);
3500
+ const ids = [];
3501
+ for (const run of runs) {
3502
+ const activity = runActivityMs(run);
3503
+ if (activity !== void 0 && activity >= cutoff) {
3504
+ ids.push(run.runId);
3505
+ }
3506
+ }
3507
+ return ids.sort((a, b) => a.localeCompare(b));
3508
+ }
3509
+ function findSession(index, sessionId) {
3510
+ return index.sessions.find((session) => session.sessionId === sessionId);
3511
+ }
3512
+ function resolveBundleRunIds(index, runs, options) {
3513
+ const runId = options.runId?.trim();
3514
+ const sessionId = options.sessionId?.trim();
3515
+ const since = options.since?.trim();
3516
+ const modes = [runId ? 1 : 0, sessionId ? 1 : 0, since ? 1 : 0].reduce((a, b) => a + b, 0);
3517
+ if (modes === 0) {
3518
+ throw new Error(
3519
+ "bundle requires a run id, --session <sessionId>, or --since <duration>."
3520
+ );
3521
+ }
3522
+ if (modes > 1) {
3523
+ throw new Error(
3524
+ "bundle accepts only one target: a run id, --session, or --since (not combined)."
3525
+ );
3526
+ }
3527
+ if (runId) {
3528
+ const known = runs.some((run) => run.runId === runId);
3529
+ if (!known) {
3530
+ throw new Error(`Run "${runId}" was not found in the trace directory.`);
3531
+ }
3532
+ return { runIds: [runId] };
3533
+ }
3534
+ if (sessionId) {
3535
+ const session = findSession(index, sessionId);
3536
+ if (!session) {
3537
+ throw new Error(`Session "${sessionId}" was not found.`);
3538
+ }
3539
+ if (session.runIds.length === 0) {
3540
+ throw new Error(`Session "${sessionId}" has no runs to bundle.`);
3541
+ }
3542
+ return {
3543
+ runIds: [...session.runIds].sort((a, b) => a.localeCompare(b)),
3544
+ sessionId
3545
+ };
3546
+ }
3547
+ const runIds = runsInSinceWindow(runs, since);
3548
+ if (runIds.length === 0) {
3549
+ throw new Error(`No runs matched --since ${since}.`);
3550
+ }
3551
+ return { runIds, since };
3552
+ }
3553
+
3554
+ // packages/core/src/bundle/safety-status.ts
3555
+ function aggregateBundleSafeStatus(statuses) {
3556
+ if (statuses.length === 0) return "UNKNOWN";
3557
+ if (statuses.some((status) => status === "UNSAFE")) return "UNSAFE";
3558
+ if (statuses.some((status) => status === "UNKNOWN")) return "UNKNOWN";
3559
+ if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
3560
+ return "SAFE";
3561
+ }
3562
+ function toMetadataSafeStatus(status) {
3563
+ if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
3564
+ return status;
3565
+ }
3566
+ function bundleFailsOnSafety(status, allowUnsafe) {
3567
+ if (allowUnsafe) return false;
3568
+ return status === "UNSAFE" || status === "UNKNOWN";
3569
+ }
3570
+
3571
+ // packages/core/src/bundle/manifest.ts
3572
+ var BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
3573
+ var PLACEHOLDER_NOTE = "No eval or performance artifacts were requested for this bundle.";
3574
+ function buildBundleMetadata(parts) {
3575
+ const aggregate = aggregateBundleSafeStatus(
3576
+ parts.checks.runs.map((run) => run.status)
3577
+ );
3578
+ return {
3579
+ createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
3580
+ agentInspectVersion: parts.agentInspectVersion,
3581
+ redactionProfile: parts.profile,
3582
+ sourceTraceCount: parts.resolve.runIds.length,
3583
+ runIds: [...parts.resolve.runIds],
3584
+ safeStatus: toMetadataSafeStatus(aggregate),
3585
+ files: [...parts.files].sort((a, b) => a.localeCompare(b)),
3586
+ note: BUNDLE_NOTE,
3587
+ ...parts.resolve.sessionId !== void 0 ? { sessionId: parts.resolve.sessionId } : {},
3588
+ ...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
3589
+ };
3590
+ }
3591
+ function buildPlaceholderArtifact() {
3592
+ return {
3593
+ status: "not_requested",
3594
+ note: PLACEHOLDER_NOTE
3595
+ };
3596
+ }
3597
+
3598
+ // packages/core/src/bundle/summary.ts
3599
+ function markdownTable(rows) {
3600
+ const lines = ["| Field | Value |", "| --- | --- |"];
3601
+ for (const [key, value] of rows) {
3602
+ lines.push(`| ${key} | ${value ?? "unknown"} |`);
3603
+ }
3604
+ return lines.join("\n");
3605
+ }
3606
+ function buildBundleSummaryMarkdown(parts) {
3607
+ const { metadata, checks, redaction } = parts;
3608
+ const lines = [
3609
+ "# AgentInspect trace bundle",
3610
+ "",
3611
+ metadata.note,
3612
+ "",
3613
+ "## Overview",
3614
+ "",
3615
+ markdownTable([
3616
+ ["Created", metadata.createdAt],
3617
+ ["AgentInspect", metadata.agentInspectVersion],
3618
+ ["Redaction profile", metadata.redactionProfile],
3619
+ ["Safe status", metadata.safeStatus],
3620
+ ["Source traces", metadata.sourceTraceCount],
3621
+ ["Runs", metadata.runIds.join(", ")],
3622
+ ...metadata.sessionId ? [["Session", metadata.sessionId]] : [],
3623
+ ...metadata.since ? [["Since", metadata.since]] : []
3624
+ ]),
3625
+ "",
3626
+ "## Safety checks",
3627
+ "",
3628
+ `Aggregate: **${checks.aggregateStatus}**`,
3629
+ ""
3630
+ ];
3631
+ for (const run of checks.runs) {
3632
+ lines.push(
3633
+ `- \`${run.runId}\`: ${run.status} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
3634
+ );
3635
+ }
3636
+ lines.push("", "## Redaction", "", `Total findings: ${redaction.totalFindings}`, "");
3637
+ for (const run of redaction.runs) {
3638
+ const detectors = run.detectors.length > 0 ? run.detectors.join(", ") : "none";
3639
+ lines.push(`- \`${run.runId}\`: ${run.findings} finding(s); detectors: ${detectors}`);
3640
+ }
3641
+ lines.push(
3642
+ "",
3643
+ "## Files",
3644
+ "",
3645
+ ...metadata.files.map((file) => `- \`${file}\``),
3646
+ "",
3647
+ "_Review every generated artifact before sharing outside your team._",
3648
+ ""
3649
+ );
3650
+ return lines.join("\n");
3651
+ }
3652
+ function normalizeBundleOutputPath(out) {
3653
+ const trimmed = out.trim();
3654
+ if (trimmed === "") {
3655
+ throw new Error("--out requires a non-empty path.");
3656
+ }
3657
+ const resolved = path.resolve(trimmed);
3658
+ if (resolved.toLowerCase().endsWith(".zip")) {
3659
+ return resolved.slice(0, -4);
3660
+ }
3661
+ return resolved;
3662
+ }
3663
+ function defaultBundleOutputPath(runIds) {
3664
+ const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
3665
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3666
+ return path.resolve(`agent-inspect-bundle-${label}-${stamp}`);
3667
+ }
3668
+
3669
+ export { Redactor, TraceDirectory, __commonJS, __require, __toESM, aggregateBundleSafeStatus, aggregateSessionCheckResults, applyProfileMetadataCaps, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildLocalExplanation, buildPlaceholderArtifact, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, bundleFailsOnSafety, defaultBundleOutputPath, enrichSessionRunRecord, extractCorrelationMetadata, extractMetadata, filterMetasBySessionScope, filterTraces, formatDuration2 as formatDuration, formatTimestamp, getIndent, getTraceFilePath, isAgentInspectTrace, isPersistedInspectEvent, loadSessionRunRecords, loadTraceMetadataList, nanoid, normalizeBundleOutputPath, parseDuration, parseDurationFilter, parseTraceJsonl, persistedInspectEventsToTraceEvents, renderActivitySummaryHuman, renderErrorLine, renderRunWhat, renderStepLine, renderTimeline, renderTraceStats, resolveBundleRunIds, resolveRedactionProfile, resolveTraceDir, searchTraces, source_default, truncateName, truncateStringForProfile, validateEvent };
3670
+ //# sourceMappingURL=chunk-BS5LSKZ3.mjs.map
3671
+ //# sourceMappingURL=chunk-BS5LSKZ3.mjs.map