agent-inspect 6.31.28 → 6.31.30

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,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.31.30
4
+
5
+ ### Patch Changes
6
+
7
+ - 35923dd: Join sibling start/complete lifecycle rows in HTML/Markdown presentation and coalesce bundle HTML trees so completed steps are not shown as still running.
8
+ - f1c1752: Nest tenant recipe now executes real SupportService/PolicyService seams and scores consumption from reopened saved outcomes (N01–N08), not in-memory joinOk.
9
+ - f664b85: Om browser recipe records injected-clock wait/readiness diagnostics (document, in-flight, obstruction, elapsed/budget/remaining, stop reason) without inventing overlay from LocatorNotFound.
10
+
11
+ ## 6.31.29
12
+
13
+ ### Patch Changes
14
+
15
+ - dcd714d: Tighten saved-evidence recipe controls: timeout-ledger same-run/marker/claim coverage, comparable-cohort one-pass reopen verification, and Nest opaque tenant/operation refs.
16
+
3
17
  ## 6.31.28
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -213,7 +213,7 @@ The root package is enough for custom capture, the CLI, checks, and Evidence wor
213
213
 
214
214
  ## Status and documentation
215
215
 
216
- **Current published baseline:** **6.31.28** · persisted schema `1.0` · Node.js `>=20` · MIT.
216
+ **Current published baseline:** **6.31.30** · persisted schema `1.0` · Node.js `>=20` · MIT.
217
217
 
218
218
  Legacy v0.1 and v0.2 traces remain readable. Check the npm badge and [changelog](CHANGELOG.md) for the current published version.
219
219
 
package/docs/CLI.md CHANGED
@@ -382,12 +382,37 @@ Rich TraceContract JSON (6.30+):
382
382
  }
