agent-inspect 4.2.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/docs/API.md +3 -0
  3. package/docs/CLI.md +36 -0
  4. package/docs/SCHEMA.md +27 -1
  5. package/package.json +1 -1
  6. package/packages/cli/dist/{chunk-5VSPJEZ7.mjs → chunk-ORHDQUT6.mjs} +385 -10
  7. package/packages/cli/dist/chunk-ORHDQUT6.mjs.map +1 -0
  8. package/packages/cli/dist/index.cjs +2548 -1567
  9. package/packages/cli/dist/index.cjs.map +1 -1
  10. package/packages/cli/dist/index.mjs +2028 -1495
  11. package/packages/cli/dist/index.mjs.map +1 -1
  12. package/packages/cli/dist/{src-VQT7QLGV.mjs → src-FJOHPC4X.mjs} +3 -3
  13. package/packages/cli/dist/{src-VQT7QLGV.mjs.map → src-FJOHPC4X.mjs.map} +1 -1
  14. package/packages/core/dist/advanced.cjs +397 -12
  15. package/packages/core/dist/advanced.cjs.map +1 -1
  16. package/packages/core/dist/advanced.d.cts +105 -9
  17. package/packages/core/dist/advanced.d.ts +105 -9
  18. package/packages/core/dist/advanced.mjs +219 -17
  19. package/packages/core/dist/advanced.mjs.map +1 -1
  20. package/packages/core/dist/checks.cjs +117 -18
  21. package/packages/core/dist/checks.cjs.map +1 -1
  22. package/packages/core/dist/checks.d.cts +11 -3
  23. package/packages/core/dist/checks.d.ts +11 -3
  24. package/packages/core/dist/checks.mjs +42 -1
  25. package/packages/core/dist/checks.mjs.map +1 -1
  26. package/packages/core/dist/{chunk-2ZXEMWGK.mjs → chunk-3USJVBLA.mjs} +3 -3
  27. package/packages/core/dist/{chunk-2ZXEMWGK.mjs.map → chunk-3USJVBLA.mjs.map} +1 -1
  28. package/packages/core/dist/{chunk-HY7H3CQM.mjs → chunk-CAWPF22J.mjs} +4 -3
  29. package/packages/core/dist/chunk-CAWPF22J.mjs.map +1 -0
  30. package/packages/core/dist/{chunk-XUNJMAPD.mjs → chunk-KNYL56KZ.mjs} +38 -3
  31. package/packages/core/dist/chunk-KNYL56KZ.mjs.map +1 -0
  32. package/packages/core/dist/{chunk-IZBJAZGF.mjs → chunk-P366CCL2.mjs} +34 -3
  33. package/packages/core/dist/chunk-P366CCL2.mjs.map +1 -0
  34. package/packages/core/dist/{chunk-4JVGFNQH.mjs → chunk-ULCGIRTC.mjs} +95 -8
  35. package/packages/core/dist/chunk-ULCGIRTC.mjs.map +1 -0
  36. package/packages/core/dist/chunk-VFO76UH3.mjs +228 -0
  37. package/packages/core/dist/chunk-VFO76UH3.mjs.map +1 -0
  38. package/packages/core/dist/{chunk-NQZPZ2FN.mjs → chunk-WRE4CNPF.mjs} +6 -3
  39. package/packages/core/dist/chunk-WRE4CNPF.mjs.map +1 -0
  40. package/packages/core/dist/{chunk-A4V2H3YU.mjs → chunk-ZWA5RWMV.mjs} +40 -4
  41. package/packages/core/dist/chunk-ZWA5RWMV.mjs.map +1 -0
  42. package/packages/core/dist/{context-DjY-jvQk.d.cts → context-BbfkMYjz.d.cts} +5 -3
  43. package/packages/core/dist/{context-BDZmi53V.d.ts → context-Cp8NRbBX.d.ts} +5 -3
  44. package/packages/core/dist/diff.cjs.map +1 -1
  45. package/packages/core/dist/diff.d.cts +3 -3
  46. package/packages/core/dist/diff.d.ts +3 -3
  47. package/packages/core/dist/diff.mjs +1 -1
  48. package/packages/core/dist/exporters.cjs +137 -1
  49. package/packages/core/dist/exporters.cjs.map +1 -1
  50. package/packages/core/dist/exporters.d.cts +6 -3
  51. package/packages/core/dist/exporters.d.ts +6 -3
  52. package/packages/core/dist/exporters.mjs +71 -6
  53. package/packages/core/dist/exporters.mjs.map +1 -1
  54. package/packages/core/dist/index.cjs +181 -6
  55. package/packages/core/dist/index.cjs.map +1 -1
  56. package/packages/core/dist/index.d.cts +14 -7
  57. package/packages/core/dist/index.d.ts +14 -7
  58. package/packages/core/dist/index.mjs +51 -7
  59. package/packages/core/dist/index.mjs.map +1 -1
  60. package/packages/core/dist/{inspect-event-CYAV7Hxm.d.cts → inspect-event-IqwOHbGo.d.cts} +1 -1
  61. package/packages/core/dist/{inspect-event-CYAV7Hxm.d.ts → inspect-event-IqwOHbGo.d.ts} +1 -1
  62. package/packages/core/dist/{log-config-BNQ9UTmP.d.cts → log-config-CW8w5YQ9.d.cts} +1 -1
  63. package/packages/core/dist/{log-config-D3Yp1SZN.d.ts → log-config-SRA4xTp8.d.ts} +1 -1
  64. package/packages/core/dist/logs.d.cts +3 -3
  65. package/packages/core/dist/logs.d.ts +3 -3
  66. package/packages/core/dist/{persisted-inspect-event-BLvb0jSX.d.ts → persisted-inspect-event-DruwqARZ.d.ts} +1 -1
  67. package/packages/core/dist/{persisted-inspect-event-D5SjBz9b.d.cts → persisted-inspect-event-Gd-W8vB7.d.cts} +1 -1
  68. package/packages/core/dist/persisted.cjs +67 -1
  69. package/packages/core/dist/persisted.cjs.map +1 -1
  70. package/packages/core/dist/persisted.d.cts +5 -5
  71. package/packages/core/dist/persisted.d.ts +5 -5
  72. package/packages/core/dist/persisted.mjs +3 -3
  73. package/packages/core/dist/readers.cjs +72 -3
  74. package/packages/core/dist/readers.cjs.map +1 -1
  75. package/packages/core/dist/readers.d.cts +2 -2
  76. package/packages/core/dist/readers.d.ts +2 -2
  77. package/packages/core/dist/readers.mjs +5 -5
  78. package/packages/core/dist/readers.mjs.map +1 -1
  79. package/packages/core/dist/{types-CQun9LW6.d.ts → types-B1X87dUW.d.cts} +16 -2
  80. package/packages/core/dist/{types-C-llnPH0.d.cts → types-BVkjSd0q.d.ts} +16 -2
  81. package/packages/core/dist/types-CvsKGweN.d.cts +12 -0
  82. package/packages/core/dist/types-CvsKGweN.d.ts +12 -0
  83. package/packages/core/dist/writers.cjs +2 -1
  84. package/packages/core/dist/writers.cjs.map +1 -1
  85. package/packages/core/dist/writers.d.cts +2 -2
  86. package/packages/core/dist/writers.d.ts +2 -2
  87. package/packages/core/dist/writers.mjs +3 -4
  88. package/packages/core/dist/writers.mjs.map +1 -1
  89. package/packages/cli/dist/chunk-5VSPJEZ7.mjs.map +0 -1
  90. package/packages/core/dist/chunk-4JVGFNQH.mjs.map +0 -1
  91. package/packages/core/dist/chunk-A4V2H3YU.mjs.map +0 -1
  92. package/packages/core/dist/chunk-EDTQHZPM.mjs +0 -88
  93. package/packages/core/dist/chunk-EDTQHZPM.mjs.map +0 -1
  94. package/packages/core/dist/chunk-HY7H3CQM.mjs.map +0 -1
  95. package/packages/core/dist/chunk-IZBJAZGF.mjs.map +0 -1
  96. package/packages/core/dist/chunk-NQZPZ2FN.mjs.map +0 -1
  97. package/packages/core/dist/chunk-XUNJMAPD.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 42635d8: Add observed outcomes (`observeOutcome`) with OUTCOME events, report/check/search integration, and redaction-bounded evidence fields.
