agent-inspect 1.9.0 → 2.0.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 (98) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +13 -9
  3. package/docs/ADAPTER-CONFORMANCE.md +1 -1
  4. package/docs/API.md +30 -29
  5. package/docs/CLI.md +41 -11
  6. package/docs/EXPORTS.md +1 -1
  7. package/docs/GETTING-STARTED.md +2 -1
  8. package/docs/KNOWN-ISSUES.md +8 -3
  9. package/docs/LIMITATIONS.md +6 -6
  10. package/docs/MIGRATION.md +8 -8
  11. package/docs/SCHEMA.md +29 -12
  12. package/package.json +1 -1
  13. package/packages/cli/dist/index.cjs +7135 -7481
  14. package/packages/cli/dist/index.cjs.map +1 -1
  15. package/packages/cli/dist/index.mjs +7135 -7481
  16. package/packages/cli/dist/index.mjs.map +1 -1
  17. package/packages/core/dist/advanced.cjs +2047 -26
  18. package/packages/core/dist/advanced.cjs.map +1 -1
  19. package/packages/core/dist/advanced.d.cts +346 -196
  20. package/packages/core/dist/advanced.d.ts +346 -196
  21. package/packages/core/dist/advanced.mjs +628 -5
  22. package/packages/core/dist/advanced.mjs.map +1 -1
  23. package/packages/core/dist/checks.d.cts +1 -1
  24. package/packages/core/dist/checks.d.ts +1 -1
  25. package/packages/core/dist/chunk-2ZXEMWGK.mjs +549 -0
  26. package/packages/core/dist/chunk-2ZXEMWGK.mjs.map +1 -0
  27. package/packages/core/dist/{chunk-57S5D6HR.mjs → chunk-4JVGFNQH.mjs} +50 -6
  28. package/packages/core/dist/chunk-4JVGFNQH.mjs.map +1 -0
  29. package/packages/core/dist/{chunk-6QSLZCBJ.mjs → chunk-A4V2H3YU.mjs} +329 -163
  30. package/packages/core/dist/chunk-A4V2H3YU.mjs.map +1 -0
  31. package/packages/core/dist/{chunk-74XZ6N7Q.mjs → chunk-IZBJAZGF.mjs} +12 -4
  32. package/packages/core/dist/chunk-IZBJAZGF.mjs.map +1 -0
  33. package/packages/core/dist/{chunk-VTIB5MDK.mjs → chunk-NQZPZ2FN.mjs} +15 -10
  34. package/packages/core/dist/chunk-NQZPZ2FN.mjs.map +1 -0
  35. package/packages/core/dist/{chunk-TZISEVLQ.mjs → chunk-XUNJMAPD.mjs} +3 -3
  36. package/packages/core/dist/{chunk-TZISEVLQ.mjs.map → chunk-XUNJMAPD.mjs.map} +1 -1
  37. package/packages/core/dist/context-CSKnzpXR.d.cts +177 -0
  38. package/packages/core/dist/context-yv2VSDQF.d.ts +177 -0
  39. package/packages/core/dist/diff.d.cts +2 -2
  40. package/packages/core/dist/diff.d.ts +2 -2
  41. package/packages/core/dist/diff.mjs +473 -3
  42. package/packages/core/dist/diff.mjs.map +1 -1
  43. package/packages/core/dist/exporters.cjs +691 -0
  44. package/packages/core/dist/exporters.cjs.map +1 -1
  45. package/packages/core/dist/exporters.d.cts +24 -3
  46. package/packages/core/dist/exporters.d.ts +24 -3
  47. package/packages/core/dist/exporters.mjs +1113 -3
  48. package/packages/core/dist/exporters.mjs.map +1 -1
  49. package/packages/core/dist/index.cjs +1385 -8168
  50. package/packages/core/dist/index.cjs.map +1 -1
  51. package/packages/core/dist/index.d.cts +8 -350
  52. package/packages/core/dist/index.d.ts +8 -350
  53. package/packages/core/dist/index.mjs +18 -1478
  54. package/packages/core/dist/index.mjs.map +1 -1
  55. package/packages/core/dist/{log-config-DanPV3P9.d.cts → log-config-8aE5Kxtr.d.cts} +1 -1
  56. package/packages/core/dist/{log-config-BPHS4Sds.d.ts → log-config-DVimQJho.d.ts} +1 -1
  57. package/packages/core/dist/logs.d.cts +2 -2
  58. package/packages/core/dist/logs.d.ts +2 -2
  59. package/packages/core/dist/logs.mjs +785 -2
  60. package/packages/core/dist/logs.mjs.map +1 -1
  61. package/packages/core/dist/{persisted-inspect-event-Cw7TeYGr.d.ts → persisted-inspect-event-D-WpXeZX.d.ts} +14 -5
  62. package/packages/core/dist/{persisted-inspect-event-DHPfzUd8.d.cts → persisted-inspect-event-DHcHPUKv.d.cts} +14 -5
  63. package/packages/core/dist/persisted.cjs +11 -1
  64. package/packages/core/dist/persisted.cjs.map +1 -1
  65. package/packages/core/dist/persisted.d.cts +4 -4
  66. package/packages/core/dist/persisted.d.ts +4 -4
  67. package/packages/core/dist/persisted.mjs +150 -3
  68. package/packages/core/dist/persisted.mjs.map +1 -1
  69. package/packages/core/dist/readers.cjs +27 -10
  70. package/packages/core/dist/readers.cjs.map +1 -1
  71. package/packages/core/dist/readers.d.cts +1 -1
  72. package/packages/core/dist/readers.d.ts +1 -1
  73. package/packages/core/dist/readers.mjs +1580 -4
  74. package/packages/core/dist/readers.mjs.map +1 -1
  75. package/packages/core/dist/{types-Ap9uMdx_.d.ts → types-D-Y1kOU-.d.ts} +2 -2
  76. package/packages/core/dist/{types-B2-BU5CS.d.cts → types-DgMN3qow.d.cts} +2 -2
  77. package/packages/core/dist/writers.cjs +49 -3
  78. package/packages/core/dist/writers.cjs.map +1 -1
  79. package/packages/core/dist/writers.d.cts +1 -1
  80. package/packages/core/dist/writers.d.ts +1 -1
  81. package/packages/core/dist/writers.mjs +341 -4
  82. package/packages/core/dist/writers.mjs.map +1 -1
  83. package/packages/core/dist/chunk-57S5D6HR.mjs.map +0 -1
  84. package/packages/core/dist/chunk-6QSLZCBJ.mjs.map +0 -1
  85. package/packages/core/dist/chunk-6SZPTECC.mjs +0 -342
  86. package/packages/core/dist/chunk-6SZPTECC.mjs.map +0 -1
  87. package/packages/core/dist/chunk-74XZ6N7Q.mjs.map +0 -1
  88. package/packages/core/dist/chunk-HR7G62IE.mjs +0 -785
  89. package/packages/core/dist/chunk-HR7G62IE.mjs.map +0 -1
  90. package/packages/core/dist/chunk-TFLPUZ56.mjs +0 -1571
  91. package/packages/core/dist/chunk-TFLPUZ56.mjs.map +0 -1
  92. package/packages/core/dist/chunk-U2BGPESY.mjs +0 -150
  93. package/packages/core/dist/chunk-U2BGPESY.mjs.map +0 -1
  94. package/packages/core/dist/chunk-VTIB5MDK.mjs.map +0 -1
  95. package/packages/core/dist/chunk-Y56BPA3B.mjs +0 -990
  96. package/packages/core/dist/chunk-Y56BPA3B.mjs.map +0 -1
  97. package/packages/core/dist/chunk-YWAOOXLR.mjs +0 -475
  98. package/packages/core/dist/chunk-YWAOOXLR.mjs.map +0 -1
