agent-inspect 6.17.4 → 6.17.6

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 (33) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/ADAPTERS.md +3 -3
  4. package/docs/API.md +13 -7
  5. package/docs/CLI.md +2 -0
  6. package/docs/LIMITATIONS.md +1 -0
  7. package/docs/TRACE-CONTRACTS.md +90 -0
  8. package/package.json +8 -5
  9. package/packages/cli/dist/{chunk-YX6RFLV5.mjs → chunk-GOHYSE7W.mjs} +86 -17
  10. package/packages/cli/dist/chunk-GOHYSE7W.mjs.map +1 -0
  11. package/packages/cli/dist/index.cjs +522 -81
  12. package/packages/cli/dist/index.cjs.map +1 -1
  13. package/packages/cli/dist/index.mjs +441 -69
  14. package/packages/cli/dist/index.mjs.map +1 -1
  15. package/packages/cli/dist/{src-X6SLMBNH.mjs → src-OYGG7QAZ.mjs} +3 -3
  16. package/packages/cli/dist/{src-X6SLMBNH.mjs.map → src-OYGG7QAZ.mjs.map} +1 -1
  17. package/packages/core/dist/advanced.cjs +85 -16
  18. package/packages/core/dist/advanced.cjs.map +1 -1
  19. package/packages/core/dist/advanced.d.cts +1 -1
  20. package/packages/core/dist/advanced.d.ts +1 -1
  21. package/packages/core/dist/advanced.mjs +15 -5
  22. package/packages/core/dist/advanced.mjs.map +1 -1
  23. package/packages/core/dist/checks.cjs +119 -26
  24. package/packages/core/dist/checks.cjs.map +1 -1
  25. package/packages/core/dist/checks.d.cts +28 -2
  26. package/packages/core/dist/checks.d.ts +28 -2
  27. package/packages/core/dist/checks.mjs +1 -1
  28. package/packages/core/dist/{chunk-HN377P23.mjs → chunk-DIZPIPY2.mjs} +121 -28
  29. package/packages/core/dist/chunk-DIZPIPY2.mjs.map +1 -0
  30. package/packages/core/dist/{index-Xk9X-yjY.d.cts → index-DWu54Y28.d.cts} +107 -122
  31. package/packages/core/dist/{index-BsCcOKxy.d.ts → index-DlwbVqEs.d.ts} +107 -122
  32. package/packages/cli/dist/chunk-YX6RFLV5.mjs.map +0 -1
  33. package/packages/core/dist/chunk-HN377P23.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.17.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 075dc87: Security containment: enforce Studio ingest byte limits, reject symlinks, stream and atomically stage imports (bundle / file-drop / GitHub / HTTP), remediate Vitest/nanoid and website/example advisories, add the default-workflow no-egress harness (#225), lock the published API surface snapshot (#211), correct Evidence format docs (no signing; required sourceHashes), and extend free-text redaction residual coverage.
8
+
9
+ ## 6.17.5
10
+
11
+ ### Patch Changes
12
+
13
+ - 093811b: Harden deterministic TraceContract / check gates against fail-open empty configs (rule execution evidence, unique order IDs, requiredOrder implies presence, tool policy includes running invocations, ObservedOutcome requireAny), map #308–#311 release ownership, and make demo:verify / pack:smoke validation cross-platform without unnecessary shell invocation.
14
+
3
15
  ## 6.17.4
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -210,7 +210,7 @@ The root package is enough for custom capture, the CLI, checks, and Evidence wor
210
210
 
211
211
  ## Status and documentation
212
212
 
213
- **Current published baseline:** **6.17.3** · persisted schema `1.0` · Node.js `>=20` · MIT.
213
+ **Current published baseline:** **6.17.6** · persisted schema `1.0` · Node.js `>=20` · MIT.
214
214
 
215
215
  Legacy v0.1 and v0.2 traces remain readable. Check the npm badge and [changelog](CHANGELOG.md) for the current published version.
216
216
 
package/docs/ADAPTERS.md CHANGED
@@ -22,7 +22,7 @@ Reporters (`@agent-inspect/vitest` and `@agent-inspect/jest`) are public package
22
22
 
23
23
  **Status:** experimental adapter — optional package published in the aligned v2.2.0 package set and hardened in the v2.3 adapter train.
24
24
 
25
- The adapter has hardened lifecycle identity and parallel integration isolation. It remains metadata-only: `capture: "preview"` and preview-only redaction options emit diagnostics and fall back to metadata-only capture until bounded free-text previews are implemented.
25
+ The adapter has hardened lifecycle identity and parallel integration isolation. It remains metadata-only: `capture: "preview"` and preview-only redaction options emit diagnostics **and one visible** `AI_ADAPTER_PREVIEW_NOT_AVAILABLE` warning per adapter instance, then fall back to metadata-only capture. Bounded free-text preview capture is planned for a later train; it is not implemented here.
26
26
 
27
27
  ### Install
28
28
 
@@ -59,7 +59,7 @@ const result = await generateText({
59
59
  - **Metadata-only by default** — records model, finish reason, token usage, timing, and safe counts/summaries.
60
60
  - **Required safe telemetry settings** — set `recordInputs: false` and `recordOutputs: false` on every AI SDK call using this adapter.
61
61
  - **No raw payload capture by default** — prompts, messages, generated text, stream chunks, tool inputs/outputs, headers, request bodies, and response bodies are not persisted.
62
- - **Preview capture is not enabled yet** — `capture: "preview"`, `redactionProfile`, and `maxPreviewChars` are diagnosed through `getDiagnostics()` and do not persist raw previews.
62
+ - **Preview capture is not enabled yet** — requesting `capture: "preview"` emits one console warning with code `AI_ADAPTER_PREVIEW_NOT_AVAILABLE`, records diagnostics via `getDiagnostics()`, and does not persist raw previews. Effective capture remains metadata-only.
63
63
 
64
64
  ### Local no-network recipe
65
65
 
@@ -375,7 +375,7 @@ Integration modes:
375
375
  - **No upload behavior** — the processor writes only to an explicit local writer or `traceDir`.
376
376
  - **Metadata-only by default** — records trace/span IDs, parentage, names, timing, status, errors, safe model/tool names, token counts, and bounded summaries.
377
377
  - **No raw payload capture by default** — prompts, messages, generated text, function inputs/outputs, arbitrary custom data, trace exporter credentials, headers, request bodies, response bodies, and hosted tool payloads are not persisted.
378
- - **Preview capture is not enabled yet** — `capture: "preview"`, `redactionProfile`, and `maxPreviewChars` are diagnosed through `getDiagnostics()` and do not persist raw previews.
378
+ - **Preview capture is not enabled yet** — requesting `capture: "preview"` emits one console warning with code `AI_ADAPTER_PREVIEW_NOT_AVAILABLE`, records diagnostics via `getDiagnostics()`, and does not persist raw previews. Effective capture remains metadata-only.
379
379
  - **Fixture-backed lifecycle coverage** — local tests and the recipe cover agent, generation, function tool, handoff, guardrail, response, MCP tools, custom, transcription, and speech span shapes without provider calls.
380
380
 
381
381
  Full API: [API.md](./API.md) §14.
package/docs/API.md CHANGED
@@ -180,7 +180,11 @@ Diff is local and read-only. Programmatic diff surfaces are experimental until t
180
180
  - In-memory **`getEvents()`** / **`clear()`** unchanged when `persist` is false
181
181
  - Metadata helpers: `extractModelName`, `extractTokenUsage`, `safePreview`, `toPlainMetadata`
182
182
 
183
- Rationale: v1.x includes one official adapter and **zero production sinks**, so adapter surfaces remain experimental.
183
+ Rationale: one official adapter and **zero production sinks** keep adapter surfaces experimental; they remain additive in minors.
184
+
185
+ ### TraceContract `requiredOrder`
186
+
187
+ When defining `tools.requiredOrder`, the array expands into adjacent first-occurrence pair checks. Unlisted intermediate tools are allowed; later repetitions do not invalidate an earlier valid first-occurrence order. Missing tools are handled by required-tool rules, not ordering alone. See [TRACE-CONTRACTS.md](./TRACE-CONTRACTS.md).
184
188
 
185
189
  ## 10. Experimental `@agent-inspect/tui` APIs
186
190
 
@@ -249,7 +253,7 @@ The manifest records framework, generation time, bounded test results, artifact
249
253
 
250
254
  ## 12. Experimental `@agent-inspect/vitest` APIs
251
255
 
252
- `@agent-inspect/vitest` is an optional experimental workspace package for local Vitest failure artifacts. It remains private/unpublished pending maintainer first-publication setup. It does not add a Vitest dependency to root/core, does not upload artifacts, and does not infer trace relationships by timestamp.
256
+ `@agent-inspect/vitest` is an optional experimental workspace package for local Vitest failure artifacts. It is published with the fixed release group. It does not add a Vitest dependency to root/core, does not upload artifacts, and does not infer trace relationships by timestamp.
253
257
 
254
258
  Import from `@agent-inspect/vitest`:
255
259
 
@@ -282,7 +286,7 @@ Artifacts are safe structural summaries. The reporter writes a shared `schemaVer
282
286
 
283
287
  ## 13. Experimental `@agent-inspect/jest` APIs
284
288
 
285
- `@agent-inspect/jest` is an optional experimental workspace package for local Jest failure artifacts. It remains private/unpublished pending maintainer first-publication setup. It does not add a Jest dependency to root/core, does not upload artifacts, and does not infer trace relationships by timestamp.
289
+ `@agent-inspect/jest` is an optional experimental workspace package for local Jest failure artifacts. It is published with the fixed release group. It does not add a Jest dependency to root/core, does not upload artifacts, and does not infer trace relationships by timestamp.
286
290
 
287
291
  Import from `@agent-inspect/jest`:
288
292
 
@@ -423,8 +427,8 @@ import type {
423
427
  ```
424
428
 
425
429
  - **`TraceWriter`**: async `write(event)`, optional `flush()`, optional `close()`, optional `getStats()`.
426
- - **`fileWriter({ dir?, filePath? })`**: appends `PersistedInspectEvent` JSONL rows to local disk. `createInspector()` emits schema 1.0 rows by default; compatibility adapters may still pass readable v0.2 rows. By default it derives one file per `event.runId`; `filePath` writes all events to an explicit local file. Filesystem and serialization failures are reflected in writer stats instead of being thrown into application code.
427
- - **`bufferedFileWriter({ dir?, filePath?, maxQueueSize?, flushIntervalMs?, maxBatchSize?, overflow? })`**: buffers local JSONL writes with bounded queue behavior. Overflow supports `drop-oldest` and `drop-newest`; neither mode throws into application code.
430
+ - **`fileWriter({ dir?, filePath? })`**: appends `PersistedInspectEvent` JSONL rows to local disk. `createInspector()` emits schema 1.0 rows by default; compatibility adapters may still pass readable v0.2 rows. By default it derives one file per `event.runId`; `filePath` writes all events to an explicit local file. Filesystem and serialization failures are reflected in writer stats instead of being thrown into application code. Append-only local JSONL is diagnostic persistence — not a write-ahead log or fsync-guaranteed durable-before-effect journal.
431
+ - **`bufferedFileWriter({ dir?, filePath?, maxQueueSize?, flushIntervalMs?, maxBatchSize?, overflow? })`**: buffers local JSONL writes with bounded queue behavior. Overflow supports `drop-oldest` and `drop-newest`; neither mode throws into application code. On abrupt process exit, a bounded unflushed tail may be lost.
428
432
  - **`compositeWriter([...writers])`**: fans out events to multiple explicit local/custom writers. A failing child writer does not prevent other children from receiving events; failures are reflected in composite stats.
429
433
  - **`memoryWriter()`**: stores cloned `PersistedInspectEvent` rows in memory for tests, adapter fixtures, and eval harnesses.
430
434
  - **`nullWriter()`**: accepts events without retaining them for disabled mode, overhead comparisons, and no-output tests.
@@ -433,7 +437,7 @@ No network writer or vendor sink exists in this package.
433
437
 
434
438
  ## 19. Experimental inspector API/runtime (v1.6)
435
439
 
436
- `createInspector()` is the public instance API for local-first tracing with explicit writers. It owns an instance-specific runtime context, records schema 1.0 persisted inspect events, preserves application return values/errors, and exposes diagnostics plus deterministic `flush()`/`close()` lifecycle hooks.
440
+ `createInspector()` is the public instance API for local-first tracing with explicit writers. It owns an instance-specific runtime context, records schema 1.0 persisted inspect events, preserves application return values/errors, and exposes diagnostics plus deterministic `flush()`/`close()` lifecycle hooks (`flush`/`close` drain writer queues idempotently — they do not claim crash durability or fsync).
437
441
 
438
442
  Import from `agent-inspect`:
439
443
 
@@ -537,7 +541,9 @@ import type { TraceCheckRule, TraceCheckResult, TraceFacts } from "agent-inspect
537
541
  - **`TraceCheckRule`**: synchronous pure rule contract.
538
542
  - **`TraceCheckResult`**: deterministic aggregate result with findings, evidence, summary counts, and execution diagnostics.
539
543
 
540
- The checks API is experimental in v1.x. The `agent-inspect check` CLI uses this API for local reader-backed checks and deterministic JSON output; `agent-inspect artifacts` reuses the same safe findings for local CI artifact bundles and optional step-summary file output. Built-in rules operate on normalized event metadata, tree relationships, bounded summaries, token counts, and normalized baseline facts; safety and baseline findings identify event IDs and field paths rather than emitting raw prompts, outputs, secrets, headers, request/response bodies, or full tool payloads.
544
+ The checks API is experimental. The `agent-inspect check` CLI uses this API for local reader-backed checks and deterministic JSON output; `agent-inspect artifacts` reuses the same safe findings for local CI artifact bundles and optional step-summary file output. Built-in rules operate on normalized event metadata, tree relationships, bounded summaries, token counts, and normalized baseline facts; safety and baseline findings identify event IDs and field paths rather than emitting raw prompts, outputs, secrets, headers, request/response bodies, or full tool payloads.
545
+
546
+ @beta Available through `agent-inspect/checks`. Additive changes may ship in minor releases; breaking changes require a future major.
541
547
 
542
548
  Recipes: [deterministic-ci-checks](../examples/recipes/deterministic-ci-checks/README.md) for check/baseline/artifact workflows, and [test-reporter-artifacts](../examples/recipes/test-reporter-artifacts/README.md) for Vitest/Jest reporter configuration patterns.
543
549
 
package/docs/CLI.md CHANGED
@@ -204,6 +204,8 @@ Options:
204
204
  - `--refresh <ms>`: minimum time between renders
205
205
  - `--json`: newline-delimited JSON updates
206
206
 
207
+ **Truncation recovery:** when a watched `--file` shrinks below the last read offset (for example a truncate-and-rewrite), `tail` resets the offset, discards any buffered partial line, and continues the same session from the start of the current file contents. Full inode-aware rename/recreate rotation is **not** claimed unless separately implemented and tested.
208
+
207
209
  Important: `tail` is a local developer tool, not a production monitor.
208
210
 
209
211
  ### 6.6 `export`
@@ -23,6 +23,7 @@ This document states what AgentInspect **does not** provide today. It complement
23
23
 
24
24
  - **Subpath APIs:** `agent-inspect/writers`, `agent-inspect/readers`, and advanced helpers are available for local adoption from their owning subpaths. `createInspector()` is part of the small root API.
25
25
  - **Explicit writer ownership:** `createInspector()` does not print terminal lifecycle output or implicitly choose a disk writer. Use `fileWriter()` / `bufferedFileWriter()` / custom writers when persistence is desired.
26
+ - **Diagnostic evidence, not an event-sourced runtime:** AgentInspect traces are local diagnostic evidence of what an agent did. They are **not** a write-ahead log, durable-before-effect journal, or application event-sourcing runtime. Buffered writers may lose a bounded tail of events on abrupt process termination. Call `flush()` / `close()` according to writer docs when you need the queue drained before exit; that lifecycle is deterministic and idempotent, but it is **not** an fsync / crash-durability guarantee.
26
27
  - **No standards upload:** OpenInference and OTLP JSON support is local read/export compatibility only. There is no OTLP gRPC/HTTP streaming sink, collector client, or hosted ingestion behavior.
27
28
  - **Conservative detection:** `agent-inspect open` does not silently accept arbitrary JSON. Unsupported or ambiguous inputs produce errors/warnings rather than guessed traces.
28
29
  - **Large inputs:** reader inputs are bounded and read into local memory. This is not a database index or production log warehouse.
@@ -14,6 +14,36 @@ Contracts compile to deterministic check rules for common cases:
14
14
  - evidence-bearing findings on failures
15
15
  - evaluation over **logical** TraceFacts (raw events remain available)
16
16
 
17
+ ## `tools.requiredOrder` semantics
18
+
19
+ `requiredOrder` is expanded into **adjacent pair** ordering rules with unique ids:
20
+
21
+ ```text
22
+ [A, B, C]
23
+ → contract.tool.order.0: A before B
24
+ → contract.tool.order.1: B before C
25
+ ```
26
+
27
+ Each pair compares the **first occurrence** (start/encounter order in the evaluated event stream):
28
+
29
+ - unlisted intermediate tools are allowed;
30
+ - later repetitions do not invalidate an earlier valid first-occurrence order;
31
+ - TraceContract `requiredOrder` **implies presence** — every listed name is added to the effective required-tool set;
32
+ - this is **not** causal happens-before; overlapping intervals emit a non-failing `tool.order.overlap` warning;
33
+ - combine ordering with `maxCalls` or custom rules when repeated calls matter.
34
+
35
+ Examples for `requiredOrder: ["retrieve", "generate"]`:
36
+
37
+ | Trajectory | Result |
38
+ | --- | --- |
39
+ | `retrieve → generate` | PASS |
40
+ | `retrieve → rerank → generate` | PASS |
41
+ | `retrieve → generate → retrieve` | PASS (first-occurrence) |
42
+ | `generate → retrieve` | FAIL (order) |
43
+ | `cache_lookup → generate` | FAIL (missing `retrieve` via implied presence) |
44
+
45
+ Low-level `createToolOrderingRule({ before, after })` alone may still pass when an endpoint is missing (compositional). TraceContract `requiredOrder` does not.
46
+
17
47
  ### Experimental Vitest / Jest matchers (shipped)
18
48
 
19
49
  | Package | Export | Matchers |
@@ -25,6 +55,66 @@ These are **Experimental** — API names may evolve. There is no `expectTrace(..
25
55
 
26
56
  See [API.md](./API.md), [TRACE-FACTS.md](./TRACE-FACTS.md), and `packages/core/src/checks/contract.ts`.
27
57
 
58
+ ## Rule kinds (shipped vs planned)
59
+
60
+ TraceContract rules fall into distinct categories. Mixing them incorrectly is a common source of false failures (see GitHub #308 and #309).
61
+
62
+ ### `tools.required` (shipped)
63
+
64
+ Unconditional path invariant: every named tool must appear **at least once** in the trace.
65
+
66
+ - Use when the tool is always part of a valid execution path.
67
+ - **Do not** use for steps that legitimate shortcuts may skip (for example cache hits that bypass `retrieve`).
68
+ - When a shortcut is valid but you still need evidence of the outcome, prefer `observations.required` until `alternatives.anyOf` ships (6.20.0).
69
+
70
+ ### `tools.requiredOrder` (shipped — first-occurrence / start-encounter)
71
+
72
+ Legacy first-start / encounter ordering. The evaluator walks the trace and checks that each listed tool's **first occurrence** appears after the previous tool's first occurrence.
73
+
74
+ - TraceContract `requiredOrder` **implies presence** of every listed tool (unioned into `tools.required`).
75
+ - Default mode is **first-occurrence** start/encounter order — **not** causal happens-before.
76
+ - Overlapping intervals that still satisfy start order emit a non-failing overlap warning.
77
+ - **Planned (6.20.0, GitHub #308):**
78
+ - `requiredOrderMode: "happens-before"` — first before must **end** before first after **starts**
79
+ - `requiredOrderMode: "all-occurrences"` — every before must end before every after starts
80
+
81
+ ### `observations.required` (shipped)
82
+
83
+ Requires externally observed or effect evidence (for example HTTP status, file write, cache key) rather than a specific tool call. Prefer this when the invariant is about **outcome** rather than **which tool ran**.
84
+
85
+ ### Planned (6.20.0 — not shipped)
86
+
87
+ Document only; **do not** use these fields in contracts today:
88
+
89
+ | Planned field | Purpose | GitHub |
90
+ |---------------|---------|--------|
91
+ | `alternatives.anyOf` | One of several deterministic valid paths (one level, no nested groups, no predicates) | #309 |
92
+ | `requiredOrderMode: "happens-before"` | Causal completion-before-start ordering | #308 |
93
+ | `requiredOrderMode: "all-occurrences"` | Strict ordering across all tool occurrences | #308 |
94
+
95
+ API shape for both requires maintainer approval before external PR lands. @HsienW volunteered on #308 for `requiredOrderMode` implementation.
96
+
97
+ ## Workaround until 6.20.0
98
+
99
+ When a legitimate shortcut skips a tool you would otherwise require:
100
+
101
+ 1. **Remove** unconditional `tools.required` for that step.
102
+ 2. **Express** the verified outcome via `observations.required` when possible.
103
+ 3. **Document** the cache-hit or alternate path in contract comments for reviewers.
104
+
105
+ Example matching GitHub #309 (cache hit skips second `retrieve`):
106
+
107
+ ```yaml
108
+ contract:
109
+ tools:
110
+ required: [generate] # not retrieve — cache may skip it
111
+ requiredOrder: [generate] # ordering only among tools that ran
112
+ observations:
113
+ required: [cache_hit_or_retrieve_evidence]
114
+ ```
115
+
116
+ With first-occurrence ordering, `retrieve → generate → retrieve` still **passes** when both retrieves are present (see worked example below).
117
+
28
118
  ## What is not shipped (yet)
29
119
 
30
120
  Do **not** document these as available:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-inspect",
3
- "version": "6.17.4",
3
+ "version": "6.17.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Local evidence debugger and trajectory-test toolkit for TypeScript AI agents — execution trees, TraceContract checks, Evidence v2, and read-only MCP",
@@ -211,18 +211,18 @@
211
211
  },
212
212
  "dependencies": {
213
213
  "chalk": "^5.3.0",
214
- "nanoid": "^5.0.9",
214
+ "nanoid": "^5.1.16",
215
215
  "commander": "^12.1.0"
216
216
  },
217
217
  "devDependencies": {
218
218
  "@changesets/cli": "^2.27.10",
219
219
  "@size-limit/preset-small-lib": "^11.1.6",
220
220
  "@types/node": "^22.10.2",
221
- "@vitest/coverage-v8": "^2.1.8",
221
+ "@vitest/coverage-v8": "^3.2.7",
222
222
  "size-limit": "^11.1.6",
223
223
  "tsup": "^8.3.5",
224
224
  "typescript": "^5.7.2",
225
- "vitest": "^2.1.8"
225
+ "vitest": "^3.2.7"
226
226
  },
227
227
  "scripts": {
228
228
  "clean": "pnpm -r exec -- node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
@@ -239,10 +239,12 @@
239
239
  "linked-versions:check": "node scripts/check-linked-versions.mjs",
240
240
  "docs:commands": "node scripts/validate-doc-commands.mjs",
241
241
  "docs:links": "node scripts/validate-doc-links.mjs",
242
+ "public-truth:sync": "node scripts/sync-public-truth.mjs",
242
243
  "public-truth:check": "node scripts/validate-public-truth.mjs",
243
244
  "ai-assets:check": "node scripts/validate-ai-assets.mjs",
244
245
  "package-readmes:check": "node scripts/validate-package-readmes.mjs",
245
- "docs:check": "pnpm run docs:commands && pnpm run docs:links && pnpm run public-truth:check && pnpm run ai-assets:check && pnpm run package-readmes:check && pnpm run repo:health && pnpm run demo:verify",
246
+ "docs:check": "pnpm run docs:commands && pnpm run docs:links && pnpm run public-truth:check && pnpm run ai-assets:check && pnpm run package-readmes:check && pnpm run repo:health && pnpm run build:cli && pnpm run demo:verify",
247
+ "build:cli": "pnpm exec tsup --config tsup.cli.config.ts",
246
248
  "repo:health": "node scripts/validate-repo-health.mjs",
247
249
  "demo:generate": "node scripts/demo-generate.mjs",
248
250
  "demo:verify": "node scripts/demo-verify.mjs",
@@ -252,6 +254,7 @@
252
254
  "perf:baseline": "node scripts/performance-baseline.mjs",
253
255
  "examples:check": "pnpm install && pnpm --filter agent-inspect-example-01-basic run start",
254
256
  "changeset": "changeset",
257
+ "version:packages": "changeset version && pnpm public-truth:sync && pnpm public-truth:check",
255
258
  "release": "changeset publish",
256
259
  "website:dev": "pnpm --filter @agent-inspect/website dev",
257
260
  "website:build": "pnpm --filter @agent-inspect/website build",
@@ -3230,14 +3230,15 @@ function enrichSessionSummary(summary, runs, options = {}) {
3230
3230
 
3231
3231
  // packages/core/src/sessions/checks.ts
3232
3232
  function emptySummary() {
3233
- return { passed: 0, failed: 0, warnings: 0, errors: 0 };
3233
+ return { passed: 0, failed: 0, warnings: 0, errors: 0, rulesEvaluated: 0 };
3234
3234
  }
3235
3235
  function mergeSummary(target, source) {
3236
3236
  return {
3237
3237
  passed: target.passed + source.passed,
3238
3238
  failed: target.failed + source.failed,
3239
3239
  warnings: target.warnings + source.warnings,
3240
- errors: target.errors + source.errors
3240
+ errors: target.errors + source.errors,
3241
+ rulesEvaluated: target.rulesEvaluated + source.rulesEvaluated
3241
3242
  };
3242
3243
  }
3243
3244
  function sessionDiagnostic(code, message) {
@@ -3261,6 +3262,7 @@ function aggregateSessionCheckResults(perRun, scope) {
3261
3262
  `${scope.scopeKind} not found: ${scope.scopeLabel}`
3262
3263
  )
3263
3264
  ],
3265
+ ruleExecutions: [],
3264
3266
  ...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
3265
3267
  };
3266
3268
  }
@@ -3281,17 +3283,20 @@ function aggregateSessionCheckResults(perRun, scope) {
3281
3283
  `No readable traces in ${scope.scopeKind}: ${scope.scopeLabel}`
3282
3284
  )
3283
3285
  ],
3286
+ ruleExecutions: [],
3284
3287
  ...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
3285
3288
  };
3286
3289
  }
3287
3290
  let summary = emptySummary();
3288
3291
  const findings = [];
3289
3292
  const diagnostics = [];
3293
+ const ruleExecutions = [];
3290
3294
  const runResults = [];
3291
3295
  for (const result of perRun) {
3292
3296
  summary = mergeSummary(summary, result.summary);
3293
3297
  findings.push(...result.findings);
3294
3298
  diagnostics.push(...result.diagnostics);
3299
+ ruleExecutions.push(...result.ruleExecutions ?? []);
3295
3300
  if (result.runId) {
3296
3301
  runResults.push({ runId: result.runId, status: result.status });
3297
3302
  }
@@ -3304,6 +3309,11 @@ function aggregateSessionCheckResults(perRun, scope) {
3304
3309
  if (runCmp !== 0) return runCmp;
3305
3310
  return a.ruleId.localeCompare(b.ruleId);
3306
3311
  });
3312
+ ruleExecutions.sort((a, b) => {
3313
+ const runCmp = (a.runId ?? "").localeCompare(b.runId ?? "");
3314
+ if (runCmp !== 0) return runCmp;
3315
+ return a.ruleId.localeCompare(b.ruleId);
3316
+ });
3307
3317
  const hasErrors = diagnostics.some((item) => item.severity === "error");
3308
3318
  const status = hasErrors ? "error" : summary.failed > 0 ? "fail" : "pass";
3309
3319
  return {
@@ -3317,6 +3327,7 @@ function aggregateSessionCheckResults(perRun, scope) {
3317
3327
  summary,
3318
3328
  findings,
3319
3329
  diagnostics,
3330
+ ruleExecutions,
3320
3331
  ...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
3321
3332
  };
3322
3333
  }
@@ -6401,10 +6412,11 @@ function emptySummary2() {
6401
6412
  passed: 0,
6402
6413
  failed: 0,
6403
6414
  warnings: 0,
6404
- errors: 0
6415
+ errors: 0,
6416
+ rulesEvaluated: 0
6405
6417
  };
6406
6418
  }
6407
- function errorResult(input, diagnostics, selectedRun) {
6419
+ function errorResult(input, diagnostics, selectedRun, ruleExecutions = []) {
6408
6420
  return {
6409
6421
  ok: false,
6410
6422
  status: "error",
@@ -6412,10 +6424,12 @@ function errorResult(input, diagnostics, selectedRun) {
6412
6424
  ...selectedRun ? { runId: selectedRun.runId } : {},
6413
6425
  summary: {
6414
6426
  ...emptySummary2(),
6415
- errors: diagnostics.filter((item) => item.severity === "error").length
6427
+ errors: diagnostics.filter((item) => item.severity === "error").length,
6428
+ rulesEvaluated: ruleExecutions.length
6416
6429
  },
6417
6430
  findings: [],
6418
- diagnostics: [...diagnostics]
6431
+ diagnostics: [...diagnostics],
6432
+ ruleExecutions: [...ruleExecutions]
6419
6433
  };
6420
6434
  }
6421
6435
  function flattenNodes(nodes) {
@@ -6571,7 +6585,7 @@ function normalizeFinding(rule, finding) {
6571
6585
  ...finding.action !== void 0 ? { action: finding.action } : {}
6572
6586
  };
6573
6587
  }
6574
- function summarize(findings, diagnostics) {
6588
+ function summarize(findings, diagnostics, rulesEvaluated) {
6575
6589
  return {
6576
6590
  passed: findings.filter((finding) => finding.status === "pass").length,
6577
6591
  failed: findings.filter(
@@ -6580,9 +6594,21 @@ function summarize(findings, diagnostics) {
6580
6594
  warnings: findings.filter(
6581
6595
  (finding) => finding.status === "warning" || finding.severity === "warning"
6582
6596
  ).length,
6583
- errors: diagnostics.filter((item) => item.severity === "error").length
6597
+ errors: diagnostics.filter((item) => item.severity === "error").length,
6598
+ rulesEvaluated
6584
6599
  };
6585
6600
  }
6601
+ function classifyRuleExecution(findings, threw) {
6602
+ if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
6603
+ return "fail";
6604
+ }
6605
+ if (findings.some(
6606
+ (finding) => finding.status === "warning" || finding.severity === "warning"
6607
+ )) {
6608
+ return "warning";
6609
+ }
6610
+ return "pass";
6611
+ }
6586
6612
  function stringAttr(event, keys) {
6587
6613
  for (const key of keys) {
6588
6614
  const value = event.attributes?.[key];
@@ -6679,6 +6705,9 @@ function finishedEvents(context, kind) {
6679
6705
  (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
6680
6706
  );
6681
6707
  }
6708
+ function toolInvocationEvents(context) {
6709
+ return semanticEvents(context).filter((event) => event.kind === "TOOL");
6710
+ }
6682
6711
  function isRecord8(value) {
6683
6712
  return typeof value === "object" && value !== null && !Array.isArray(value);
6684
6713
  }
@@ -7079,7 +7108,7 @@ function createToolUsageRule(options) {
7079
7108
  category: "tool",
7080
7109
  defaultSeverity: "error",
7081
7110
  evaluate(context) {
7082
- const tools = finishedEvents(context, "TOOL");
7111
+ const tools = toolInvocationEvents(context);
7083
7112
  const names = tools.map(toolName);
7084
7113
  const nameSet = new Set(names);
7085
7114
  const findings = [];
@@ -7729,12 +7758,24 @@ function createBaselineRegressionRule(options) {
7729
7758
  }
7730
7759
  function createObservedOutcomeRule(options = {}) {
7731
7760
  const failOn = options.failOn ?? ["failed"];
7761
+ const requireAny = options.requireAny === true;
7732
7762
  return {
7733
7763
  id: "outcome.status",
7734
7764
  category: "run",
7735
7765
  defaultSeverity: "error",
7736
7766
  evaluate(context) {
7737
7767
  const outcomes = extractOutcomesFromPersistedEvents(context.events);
7768
+ if (requireAny && outcomes.length === 0) {
7769
+ return [
7770
+ failFinding(
7771
+ "outcome.status",
7772
+ "Expected at least one observed outcome.",
7773
+ runEvidence(context.selectedRun),
7774
+ { requireAny: true, expected: "at least one observed outcome" },
7775
+ { code: "outcome.missing", actual: 0 }
7776
+ )
7777
+ ];
7778
+ }
7738
7779
  const matching = outcomesMatchingStatus(outcomes, failOn);
7739
7780
  if (matching.length === 0) return [];
7740
7781
  return [
@@ -7770,6 +7811,18 @@ function runTraceChecks(input, options = {}) {
7770
7811
  if (rules.diagnostics.length > 0) {
7771
7812
  return errorResult(input, rules.diagnostics, selected.run);
7772
7813
  }
7814
+ if (rules.rules.length === 0) {
7815
+ return errorResult(
7816
+ input,
7817
+ [
7818
+ diagnostic3(
7819
+ "AI_CHECK_NO_RULES_EVALUATED",
7820
+ "No trace check rules were evaluated. Configure at least one rule, contract, or CLI check option."
7821
+ )
7822
+ ],
7823
+ selected.run
7824
+ );
7825
+ }
7773
7826
  const facts = buildFacts2(input, selected.run);
7774
7827
  const context = {
7775
7828
  ...facts,
@@ -7778,22 +7831,38 @@ function runTraceChecks(input, options = {}) {
7778
7831
  };
7779
7832
  const diagnostics = [];
7780
7833
  const findings = [];
7834
+ const ruleExecutions = [];
7781
7835
  for (const rule of rules.rules) {
7782
7836
  try {
7783
- findings.push(...rule.evaluate(context).map((finding) => normalizeFinding(rule, finding)));
7837
+ const ruleFindings = rule.evaluate(context).map((finding) => normalizeFinding(rule, finding));
7838
+ findings.push(...ruleFindings);
7839
+ ruleExecutions.push({
7840
+ ruleId: rule.id,
7841
+ category: rule.category,
7842
+ status: classifyRuleExecution(ruleFindings, false),
7843
+ findingCount: ruleFindings.length,
7844
+ ...selected.run ? { runId: selected.run.runId } : {}
7845
+ });
7784
7846
  } catch (error) {
7785
7847
  const message = error instanceof Error ? error.message : String(error);
7786
7848
  diagnostics.push(
7787
7849
  diagnostic3("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
7788
7850
  );
7851
+ ruleExecutions.push({
7852
+ ruleId: rule.id,
7853
+ category: rule.category,
7854
+ status: "error",
7855
+ findingCount: 0,
7856
+ ...selected.run ? { runId: selected.run.runId } : {}
7857
+ });
7789
7858
  }
7790
7859
  }
7791
7860
  if (diagnostics.length > 0) {
7792
- return errorResult(input, diagnostics, selected.run);
7861
+ return errorResult(input, diagnostics, selected.run, ruleExecutions);
7793
7862
  }
7794
7863
  const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
7795
7864
  const sortedFindings = findings.sort(compareFindings(eventById));
7796
- const summary = summarize(sortedFindings, diagnostics);
7865
+ const summary = summarize(sortedFindings, diagnostics, ruleExecutions.length);
7797
7866
  const status = summary.failed > 0 ? "fail" : "pass";
7798
7867
  return {
7799
7868
  ok: status === "pass",
@@ -7802,7 +7871,8 @@ function runTraceChecks(input, options = {}) {
7802
7871
  ...selected.run ? { runId: selected.run.runId } : {},
7803
7872
  summary,
7804
7873
  findings: sortedFindings,
7805
- diagnostics
7874
+ diagnostics,
7875
+ ruleExecutions
7806
7876
  };
7807
7877
  }
7808
7878
 
@@ -10166,8 +10236,7 @@ async function runSuiteCase(suiteCase, config, options) {
10166
10236
  status: "pass",
10167
10237
  format: read.format,
10168
10238
  findings: [],
10169
- diagnostics: []
10170
- };
10239
+ diagnostics: []};
10171
10240
  const observationResult = validateExpectedObservations(suiteCase, read);
10172
10241
  const diagnostics = [
10173
10242
  ...checkResult.diagnostics.map(
@@ -11346,5 +11415,5 @@ function renderGateReport(result, options = {}) {
11346
11415
  }
11347
11416
 
11348
11417
  export { COHORT_METRIC_IDS, DEFAULT_SUITE_ARTIFACTS_DIR, EVIDENCE_FORMAT_VERSION, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, Redactor, TraceDirectory, TraceReadError, TreeBuilder, aggregateBundleSafeStatus, aggregateSessionCheckResults, analyzeCohort, applyProfileMetadataCaps, assertBundlePathContained, assertEvidenceRelativePath, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceCausalFailureViewHtml, buildEvidenceCiPackage, buildEvidenceCircuitViewHtml, buildEvidenceContractsViewHtml, buildEvidenceDiffViewHtml, buildEvidenceHtmlShell, buildEvidenceManifest, buildEvidenceOutcomesViewHtml, buildEvidenceProvenanceViewHtml, buildEvidenceSafetyViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceTreeViewHtml, buildLocalExplanation, buildPlaceholderArtifact, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, buildZipArchive, bundleFailsOnSafety, bundleRunAssetRelativePath, collectTraceSchemaVersions, compactAttributes, createBaselineRegressionRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRunDepthRule, createRunDurationRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStructureCycleRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolUsageRule, defaultBundleOutputPath, defaultSuiteConfigTemplate, diffRuns, diffTraceEvents, enrichSessionRunRecord, escapeHtml, escapeMarkdown, extractMetadata, extractOutcomesFromTraceEvents, filterMetasBySessionScope, filterTraces, flattenTree, formatDuration2 as formatDuration, formatStepLabel, formatTimestamp, gateHasThresholds, getIndent, getTraceFilePath, inferEvidenceFileRole, isAgentInspectTrace, isPersistedInspectEvent, loadSessionRunRecords, loadSuiteConfig, loadTraceMetadataList, manualTraceEventsToComparableRun, nanoid, normalizeBundleOutputPath, openTrace, parseCohortMetricList, parseDuration, parseDurationFilter, parseGateList, parseTraceJsonl, persistedInspectEventsToTraceEvents, renderActivitySummaryHuman, renderCohortReport, renderErrorLine, renderGateReport, renderObservedOutcomesHtml, renderObservedOutcomesMarkdown, renderRunDiff, renderRunWhat, renderStepLine, renderSuiteReport, renderTimeline, renderTraceStats, resolveBundleRunIds, resolveRedactionProfile, resolveSuiteTemplate, resolveTraceDir, runGate, runSuite, runTraceChecks, safeString, sanitizeBundleRunId, searchTraces, serializeEvidenceManifest, sha256Hex, stableJson, summarizeObservedOutcomes, summarizeSemanticParity, traceEventToPersistedInspectEvent, truncateName, truncateStringForProfile, validateEvent, validateSuiteConfig, verifyEvidenceDirectory, zeroKinds };
11349
- //# sourceMappingURL=chunk-YX6RFLV5.mjs.map
11350
- //# sourceMappingURL=chunk-YX6RFLV5.mjs.map
11418
+ //# sourceMappingURL=chunk-GOHYSE7W.mjs.map
11419
+ //# sourceMappingURL=chunk-GOHYSE7W.mjs.map