8
+
9
+ ## 4.3.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 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.
14
+
3
15
  ## 4.2.0
4
16
 
5
17
  ### Minor Changes
package/docs/API.md CHANGED
@@ -84,6 +84,7 @@ import {
84
84
  - **`step.llm(model, fn)`**: convenience wrapper (`type: "llm"`, `metadata.model`).
85
85
  - **`step.tool(toolName, fn)`**: convenience wrapper (`type: "tool"`, `metadata.toolName`).
86
86
  - **`observe(agent, options?)`**: proxy wrapper that traces top-level `run` / `execute` / `invoke` methods via `inspectRun`.
87
+ - **`observeOutcome(name, options)`** (v4.4.0+): records an observed outcome (`outcome_observed`) inside an active `inspectRun` context. Requires `expectation` and `status` (`passed` | `failed` | `unknown` | `skipped`); optional `method`, `actual`, and `evidence`. Outside a run → warn and no-op (never throws). `actual` / `evidence` are bounded and redacted before disk.
87
88
  - **`getCurrentCorrelationMetadata()`**: returns active run correlation fields (`correlationId`, `requestId`, `decisionId`, `groupId`) inside `inspectRun` / `maybeInspectRun`; `undefined` outside a traced run or when none were set.
88
89
  - **`RedactionProfile`**: `"local" | "share" | "strict"` — see `redactionProfile` on `InspectRunOptions` and `ExportOptions`.
89
90
  - **`resolveRedactionProfile(profile?)`**: advanced helper available from `agent-inspect/advanced`; resolves profile extra keys and metadata caps for integrations.
@@ -109,6 +110,7 @@ These APIs support local workflows like listing traces, extracting metadata/summ
109
110
  - `RunCompletedEvent` (`event: "run_completed"`)
110
111
  - `StepStartedEvent` (`event: "step_started"`)
111
112
  - `StepCompletedEvent` (`event: "step_completed"`)
113
+ - `OutcomeObservedEvent` (`event: "outcome_observed"`, v4.4.0+)
112
114
  - Related types: `StepType`, `StepStatus`, `RunStatus`, `ErrorInfo`, `StepMetadata`, `TokenMetadata`
113
115
 
114
116
  ### Log-derived normalized model (stable as a model, not necessarily the parsing APIs)
@@ -460,6 +462,7 @@ Public methods:
460
462
  - **`step(name, fn, options?)`**: writes nested step lifecycle events when called inside the same inspector's run context; outside a context it passes through.
461
463
  - **`tool(name, fn, options?)`** / **`llm(name, fn, options?)`**: convenience wrappers that set `type` and metadata.
462
464
  - **`observe(name, fn, options?)`**: returns an async wrapper that records the function call as an inspector step.
465
+ - **`observeOutcome(name, options)`** (v4.4.0+): writes `outcome_observed` / `OUTCOME` events through the configured writer when inside an inspector run context.
463
466
  - **`getDiagnostics()`**: returns instrumentation error counts and writer stats without requiring direct runtime access.
464
467
  - **`flush()`** / **`close()`**: delegate to the configured writer through the runtime.
465
468
 
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)
@@ -316,6 +317,7 @@ Options:
316
317
  - `--correlate-group`: when using `--session`, also match synthetic `group:` session keys