@@ -1,6 +1,1116 @@
1
- export { EXPORT_PAYLOAD_VERSION, exportHtml, exportMarkdown, exportOpenInference, exportOtlpJson, exportRunTree, manualTraceEventsToRunTree, mergeExportDefaults, redactRunTreeForExport, validateExport, validateExportContent } from './chunk-Y56BPA3B.mjs';
1
+ import { buildRunWhatSummary, renderRunWhat, renderTimeline, buildRunTimeline } from './chunk-2ZXEMWGK.mjs';
2
+ import { escapeHtml, flattenTree, safeString, compactAttributes, stableJson, escapeMarkdown, zeroKinds } from './chunk-HY7H3CQM.mjs';
2
3
  export { compactAttributes, escapeHtml, escapeMarkdown, flattenTree, safeString, stableJson, summarizeTree } from './chunk-HY7H3CQM.mjs';
3
- import './chunk-EDTQHZPM.mjs';
4
- import './chunk-VU6O5QAH.mjs';
4
+ import { resolveRedactionProfile, applyProfileMetadataCaps, truncateStringForProfile } from './chunk-EDTQHZPM.mjs';
5
+ import { Redactor } from './chunk-VU6O5QAH.mjs';
6
+ import './chunk-NQZPZ2FN.mjs';
7
+ import './chunk-IZBJAZGF.mjs';
8
+ import './chunk-7TGZLWEE.mjs';
9
+ import crypto from 'crypto';
10
+
11
+ // packages/core/src/exporters/types.ts
12
+ var EXPORT_PAYLOAD_VERSION = "0.1.2";
13
+
14
+ // packages/core/src/exporters/redact-export.ts
15
+ function isRecord(value) {
16
+ return typeof value === "object" && value !== null && !Array.isArray(value);
17
+ }
18
+ function deepClone(value) {
19
+ if (value === null || typeof value !== "object") {
20
+ return value;
21
+ }
22
+ if (Array.isArray(value)) {
23
+ return value.map((item) => deepClone(item));
24
+ }
25
+ const out = {};
26
+ for (const [k, v] of Object.entries(value)) {
27
+ out[k] = deepClone(v);
28
+ }
29
+ return out;
30
+ }
31
+ function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
32
+ if (depth > 32) {
33
+ return { truncated: true, reason: "maxDepth" };
34
+ }
35
+ const out = {};
36
+ for (const [key, value] of Object.entries(record)) {
37
+ out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
38
+ }
39
+ return out;
40
+ }
41
+ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
42
+ if (value === null || typeof value !== "object") {
43
+ if (typeof value === "string") {
44
+ return truncateStringForProfile(
45
+ value,
46
+ key,
47
+ maxMetadataValueLength,
48
+ maxPreviewLength
49
+ );
50
+ }
51
+ return value;
52
+ }
53
+ if (seen.has(value)) return "[Circular]";
54
+ seen.add(value);
55
+ if (Array.isArray(value)) {
56
+ return value.slice(0, 50).map(
57
+ (item, index) => boundValue(
58
+ item,
59
+ String(index),
60
+ maxMetadataValueLength,
61
+ maxPreviewLength,
62
+ seen,
63
+ depth + 1
64
+ )
65
+ );
66
+ }
67
+ return boundAttributeValues(
68
+ value,
69
+ maxMetadataValueLength,
70
+ maxPreviewLength,
71
+ seen,
72
+ depth + 1
73
+ );
74
+ }
75
+ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPreviewLength) {
76
+ if (!attrs || Object.keys(attrs).length === 0) {
77
+ return attrs;
78
+ }
79
+ const redacted = redactor.redactRecord(attrs);
80
+ const seen = /* @__PURE__ */ new WeakSet();
81
+ const bounded = boundAttributeValues(
82
+ redacted,
83
+ maxMetadataValueLength,
84
+ maxPreviewLength,
85
+ seen,
86
+ 0
87
+ );
88
+ const err = bounded.error;
89
+ if (isRecord(err) && typeof err.message === "string") {
90
+ bounded.error = {
91
+ ...err,
92
+ message: truncateStringForProfile(
93
+ err.message,
94
+ "message",
95
+ maxMetadataValueLength,
96
+ maxPreviewLength
97
+ ),
98
+ ...typeof err.stack === "string" ? {
99
+ stack: truncateStringForProfile(
100
+ err.stack,
101
+ "stack",
102
+ maxMetadataValueLength,
103
+ maxPreviewLength
104
+ )
105
+ } : {}
106
+ };
107
+ }
108
+ return bounded;
109
+ }
110
+ function redactErrorInfo(error, redactor, maxMetadataValueLength, maxPreviewLength) {
111
+ if (error === void 0) return void 0;
112
+ const record = redactEventAttributes(
113
+ { error },
114
+ redactor,
115
+ maxMetadataValueLength,
116
+ maxPreviewLength
117
+ );
118
+ const redacted = record?.error;
119
+ if (!isRecord(redacted) || typeof redacted.message !== "string") {
120
+ return void 0;
121
+ }
122
+ return {
123
+ message: redacted.message,
124
+ ...typeof redacted.stack === "string" ? { stack: redacted.stack } : {}
125
+ };
126
+ }
127
+ function redactTraceEventsForReport(events, options) {
128
+ const profile = options?.redactionProfile ?? "local";
129
+ if (profile === "local") {
130
+ return deepClone(events);
131
+ }
132
+ const resolved = resolveRedactionProfile(profile);
133
+ const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
134
+ 2e3,
135
+ 500,
136
+ resolved
137
+ );
138
+ const redactor = new Redactor({ extraKeys: resolved.extraKeys });
139
+ return events.map((event) => {
140
+ switch (event.event) {
141
+ case "run_started":
142
+ return {
143
+ ...event,
144
+ name: truncateStringForProfile(
145
+ event.name,
146
+ "name",
147
+ maxMetadataValueLength,
148
+ maxPreviewLength
149
+ ),
150
+ ...event.metadata !== void 0 ? {
151
+ metadata: redactEventAttributes(
152
+ event.metadata,
153
+ redactor,
154
+ maxMetadataValueLength,
155
+ maxPreviewLength
156
+ )
157
+ } : {}
158
+ };
159
+ case "step_started":
160
+ return {
161
+ ...event,
162
+ name: truncateStringForProfile(
163
+ event.name,
164
+ "name",
165
+ maxMetadataValueLength,
166
+ maxPreviewLength
167
+ ),
168
+ ...event.metadata !== void 0 ? {
169
+ metadata: redactEventAttributes(
170
+ event.metadata,
171
+ redactor,
172
+ maxMetadataValueLength,
173
+ maxPreviewLength
174
+ )
175
+ } : {}
176
+ };
177
+ case "run_completed":
178
+ case "step_completed":
179
+ return {
180
+ ...event,
181
+ ...event.error !== void 0 ? {
182
+ error: redactErrorInfo(
183
+ event.error,
184
+ redactor,
185
+ maxMetadataValueLength,
186
+ maxPreviewLength
187
+ )
188
+ } : {}
189
+ };
190
+ }
191
+ });
192
+ }
193
+ function redactRunTreeForExport(tree, options) {
194
+ const profile = options?.redactionProfile ?? "local";
195
+ if (profile === "local") {
196
+ return deepClone(tree);
197
+ }
198
+ const resolved = resolveRedactionProfile(profile);
199
+ const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
200
+ 2e3,
201
+ 500,
202
+ resolved
203
+ );
204
+ const redactor = new Redactor({ extraKeys: resolved.extraKeys });
205
+ const clone = deepClone(tree);
206
+ function walk(nodes) {
207
+ for (const node of nodes) {
208
+ if (node.event.attributes !== void 0) {
209
+ node.event.attributes = redactEventAttributes(
210
+ node.event.attributes,
211
+ redactor,
212
+ maxMetadataValueLength,
213
+ maxPreviewLength
214
+ );
215
+ }
216
+ if (node.children.length > 0) {
217
+ walk(node.children);
218
+ }
219
+ }
220
+ }
221
+ walk(clone.children);
222
+ return clone;
223
+ }
224
+
225
+ // packages/core/src/exporters/html-exporter.ts
226
+ function renderTreeHtml(nodes, ulClass = "tree") {
227
+ if (nodes.length === 0) return "";
228
+ const parts = [`<ul class="${ulClass}">`];
229
+ for (const n of nodes) {
230
+ const ev = n.event;
231
+ const status = ev.status ?? "?";
232
+ const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
233
+ parts.push("<li>");
234
+ parts.push(
235
+ `<span class="nm">${escapeHtml(ev.name)}</span> <span class="meta">[${escapeHtml(ev.kind)}] ${escapeHtml(status)} (${escapeHtml(dur)})</span>`
236
+ );
237
+ if (n.children.length > 0) {
238
+ parts.push(renderTreeHtml(n.children, "tree nested"));
239
+ }
240
+ parts.push("</li>");
241
+ }
242
+ parts.push("</ul>");
243
+ return parts.join("");
244
+ }
245
+ function exportHtml(tree, options) {
246
+ const warnings = [];
247
+ const includeMetadata = options?.includeMetadata ?? true;
248
+ const includeAttributes = options?.includeAttributes ?? false;
249
+ const includeErrors = options?.includeErrors ?? true;
250
+ const maxLen = options?.maxAttributeLength ?? 500;
251
+ const redacted = options?.redacted ?? true;
252
+ const titleName = escapeHtml(tree.name ?? tree.runId);
253
+ const summaryRows = [];
254
+ summaryRows.push(
255
+ `<tr><th scope="row">runId</th><td><code>${escapeHtml(tree.runId)}</code></td></tr>`
256
+ );
257
+ if (tree.name !== void 0) {
258
+ summaryRows.push(`<tr><th scope="row">name</th><td>${escapeHtml(tree.name)}</td></tr>`);
259
+ }
260
+ summaryRows.push(
261
+ `<tr><th scope="row">status</th><td>${escapeHtml(String(tree.status ?? "unknown"))}</td></tr>`
262
+ );
263
+ summaryRows.push(
264
+ `<tr><th scope="row">durationMs</th><td>${tree.durationMs !== void 0 ? escapeHtml(String(tree.durationMs)) : "\u2014"}</td></tr>`
265
+ );
266
+ summaryRows.push(
267
+ `<tr><th scope="row">startedAt</th><td>${tree.startedAt !== void 0 ? escapeHtml(String(tree.startedAt)) : "\u2014"}</td></tr>`
268
+ );
269
+ summaryRows.push(
270
+ `<tr><th scope="row">endedAt</th><td>${tree.endedAt !== void 0 ? escapeHtml(String(tree.endedAt)) : "\u2014"}</td></tr>`
271
+ );
272
+ summaryRows.push(
273
+ `<tr><th scope="row">totalEvents</th><td>${escapeHtml(String(tree.metadata.totalEvents))}</td></tr>`
274
+ );
275
+ let confidenceHtml = "";
276
+ if (includeMetadata) {
277
+ const cb = tree.metadata.confidenceBreakdown;
278
+ confidenceHtml += "<h3>Confidence breakdown</h3><table><thead><tr><th>bucket</th><th>count</th></tr></thead><tbody>";
279
+ for (const k of Object.keys(cb).sort()) {
280
+ const key = k;
281
+ confidenceHtml += `<tr><td>${escapeHtml(key)}</td><td>${cb[key]}</td></tr>`;
282
+ }
283
+ confidenceHtml += "</tbody></table>";
284
+ confidenceHtml += "<h3>Kind breakdown</h3><table><thead><tr><th>kind</th><th>count</th></tr></thead><tbody>";
285
+ for (const k of Object.keys(tree.metadata.kinds).sort()) {
286
+ const key = k;
287
+ const c = tree.metadata.kinds[key];
288
+ if (c > 0) confidenceHtml += `<tr><td>${escapeHtml(key)}</td><td>${c}</td></tr>`;
289
+ }
290
+ confidenceHtml += "</tbody></table>";
291
+ }
292
+ const flat = flattenTree(tree);
293
+ const errors = flat.filter((n) => n.event.status === "error");
294
+ let errorsHtml = "";
295
+ if (includeErrors && errors.length > 0) {
296
+ errorsHtml += "<h2>Errors</h2><ul>";
297
+ for (const n of errors) {
298
+ const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
299
+ n.event.attributes.error.message,
300
+ maxLen
301
+ ) : "";
302
+ errorsHtml += `<li><strong>${escapeHtml(n.event.name)}</strong> (${escapeHtml(n.event.eventId)}): ${escapeHtml(msg || "error")}</li>`;
303
+ }
304
+ errorsHtml += "</ul>";
305
+ }
306
+ let attrsHtml = "";
307
+ if (includeAttributes) {
308
+ attrsHtml += "<h2>Attributes (bounded)</h2>";
309
+ for (const n of flat) {
310
+ if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
311
+ const compact = compactAttributes(n.event.attributes, {
312
+ maxLength: maxLen,
313
+ redacted
314
+ });
315
+ attrsHtml += `<h3>${escapeHtml(n.event.name)}</h3><pre class="json">${escapeHtml(stableJson(compact, true))}</pre>`;
316
+ }
317
+ warnings.push(
318
+ "Attributes may still contain sensitive data; review exports before sharing."
319
+ );
320
+ }
321
+ const css = `
322
+ body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}
323
+ h1{font-size:1.35rem}
324
+ h2{font-size:1.1rem;margin-top:1.5rem}
325
+ table{border-collapse:collapse;margin:0.75rem 0}
326
+ th,td{border:1px solid #ccc;padding:0.35rem 0.6rem;text-align:left}
327
+ th{background:#f5f5f5}
328
+ pre.json{background:#f8f8f8;padding:0.75rem;overflow:auto;font-size:0.85rem}
329
+ ul.tree{list-style:none;padding-left:1rem}
330
+ ul.tree.nested{padding-left:1.25rem;border-left:1px solid #ddd;margin:0.25rem 0}
331
+ .nm{font-weight:600}
332
+ .meta{color:#555;font-size:0.9rem}
333
+ footer{margin-top:2rem;font-size:0.85rem;color:#555}
334
+ `.trim();
335
+ const html = `<!doctype html>
336
+ <html lang="en">
337
+ <head>
338
+ <meta charset="utf-8"/>
339
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
340
+ <title>${titleName}</title>
341
+ <style>${css}</style>
342
+ </head>
343
+ <body>
344
+ <header><h1>AgentInspect Run: ${titleName}</h1></header>
345
+ <p class="note">Generated locally by AgentInspect.</p>
346
+ ${includeMetadata ? `<section class="summary"><h2>Summary</h2><table>${summaryRows.join("")}</table>${confidenceHtml}</section>` : ""}
347
+ <section class="tree"><h2>Execution tree</h2>${tree.children.length > 0 ? renderTreeHtml(tree.children) : "<p>No steps recorded.</p>"}</section>
348
+ ${errorsHtml}
349
+ ${attrsHtml}
350
+ <footer>Generated locally by AgentInspect. Review for sensitive data before sharing.</footer>
351
+ </body>
352
+ </html>`;
353
+ return {
354
+ format: "html",
355
+ content: html,
356
+ contentType: "text/html",
357
+ fileExtension: ".html",
358
+ warnings
359
+ };
360
+ }
361
+
362
+ // packages/core/src/exporters/markdown-exporter.ts
363
+ function renderTreeAscii(nodes, indent = "") {
364
+ const lines = [];
365
+ for (let i = 0; i < nodes.length; i++) {
366
+ const n = nodes[i];
367
+ const last = i === nodes.length - 1;
368
+ const branch = last ? "\u2514\u2500 " : "\u251C\u2500 ";
369
+ const ev = n.event;
370
+ const status = ev.status ?? "?";
371
+ const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
372
+ lines.push(`${indent}${branch}${escapeMarkdown(ev.name)} [${ev.kind}] ${status} (${dur})`);
373
+ const nextIndent = indent + (last ? " " : "\u2502 ");
374
+ if (n.children.length > 0) {
375
+ const childStr = renderTreeAscii(n.children, nextIndent);
376
+ if (childStr.length > 0) lines.push(childStr);
377
+ }
378
+ }
379
+ return lines.join("\n");
380
+ }
381
+ function exportMarkdown(tree, options) {
382
+ const warnings = [];
383
+ const includeMetadata = options?.includeMetadata ?? true;
384
+ const includeAttributes = options?.includeAttributes ?? false;
385
+ const includeErrors = options?.includeErrors ?? true;
386
+ const maxLen = options?.maxAttributeLength ?? 500;
387
+ const redacted = options?.redacted ?? true;
388
+ const titleName = tree.name ?? tree.runId;
389
+ const lines = [];
390
+ lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
391
+ lines.push("");
392
+ lines.push("Generated locally by AgentInspect. Review for sensitive data before sharing.");
393
+ lines.push("");
394
+ if (includeMetadata) {
395
+ lines.push("## Summary");
396
+ lines.push("");
397
+ lines.push(`- **runId**: ${escapeMarkdown(tree.runId)}`);
398
+ if (tree.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(tree.name)}`);
399
+ lines.push(`- **status**: ${escapeMarkdown(String(tree.status ?? "unknown"))}`);
400
+ lines.push(
401
+ `- **durationMs**: ${tree.durationMs !== void 0 ? escapeMarkdown(String(tree.durationMs)) : "-"}`
402
+ );
403
+ lines.push(
404
+ `- **startedAt**: ${tree.startedAt !== void 0 ? escapeMarkdown(String(tree.startedAt)) : "-"}`
405
+ );
406
+ lines.push(
407
+ `- **endedAt**: ${tree.endedAt !== void 0 ? escapeMarkdown(String(tree.endedAt)) : "-"}`
408
+ );
409
+ lines.push(`- **totalEvents**: ${tree.metadata.totalEvents}`);
410
+ lines.push("");
411
+ lines.push("### Confidence breakdown");
412
+ lines.push("");
413
+ lines.push("| bucket | count |");
414
+ lines.push("| --- | --- |");
415
+ for (const k of Object.keys(tree.metadata.confidenceBreakdown).sort()) {
416
+ const key = k;
417
+ lines.push(
418
+ `| ${escapeMarkdown(key)} | ${tree.metadata.confidenceBreakdown[key]} |`
419
+ );
420
+ }
421
+ lines.push("");
422
+ lines.push("### Kind breakdown");
423
+ lines.push("");
424
+ lines.push("| kind | count |");
425
+ lines.push("| --- | --- |");
426
+ for (const k of Object.keys(tree.metadata.kinds).sort()) {
427
+ const key = k;
428
+ const c = tree.metadata.kinds[key];
429
+ if (c > 0) lines.push(`| ${escapeMarkdown(key)} | ${c} |`);
430
+ }
431
+ lines.push("");
432
+ }
433
+ lines.push("## Execution tree");
434
+ lines.push("");
435
+ lines.push("```text");
436
+ lines.push(
437
+ tree.children.length > 0 ? renderTreeAscii(tree.children) : "(no steps)"
438
+ );
439
+ lines.push("```");
440
+ lines.push("");
441
+ const flat = flattenTree(tree);
442
+ const errors = flat.filter((n) => n.event.status === "error");
443
+ if (includeErrors && errors.length > 0) {
444
+ lines.push("## Errors");
445
+ lines.push("");
446
+ for (const n of errors) {
447
+ const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
448
+ n.event.attributes.error.message,
449
+ maxLen
450
+ ) : "";
451
+ lines.push(
452
+ `- **${escapeMarkdown(n.event.name)}** (${escapeMarkdown(n.event.eventId)}): ${escapeMarkdown(msg || "error")}`
453
+ );
454
+ }
455
+ lines.push("");
456
+ }
457
+ if (includeAttributes) {
458
+ lines.push("## Attributes (bounded)");
459
+ lines.push("");
460
+ for (const n of flat) {
461
+ if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
462
+ const compact = compactAttributes(n.event.attributes, {
463
+ maxLength: maxLen,
464
+ redacted
465
+ });
466
+ lines.push(`### ${escapeMarkdown(n.event.name)}`);
467
+ lines.push("");
468
+ lines.push("```json");
469
+ lines.push(stableJson(compact, true));
470
+ lines.push("```");
471
+ lines.push("");
472
+ }
473
+ warnings.push(
474
+ "Attributes may still contain sensitive data; review exports before sharing."
475
+ );
476
+ }
477
+ return {
478
+ format: "markdown",
479
+ content: lines.join("\n"),
480
+ contentType: "text/markdown",
481
+ fileExtension: ".md",
482
+ warnings
483
+ };
484
+ }
485
+ function hexFrom(seed, byteLen) {
486
+ return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
487
+ }
488
+ function mapInspectKindToOI(kind, warnings) {
489
+ switch (kind) {
490
+ case "LLM":
491
+ return { openInferenceKind: "LLM" };
492
+ case "TOOL":
493
+ return { openInferenceKind: "TOOL" };
494
+ case "CHAIN":
495
+ return { openInferenceKind: "CHAIN" };
496
+ case "RETRIEVER":
497
+ return { openInferenceKind: "RETRIEVER" };
498
+ case "AGENT":
499
+ return { openInferenceKind: "AGENT" };
500
+ case "DECISION":
501
+ warnings.push(
502
+ `Ambiguous kind DECISION mapped to CHAIN for span compatibility (${EXPORT_PAYLOAD_VERSION}).`
503
+ );
504
+ return { openInferenceKind: "CHAIN" };
505
+ case "RESULT":
506
+ warnings.push(
507
+ `Ambiguous kind RESULT mapped to UNKNOWN for span compatibility (${EXPORT_PAYLOAD_VERSION}).`
508
+ );
509
+ return { openInferenceKind: "UNKNOWN" };
510
+ case "ERROR":
511
+ warnings.push(`ERROR kind mapped to CHAIN for span compatibility.`);
512
+ return { openInferenceKind: "CHAIN" };
513
+ case "LOG":
514
+ case "LOGIC":
515
+ case "RUN":
516
+ warnings.push(`${kind} mapped to CHAIN for span compatibility.`);
517
+ return { openInferenceKind: "CHAIN" };
518
+ default:
519
+ warnings.push(`Unhandled InspectKind ${kind} mapped to UNKNOWN.`);
520
+ return { openInferenceKind: "UNKNOWN" };
521
+ }
522
+ }
523
+ function exportOpenInference(tree, options) {
524
+ const warnings = [
525
+ "OpenInference-compatible JSON export is experimental until verified against specific backends.",
526
+ "This file was generated locally and not sent anywhere."
527
+ ];
528
+ const traceId = hexFrom(`trace:${tree.runId}`, 16);
529
+ const includeAttributes = options?.includeAttributes ?? false;
530
+ const maxLen = options?.maxAttributeLength ?? 500;
531
+ const pretty = options?.pretty ?? true;
532
+ const spans = [];
533
+ for (const n of flattenTree(tree)) {
534
+ const ev = n.event;
535
+ const spanId = hexFrom(`${tree.runId}:${ev.eventId}`, 8);
536
+ const parentSpanHex = ev.parentId ? hexFrom(`${tree.runId}:${ev.parentId}`, 8) : void 0;
537
+ const startNs = Math.round(ev.timestamp * 1e6);
538
+ let endNs;
539
+ if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
540
+ endNs = startNs + Math.round(ev.durationMs * 1e6);
541
+ }
542
+ const { openInferenceKind } = mapInspectKindToOI(ev.kind, warnings);
543
+ const attrs = {
544
+ "openinference.span.kind": openInferenceKind,
545
+ "agent_inspect.kind": ev.kind,
546
+ "agent_inspect.confidence": ev.confidence,
547
+ "agent_inspect.source.type": ev.source.type,
548
+ "agent_inspect.run_id": tree.runId,
549
+ "agent_inspect.event_id": ev.eventId,
550
+ "agent_inspect.status": ev.status ?? "unset"
551
+ };
552
+ if (ev.durationMs !== void 0) {
553
+ attrs["agent_inspect.duration_ms"] = ev.durationMs;
554
+ }
555
+ const meta = ev.attributes;
556
+ if (meta?.model !== void 0 && typeof meta.model === "string") {
557
+ attrs["llm.model_name"] = meta.model;
558
+ }
559
+ const tokens = meta?.tokens;
560
+ if (tokens && typeof tokens === "object" && tokens !== null) {
561
+ const inp = tokens.input;
562
+ const outp = tokens.output;
563
+ if (typeof inp === "number") attrs["llm.token_count.prompt"] = inp;
564
+ if (typeof outp === "number") attrs["llm.token_count.completion"] = outp;
565
+ }
566
+ if (includeAttributes && meta && typeof meta === "object") {
567
+ for (const [k, v] of Object.entries(meta)) {
568
+ if (k === "tokens" || k === "model") continue;
569
+ if (v !== void 0 && v !== null && typeof v !== "object") {
570
+ attrs[`agent_inspect.preview.${k}`] = typeof v === "string" ? v.slice(0, maxLen) : v;
571
+ }
572
+ }
573
+ }
574
+ let status;
575
+ if (ev.status === "error") {
576
+ const msg = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error") : "error";
577
+ status = { code: "ERROR", message: msg.slice(0, maxLen) };
578
+ } else if (ev.status === "ok") {
579
+ status = { code: "OK" };
580
+ } else {
581
+ status = { code: "UNSET" };
582
+ }
583
+ spans.push({
584
+ trace_id: traceId,
585
+ span_id: spanId,
586
+ parent_span_id: parentSpanHex,
587
+ name: ev.name,
588
+ start_time_unix_nano: startNs,
589
+ end_time_unix_nano: endNs,
590
+ attributes: attrs,
591
+ status
592
+ });
593
+ }
594
+ const payload = {
595
+ exporter: "agent-inspect",
596
+ format: "openinference",
597
+ compatibility: "openinference-compatible",
598
+ version: EXPORT_PAYLOAD_VERSION,
599
+ trace_id: traceId,
600
+ spans,
601
+ warnings
602
+ };
603
+ return {
604
+ format: "openinference",
605
+ content: JSON.stringify(payload, null, pretty ? 2 : void 0),
606
+ contentType: "application/json",
607
+ fileExtension: ".openinference.json",
608
+ warnings
609
+ };
610
+ }
611
+ function hexFrom2(seed, byteLen) {
612
+ return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
613
+ }
614
+ function stringAttr(key, value) {
615
+ return { key, value: { stringValue: value } };
616
+ }
617
+ function intAttr(key, value) {
618
+ return { key, value: { intValue: String(value) } };
619
+ }
620
+ function genAiOperationName(kind) {
621
+ switch (kind) {
622
+ case "LLM":
623
+ return "generate_content";
624
+ case "TOOL":
625
+ return "execute_tool";
626
+ case "AGENT":
627
+ return "invoke_agent";
628
+ default:
629
+ return void 0;
630
+ }
631
+ }
632
+ function exportOtlpJson(tree, options) {
633
+ const warnings = [
634
+ "OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
635
+ "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
636
+ ];
637
+ const traceId = hexFrom2(`trace:${tree.runId}`, 16);
638
+ const includeAttributes = options?.includeAttributes ?? false;
639
+ const maxLen = options?.maxAttributeLength ?? 500;
640
+ const pretty = options?.pretty ?? true;
641
+ const flat = flattenTree(tree);
642
+ const spans = [];
643
+ for (const n of flat) {
644
+ const ev = n.event;
645
+ const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
646
+ const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
647
+ const startNs = String(Math.round(ev.timestamp * 1e6));
648
+ let endNs;
649
+ if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
650
+ endNs = String(Math.round(ev.timestamp * 1e6 + ev.durationMs * 1e6));
651
+ }
652
+ const attrs = [
653
+ stringAttr("agent_inspect.kind", ev.kind),
654
+ stringAttr("agent_inspect.confidence", ev.confidence),
655
+ stringAttr("agent_inspect.source.type", ev.source.type),
656
+ stringAttr("agent_inspect.run_id", tree.runId),
657
+ stringAttr("agent_inspect.event_id", ev.eventId),
658
+ stringAttr("agent_inspect.status", ev.status ?? "unset")
659
+ ];
660
+ if (ev.durationMs !== void 0) {
661
+ attrs.push(intAttr("agent_inspect.duration_ms", ev.durationMs));
662
+ }
663
+ const op = genAiOperationName(ev.kind);
664
+ if (op !== void 0) {
665
+ attrs.push(stringAttr("gen_ai.operation.name", op));
666
+ }
667
+ const meta = ev.attributes;
668
+ if (meta?.model !== void 0 && typeof meta.model === "string") {
669
+ attrs.push(stringAttr("gen_ai.request.model", meta.model.slice(0, maxLen)));
670
+ }
671
+ const tokens = meta?.tokens;
672
+ if (tokens && typeof tokens === "object" && tokens !== null) {
673
+ const inp = tokens.input;
674
+ const outp = tokens.output;
675
+ if (typeof inp === "number") attrs.push(intAttr("gen_ai.usage.input_tokens", inp));
676
+ if (typeof outp === "number") attrs.push(intAttr("gen_ai.usage.output_tokens", outp));
677
+ }
678
+ if (includeAttributes && meta && typeof meta === "object") {
679
+ for (const [k, v] of Object.entries(meta)) {
680
+ if (k === "tokens" || k === "model") continue;
681
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
682
+ attrs.push(
683
+ stringAttr(
684
+ `agent_inspect.preview.${k}`,
685
+ typeof v === "string" ? v.slice(0, maxLen) : String(v)
686
+ )
687
+ );
688
+ }
689
+ }
690
+ }
691
+ let statusCode = "STATUS_CODE_UNSET";
692
+ let statusMessage;
693
+ if (ev.status === "error") {
694
+ statusCode = "STATUS_CODE_ERROR";
695
+ statusMessage = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error").slice(0, maxLen) : "error";
696
+ } else if (ev.status === "ok") {
697
+ statusCode = "STATUS_CODE_OK";
698
+ }
699
+ const spanJson = {
700
+ traceId,
701
+ spanId,
702
+ name: ev.name,
703
+ kind: "SPAN_KIND_INTERNAL",
704
+ startTimeUnixNano: startNs,
705
+ attributes: attrs,
706
+ status: {
707
+ code: statusCode,
708
+ ...statusMessage !== void 0 ? { message: statusMessage } : {}
709
+ }
710
+ };
711
+ if (parentSpanId !== void 0) {
712
+ spanJson.parentSpanId = parentSpanId;
713
+ }
714
+ if (endNs !== void 0) {
715
+ spanJson.endTimeUnixNano = endNs;
716
+ }
717
+ spans.push(spanJson);
718
+ }
719
+ const payload = {
720
+ resourceSpans: [
721
+ {
722
+ resource: {
723
+ attributes: [stringAttr("service.name", "agent-inspect")]
724
+ },
725
+ scopeSpans: [
726
+ {
727
+ scope: { name: "agent-inspect" },
728
+ spans
729
+ }
730
+ ]
731
+ }
732
+ ]
733
+ };
734
+ return {
735
+ format: "otlp-json",
736
+ content: JSON.stringify(payload, null, pretty ? 2 : void 0),
737
+ contentType: "application/json",
738
+ fileExtension: ".otlp.json",
739
+ warnings
740
+ };
741
+ }
742
+
743
+ // packages/core/src/exporters/validation.ts
744
+ var EXPERIMENTAL = "Experimental compatibility export \u2014 verify against your target tooling before relying on it.";
745
+ function validateExportContent(format, content) {
746
+ const errors = [];
747
+ const warnings = [EXPERIMENTAL];
748
+ if (format === "markdown") {
749
+ if (!content.startsWith("# AgentInspect Run")) {
750
+ errors.push('Markdown export must start with "# AgentInspect Run"');
751
+ }
752
+ return { ok: errors.length === 0, format, errors, warnings };
753
+ }
754
+ if (format === "html") {
755
+ const lower = content.toLowerCase();
756
+ if (!lower.includes("<!doctype html")) {
757
+ errors.push("HTML export must include <!doctype html>");
758
+ }
759
+ if (/<\s*script\b/i.test(content)) {
760
+ errors.push("HTML export must not contain script tags");
761
+ }
762
+ if (/<\s*link\b[^>]*href\s*=/i.test(content)) {
763
+ warnings.push("HTML export contains link tags \u2014 ensure no external stylesheets.");
764
+ }
765
+ return { ok: errors.length === 0, format, errors, warnings };
766
+ }
767
+ if (format === "openinference") {
768
+ let parsed;
769
+ try {
770
+ parsed = JSON.parse(content);
771
+ } catch {
772
+ errors.push("OpenInference export is not valid JSON");
773
+ return { ok: false, format, errors, warnings };
774
+ }
775
+ if (!parsed || typeof parsed !== "object") {
776
+ errors.push("OpenInference export JSON must be an object");
777
+ return { ok: false, format, errors, warnings };
778
+ }
779
+ const o = parsed;
780
+ if (o.format !== "openinference") {
781
+ errors.push('OpenInference export must include format: "openinference"');
782
+ }
783
+ if (!Array.isArray(o.spans)) {
784
+ errors.push("OpenInference export must include a spans array");
785
+ }
786
+ warnings.push("OpenInference-compatible JSON is not guaranteed for every backend.");
787
+ return { ok: errors.length === 0, format, errors, warnings };
788
+ }
789
+ if (format === "otlp-json") {
790
+ let parsed;
791
+ try {
792
+ parsed = JSON.parse(content);
793
+ } catch {
794
+ errors.push("OTLP JSON export is not valid JSON");
795
+ return { ok: false, format, errors, warnings };
796
+ }
797
+ if (!parsed || typeof parsed !== "object") {
798
+ errors.push("OTLP JSON export must be an object");
799
+ return { ok: false, format, errors, warnings };
800
+ }
801
+ const o = parsed;
802
+ if (!Array.isArray(o.resourceSpans)) {
803
+ errors.push("OTLP JSON export must include resourceSpans array");
804
+ }
805
+ warnings.push(
806
+ "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation."
807
+ );
808
+ return { ok: errors.length === 0, format, errors, warnings };
809
+ }
810
+ errors.push(`Unsupported export format`);
811
+ return { ok: false, format, errors, warnings };
812
+ }
813
+
814
+ // packages/core/src/exporters/manual-trace-adapter.ts
815
+ function stepTypeToInspectKind(t) {
816
+ switch (t) {
817
+ case "llm":
818
+ return "LLM";
819
+ case "tool":
820
+ return "TOOL";
821
+ case "decision":
822
+ return "DECISION";
823
+ case "run":
824
+ return "CHAIN";
825
+ default:
826
+ return "LOGIC";
827
+ }
828
+ }
829
+ function mapStepStatus(s) {
830
+ if (s === void 0) return "running";
831
+ if (s === "success") return "ok";
832
+ return "error";
833
+ }
834
+ function manualTraceEventsToRunTree(events) {
835
+ const started = events.find((e) => e.event === "run_started");
836
+ if (!started || started.event !== "run_started") {
837
+ throw new Error("Invalid trace: missing run_started");
838
+ }
839
+ const runId = started.runId;
840
+ const runName = started.name;
841
+ const completedAll = events.filter((e) => e.event === "run_completed");
842
+ const lastCompleted = completedAll[completedAll.length - 1];
843
+ let runStatus;
844
+ if (lastCompleted === void 0) {
845
+ runStatus = "running";
846
+ } else if (lastCompleted.status === "success") {
847
+ runStatus = "ok";
848
+ } else {
849
+ runStatus = "error";
850
+ }
851
+ const startedAt = started.startTime;
852
+ const endedAt = lastCompleted !== void 0 && runStatus !== "running" ? lastCompleted.endTime : void 0;
853
+ const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
854
+ const steps = /* @__PURE__ */ new Map();
855
+ for (const e of events) {
856
+ if (e.event !== "step_started") continue;
857
+ const s = e;
858
+ steps.set(s.stepId, {
859
+ id: s.stepId,
860
+ parentId: s.parentId,
861
+ name: s.name,
862
+ type: s.type,
863
+ startTime: s.startTime,
864
+ timestamp: s.timestamp,
865
+ metadata: s.metadata
866
+ });
867
+ }
868
+ for (const e of events) {
869
+ if (e.event !== "step_completed") continue;
870
+ const acc = steps.get(e.stepId);
871
+ if (!acc) continue;
872
+ acc.status = e.status;
873
+ acc.endTime = e.endTime;
874
+ acc.durationMs = e.durationMs;
875
+ if (e.error?.message) {
876
+ acc.error = e.error;
877
+ }
878
+ }
879
+ const inspectNodes = /* @__PURE__ */ new Map();
880
+ for (const acc of steps.values()) {
881
+ const kind = stepTypeToInspectKind(acc.type);
882
+ const status = mapStepStatus(acc.status);
883
+ const attrs = { ...acc.metadata ?? {} };
884
+ if (acc.error?.message) {
885
+ attrs.error = acc.error;
886
+ }
887
+ const evt = {
888
+ eventId: acc.id,
889
+ runId,
890
+ parentId: acc.parentId,
891
+ name: acc.name,
892
+ kind,
893
+ timestamp: acc.timestamp,
894
+ status,
895
+ durationMs: acc.durationMs,
896
+ attributes: Object.keys(attrs).length > 0 ? attrs : void 0,
897
+ confidence: "explicit",
898
+ source: { type: "manual" }
899
+ };
900
+ inspectNodes.set(acc.id, { event: evt, children: [], depth: 0 });
901
+ }
902
+ const roots = [];
903
+ const sortByStart = (a, b) => a.event.timestamp - b.event.timestamp;
904
+ for (const node of inspectNodes.values()) {
905
+ const pid = node.event.parentId;
906
+ if (pid !== void 0 && inspectNodes.has(pid)) {
907
+ inspectNodes.get(pid).children.push(node);
908
+ } else {
909
+ roots.push(node);
910
+ }
911
+ }
912
+ roots.sort(sortByStart);
913
+ for (const n of inspectNodes.values()) {
914
+ n.children.sort(sortByStart);
915
+ }
916
+ const assignDepth = (n, depth) => {
917
+ n.depth = depth;
918
+ for (const c of n.children) assignDepth(c, depth + 1);
919
+ };
920
+ for (const r of roots) assignDepth(r, 0);
921
+ const confidenceBreakdown = {
922
+ explicit: 0,
923
+ correlated: 0,
924
+ heuristic: 0,
925
+ unknown: 0
926
+ };
927
+ const kinds = zeroKinds();
928
+ function countWalk(nodes) {
929
+ for (const n of nodes) {
930
+ confidenceBreakdown[n.event.confidence] += 1;
931
+ kinds[n.event.kind] += 1;
932
+ if (n.children.length > 0) countWalk(n.children);
933
+ }
934
+ }
935
+ countWalk(roots);
936
+ return {
937
+ runId,
938
+ name: runName,
939
+ status: runStatus,
940
+ startedAt,
941
+ endedAt,
942
+ durationMs,
943
+ children: roots,
944
+ metadata: {
945
+ totalEvents: inspectNodes.size,
946
+ confidenceBreakdown,
947
+ kinds
948
+ }
949
+ };
950
+ }
951
+
952
+ // packages/core/src/exporters/index.ts
953
+ function mergeExportDefaults(options) {
954
+ return {
955
+ format: options.format,
956
+ includeMetadata: options.includeMetadata ?? true,
957
+ includeAttributes: options.includeAttributes ?? false,
958
+ includeErrors: options.includeErrors ?? true,
959
+ pretty: options.pretty ?? true,
960
+ redacted: options.redacted ?? true,
961
+ maxAttributeLength: options.maxAttributeLength ?? 500,
962
+ redactionProfile: options.redactionProfile ?? "local"
963
+ };
964
+ }
965
+ function exportRunTree(tree, options) {
966
+ const opts = mergeExportDefaults(options);
967
+ const exportTree = opts.redactionProfile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: opts.redactionProfile });
968
+ switch (opts.format) {
969
+ case "markdown":
970
+ return exportMarkdown(exportTree, opts);
971
+ case "html":
972
+ return exportHtml(exportTree, opts);
973
+ case "openinference":
974
+ return exportOpenInference(exportTree, opts);
975
+ case "otlp-json":
976
+ return exportOtlpJson(exportTree, opts);
977
+ default: {
978
+ const _x = opts.format;
979
+ throw new Error(`Unsupported export format: ${String(_x)}`);
980
+ }
981
+ }
982
+ }
983
+ function validateExport(result) {
984
+ const base = validateExportContent(result.format, result.content);
985
+ return {
986
+ ok: base.ok,
987
+ format: base.format,
988
+ errors: base.errors,
989
+ warnings: [...result.warnings, ...base.warnings]
990
+ };
991
+ }
992
+
993
+ // packages/core/src/report.ts
994
+ function resolveTree(events, profile) {
995
+ const tree = manualTraceEventsToRunTree(events);
996
+ return profile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: profile });
997
+ }
998
+ function tailMarkdownSection(content, heading) {
999
+ const marker = `## ${heading}`;
1000
+ const idx = content.indexOf(marker);
1001
+ if (idx < 0) return "";
1002
+ return content.slice(idx).trimEnd();
1003
+ }
1004
+ function extractHtmlFragment(content, start, end) {
1005
+ const startIdx = content.indexOf(start);
1006
+ if (startIdx < 0) return "";
1007
+ const endIdx = content.indexOf(end, startIdx + start.length);
1008
+ if (endIdx < 0) return content.slice(startIdx);
1009
+ return content.slice(startIdx, endIdx);
1010
+ }
1011
+ var REPORT_HTML_CSS = `
1012
+ body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}
1013
+ h1{font-size:1.35rem}
1014
+ h2{font-size:1.1rem;margin-top:1.5rem}
1015
+ pre{white-space:pre-wrap;background:#f8f8f8;padding:0.75rem;overflow:auto;font-size:0.9rem}
1016
+ ul.tree{list-style:none;padding-left:1rem}
1017
+ ul.tree.nested{padding-left:1.25rem;border-left:1px solid #ddd;margin:0.25rem 0}
1018
+ .nm{font-weight:600}
1019
+ .meta{color:#555;font-size:0.9rem}
1020
+ footer{margin-top:2rem;font-size:0.85rem;color:#555}
1021
+ `.trim();
1022
+ function buildRunReport(events, options) {
1023
+ const profile = options.redactionProfile ?? "local";
1024
+ const safeEvents = redactTraceEventsForReport(events, {
1025
+ redactionProfile: profile
1026
+ });
1027
+ const whatSummary = buildRunWhatSummary(safeEvents);
1028
+ const whatText = renderRunWhat(whatSummary, {
1029
+ correlation: options.correlation !== false
1030
+ });
1031
+ const timelineText = renderTimeline(buildRunTimeline(safeEvents));
1032
+ const tree = resolveTree(safeEvents, profile);
1033
+ const exportOpts = {
1034
+ includeMetadata: false,
1035
+ includeAttributes: options.includeAttributes === true,
1036
+ includeErrors: options.includeErrors !== false,
1037
+ redacted: true,
1038
+ maxAttributeLength: 500
1039
+ };
1040
+ if (options.format === "markdown") {
1041
+ const treeMd = exportMarkdown(tree, exportOpts);
1042
+ const tail = tailMarkdownSection(treeMd.content, "Execution tree");
1043
+ const title2 = escapeMarkdown(whatSummary.name ?? whatSummary.runId);
1044
+ const lines = [
1045
+ `# AgentInspect Report: ${title2}`,
1046
+ "",
1047
+ "Generated locally by AgentInspect. Review for sensitive data before sharing.",
1048
+ "",
1049
+ "## What happened",
1050
+ "",
1051
+ "```text",
1052
+ whatText,
1053
+ "```",
1054
+ "",
1055
+ "## Timeline",
1056
+ "",
1057
+ "```text",
1058
+ timelineText,
1059
+ "```",
1060
+ ""
1061
+ ];
1062
+ if (tail) {
1063
+ lines.push(tail, "");
1064
+ } else {
1065
+ lines.push("## Execution tree", "", "(no steps)", "");
1066
+ }
1067
+ return {
1068
+ format: "markdown",
1069
+ content: lines.join("\n"),
1070
+ contentType: "text/markdown",
1071
+ fileExtension: ".md"
1072
+ };
1073
+ }
1074
+ const treeHtml = exportHtml(tree, exportOpts);
1075
+ const title = escapeHtml(whatSummary.name ?? whatSummary.runId);
1076
+ const treeSection = extractHtmlFragment(
1077
+ treeHtml.content,
1078
+ '<section class="tree">',
1079
+ "</section>"
1080
+ );
1081
+ const errorsSection = treeHtml.content.includes("<h2>Errors</h2>") ? extractHtmlFragment(treeHtml.content, "<h2>Errors</h2>", "<footer>") : "";
1082
+ const attrsSection = treeHtml.content.includes("<h2>Attributes (bounded)</h2>") ? extractHtmlFragment(
1083
+ treeHtml.content,
1084
+ "<h2>Attributes (bounded)</h2>",
1085
+ "<footer>"
1086
+ ) : "";
1087
+ const html = `<!doctype html>
1088
+ <html lang="en">
1089
+ <head>
1090
+ <meta charset="utf-8"/>
1091
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
1092
+ <title>AgentInspect Report: ${title}</title>
1093
+ <style>${REPORT_HTML_CSS}</style>
1094
+ </head>
1095
+ <body>
1096
+ <header><h1>AgentInspect Report: ${title}</h1></header>
1097
+ <p class="note">Generated locally by AgentInspect. Review for sensitive data before sharing.</p>
1098
+ <section class="what"><h2>What happened</h2><pre>${escapeHtml(whatText)}</pre></section>
1099
+ <section class="timeline"><h2>Timeline</h2><pre>${escapeHtml(timelineText)}</pre></section>
1100
+ ${treeSection || '<section class="tree"><h2>Execution tree</h2><p>No steps recorded.</p></section>'}
1101
+ ${errorsSection}
1102
+ ${attrsSection}
1103
+ <footer>Generated locally by AgentInspect. Review for sensitive data before sharing.</footer>
1104
+ </body>
1105
+ </html>`;
1106
+ return {
1107
+ format: "html",
1108
+ content: html,
1109
+ contentType: "text/html",
1110
+ fileExtension: ".html"
1111
+ };
1112
+ }
1113
+
1114
+ export { EXPORT_PAYLOAD_VERSION, buildRunReport, exportHtml, exportMarkdown, exportOpenInference, exportOtlpJson, exportRunTree, manualTraceEventsToRunTree, mergeExportDefaults, redactRunTreeForExport, validateExport, validateExportContent };
5
1115
  //# sourceMappingURL=exporters.mjs.map
6
1116
  //# sourceMappingURL=exporters.mjs.map