agent-inspect 6.31.29 → 6.31.31

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { ATTRIBUTION_CONFIDENCES, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, IDENTITY_CONFLICT_ATTR, DELIVERY_CONFLICT_ATTR, DELIVERY_EVENT_BASE_ATTR, DEFAULT_REDACT_KEYS, truncateStringForProfile, stringContainsHighConfidenceCredential, isCredentialSensitiveKey, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, renderErrorLine, isAgentInspectTrace, persistedInspectEventsToRunTrees, defineTraceContract, filterMetasBySessionScope, evaluateTraceContract, aggregateSessionCheckResults, createBaselineRegressionRule, summarizeSemanticParity, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, formatStepLabel, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, getIndent, TreeBuilder, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, projectLogicalEvents, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, isAttributionConfidence, zeroKinds, diffRuns, manualTraceEventsToComparableRun } from './chunk-4GTOJ3G2.mjs';
2
+ import { ATTRIBUTION_CONFIDENCES, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, persistedInspectEventsToRunTrees, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, IDENTITY_CONFLICT_ATTR, DELIVERY_CONFLICT_ATTR, DELIVERY_EVENT_BASE_ATTR, DEFAULT_REDACT_KEYS, truncateStringForProfile, stringContainsHighConfidenceCredential, isCredentialSensitiveKey, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, renderErrorLine, isAgentInspectTrace, defineTraceContract, filterMetasBySessionScope, evaluateTraceContract, aggregateSessionCheckResults, createBaselineRegressionRule, summarizeSemanticParity, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, formatStepLabel, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, getIndent, TreeBuilder, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, projectLogicalEvents, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, isAttributionConfidence, zeroKinds, diffRuns, manualTraceEventsToComparableRun } from './chunk-4GTOJ3G2.mjs';
3
3
  import { realpathSync, constants as constants$1, existsSync, readFileSync } from 'fs';
4
4
  import path19 from 'path';
5
5
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -14,7 +14,7 @@ import { createServer } from 'http';
14
14
  import { createRequire } from 'module';
15
15
 
16
16
  // package.json
17
- var version = "6.31.29";
17
+ var version = "6.31.31";
18
18
 
19
19
  // packages/cli/src/trace-dir-scale.ts
20
20
  var TRACE_COUNT_WARN = 1e3;
@@ -2173,6 +2173,159 @@ function redactRunTreeForExport(tree, options) {
2173
2173
  return clone;
2174
2174
  }
2175
2175
 