383
383
  ```
384
384
 
385
- Supported `contract` fields: `run`, `tools` (including `arguments`, `orderRules`),
385
+ Supported `contract` fields: `run`, `tools` (including `arguments`, `orderRules`,
386
+ `requiredOrder`, `requiredOrderMode`), `steps` (including `orderRelations`),
386
387
  `llm`, `observations` (including `requireProvenance`), `scope`, `alternatives.anyOf`,
387
388
  `controls`, and `retry`. Unknown keys fail closed (exit 2). With `--evidence-on`,
388
389
  contract mode writes `contract.resolved.json` and binds digests into check-results.
389
390
  YAML is not supported. TypeScript config files (`.ts`, `.mts`, `.cts`) fail clearly unless a future explicit loader strategy is added; use precompiled JavaScript config instead.
390
391
 
392
+ `steps.orderRelations` (6.31+) declares typed cross-kind ordering (for example TOOL
393
+ before LLM). Each relation has `before` / `after` step refs (`kind` + `name`), optional
394
+ `mode` (`first-occurrence` | `happens-before` | `all-occurrences`; default
395
+ `first-occurrence`), and optional `requireEndpoints` (default `true`). This is separate
396
+ from TOOL-only `tools.requiredOrder` / `orderRules`. Invalid shapes and unknown keys
397
+ fail closed.
398
+
399
+ ```json
400
+ {
401
+ "contract": {
402
+ "tools": { "required": ["retrieve_policy"] },
403
+ "steps": {
404
+ "orderRelations": [
405
+ {
406
+ "before": { "kind": "TOOL", "name": "retrieve_policy" },
407
+ "after": { "kind": "LLM", "name": "answer" },
408
+ "mode": "first-occurrence"
409
+ }
410
+ ]
411
+ }
412
+ }
413
+ }
414
+ ```
415
+
391
416
  Examples:
392
417
 
393
418
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-inspect",
3
- "version": "6.31.28",
3
+ "version": "6.31.30",
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",
@@ -17304,7 +17304,7 @@ var init_src = __esm({
17304
17304
  });
17305
17305
 
17306
17306
  // package.json
17307
- var version = "6.31.28";
17307
+ var version = "6.31.30";
17308
17308
 
17309
17309
  // packages/cli/src/list.ts
17310
17310
  init_advanced();
@@ -19516,6 +19516,98 @@ function redactRunTreeForExport(tree, options) {
19516
19516
 
19517
19517
  // packages/core/src/exporters/html-exporter.ts
19518
19518
  init_helpers();
19519
+
19520
+ // packages/core/src/exporters/presentation-lifecycle.ts
19521
+ function stepIdOf2(event) {
19522
+ const value = event.attributes?.stepId;
19523
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
19524
+ }
19525
+ function lifecycleKey(event) {
19526
+ if (event.kind === "RUN") return `run:${event.runId}`;
19527
+ const stepId = stepIdOf2(event);
19528
+ if (stepId !== void 0) return `step:${stepId}`;
19529
+ return `event:${event.eventId}`;
19530
+ }
19531
+ function isTerminal(status) {
19532
+ return status === "ok" || status === "error";
19533
+ }
19534
+ function cloneNode(node) {
19535
+ return {
19536
+ event: {
19537
+ ...node.event,
19538
+ ...node.event.attributes !== void 0 ? { attributes: { ...node.event.attributes } } : {}
19539
+ },
19540
+ children: node.children.map(cloneNode),
19541
+ depth: node.depth
19542
+ };
19543
+ }
19544
+ function joinSiblingGroup(group) {
19545
+ if (group.length <= 1) return group;
19546
+ const terminals = group.filter((n) => isTerminal(n.event.status));
19547
+ const runnings = group.filter((n) => n.event.status === "running");
19548
+ const terminalStatuses = new Set(terminals.map((n) => n.event.status));
19549
+ if (terminalStatuses.size > 1) {
19550
+ return group;
19551
+ }
19552
+ if (terminals.length >= 1) {
19553
+ const terminal = terminals[terminals.length - 1];
19554
+ const start = runnings[0];
19555
+ const childMap = /* @__PURE__ */ new Map();
19556
+ for (const n of group) {
19557
+ for (const child of n.children) {
19558
+ childMap.set(child.event.eventId, child);
19559
+ }
19560
+ }
19561
+ return [
19562
+ {
19563
+ event: {
19564
+ ...terminal.event,
19565
+ name: start?.event.name ?? terminal.event.name,
19566
+ kind: start?.event.kind ?? terminal.event.kind,
19567
+ ...start?.event.timestamp !== void 0 && terminal.event.durationMs === void 0 ? {} : {}
19568
+ },
19569
+ children: presentSiblingNodes([...childMap.values()]),
19570
+ depth: terminal.depth
19571
+ }
19572
+ ];
19573
+ }
19574
+ return group;
19575
+ }
19576
+ function presentSiblingNodes(nodes) {
19577
+ const prepared = nodes.map((n) => ({
19578
+ ...cloneNode(n),
19579
+ children: presentSiblingNodes(n.children)
19580
+ }));
19581
+ const order = [];
19582
+ const groups = /* @__PURE__ */ new Map();
19583
+ for (const node of prepared) {
19584
+ const key = lifecycleKey(node.event);
19585
+ if (!groups.has(key)) {
19586
+ groups.set(key, []);
19587
+ order.push(key);
19588
+ }
19589
+ groups.get(key).push(node);
19590
+ }
19591
+ const out = [];
19592
+ for (const key of order) {
19593
+ out.push(...joinSiblingGroup(groups.get(key)));
19594
+ }
19595
+ return out;
19596
+ }
19597
+ function presentLifecycleJoinedTree(tree) {
19598
+ return {
19599
+ ...tree,
19600
+ children: presentSiblingNodes(tree.children),
19601
+ metadata: {
19602
+ ...tree.metadata,
19603
+ confidenceBreakdown: { ...tree.metadata.confidenceBreakdown },
19604
+ kinds: { ...tree.metadata.kinds },
19605
+ ...tree.metadata.relationshipSummary !== void 0 ? { relationshipSummary: { ...tree.metadata.relationshipSummary } } : {}
19606
+ }
19607
+ };
19608
+ }
19609
+
19610
+ // packages/core/src/exporters/html-exporter.ts
19519
19611
  function renderTreeHtml2(nodes, ulClass = "tree") {
19520
19612
  if (nodes.length === 0) return "";
19521
19613
  const parts = [`<ul class="${ulClass}">`];
@@ -19542,32 +19634,33 @@ function exportHtml(tree, options) {
19542
19634
  const includeErrors = options?.includeErrors ?? true;
19543
19635
  const maxLen = options?.maxAttributeLength ?? 500;
19544
19636
  const redacted = options?.redacted ?? true;
19545
- const titleName = escapeHtml(tree.name ?? tree.runId);
19637
+ const view2 = presentLifecycleJoinedTree(tree);
19638
+ const titleName = escapeHtml(view2.name ?? view2.runId);
19546
19639
  const summaryRows = [];
19547
19640
  summaryRows.push(
19548
- `<tr><th scope="row">runId</th><td><code>${escapeHtml(tree.runId)}</code></td></tr>`
19641
+ `<tr><th scope="row">runId</th><td><code>${escapeHtml(view2.runId)}</code></td></tr>`
19549
19642
  );
19550
- if (tree.name !== void 0) {
19551
- summaryRows.push(`<tr><th scope="row">name</th><td>${escapeHtml(tree.name)}</td></tr>`);
19643
+ if (view2.name !== void 0) {
19644
+ summaryRows.push(`<tr><th scope="row">name</th><td>${escapeHtml(view2.name)}</td></tr>`);
19552
19645
  }
19553
19646
  summaryRows.push(
19554
- `<tr><th scope="row">status</th><td>${escapeHtml(String(tree.status ?? "unknown"))}</td></tr>`
19647
+ `<tr><th scope="row">status</th><td>${escapeHtml(String(view2.status ?? "unknown"))}</td></tr>`
19555
19648
  );
19556
19649
  summaryRows.push(
19557
- `<tr><th scope="row">durationMs</th><td>${tree.durationMs !== void 0 ? escapeHtml(String(tree.durationMs)) : "\u2014"}</td></tr>`
19650
+ `<tr><th scope="row">durationMs</th><td>${view2.durationMs !== void 0 ? escapeHtml(String(view2.durationMs)) : "\u2014"}</td></tr>`
19558
19651
  );
19559
19652
  summaryRows.push(
19560
- `<tr><th scope="row">startedAt</th><td>${tree.startedAt !== void 0 ? escapeHtml(String(tree.startedAt)) : "\u2014"}</td></tr>`
19653
+ `<tr><th scope="row">startedAt</th><td>${view2.startedAt !== void 0 ? escapeHtml(String(view2.startedAt)) : "\u2014"}</td></tr>`
19561
19654
  );
19562
19655
  summaryRows.push(
19563
- `<tr><th scope="row">endedAt</th><td>${tree.endedAt !== void 0 ? escapeHtml(String(tree.endedAt)) : "\u2014"}</td></tr>`
19656
+ `<tr><th scope="row">endedAt</th><td>${view2.endedAt !== void 0 ? escapeHtml(String(view2.endedAt)) : "\u2014"}</td></tr>`
19564
19657
  );
19565
19658
  summaryRows.push(
19566
- `<tr><th scope="row">totalEvents</th><td>${escapeHtml(String(tree.metadata.totalEvents))}</td></tr>`
19659
+ `<tr><th scope="row">totalEvents</th><td>${escapeHtml(String(view2.metadata.totalEvents))}</td></tr>`
19567
19660
  );
19568
19661
  let confidenceHtml = "";
19569
19662
  if (includeMetadata) {
19570
- const cb = tree.metadata.confidenceBreakdown;
19663
+ const cb = view2.metadata.confidenceBreakdown;
19571
19664
  confidenceHtml += "<h3>Confidence breakdown</h3><table><thead><tr><th>bucket</th><th>count</th></tr></thead><tbody>";
19572
19665
  for (const k of Object.keys(cb).sort()) {
19573
19666
  const key = k;
@@ -19575,14 +19668,14 @@ function exportHtml(tree, options) {
19575
19668
  }
19576
19669
  confidenceHtml += "</tbody></table>";
19577
19670
  confidenceHtml += "<h3>Kind breakdown</h3><table><thead><tr><th>kind</th><th>count</th></tr></thead><tbody>";
19578
- for (const k of Object.keys(tree.metadata.kinds).sort()) {
19671
+ for (const k of Object.keys(view2.metadata.kinds).sort()) {
19579
19672
  const key = k;
19580
- const c = tree.metadata.kinds[key];
19673
+ const c = view2.metadata.kinds[key];
19581
19674
  if (c > 0) confidenceHtml += `<tr><td>${escapeHtml(key)}</td><td>${c}</td></tr>`;
19582
19675
  }
19583
19676
  confidenceHtml += "</tbody></table>";
19584
19677
  }
19585
- const flat = flattenTree(tree);
19678
+ const flat = flattenTree(view2);
19586
19679
  const errors = flat.filter((n) => n.event.status === "error");
19587
19680
  let errorsHtml = "";
19588
19681
  if (includeErrors && errors.length > 0) {
@@ -19637,7 +19730,7 @@ footer{margin-top:2rem;font-size:0.85rem;color:#555}
19637
19730
  <header><h1>AgentInspect Run: ${titleName}</h1></header>
19638
19731
  <p class="note">Generated locally by AgentInspect.</p>
19639
19732
  ${includeMetadata ? `<section class="summary"><h2>Summary</h2><table>${summaryRows.join("")}</table>${confidenceHtml}</section>` : ""}
19640
- <section class="tree"><h2>Execution tree</h2>${tree.children.length > 0 ? renderTreeHtml2(tree.children) : "<p>No steps recorded.</p>"}</section>
19733
+ <section class="tree"><h2>Execution tree</h2>${view2.children.length > 0 ? renderTreeHtml2(view2.children) : "<p>No steps recorded.</p>"}</section>
19641
19734
  ${errorsHtml}
19642
19735
  ${attrsHtml}
19643
19736
  <footer>Generated locally by AgentInspect. Review for sensitive data before sharing.</footer>
@@ -19679,7 +19772,8 @@ function exportMarkdown(tree, options) {
19679
19772
  const includeErrors = options?.includeErrors ?? true;
19680
19773
  const maxLen = options?.maxAttributeLength ?? 500;
19681
19774
  const redacted = options?.redacted ?? true;
19682
- const titleName = tree.name ?? tree.runId;
19775
+ const view2 = presentLifecycleJoinedTree(tree);
19776
+ const titleName = view2.name ?? view2.runId;
19683
19777
  const lines = [];
19684
19778
  lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
19685
19779
  lines.push("");
@@ -19688,28 +19782,28 @@ function exportMarkdown(tree, options) {
19688
19782
  if (includeMetadata) {
19689
19783
  lines.push("## Summary");
19690
19784
  lines.push("");
19691
- lines.push(`- **runId**: ${escapeMarkdown(tree.runId)}`);
19692
- if (tree.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(tree.name)}`);
19693
- lines.push(`- **status**: ${escapeMarkdown(String(tree.status ?? "unknown"))}`);
19785
+ lines.push(`- **runId**: ${escapeMarkdown(view2.runId)}`);
19786
+ if (view2.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(view2.name)}`);
19787
+ lines.push(`- **status**: ${escapeMarkdown(String(view2.status ?? "unknown"))}`);
19694
19788
  lines.push(
19695
- `- **durationMs**: ${tree.durationMs !== void 0 ? escapeMarkdown(String(tree.durationMs)) : "-"}`
19789
+ `- **durationMs**: ${view2.durationMs !== void 0 ? escapeMarkdown(String(view2.durationMs)) : "-"}`
19696
19790
  );
19697
19791
  lines.push(
19698
- `- **startedAt**: ${tree.startedAt !== void 0 ? escapeMarkdown(String(tree.startedAt)) : "-"}`
19792
+ `- **startedAt**: ${view2.startedAt !== void 0 ? escapeMarkdown(String(view2.startedAt)) : "-"}`
19699
19793
  );
19700
19794
  lines.push(
19701
- `- **endedAt**: ${tree.endedAt !== void 0 ? escapeMarkdown(String(tree.endedAt)) : "-"}`
19795
+ `- **endedAt**: ${view2.endedAt !== void 0 ? escapeMarkdown(String(view2.endedAt)) : "-"}`
19702
19796
  );
19703
- lines.push(`- **totalEvents**: ${tree.metadata.totalEvents}`);
19797
+ lines.push(`- **totalEvents**: ${view2.metadata.totalEvents}`);
19704
19798
  lines.push("");
19705
19799
  lines.push("### Confidence breakdown");
19706
19800
  lines.push("");
19707
19801
  lines.push("| bucket | count |");
19708
19802
  lines.push("| --- | --- |");
19709
- for (const k of Object.keys(tree.metadata.confidenceBreakdown).sort()) {
19803
+ for (const k of Object.keys(view2.metadata.confidenceBreakdown).sort()) {
19710
19804
  const key = k;
19711
19805
  lines.push(
19712
- `| ${escapeMarkdown(key)} | ${tree.metadata.confidenceBreakdown[key]} |`
19806
+ `| ${escapeMarkdown(key)} | ${view2.metadata.confidenceBreakdown[key]} |`
19713
19807
  );
19714
19808
  }
19715
19809
  lines.push("");
@@ -19717,9 +19811,9 @@ function exportMarkdown(tree, options) {
19717
19811
  lines.push("");
19718
19812
  lines.push("| kind | count |");
19719
19813
  lines.push("| --- | --- |");
19720
- for (const k of Object.keys(tree.metadata.kinds).sort()) {
19814
+ for (const k of Object.keys(view2.metadata.kinds).sort()) {
19721
19815
  const key = k;
19722
- const c = tree.metadata.kinds[key];
19816
+ const c = view2.metadata.kinds[key];
19723
19817
  if (c > 0) lines.push(`| ${escapeMarkdown(key)} | ${c} |`);
19724
19818
  }
19725
19819
  lines.push("");
@@ -19728,11 +19822,11 @@ function exportMarkdown(tree, options) {
19728
19822
  lines.push("");
19729
19823
  lines.push("```text");
