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