2176
+ // packages/core/src/exporters/presentation-lifecycle.ts
2177
+ function stepIdOf(event) {
2178
+ const value = event.attributes?.stepId;
2179
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
2180
+ }
2181
+ function attemptIdOf(event) {
2182
+ const direct = event.attributes?.attemptId;
2183
+ if (typeof direct === "string" && direct.trim() !== "") return direct;
2184
+ const meta = event.attributes?.metadata;
2185
+ if (meta && typeof meta === "object" && !Array.isArray(meta)) {
2186
+ const nested = meta.attemptId;
2187
+ if (typeof nested === "string" && nested.trim() !== "") return nested;
2188
+ }
2189
+ return void 0;
2190
+ }
2191
+ function lifecycleKey(event) {
2192
+ if (event.kind === "RUN") return `run:${event.runId}`;
2193
+ const stepId = stepIdOf(event);
2194
+ if (stepId !== void 0) return `step:${stepId}`;
2195
+ return `event:${event.eventId}`;
2196
+ }
2197
+ function isTerminal(status) {
2198
+ return status === "ok" || status === "error";
2199
+ }
2200
+ function eventTime(event) {
2201
+ return typeof event.timestamp === "number" ? event.timestamp : 0;
2202
+ }
2203
+ function cloneNode(node) {
2204
+ return {
2205
+ event: {
2206
+ ...node.event,
2207
+ ...node.event.attributes !== void 0 ? { attributes: { ...node.event.attributes } } : {}
2208
+ },
2209
+ children: node.children.map(cloneNode),
2210
+ depth: node.depth
2211
+ };
2212
+ }
2213
+ function joinOnePair(start, terminal) {
2214
+ const childMap = /* @__PURE__ */ new Map();
2215
+ if (start) {
2216
+ for (const child of start.children) {
2217
+ childMap.set(child.event.eventId, child);
2218
+ }
2219
+ }
2220
+ for (const child of terminal.children) {
2221
+ childMap.set(child.event.eventId, child);
2222
+ }
2223
+ return {
2224
+ event: {
2225
+ ...terminal.event,
2226
+ name: start?.event.name ?? terminal.event.name,
2227
+ kind: start?.event.kind ?? terminal.event.kind,
2228
+ ...start && attemptIdOf(terminal.event) === void 0 && attemptIdOf(start.event) ? {
2229
+ attributes: {
2230
+ ...terminal.event.attributes,
2231
+ attemptId: attemptIdOf(start.event)
2232
+ }
2233
+ } : {}
2234
+ },
2235
+ children: presentSiblingNodes([...childMap.values()]),
2236
+ depth: terminal.depth
2237
+ };
2238
+ }
2239
+ function joinSiblingGroup(group) {
2240
+ if (group.length <= 1) return group;
2241
+ const terminals = group.filter((n) => isTerminal(n.event.status)).sort((a, b) => eventTime(a.event) - eventTime(b.event));
2242
+ const runnings = group.filter((n) => n.event.status === "running").sort((a, b) => eventTime(a.event) - eventTime(b.event));
2243
+ const others = group.filter(
2244
+ (n) => !isTerminal(n.event.status) && n.event.status !== "running"
2245
+ );
2246
+ const terminalStatuses = new Set(terminals.map((n) => n.event.status));
2247
+ if (terminalStatuses.size > 1) {
2248
+ return group;
2249
+ }
2250
+ if (terminals.length > 1 && terminals.every((t) => attemptIdOf(t.event) === void 0) && runnings.every((r) => attemptIdOf(r.event) === void 0)) {
2251
+ return group;
2252
+ }
2253
+ const usedRunning = /* @__PURE__ */ new Set();
2254
+ const out = [];
2255
+ for (const terminal of terminals) {
2256
+ const terminalAttempt = attemptIdOf(terminal.event);
2257
+ const terminalTs = eventTime(terminal.event);
2258
+ const candidate = runnings.find((r) => {
2259
+ if (usedRunning.has(r.event.eventId)) return false;
2260
+ if (eventTime(r.event) > terminalTs) return false;
2261
+ const runningAttempt = attemptIdOf(r.event);
2262
+ if (terminalAttempt !== void 0) {
2263
+ return runningAttempt === terminalAttempt;
2264
+ }
2265
+ if (runningAttempt !== void 0) {
2266
+ return true;
2267
+ }
2268
+ return true;
2269
+ });
2270
+ if (candidate) {
2271
+ usedRunning.add(candidate.event.eventId);
2272
+ out.push(joinOnePair(candidate, terminal));
2273
+ } else {
2274
+ out.push({
2275
+ ...terminal,
2276
+ children: presentSiblingNodes(terminal.children)
2277
+ });
2278
+ }
2279
+ }
2280
+ for (const running of runnings) {
2281
+ if (usedRunning.has(running.event.eventId)) continue;
2282
+ out.push({
2283
+ ...running,
2284
+ children: presentSiblingNodes(running.children)
2285
+ });
2286
+ }
2287
+ for (const other of others) {
2288
+ out.push({
2289
+ ...other,
2290
+ children: presentSiblingNodes(other.children)
2291
+ });
2292
+ }
2293
+ return out;
2294
+ }
2295
+ function presentSiblingNodes(nodes) {
2296
+ const prepared = nodes.map((n) => ({
2297
+ ...cloneNode(n),
2298
+ children: presentSiblingNodes(n.children)
2299
+ }));
2300
+ const order = [];
2301
+ const groups = /* @__PURE__ */ new Map();
2302
+ for (const node of prepared) {
2303
+ const key = lifecycleKey(node.event);
2304
+ if (!groups.has(key)) {
2305
+ groups.set(key, []);
2306
+ order.push(key);
2307
+ }
2308
+ groups.get(key).push(node);
2309
+ }
2310
+ const out = [];
2311
+ for (const key of order) {
2312
+ out.push(...joinSiblingGroup(groups.get(key)));
2313
+ }
2314
+ return out;
2315
+ }
2316
+ function presentLifecycleJoinedTree(tree) {
2317
+ return {
2318
+ ...tree,
2319
+ children: presentSiblingNodes(tree.children),
2320
+ metadata: {
2321
+ ...tree.metadata,
2322
+ confidenceBreakdown: { ...tree.metadata.confidenceBreakdown },
2323
+ kinds: { ...tree.metadata.kinds },
2324
+ ...tree.metadata.relationshipSummary !== void 0 ? { relationshipSummary: { ...tree.metadata.relationshipSummary } } : {}
2325
+ }
2326
+ };
2327
+ }
2328
+
2176
2329
  // packages/core/src/exporters/html-exporter.ts