317
318
  - `--guardrails <rule>`: optional deterministic guardrail rules (`banned-phrase`, `pii-leak`, `prompt-injection`, …); repeatable
318
319
  - `--circuit <rule>`: optional circuit analyzers (`same-tool-repetition`, `max-retries`, …); repeatable
320
+ - `--fail-on-observation <status>`: add `outcome.status` rule; repeatable (`failed`, `passed`, `unknown`, `skipped`; default when flag present without value: `failed`)
319
321
 
320
322
  By default, `check` runs `run.status`. Additional built-in rules can be selected with `--rule` or config when their options are available.
321
323
 
@@ -666,6 +668,7 @@ Options:
666
668
  - `--limit <number>` — default 50
667
669
  - `--session <id>` — limit to runs in one workflow session
668
670
  - `--correlate-group` — when using `--session`, also match synthetic `group:` keys
671
+ - `--observation <status>` — filter runs with observed outcomes matching status (`passed`, `failed`, `unknown`, `skipped`)
669
672
  - `--json`
670
673
 
671
674
  Examples:
@@ -778,6 +781,7 @@ Options:
778
781
  - `-o, --output <path>` — write to file
779
782
  - `--json` — JSON wrapper (includes `content` when writing to stdout)
780
783
  - `--include-attributes` — bounded attributes in the execution tree section
784
+ - `--section <name>` — include report sections; repeatable (`what`, `timeline`, `tree`, `observations`; default: all except when narrowed)
781
785
  - `--no-errors` — omit error details from the execution tree section
782
786
  - `--no-correlation` — omit correlation ids from what section
783
787
  - `--redaction-profile <local|share|strict>` — key-based redaction profile applied to the complete report (default `local`); review output before sharing
@@ -860,6 +864,38 @@ agent-inspect index sqlite clean [--dir <path>] [--json]
860
864
 
861
865
  If `@agent-inspect/index-sqlite` is not installed, these subcommands print a short install hint and exit non-zero.
862
866
 
867
+ ### 6.24 `bundle`
868
+
869
+ 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).
870
+
871
+ ```bash
872
+ agent-inspect bundle <run-id> [options]
873
+ agent-inspect bundle --session <session-id> [options]
874
+ agent-inspect bundle --since <duration> [options]
875
+ ```
876
+
877
+ Options:
878
+
879
+ - `--dir <path>` — trace directory
880
+ - `--session <session-id>` — bundle all runs in a session
881
+ - `--since <duration>` — bundle runs with activity since a window (e.g. `24h`, `7d`)
882
+ - `--profile <profile>` — `local`, `share` (default), or `strict` redaction for exported copies
883
+ - `--out <path>` — output directory; `.zip` suffix is stripped (folder-first)
884
+ - `--allow-unsafe` — write bundle even when verify-safe reports UNSAFE
885
+ - `--json` — print deterministic JSON manifest
886
+
887
+ Output includes `trace.html`, `trace.jsonl`, `summary.md`, `metadata.json`, `check-results.json`, `redaction-report.json`, and `assets/runs/` mirrors for multi-run bundles.
888
+
889
+ Examples:
890
+
891
+ ```bash
892
+ npx agent-inspect bundle minimal-success --dir fixtures/traces --out ./bundle-out
893
+ npx agent-inspect bundle --session sess-handoff-001 --dir ./.agent-inspect --profile strict
894
+ npx agent-inspect bundle --since 24h --dir ./.agent-inspect --json
895
+ ```
896
+
897
+ Recipe: [shareable-bundle-basic](../examples/recipes/shareable-bundle-basic/README.md).
898
+
863
899
  ## 7. Optional TUI behavior
864
900
 
865
901
  `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/docs/SCHEMA.md CHANGED
@@ -36,6 +36,7 @@ The stable event names are:
36
36
  - `run_completed`
37
37
  - `step_started`
38
38
  - `step_completed`
39
+ - `outcome_observed` (v4.4.0+)
39
40
 
40
41
  There is **no** `step_failed` event. Failures are represented by `step_completed` with `status: "error"`.
41
42
 
@@ -111,6 +112,31 @@ Every trace line contains:
111
112
  }
112
113
  ```
113
114
 