19730
19824
  lines.push(
19731
- tree.children.length > 0 ? renderTreeAscii(tree.children) : "(no steps)"
19825
+ view2.children.length > 0 ? renderTreeAscii(view2.children) : "(no steps)"
19732
19826
  );
19733
19827
  lines.push("```");
19734
19828
  lines.push("");
19735
- const flat = flattenTree(tree);
19829
+ const flat = flattenTree(view2);
19736
19830
  const errors = flat.filter((n) => n.event.status === "error");
19737
19831
  if (includeErrors && errors.length > 0) {
19738
19832
  lines.push("## Errors");
@@ -29434,11 +29528,14 @@ async function bundleCommand(runIdArg, options = {}) {
29434
29528
  ...finding.action !== void 0 ? { action: finding.action } : {}
29435
29529
  });
29436
29530
  }
29437
- if (artifactTree) {
29438
- artifactTrees.push(artifactTree);
29439
- } else {
29440
- const selected2 = read.runs.find((run) => run.runId === runId);
29441
- if (selected2) artifactTrees.push(selected2);
29531
+ const persistedForRun = artifactEventsByRun.get(runId) ?? read.events.filter((event) => event.runId === runId);
29532
+ const presentationTrees = persistedInspectEventsToRunTrees(persistedForRun, {
29533
+ coalesceLifecycle: true,
29534
+ skipInvalid: true
29535
+ });
29536
+ const presentationTree = presentationTrees.find((candidate) => candidate.runId === runId) ?? presentationTrees[0] ?? artifactTree ?? read.runs.find((run) => run.runId === runId);
29537
+ if (presentationTree) {
29538
+ artifactTrees.push(presentationTree);
29442
29539
  }
29443
29540
  const status = safetyStatusFromAssess(artifactSafety.status);
29444
29541
  checkRuns.push({
@@ -29449,8 +29546,17 @@ async function bundleCommand(runIdArg, options = {}) {
29449
29546
  warnings: artifactSafety.summary.warnings,
29450
29547
  findings: artifactSafety.summary.findings
29451
29548
  });
29452
- const selected = read.runs.find((run) => run.runId === runId);
29453
- const runReport = exportRunTree(selected ?? read.runs[0], {
29549
+ if (!presentationTree) {
29550
+ const message = `No run tree could be built for HTML presentation: ${runId}`;
29551
+ if (options.json) {
29552
+ console.log(writeJson4({ ok: false, error: message }).trimEnd());
29553
+ } else {
29554
+ console.error(`[AgentInspect] bundle failed: ${message}`);
29555
+ }
29556
+ process.exitCode = 1;
29557
+ return;
29558
+ }
29559
+ const runReport = exportRunTree(presentationTree, {
29454
29560
  format: "html",
29455
29561
  redacted: true,
29456
29562
  redactionProfile: toReportProfile(profile)