2177
2330
  function renderTreeHtml(nodes, ulClass = "tree") {
2178
2331
  if (nodes.length === 0) return "";
@@ -2200,32 +2353,33 @@ function exportHtml(tree, options) {
2200
2353
  const includeErrors = options?.includeErrors ?? true;
2201
2354
  const maxLen = options?.maxAttributeLength ?? 500;
2202
2355
  const redacted = options?.redacted ?? true;
2203
- const titleName = escapeHtml(tree.name ?? tree.runId);
2356
+ const view2 = presentLifecycleJoinedTree(tree);
2357
+ const titleName = escapeHtml(view2.name ?? view2.runId);
2204
2358
  const summaryRows = [];
2205
2359
  summaryRows.push(
2206
- `<tr><th scope="row">runId</th><td><code>${escapeHtml(tree.runId)}</code></td></tr>`
2360
+ `<tr><th scope="row">runId</th><td><code>${escapeHtml(view2.runId)}</code></td></tr>`
2207
2361
  );
2208
- if (tree.name !== void 0) {
2209
- summaryRows.push(`<tr><th scope="row">name</th><td>${escapeHtml(tree.name)}</td></tr>`);
2362
+ if (view2.name !== void 0) {
2363
+ summaryRows.push(`<tr><th scope="row">name</th><td>${escapeHtml(view2.name)}</td></tr>`);
2210
2364
  }
2211
2365
  summaryRows.push(
2212
- `<tr><th scope="row">status</th><td>${escapeHtml(String(tree.status ?? "unknown"))}</td></tr>`
2366
+ `<tr><th scope="row">status</th><td>${escapeHtml(String(view2.status ?? "unknown"))}</td></tr>`
2213
2367
  );
2214
2368
  summaryRows.push(
2215
- `<tr><th scope="row">durationMs</th><td>${tree.durationMs !== void 0 ? escapeHtml(String(tree.durationMs)) : "\u2014"}</td></tr>`
2369
+ `<tr><th scope="row">durationMs</th><td>${view2.durationMs !== void 0 ? escapeHtml(String(view2.durationMs)) : "\u2014"}</td></tr>`
2216
2370
  );
2217
2371
  summaryRows.push(
2218
- `<tr><th scope="row">startedAt</th><td>${tree.startedAt !== void 0 ? escapeHtml(String(tree.startedAt)) : "\u2014"}</td></tr>`
2372
+ `<tr><th scope="row">startedAt</th><td>${view2.startedAt !== void 0 ? escapeHtml(String(view2.startedAt)) : "\u2014"}</td></tr>`
2219
2373
  );
2220
2374
  summaryRows.push(
2221
- `<tr><th scope="row">endedAt</th><td>${tree.endedAt !== void 0 ? escapeHtml(String(tree.endedAt)) : "\u2014"}</td></tr>`
2375
+ `<tr><th scope="row">endedAt</th><td>${view2.endedAt !== void 0 ? escapeHtml(String(view2.endedAt)) : "\u2014"}</td></tr>`
2222
2376
  );
2223
2377
  summaryRows.push(
2224
- `<tr><th scope="row">totalEvents</th><td>${escapeHtml(String(tree.metadata.totalEvents))}</td></tr>`
2378
+ `<tr><th scope="row">totalEvents</th><td>${escapeHtml(String(view2.metadata.totalEvents))}</td></tr>`
2225
2379
  );
2226
2380
  let confidenceHtml = "";
2227
2381
  if (includeMetadata) {
2228
- const cb = tree.metadata.confidenceBreakdown;
2382
+ const cb = view2.metadata.confidenceBreakdown;
2229
2383
  confidenceHtml += "<h3>Confidence breakdown</h3><table><thead><tr><th>bucket</th><th>count</th></tr></thead><tbody>";
2230
2384
  for (const k of Object.keys(cb).sort()) {
2231
2385
  const key = k;
@@ -2233,14 +2387,14 @@ function exportHtml(tree, options) {
2233
2387
  }
2234
2388
  confidenceHtml += "</tbody></table>";
2235
2389
  confidenceHtml += "<h3>Kind breakdown</h3><table><thead><tr><th>kind</th><th>count</th></tr></thead><tbody>";
2236
- for (const k of Object.keys(tree.metadata.kinds).sort()) {
2390
+ for (const k of Object.keys(view2.metadata.kinds).sort()) {
2237
2391
  const key = k;
2238
- const c = tree.metadata.kinds[key];
2392
+ const c = view2.metadata.kinds[key];
2239
2393
  if (c > 0) confidenceHtml += `<tr><td>${escapeHtml(key)}</td><td>${c}</td></tr>`;
2240
2394
  }
2241
2395
  confidenceHtml += "</tbody></table>";
2242
2396
  }
2243
- const flat = flattenTree(tree);
2397
+ const flat = flattenTree(view2);
2244
2398
  const errors = flat.filter((n) => n.event.status === "error");
2245
2399
  let errorsHtml = "";
2246
2400
  if (includeErrors && errors.length > 0) {
@@ -2295,7 +2449,7 @@ footer{margin-top:2rem;font-size:0.85rem;color:#555}
2295
2449
  <header><h1>AgentInspect Run: ${titleName}</h1></header>
2296
2450
  <p class="note">Generated locally by AgentInspect.</p>
2297
2451
  ${includeMetadata ? `<section class="summary"><h2>Summary</h2><table>${summaryRows.join("")}</table>${confidenceHtml}</section>` : ""}
2298
- <section class="tree"><h2>Execution tree</h2>${tree.children.length > 0 ? renderTreeHtml(tree.children) : "<p>No steps recorded.</p>"}</section>
2452
+ <section class="tree"><h2>Execution tree</h2>${view2.children.length > 0 ? renderTreeHtml(view2.children) : "<p>No steps recorded.</p>"}</section>
2299
2453
  ${errorsHtml}
2300
2454
  ${attrsHtml}
2301
2455
  <footer>Generated locally by AgentInspect. Review for sensitive data before sharing.</footer>
@@ -2336,7 +2490,8 @@ function exportMarkdown(tree, options) {
2336
2490
  const includeErrors = options?.includeErrors ?? true;
2337
2491
  const maxLen = options?.maxAttributeLength ?? 500;
2338
2492
  const redacted = options?.redacted ?? true;
2339
- const titleName = tree.name ?? tree.runId;
2493
+ const view2 = presentLifecycleJoinedTree(tree);
2494
+ const titleName = view2.name ?? view2.runId;
2340
2495
  const lines = [];
2341
2496
  lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
2342
2497
  lines.push("");
@@ -2345,28 +2500,28 @@ function exportMarkdown(tree, options) {
2345
2500
  if (includeMetadata) {
2346
2501
  lines.push("## Summary");
2347
2502
  lines.push("");
2348
- lines.push(`- **runId**: ${escapeMarkdown(tree.runId)}`);
2349
- if (tree.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(tree.name)}`);
2350
- lines.push(`- **status**: ${escapeMarkdown(String(tree.status ?? "unknown"))}`);
2503
+ lines.push(`- **runId**: ${escapeMarkdown(view2.runId)}`);
2504
+ if (view2.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(view2.name)}`);
2505
+ lines.push(`- **status**: ${escapeMarkdown(String(view2.status ?? "unknown"))}`);
2351
2506
  lines.push(
2352
- `- **durationMs**: ${tree.durationMs !== void 0 ? escapeMarkdown(String(tree.durationMs)) : "-"}`
2507
+ `- **durationMs**: ${view2.durationMs !== void 0 ? escapeMarkdown(String(view2.durationMs)) : "-"}`
2353
2508
  );
2354
2509
  lines.push(
2355
- `- **startedAt**: ${tree.startedAt !== void 0 ? escapeMarkdown(String(tree.startedAt)) : "-"}`
2510
+ `- **startedAt**: ${view2.startedAt !== void 0 ? escapeMarkdown(String(view2.startedAt)) : "-"}`
2356
2511
  );
2357
2512
  lines.push(
2358
- `- **endedAt**: ${tree.endedAt !== void 0 ? escapeMarkdown(String(tree.endedAt)) : "-"}`
2513
+ `- **endedAt**: ${view2.endedAt !== void 0 ? escapeMarkdown(String(view2.endedAt)) : "-"}`
2359
2514
  );
2360
- lines.push(`- **totalEvents**: ${tree.metadata.totalEvents}`);
2515
+ lines.push(`- **totalEvents**: ${view2.metadata.totalEvents}`);
2361
2516
  lines.push("");
2362
2517
  lines.push("### Confidence breakdown");
2363
2518
  lines.push("");
2364
2519
  lines.push("| bucket | count |");
2365
2520
  lines.push("| --- | --- |");
2366
- for (const k of Object.keys(tree.metadata.confidenceBreakdown).sort()) {
2521
+ for (const k of Object.keys(view2.metadata.confidenceBreakdown).sort()) {
2367
2522
  const key = k;
2368
2523
  lines.push(
2369
- `| ${escapeMarkdown(key)} | ${tree.metadata.confidenceBreakdown[key]} |`
2524
+ `| ${escapeMarkdown(key)} | ${view2.metadata.confidenceBreakdown[key]} |`
2370
2525
  );
2371
2526
  }
2372
2527
  lines.push("");
@@ -2374,9 +2529,9 @@ function exportMarkdown(tree, options) {
2374
2529
  lines.push("");
2375
2530
  lines.push("| kind | count |");
2376
2531
  lines.push("| --- | --- |");
2377
- for (const k of Object.keys(tree.metadata.kinds).sort()) {
2532
+ for (const k of Object.keys(view2.metadata.kinds).sort()) {
2378
2533
  const key = k;
2379
- const c = tree.metadata.kinds[key];
2534
+ const c = view2.metadata.kinds[key];
2380
2535
  if (c > 0) lines.push(`| ${escapeMarkdown(key)} | ${c} |`);
2381
2536
  }
2382
2537
  lines.push("");
@@ -2385,11 +2540,11 @@ function exportMarkdown(tree, options) {
2385
2540
  lines.push("");
2386
2541
  lines.push("```text");
2387
2542
  lines.push(
2388
- tree.children.length > 0 ? renderTreeAscii(tree.children) : "(no steps)"
2543
+ view2.children.length > 0 ? renderTreeAscii(view2.children) : "(no steps)"
2389
2544
  );
2390
2545
  lines.push("```");
2391
2546
  lines.push("");
2392
- const flat = flattenTree(tree);
2547
+ const flat = flattenTree(view2);
2393
2548
  const errors = flat.filter((n) => n.event.status === "error");
2394
2549
  if (includeErrors && errors.length > 0) {
2395
2550
  lines.push("## Errors");
@@ -12022,11 +12177,14 @@ async function bundleCommand(runIdArg, options = {}) {
12022
12177
  ...finding.action !== void 0 ? { action: finding.action } : {}
12023
12178
  });
12024
12179
  }
12025
- if (artifactTree) {
12026
- artifactTrees.push(artifactTree);
12027
- } else {
12028
- const selected2 = read.runs.find((run) => run.runId === runId);
12029
- if (selected2) artifactTrees.push(selected2);
12180
+ const persistedForRun = artifactEventsByRun.get(runId) ?? read.events.filter((event) => event.runId === runId);
12181
+ const presentationTrees = persistedInspectEventsToRunTrees(persistedForRun, {
12182
+ coalesceLifecycle: true,
12183
+ skipInvalid: true
12184
+ });
12185
+ const presentationTree = presentationTrees.find((candidate) => candidate.runId === runId) ?? presentationTrees[0] ?? artifactTree ?? read.runs.find((run) => run.runId === runId);
12186
+ if (presentationTree) {
12187
+ artifactTrees.push(presentationTree);
12030
12188
  }
12031
12189
  const status = safetyStatusFromAssess(artifactSafety.status);
12032
12190
  checkRuns.push({
@@ -12037,8 +12195,17 @@ async function bundleCommand(runIdArg, options = {}) {
12037
12195
  warnings: artifactSafety.summary.warnings,
12038
12196
  findings: artifactSafety.summary.findings
12039
12197
  });
12040
- const selected = read.runs.find((run) => run.runId === runId);
12041
- const runReport = exportRunTree(selected ?? read.runs[0], {
12198
+ if (!presentationTree) {
12199
+ const message = `No run tree could be built for HTML presentation: ${runId}`;
12200
+ if (options.json) {
12201
+ console.log(writeJson4({ ok: false, error: message }).trimEnd());
12202
+ } else {
12203
+ console.error(`[AgentInspect] bundle failed: ${message}`);
12204
+ }
12205
+ process.exitCode = 1;
12206
+ return;
12207
+ }
12208
+ const runReport = exportRunTree(presentationTree, {
12042
12209
  format: "html",
12043
12210
  redacted: true,
12044
12211
  redactionProfile: toReportProfile(profile)