115
+ ### 3.5 `outcome_observed` (v4.4.0+)
116
+
117
+ Records whether an external side effect matched expectations. Distinct from step/tool success.
118
+
119
+ ```ts
120
+ {
121
+ schemaVersion: "0.1",
122
+ event: "outcome_observed",
123
+ timestamp: number,
124
+ runId: string,
125
+ outcomeId: string,
126
+ parentId?: string,
127
+ name: string,
128
+ expectation: string,
129
+ status: "passed" | "failed" | "unknown" | "skipped",
130
+ method?: "dom" | "accessibility" | "snapshot" | "network" | "storage"
131
+ | "filesystem" | "database" | "queue" | "custom",
132
+ actual?: unknown,
133
+ evidence?: Record<string, unknown>,
134
+ observedAt: number
135
+ }
136
+ ```
137
+
138
+ `actual` and `evidence` are bounded and redacted before disk like step metadata. See [OBSERVED-OUTCOMES-V4.4.md](./proposals/OBSERVED-OUTCOMES-V4.4.md).
139
+
114
140
  ## 4. Error representation
115
141
 
116
142
  - Errors are structured as `{ message, stack? }`.
@@ -173,7 +199,7 @@ interface InspectEvent {
173
199
  runId: string;
174
200
  parentId?: string;
175
201
  name: string;
176
- kind: "RUN" | "AGENT" | "LLM" | "TOOL" | "CHAIN" | "RETRIEVER" | "DECISION" | "RESULT" | "ERROR" | "LOGIC" | "LOG";
202
+ kind: "RUN" | "AGENT" | "LLM" | "TOOL" | "CHAIN" | "RETRIEVER" | "DECISION" | "RESULT" | "ERROR" | "LOGIC" | "LOG" | "OUTCOME";
177
203
  timestamp: number;
178
204
  status?: "running" | "ok" | "error";
179
205
  durationMs?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-inspect",
3
- "version": "4.2.0",
3
+ "version": "4.4.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",
@@ -52,7 +52,8 @@ var INSPECT_KINDS = [
52
52
  "RESULT",
53
53
  "ERROR",
54
54
  "LOGIC",
55
- "LOG"
55
+ "LOG",
56
+ "OUTCOME"
56
57
  ];
57
58
  var ATTRIBUTION_CONFIDENCES = [
58
59
  "explicit",
@@ -525,6 +526,32 @@ function fromLegacyStepCompleted(event) {
525
526
  if (error) out.error = error;
526
527
  return out;
527
528
  }
529
+ function fromLegacyOutcomeObserved(event) {
530
+ const attrs = event.attributes ?? {};
531
+ const observedAtRaw = attrs.observedAt;
532
+ const observedAt = typeof observedAtRaw === "string" ? Date.parse(observedAtRaw) : typeof observedAtRaw === "number" && Number.isFinite(observedAtRaw) ? observedAtRaw : resolveTimes(event).timestamp;
533
+ const status = attrs.outcomeStatus;
534
+ const out = {
535
+ schemaVersion: "0.1",
536
+ event: "outcome_observed",
537
+ timestamp: observedAt,
538
+ runId: event.runId,
539
+ outcomeId: typeof attrs.outcomeId === "string" ? attrs.outcomeId : event.eventId,
540
+ name: event.name,
541
+ expectation: typeof attrs.expectation === "string" ? attrs.expectation : event.name,
542
+ status: status === "passed" || status === "failed" || status === "unknown" || status === "skipped" ? status : "unknown",
543
+ observedAt
544
+ };
545
+ if (event.parentId !== void 0) out.parentId = event.parentId;
546
+ if (typeof attrs.method === "string") out.method = attrs.method;
547
+ if (attrs.actual !== void 0) out.actual = attrs.actual;
548
+ if (event.outputSummary !== void 0) out.actual = event.outputSummary;
549
+ if (attrs.evidence !== void 0) out.evidence = attrs.evidence;
550
+ return out;
551
+ }
552
+ function fromNativeOutcome(event) {
553
+ return [fromLegacyOutcomeObserved(event)];
554
+ }
528
555
  function fromNativeRun(event) {
529
556
  const { timestamp, startTime, endTime } = resolveTimes(event);
530
557
  const runStatus = mapPersistedStatusToRunStatus(event.status);
@@ -612,9 +639,13 @@ function persistedInspectEventToTraceEvents(event) {
612
639
  if (legacyEvent === "run_completed") return [fromLegacyRunCompleted(event)];
613
640
  if (legacyEvent === "step_started") return [fromLegacyStepStarted(event)];
614
641
  if (legacyEvent === "step_completed") return [fromLegacyStepCompleted(event)];
642
+ if (legacyEvent === "outcome_observed") return [fromLegacyOutcomeObserved(event)];
615
643
  if (event.kind === "RUN") {
616
644
  return fromNativeRun(event);
617
645
  }
646
+ if (event.kind === "OUTCOME") {
647
+ return fromNativeOutcome(event);
648
+ }
618
649
  return fromNativeStep(event);
619
650
  }
620
651
  function persistedInspectEventsToTraceEvents(events, options) {
@@ -663,6 +694,9 @@ function isTraceEvent(value) {
663
694
  case "step_completed": {
664
695
  return typeof value.runId === "string" && typeof value.stepId === "string" && (value.status === "success" || value.status === "error") && typeof value.endTime === "number" && typeof value.durationMs === "number";
665
696
  }
697
+ case "outcome_observed": {
698
+ return typeof value.runId === "string" && typeof value.outcomeId === "string" && typeof value.name === "string" && typeof value.expectation === "string" && (value.status === "passed" || value.status === "failed" || value.status === "unknown" || value.status === "skipped") && typeof value.observedAt === "number";
699
+ }
666
700
  default:
667
701
  return false;
668
702
  }
@@ -973,6 +1007,9 @@ function validateEvent(event) {
973
1007
  case "step_completed": {
974
1008
  return nonEmptyString(event.runId) && nonEmptyString(event.stepId) && (event.status === "success" || event.status === "error") && finiteNumber(event.endTime) && finiteNumber(event.durationMs) && optionalErrorInfo(event.error);
975
1009
  }
1010
+ case "outcome_observed": {
1011
+ return nonEmptyString(event.runId) && nonEmptyString(event.outcomeId) && nonEmptyString(event.name) && nonEmptyString(event.expectation) && (event.status === "passed" || event.status === "failed" || event.status === "unknown" || event.status === "skipped") && finiteNumber(event.observedAt);
1012
+ }
976
1013
  default:
977
1014
  return false;
978
1015
  }
@@ -2512,6 +2549,142 @@ function renderTraceStats(stats) {
2512
2549
  return lines.join("\n");
2513
2550
  }
2514
2551
 
2552
+ // packages/core/src/outcomes/types.ts
2553
+ var OBSERVED_OUTCOME_STATUSES = [
2554
+ "passed",
2555
+ "failed",
2556
+ "unknown",
2557
+ "skipped"
2558
+ ];
2559
+ var OUTCOME_ATTRIBUTE_STATUS_KEY = "outcomeStatus";
2560
+ var OUTCOME_ATTRIBUTE_EXPECTATION_KEY = "expectation";
2561
+ var OUTCOME_ATTRIBUTE_METHOD_KEY = "method";
2562
+ var OUTCOME_ATTRIBUTE_OBSERVED_AT_KEY = "observedAt";
2563
+ var OUTCOME_LEGACY_EVENT = "outcome_observed";
2564
+
2565
+ // packages/core/src/outcomes/validate.ts
2566
+ function parseObservedOutcomeStatus(value) {
2567
+ const trimmed = value.trim().toLowerCase();
2568
+ if (OBSERVED_OUTCOME_STATUSES.includes(trimmed)) {
2569
+ return trimmed;
2570
+ }
2571
+ throw new Error(
2572
+ `Unsupported observation status "${value}". Use passed, failed, unknown, or skipped.`
2573
+ );
2574
+ }
2575
+
2576
+ // packages/core/src/outcomes/extract.ts
2577
+ function isOutcomeStatus(value) {
2578
+ return value === "passed" || value === "failed" || value === "unknown" || value === "skipped";
2579
+ }
2580
+ function parseMethod(value) {
2581
+ if (typeof value !== "string" || value.trim() === "") return void 0;
2582
+ return value;
2583
+ }
2584
+ function fromOutcomeObservedEvent(event) {
2585
+ return {
2586
+ outcomeId: event.outcomeId,
2587
+ runId: event.runId,
2588
+ ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
2589
+ name: event.name,
2590
+ expectation: event.expectation,
2591
+ status: event.status,
2592
+ ...event.method !== void 0 ? { method: event.method } : {},
2593
+ ...event.actual !== void 0 ? { actual: event.actual } : {},
2594
+ ...event.evidence !== void 0 ? { evidence: event.evidence } : {},
2595
+ observedAt: event.observedAt
2596
+ };
2597
+ }
2598
+ function fromPersistedOutcome(event) {
2599
+ if (event.kind !== "OUTCOME") return void 0;
2600
+ const attrs = event.attributes ?? {};
2601
+ const statusRaw = attrs[OUTCOME_ATTRIBUTE_STATUS_KEY];
2602
+ if (!isOutcomeStatus(statusRaw)) return void 0;
2603
+ const expectation = typeof attrs[OUTCOME_ATTRIBUTE_EXPECTATION_KEY] === "string" ? attrs[OUTCOME_ATTRIBUTE_EXPECTATION_KEY] : event.name;
2604
+ const observedAtRaw = attrs[OUTCOME_ATTRIBUTE_OBSERVED_AT_KEY];
2605
+ const observedAt = typeof observedAtRaw === "string" ? Date.parse(observedAtRaw) : typeof observedAtRaw === "number" && Number.isFinite(observedAtRaw) ? observedAtRaw : Date.parse(event.timestamp);
2606
+ return {
2607
+ outcomeId: event.eventId,
2608
+ runId: event.runId,
2609
+ ...event.parentId !== void 0 ? { parentId: event.parentId } : {},
2610
+ name: event.name,
2611
+ expectation,
2612
+ status: statusRaw,
2613
+ ...parseMethod(attrs[OUTCOME_ATTRIBUTE_METHOD_KEY]) !== void 0 ? { method: parseMethod(attrs[OUTCOME_ATTRIBUTE_METHOD_KEY]) } : {},
2614
+ ...event.outputSummary !== void 0 ? { actual: event.outputSummary } : {},
2615
+ ...attrs.evidence !== void 0 ? { evidence: attrs.evidence } : {},
2616
+ observedAt: Number.isFinite(observedAt) ? observedAt : Date.parse(event.timestamp)
2617
+ };
2618
+ }
2619
+ function extractOutcomesFromTraceEvents(events) {
2620
+ const out = [];
2621
+ for (const event of events) {
2622
+ if (event.event === OUTCOME_LEGACY_EVENT) {
2623
+ out.push(fromOutcomeObservedEvent(event));
2624
+ }
2625
+ }
2626
+ return out.sort((a, b) => a.observedAt - b.observedAt || a.name.localeCompare(b.name));
2627
+ }
2628
+ function extractOutcomesFromPersistedEvents(events) {
2629
+ const out = [];
2630
+ for (const event of events) {
2631
+ const outcome = fromPersistedOutcome(event);
2632
+ if (outcome) out.push(outcome);
2633
+ }
2634
+ return out.sort((a, b) => a.observedAt - b.observedAt || a.name.localeCompare(b.name));
2635
+ }
2636
+ function summarizeObservedOutcomes(outcomes) {
2637
+ const summary = {
2638
+ total: outcomes.length,
2639
+ passed: 0,
2640
+ failed: 0,
2641
+ unknown: 0,
2642
+ skipped: 0,
2643
+ outcomes: [...outcomes]
2644
+ };
2645
+ for (const outcome of outcomes) {
2646
+ summary[outcome.status] += 1;
2647
+ }
2648
+ return summary;
2649
+ }
2650
+ function outcomesMatchingStatus(outcomes, statuses) {
2651
+ const set = new Set(statuses);
2652
+ return outcomes.filter((outcome) => set.has(outcome.status));
2653
+ }
2654
+ function parseObservationFilter(value) {
2655
+ if (value === void 0 || value.trim() === "") return void 0;
2656
+ return parseObservedOutcomeStatus(value);
2657
+ }
2658
+
2659
+ // packages/core/src/outcomes/render.ts
2660
+ function renderObservedOutcomesMarkdown(summary) {
2661
+ if (summary.total === 0) {
2662
+ return "No observed outcomes recorded for this run.";
2663
+ }
2664
+ const lines = [
2665
+ `Total: ${summary.total} (passed ${summary.passed}, failed ${summary.failed}, unknown ${summary.unknown}, skipped ${summary.skipped})`,
2666
+ "",
2667
+ "| Name | Status | Expectation | Method |",
2668
+ "| --- | --- | --- | --- |"
2669
+ ];
2670
+ for (const outcome of summary.outcomes) {
2671
+ lines.push(
2672
+ `| ${outcome.name} | ${outcome.status} | ${outcome.expectation.replace(/\|/g, "\\|")} | ${outcome.method ?? "-"} |`
2673
+ );
2674
+ }
2675
+ return lines.join("\n");
2676
+ }
2677
+ function renderObservedOutcomesHtml(summary) {
2678
+ if (summary.total === 0) {
2679
+ return "<p>No observed outcomes recorded for this run.</p>";
2680
+ }
2681
+ const rows = summary.outcomes.map((outcome) => {
2682
+ const esc = (v) => v.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
2683
+ return `<tr><td>${esc(outcome.name)}</td><td>${esc(outcome.status)}</td><td>${esc(outcome.expectation)}</td><td>${esc(outcome.method ?? "-")}</td></tr>`;
2684
+ }).join("");
2685
+ return `<p>Total: ${summary.total} (passed ${summary.passed}, failed ${summary.failed}, unknown ${summary.unknown}, skipped ${summary.skipped})</p><table><thead><tr><th>Name</th><th>Status</th><th>Expectation</th><th>Method</th></tr></thead><tbody>${rows}</tbody></table>`;
2686
+ }
2687
+
2515
2688
  // packages/core/src/search.ts
2516
2689
  function parseDurationFilter(expr) {
2517
2690
  const raw = expr.trim();
@@ -2558,8 +2731,9 @@ async function searchTraces(metas, options) {
2558
2731
  }
2559
2732
  const limit = options.limit ?? 50;
2560
2733
  const sessionId = options.session?.trim();
2734
+ const observationStatus = parseObservationFilter(options.observation);
2561
2735
  const hasContentFilter = Boolean(
2562
- options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
2736
+ options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus
2563
2737
  );
2564
2738
  const results = [];
2565
2739
  const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
@@ -2604,6 +2778,22 @@ async function searchTraces(metas, options) {
2604
2778
  statusFilter: options.status
2605
2779
  });
2606
2780
  results.push(...stepMatches);
2781
+ if (observationStatus) {
2782
+ const outcomes = extractOutcomesFromTraceEvents(events);
2783
+ const matched = outcomes.filter((outcome) => outcome.status === observationStatus);
2784
+ for (const outcome of matched) {
2785
+ results.push({
2786
+ runId: m.runId,
2787
+ runName: m.name,
2788
+ runStatus: m.status,
2789
+ stepName: outcome.name,
2790
+ timestamp: outcome.observedAt,
2791
+ matchReason: `observation status=${outcome.status}`,
2792
+ matchedFields: ["outcome.status", "outcome.name"],
2793
+ filePath: m.filePath
2794
+ });
2795
+ }
2796
+ }
2607
2797
  }
2608
2798
  results.sort((a, b) => {
2609
2799
  const ta = a.timestamp ?? 0;
@@ -2780,9 +2970,10 @@ function buildActivitySummary(index, options = {}) {
2780
2970
  entries
2781
2971
  };
2782
2972
  }
2783
- function renderActivitySummaryHuman(summary) {
2973
+ function renderActivitySummaryHuman(summary, options) {
2784
2974
  const lines = [];
2785
- const todayStart = /* @__PURE__ */ new Date();
2975
+ const nowMs = options?.nowMs ?? Date.now();
2976
+ const todayStart = new Date(nowMs);
2786
2977
  todayStart.setHours(0, 0, 0, 0);
2787
2978
  const todayMs = todayStart.getTime();
2788
2979
  const today = summary.entries.filter(
@@ -3268,12 +3459,12 @@ function buildCriticalPath(runs, handoffs) {
3268
3459
  handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
3269
3460
  );
3270
3461
  const ordered = [...runs].sort(compareRuns);
3271
- const path5 = [];
3462
+ const path6 = [];
3272
3463
  const visited = /* @__PURE__ */ new Set();
3273
3464
  const pushRun = (run, confidence, source) => {
3274
3465
  if (visited.has(run.runId)) return;
3275
3466
  visited.add(run.runId);
3276
- path5.push({
3467
+ path6.push({
3277
3468
  runId: run.runId,
3278
3469
  name: run.name,
3279
3470
  startedAt: run.startedAt,
@@ -3298,7 +3489,7 @@ function buildCriticalPath(runs, handoffs) {
3298
3489
  const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
3299
3490
  pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
3300
3491
  }
3301
- return path5;
3492
+ return path6;
3302
3493
  }
3303
3494
  function metaRunIdMatches(run, token, runById) {
3304
3495
  const meta = extractSessionWorkflowMetadata(run.metadata);
@@ -3482,6 +3673,190 @@ async function isAgentInspectTrace(filePath) {
3482
3673
  }
3483
3674
  }
3484
3675
 
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
3676
+ // packages/core/src/bundle/resolve.ts
3677
+ function parseSinceCutoff(since) {
3678
+ const trimmed = since.trim();
3679
+ if (trimmed === "") {
3680
+ throw new Error("--since requires a non-empty duration (e.g. 24h, 7d).");
3681
+ }
3682
+ return Date.now() - parseDuration(trimmed);
3683
+ }
3684
+ function runActivityMs(run) {
3685
+ if (run.startedAt !== void 0 && Number.isFinite(run.startedAt)) return run.startedAt;
3686
+ if (run.endedAt !== void 0 && Number.isFinite(run.endedAt)) return run.endedAt;
3687
+ return void 0;
3688
+ }
3689
+ function runsInSinceWindow(runs, since) {
3690
+ const cutoff = parseSinceCutoff(since);
3691
+ const ids = [];
3692
+ for (const run of runs) {
3693
+ const activity = runActivityMs(run);
3694
+ if (activity !== void 0 && activity >= cutoff) {
3695
+ ids.push(run.runId);
3696
+ }
3697
+ }
3698
+ return ids.sort((a, b) => a.localeCompare(b));
3699
+ }
3700
+ function findSession(index, sessionId) {
3701
+ return index.sessions.find((session) => session.sessionId === sessionId);
3702
+ }
3703
+ function resolveBundleRunIds(index, runs, options) {
3704
+ const runId = options.runId?.trim();
3705
+ const sessionId = options.sessionId?.trim();
3706
+ const since = options.since?.trim();
3707
+ const modes = [runId ? 1 : 0, sessionId ? 1 : 0, since ? 1 : 0].reduce((a, b) => a + b, 0);
3708
+ if (modes === 0) {
3709
+ throw new Error(
3710
+ "bundle requires a run id, --session <sessionId>, or --since <duration>."
3711
+ );
3712
+ }
3713
+ if (modes > 1) {
3714
+ throw new Error(
3715
+ "bundle accepts only one target: a run id, --session, or --since (not combined)."
3716
+ );
3717
+ }
3718
+ if (runId) {
3719
+ const known = runs.some((run) => run.runId === runId);
3720
+ if (!known) {
3721
+ throw new Error(`Run "${runId}" was not found in the trace directory.`);
3722
+ }
3723
+ return { runIds: [runId] };
3724
+ }
3725
+ if (sessionId) {
3726
+ const session = findSession(index, sessionId);
3727
+ if (!session) {
3728
+ throw new Error(`Session "${sessionId}" was not found.`);
3729
+ }
3730
+ if (session.runIds.length === 0) {
3731
+ throw new Error(`Session "${sessionId}" has no runs to bundle.`);
3732
+ }
3733
+ return {
3734
+ runIds: [...session.runIds].sort((a, b) => a.localeCompare(b)),
3735
+ sessionId
3736
+ };
3737
+ }
3738
+ const runIds = runsInSinceWindow(runs, since);
3739
+ if (runIds.length === 0) {
3740
+ throw new Error(`No runs matched --since ${since}.`);
3741
+ }
3742
+ return { runIds, since };
3743
+ }
3744
+
3745
+ // packages/core/src/bundle/safety-status.ts
3746
+ function aggregateBundleSafeStatus(statuses) {
3747
+ if (statuses.length === 0) return "UNKNOWN";
3748
+ if (statuses.some((status) => status === "UNSAFE")) return "UNSAFE";
3749
+ if (statuses.some((status) => status === "UNKNOWN")) return "UNKNOWN";
3750
+ if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
3751
+ return "SAFE";
3752
+ }
3753
+ function toMetadataSafeStatus(status) {
3754
+ if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
3755
+ return status;
3756
+ }
3757
+ function bundleFailsOnSafety(status, allowUnsafe) {
3758
+ if (allowUnsafe) return false;
3759
+ return status === "UNSAFE" || status === "UNKNOWN";
3760
+ }
3761
+
3762
+ // packages/core/src/bundle/manifest.ts
3763
+ var BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
3764
+ var PLACEHOLDER_NOTE = "No eval or performance artifacts were requested for this bundle.";
3765
+ function buildBundleMetadata(parts) {
3766
+ const aggregate = aggregateBundleSafeStatus(
3767
+ parts.checks.runs.map((run) => run.status)
3768
+ );
3769
+ return {
3770
+ createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
3771
+ agentInspectVersion: parts.agentInspectVersion,
3772
+ redactionProfile: parts.profile,
3773
+ sourceTraceCount: parts.resolve.runIds.length,
3774
+ runIds: [...parts.resolve.runIds],
3775
+ safeStatus: toMetadataSafeStatus(aggregate),
3776
+ files: [...parts.files].sort((a, b) => a.localeCompare(b)),
3777
+ note: BUNDLE_NOTE,
3778
+ ...parts.resolve.sessionId !== void 0 ? { sessionId: parts.resolve.sessionId } : {},
3779
+ ...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
3780
+ };
3781
+ }
3782
+ function buildPlaceholderArtifact() {
3783
+ return {
3784
+ status: "not_requested",
3785
+ note: PLACEHOLDER_NOTE
3786
+ };
3787
+ }
3788
+
3789
+ // packages/core/src/bundle/summary.ts
3790
+ function markdownTable(rows) {
3791
+ const lines = ["| Field | Value |", "| --- | --- |"];
3792
+ for (const [key, value] of rows) {
3793
+ lines.push(`| ${key} | ${value ?? "unknown"} |`);
3794
+ }
3795
+ return lines.join("\n");
3796
+ }
3797
+ function buildBundleSummaryMarkdown(parts) {
3798
+ const { metadata, checks, redaction } = parts;
3799
+ const lines = [
3800
+ "# AgentInspect trace bundle",
3801
+ "",
3802
+ metadata.note,
3803
+ "",
3804
+ "## Overview",
3805
+ "",
3806
+ markdownTable([
3807
+ ["Created", metadata.createdAt],
3808
+ ["AgentInspect", metadata.agentInspectVersion],
3809
+ ["Redaction profile", metadata.redactionProfile],
3810
+ ["Safe status", metadata.safeStatus],
3811
+ ["Source traces", metadata.sourceTraceCount],
3812
+ ["Runs", metadata.runIds.join(", ")],
3813
+ ...metadata.sessionId ? [["Session", metadata.sessionId]] : [],
3814
+ ...metadata.since ? [["Since", metadata.since]] : []
3815
+ ]),
3816
+ "",
3817
+ "## Safety checks",
3818
+ "",
3819
+ `Aggregate: **${checks.aggregateStatus}**`,
3820
+ ""
3821
+ ];
3822
+ for (const run of checks.runs) {
3823
+ lines.push(
3824
+ `- \`${run.runId}\`: ${run.status} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
3825
+ );
3826
+ }
3827
+ lines.push("", "## Redaction", "", `Total findings: ${redaction.totalFindings}`, "");
3828
+ for (const run of redaction.runs) {
3829
+ const detectors = run.detectors.length > 0 ? run.detectors.join(", ") : "none";
3830
+ lines.push(`- \`${run.runId}\`: ${run.findings} finding(s); detectors: ${detectors}`);
3831
+ }
3832
+ lines.push(
3833
+ "",
3834
+ "## Files",
3835
+ "",
3836
+ ...metadata.files.map((file) => `- \`${file}\``),
3837
+ "",
3838
+ "_Review every generated artifact before sharing outside your team._",
3839
+ ""
3840
+ );
3841
+ return lines.join("\n");
3842
+ }
3843
+ function normalizeBundleOutputPath(out) {
3844
+ const trimmed = out.trim();
3845
+ if (trimmed === "") {
3846
+ throw new Error("--out requires a non-empty path.");
3847
+ }
3848
+ const resolved = path.resolve(trimmed);
3849
+ if (resolved.toLowerCase().endsWith(".zip")) {
3850
+ return resolved.slice(0, -4);
3851
+ }
3852
+ return resolved;
3853
+ }
3854
+ function defaultBundleOutputPath(runIds) {
3855
+ const label = runIds.length === 1 ? runIds[0] : `multi-${runIds.length}`;
3856
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3857
+ return path.resolve(`agent-inspect-bundle-${label}-${stamp}`);
3858
+ }
3859
+
3860
+ export { Redactor, TraceDirectory, __commonJS, __require, __toESM, aggregateBundleSafeStatus, aggregateSessionCheckResults, applyProfileMetadataCaps, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildLocalExplanation, buildPlaceholderArtifact, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, bundleFailsOnSafety, defaultBundleOutputPath, enrichSessionRunRecord, extractCorrelationMetadata, extractMetadata, extractOutcomesFromPersistedEvents, extractOutcomesFromTraceEvents, filterMetasBySessionScope, filterTraces, formatDuration2 as formatDuration, formatTimestamp, getIndent, getTraceFilePath, isAgentInspectTrace, isPersistedInspectEvent, loadSessionRunRecords, loadTraceMetadataList, nanoid, normalizeBundleOutputPath, outcomesMatchingStatus, parseDuration, parseDurationFilter, parseTraceJsonl, persistedInspectEventsToTraceEvents, renderActivitySummaryHuman, renderErrorLine, renderObservedOutcomesHtml, renderObservedOutcomesMarkdown, renderRunWhat, renderStepLine, renderTimeline, renderTraceStats, resolveBundleRunIds, resolveRedactionProfile, resolveTraceDir, searchTraces, source_default, summarizeObservedOutcomes, truncateName, truncateStringForProfile, validateEvent };
3861
+ //# sourceMappingURL=chunk-ORHDQUT6.mjs.map
3862
+ //# sourceMappingURL=chunk-ORHDQUT6.mjs.map