agent-inspect 1.4.0 → 1.6.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.
- package/CHANGELOG.md +63 -0
- package/README.md +34 -6
- package/docs/ADAPTERS.md +6 -0
- package/docs/API.md +132 -9
- package/docs/ARCHITECTURE.md +4 -0
- package/docs/CLI.md +98 -5
- package/docs/DIFF.md +8 -0
- package/docs/EXPORTS.md +13 -0
- package/docs/GETTING-STARTED.md +19 -1
- package/docs/KNOWN-ISSUES.md +8 -1
- package/docs/LIMITATIONS.md +13 -2
- package/docs/LOGS.md +22 -0
- package/docs/SCHEMA.md +19 -7
- package/docs/SCREENSHOTS.md +190 -9
- package/package.json +71 -1
- package/packages/cli/dist/index.cjs +7121 -3986
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +7122 -3987
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/advanced.cjs +2258 -0
- package/packages/core/dist/advanced.cjs.map +1 -0
- package/packages/core/dist/advanced.d.cts +254 -0
- package/packages/core/dist/advanced.d.ts +254 -0
- package/packages/core/dist/advanced.mjs +11 -0
- package/packages/core/dist/advanced.mjs.map +1 -0
- package/packages/core/dist/chunk-57S5D6HR.mjs +655 -0
- package/packages/core/dist/chunk-57S5D6HR.mjs.map +1 -0
- package/packages/core/dist/chunk-6QSLZCBJ.mjs +743 -0
- package/packages/core/dist/chunk-6QSLZCBJ.mjs.map +1 -0
- package/packages/core/dist/chunk-6SZPTECC.mjs +342 -0
- package/packages/core/dist/chunk-6SZPTECC.mjs.map +1 -0
- package/packages/core/dist/chunk-74XZ6N7Q.mjs +409 -0
- package/packages/core/dist/chunk-74XZ6N7Q.mjs.map +1 -0
- package/packages/core/dist/chunk-7TGZLWEE.mjs +35 -0
- package/packages/core/dist/chunk-7TGZLWEE.mjs.map +1 -0
- package/packages/core/dist/chunk-BT7CATSD.mjs +497 -0
- package/packages/core/dist/chunk-BT7CATSD.mjs.map +1 -0
- package/packages/core/dist/chunk-E5F2LQCX.mjs +83 -0
- package/packages/core/dist/chunk-E5F2LQCX.mjs.map +1 -0
- package/packages/core/dist/chunk-EDTQHZPM.mjs +88 -0
- package/packages/core/dist/chunk-EDTQHZPM.mjs.map +1 -0
- package/packages/core/dist/chunk-HR7G62IE.mjs +785 -0
- package/packages/core/dist/chunk-HR7G62IE.mjs.map +1 -0
- package/packages/core/dist/chunk-HY7H3CQM.mjs +127 -0
- package/packages/core/dist/chunk-HY7H3CQM.mjs.map +1 -0
- package/packages/core/dist/chunk-S4YWKV4G.mjs +48 -0
- package/packages/core/dist/chunk-S4YWKV4G.mjs.map +1 -0
- package/packages/core/dist/chunk-TFLPUZ56.mjs +1571 -0
- package/packages/core/dist/chunk-TFLPUZ56.mjs.map +1 -0
- package/packages/core/dist/chunk-TZISEVLQ.mjs +390 -0
- package/packages/core/dist/chunk-TZISEVLQ.mjs.map +1 -0
- package/packages/core/dist/chunk-U2BGPESY.mjs +150 -0
- package/packages/core/dist/chunk-U2BGPESY.mjs.map +1 -0
- package/packages/core/dist/chunk-VTIB5MDK.mjs +304 -0
- package/packages/core/dist/chunk-VTIB5MDK.mjs.map +1 -0
- package/packages/core/dist/chunk-VU6O5QAH.mjs +99 -0
- package/packages/core/dist/chunk-VU6O5QAH.mjs.map +1 -0
- package/packages/core/dist/chunk-Y56BPA3B.mjs +990 -0
- package/packages/core/dist/chunk-Y56BPA3B.mjs.map +1 -0
- package/packages/core/dist/chunk-YWAOOXLR.mjs +475 -0
- package/packages/core/dist/chunk-YWAOOXLR.mjs.map +1 -0
- package/packages/core/dist/diff.cjs +993 -0
- package/packages/core/dist/diff.cjs.map +1 -0
- package/packages/core/dist/diff.d.cts +82 -0
- package/packages/core/dist/diff.d.ts +82 -0
- package/packages/core/dist/diff.mjs +5 -0
- package/packages/core/dist/diff.mjs.map +1 -0
- package/packages/core/dist/exporters.cjs +1228 -0
- package/packages/core/dist/exporters.cjs.map +1 -0
- package/packages/core/dist/exporters.d.cts +114 -0
- package/packages/core/dist/exporters.d.ts +114 -0
- package/packages/core/dist/exporters.mjs +6 -0
- package/packages/core/dist/exporters.mjs.map +1 -0
- package/packages/core/dist/index.cjs +5542 -2218
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.d.cts +113 -908
- package/packages/core/dist/index.d.ts +113 -908
- package/packages/core/dist/index.mjs +1048 -5403
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/inspect-event-Des4JDHo.d.cts +41 -0
- package/packages/core/dist/inspect-event-Des4JDHo.d.ts +41 -0
- package/packages/core/dist/log-config-BnH8Ykcb.d.cts +33 -0
- package/packages/core/dist/log-config-C1GcJPIM.d.ts +33 -0
- package/packages/core/dist/logs.cjs +1007 -0
- package/packages/core/dist/logs.cjs.map +1 -0
- package/packages/core/dist/logs.d.cts +138 -0
- package/packages/core/dist/logs.d.ts +138 -0
- package/packages/core/dist/logs.mjs +6 -0
- package/packages/core/dist/logs.mjs.map +1 -0
- package/packages/core/dist/persisted-inspect-event-0kaRADsp.d.cts +56 -0
- package/packages/core/dist/persisted-inspect-event-DiFto0K2.d.ts +56 -0
- package/packages/core/dist/persisted.cjs +1055 -0
- package/packages/core/dist/persisted.cjs.map +1 -0
- package/packages/core/dist/persisted.d.cts +111 -0
- package/packages/core/dist/persisted.d.ts +111 -0
- package/packages/core/dist/persisted.mjs +7 -0
- package/packages/core/dist/persisted.mjs.map +1 -0
- package/packages/core/dist/readers.cjs +2590 -0
- package/packages/core/dist/readers.cjs.map +1 -0
- package/packages/core/dist/readers.d.cts +80 -0
- package/packages/core/dist/readers.d.ts +80 -0
- package/packages/core/dist/readers.mjs +9 -0
- package/packages/core/dist/readers.mjs.map +1 -0
- package/packages/core/dist/types-DB8jB6Jg.d.cts +232 -0
- package/packages/core/dist/types-tSix7tfv.d.ts +232 -0
- package/packages/core/dist/writers.cjs +997 -0
- package/packages/core/dist/writers.cjs.map +1 -0
- package/packages/core/dist/writers.d.cts +62 -0
- package/packages/core/dist/writers.d.ts +62 -0
- package/packages/core/dist/writers.mjs +9 -0
- package/packages/core/dist/writers.mjs.map +1 -0
|
@@ -0,0 +1,990 @@
|
|
|
1
|
+
import { escapeHtml, flattenTree, safeString, compactAttributes, stableJson, escapeMarkdown, zeroKinds } from './chunk-HY7H3CQM.mjs';
|
|
2
|
+
import { resolveRedactionProfile, applyProfileMetadataCaps, truncateStringForProfile } from './chunk-EDTQHZPM.mjs';
|
|
3
|
+
import { Redactor } from './chunk-VU6O5QAH.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 redactErrorInfo(error, redactor, maxMetadataValueLength, maxPreviewLength) {
|
|
502
|
+
if (error === void 0) return void 0;
|
|
503
|
+
const record = redactEventAttributes(
|
|
504
|
+
{ error },
|
|
505
|
+
redactor,
|
|
506
|
+
maxMetadataValueLength,
|
|
507
|
+
maxPreviewLength
|
|
508
|
+
);
|
|
509
|
+
const redacted = record?.error;
|
|
510
|
+
if (!isRecord(redacted) || typeof redacted.message !== "string") {
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
message: redacted.message,
|
|
515
|
+
...typeof redacted.stack === "string" ? { stack: redacted.stack } : {}
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
function redactTraceEventsForReport(events, options) {
|
|
519
|
+
const profile = options?.redactionProfile ?? "local";
|
|
520
|
+
if (profile === "local") {
|
|
521
|
+
return deepClone(events);
|
|
522
|
+
}
|
|
523
|
+
const resolved = resolveRedactionProfile(profile);
|
|
524
|
+
const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
|
|
525
|
+
2e3,
|
|
526
|
+
500,
|
|
527
|
+
resolved
|
|
528
|
+
);
|
|
529
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
530
|
+
return events.map((event) => {
|
|
531
|
+
switch (event.event) {
|
|
532
|
+
case "run_started":
|
|
533
|
+
return {
|
|
534
|
+
...event,
|
|
535
|
+
name: truncateStringForProfile(
|
|
536
|
+
event.name,
|
|
537
|
+
"name",
|
|
538
|
+
maxMetadataValueLength,
|
|
539
|
+
maxPreviewLength
|
|
540
|
+
),
|
|
541
|
+
...event.metadata !== void 0 ? {
|
|
542
|
+
metadata: redactEventAttributes(
|
|
543
|
+
event.metadata,
|
|
544
|
+
redactor,
|
|
545
|
+
maxMetadataValueLength,
|
|
546
|
+
maxPreviewLength
|
|
547
|
+
)
|
|
548
|
+
} : {}
|
|
549
|
+
};
|
|
550
|
+
case "step_started":
|
|
551
|
+
return {
|
|
552
|
+
...event,
|
|
553
|
+
name: truncateStringForProfile(
|
|
554
|
+
event.name,
|
|
555
|
+
"name",
|
|
556
|
+
maxMetadataValueLength,
|
|
557
|
+
maxPreviewLength
|
|
558
|
+
),
|
|
559
|
+
...event.metadata !== void 0 ? {
|
|
560
|
+
metadata: redactEventAttributes(
|
|
561
|
+
event.metadata,
|
|
562
|
+
redactor,
|
|
563
|
+
maxMetadataValueLength,
|
|
564
|
+
maxPreviewLength
|
|
565
|
+
)
|
|
566
|
+
} : {}
|
|
567
|
+
};
|
|
568
|
+
case "run_completed":
|
|
569
|
+
case "step_completed":
|
|
570
|
+
return {
|
|
571
|
+
...event,
|
|
572
|
+
...event.error !== void 0 ? {
|
|
573
|
+
error: redactErrorInfo(
|
|
574
|
+
event.error,
|
|
575
|
+
redactor,
|
|
576
|
+
maxMetadataValueLength,
|
|
577
|
+
maxPreviewLength
|
|
578
|
+
)
|
|
579
|
+
} : {}
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
function redactRunTreeForExport(tree, options) {
|
|
585
|
+
const profile = options?.redactionProfile ?? "local";
|
|
586
|
+
if (profile === "local") {
|
|
587
|
+
return deepClone(tree);
|
|
588
|
+
}
|
|
589
|
+
const resolved = resolveRedactionProfile(profile);
|
|
590
|
+
const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
|
|
591
|
+
2e3,
|
|
592
|
+
500,
|
|
593
|
+
resolved
|
|
594
|
+
);
|
|
595
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
596
|
+
const clone = deepClone(tree);
|
|
597
|
+
function walk(nodes) {
|
|
598
|
+
for (const node of nodes) {
|
|
599
|
+
if (node.event.attributes !== void 0) {
|
|
600
|
+
node.event.attributes = redactEventAttributes(
|
|
601
|
+
node.event.attributes,
|
|
602
|
+
redactor,
|
|
603
|
+
maxMetadataValueLength,
|
|
604
|
+
maxPreviewLength
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
if (node.children.length > 0) {
|
|
608
|
+
walk(node.children);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
walk(clone.children);
|
|
613
|
+
return clone;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// packages/core/src/exporters/types.ts
|
|
617
|
+
var EXPORT_PAYLOAD_VERSION = "0.1.2";
|
|
618
|
+
function hexFrom(seed, byteLen) {
|
|
619
|
+
return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
620
|
+
}
|
|
621
|
+
function mapInspectKindToOI(kind, warnings) {
|
|
622
|
+
switch (kind) {
|
|
623
|
+
case "LLM":
|
|
624
|
+
return { openInferenceKind: "LLM" };
|
|
625
|
+
case "TOOL":
|
|
626
|
+
return { openInferenceKind: "TOOL" };
|
|
627
|
+
case "CHAIN":
|
|
628
|
+
return { openInferenceKind: "CHAIN" };
|
|
629
|
+
case "RETRIEVER":
|
|
630
|
+
return { openInferenceKind: "RETRIEVER" };
|
|
631
|
+
case "AGENT":
|
|
632
|
+
return { openInferenceKind: "AGENT" };
|
|
633
|
+
case "DECISION":
|
|
634
|
+
warnings.push(
|
|
635
|
+
`Ambiguous kind DECISION mapped to CHAIN for span compatibility (${EXPORT_PAYLOAD_VERSION}).`
|
|
636
|
+
);
|
|
637
|
+
return { openInferenceKind: "CHAIN" };
|
|
638
|
+
case "RESULT":
|
|
639
|
+
warnings.push(
|
|
640
|
+
`Ambiguous kind RESULT mapped to UNKNOWN for span compatibility (${EXPORT_PAYLOAD_VERSION}).`
|
|
641
|
+
);
|
|
642
|
+
return { openInferenceKind: "UNKNOWN" };
|
|
643
|
+
case "ERROR":
|
|
644
|
+
warnings.push(`ERROR kind mapped to CHAIN for span compatibility.`);
|
|
645
|
+
return { openInferenceKind: "CHAIN" };
|
|
646
|
+
case "LOG":
|
|
647
|
+
case "LOGIC":
|
|
648
|
+
case "RUN":
|
|
649
|
+
warnings.push(`${kind} mapped to CHAIN for span compatibility.`);
|
|
650
|
+
return { openInferenceKind: "CHAIN" };
|
|
651
|
+
default:
|
|
652
|
+
warnings.push(`Unhandled InspectKind ${kind} mapped to UNKNOWN.`);
|
|
653
|
+
return { openInferenceKind: "UNKNOWN" };
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function exportOpenInference(tree, options) {
|
|
657
|
+
const warnings = [
|
|
658
|
+
"OpenInference-compatible JSON export is experimental until verified against specific backends.",
|
|
659
|
+
"This file was generated locally and not sent anywhere."
|
|
660
|
+
];
|
|
661
|
+
const traceId = hexFrom(`trace:${tree.runId}`, 16);
|
|
662
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
663
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
664
|
+
const pretty = options?.pretty ?? true;
|
|
665
|
+
const spans = [];
|
|
666
|
+
for (const n of flattenTree(tree)) {
|
|
667
|
+
const ev = n.event;
|
|
668
|
+
const spanId = hexFrom(`${tree.runId}:${ev.eventId}`, 8);
|
|
669
|
+
const parentSpanHex = ev.parentId ? hexFrom(`${tree.runId}:${ev.parentId}`, 8) : void 0;
|
|
670
|
+
const startNs = Math.round(ev.timestamp * 1e6);
|
|
671
|
+
let endNs;
|
|
672
|
+
if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
|
|
673
|
+
endNs = startNs + Math.round(ev.durationMs * 1e6);
|
|
674
|
+
}
|
|
675
|
+
const { openInferenceKind } = mapInspectKindToOI(ev.kind, warnings);
|
|
676
|
+
const attrs = {
|
|
677
|
+
"openinference.span.kind": openInferenceKind,
|
|
678
|
+
"agent_inspect.kind": ev.kind,
|
|
679
|
+
"agent_inspect.confidence": ev.confidence,
|
|
680
|
+
"agent_inspect.source.type": ev.source.type,
|
|
681
|
+
"agent_inspect.run_id": tree.runId,
|
|
682
|
+
"agent_inspect.event_id": ev.eventId,
|
|
683
|
+
"agent_inspect.status": ev.status ?? "unset"
|
|
684
|
+
};
|
|
685
|
+
if (ev.durationMs !== void 0) {
|
|
686
|
+
attrs["agent_inspect.duration_ms"] = ev.durationMs;
|
|
687
|
+
}
|
|
688
|
+
const meta = ev.attributes;
|
|
689
|
+
if (meta?.model !== void 0 && typeof meta.model === "string") {
|
|
690
|
+
attrs["llm.model_name"] = meta.model;
|
|
691
|
+
}
|
|
692
|
+
const tokens = meta?.tokens;
|
|
693
|
+
if (tokens && typeof tokens === "object" && tokens !== null) {
|
|
694
|
+
const inp = tokens.input;
|
|
695
|
+
const outp = tokens.output;
|
|
696
|
+
if (typeof inp === "number") attrs["llm.token_count.prompt"] = inp;
|
|
697
|
+
if (typeof outp === "number") attrs["llm.token_count.completion"] = outp;
|
|
698
|
+
}
|
|
699
|
+
if (includeAttributes && meta && typeof meta === "object") {
|
|
700
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
701
|
+
if (k === "tokens" || k === "model") continue;
|
|
702
|
+
if (v !== void 0 && v !== null && typeof v !== "object") {
|
|
703
|
+
attrs[`agent_inspect.preview.${k}`] = typeof v === "string" ? v.slice(0, maxLen) : v;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
let status;
|
|
708
|
+
if (ev.status === "error") {
|
|
709
|
+
const msg = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error") : "error";
|
|
710
|
+
status = { code: "ERROR", message: msg.slice(0, maxLen) };
|
|
711
|
+
} else if (ev.status === "ok") {
|
|
712
|
+
status = { code: "OK" };
|
|
713
|
+
} else {
|
|
714
|
+
status = { code: "UNSET" };
|
|
715
|
+
}
|
|
716
|
+
spans.push({
|
|
717
|
+
trace_id: traceId,
|
|
718
|
+
span_id: spanId,
|
|
719
|
+
parent_span_id: parentSpanHex,
|
|
720
|
+
name: ev.name,
|
|
721
|
+
start_time_unix_nano: startNs,
|
|
722
|
+
end_time_unix_nano: endNs,
|
|
723
|
+
attributes: attrs,
|
|
724
|
+
status
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
const payload = {
|
|
728
|
+
exporter: "agent-inspect",
|
|
729
|
+
format: "openinference",
|
|
730
|
+
compatibility: "openinference-compatible",
|
|
731
|
+
version: EXPORT_PAYLOAD_VERSION,
|
|
732
|
+
trace_id: traceId,
|
|
733
|
+
spans,
|
|
734
|
+
warnings
|
|
735
|
+
};
|
|
736
|
+
return {
|
|
737
|
+
format: "openinference",
|
|
738
|
+
content: JSON.stringify(payload, null, pretty ? 2 : void 0),
|
|
739
|
+
contentType: "application/json",
|
|
740
|
+
fileExtension: ".openinference.json",
|
|
741
|
+
warnings
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function hexFrom2(seed, byteLen) {
|
|
745
|
+
return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
746
|
+
}
|
|
747
|
+
function stringAttr(key, value) {
|
|
748
|
+
return { key, value: { stringValue: value } };
|
|
749
|
+
}
|
|
750
|
+
function intAttr(key, value) {
|
|
751
|
+
return { key, value: { intValue: String(value) } };
|
|
752
|
+
}
|
|
753
|
+
function genAiOperationName(kind) {
|
|
754
|
+
switch (kind) {
|
|
755
|
+
case "LLM":
|
|
756
|
+
return "generate_content";
|
|
757
|
+
case "TOOL":
|
|
758
|
+
return "execute_tool";
|
|
759
|
+
case "AGENT":
|
|
760
|
+
return "invoke_agent";
|
|
761
|
+
default:
|
|
762
|
+
return void 0;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function exportOtlpJson(tree, options) {
|
|
766
|
+
const warnings = [
|
|
767
|
+
"OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
|
|
768
|
+
"Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
|
|
769
|
+
];
|
|
770
|
+
const traceId = hexFrom2(`trace:${tree.runId}`, 16);
|
|
771
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
772
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
773
|
+
const pretty = options?.pretty ?? true;
|
|
774
|
+
const flat = flattenTree(tree);
|
|
775
|
+
const spans = [];
|
|
776
|
+
for (const n of flat) {
|
|
777
|
+
const ev = n.event;
|
|
778
|
+
const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
|
|
779
|
+
const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
|
|
780
|
+
const startNs = String(Math.round(ev.timestamp * 1e6));
|
|
781
|
+
let endNs;
|
|
782
|
+
if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
|
|
783
|
+
endNs = String(Math.round(ev.timestamp * 1e6 + ev.durationMs * 1e6));
|
|
784
|
+
}
|
|
785
|
+
const attrs = [
|
|
786
|
+
stringAttr("agent_inspect.kind", ev.kind),
|
|
787
|
+
stringAttr("agent_inspect.confidence", ev.confidence),
|
|
788
|
+
stringAttr("agent_inspect.source.type", ev.source.type),
|
|
789
|
+
stringAttr("agent_inspect.run_id", tree.runId),
|
|
790
|
+
stringAttr("agent_inspect.event_id", ev.eventId),
|
|
791
|
+
stringAttr("agent_inspect.status", ev.status ?? "unset")
|
|
792
|
+
];
|
|
793
|
+
if (ev.durationMs !== void 0) {
|
|
794
|
+
attrs.push(intAttr("agent_inspect.duration_ms", ev.durationMs));
|
|
795
|
+
}
|
|
796
|
+
const op = genAiOperationName(ev.kind);
|
|
797
|
+
if (op !== void 0) {
|
|
798
|
+
attrs.push(stringAttr("gen_ai.operation.name", op));
|
|
799
|
+
}
|
|
800
|
+
const meta = ev.attributes;
|
|
801
|
+
if (meta?.model !== void 0 && typeof meta.model === "string") {
|
|
802
|
+
attrs.push(stringAttr("gen_ai.request.model", meta.model.slice(0, maxLen)));
|
|
803
|
+
}
|
|
804
|
+
const tokens = meta?.tokens;
|
|
805
|
+
if (tokens && typeof tokens === "object" && tokens !== null) {
|
|
806
|
+
const inp = tokens.input;
|
|
807
|
+
const outp = tokens.output;
|
|
808
|
+
if (typeof inp === "number") attrs.push(intAttr("gen_ai.usage.input_tokens", inp));
|
|
809
|
+
if (typeof outp === "number") attrs.push(intAttr("gen_ai.usage.output_tokens", outp));
|
|
810
|
+
}
|
|
811
|
+
if (includeAttributes && meta && typeof meta === "object") {
|
|
812
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
813
|
+
if (k === "tokens" || k === "model") continue;
|
|
814
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
815
|
+
attrs.push(
|
|
816
|
+
stringAttr(
|
|
817
|
+
`agent_inspect.preview.${k}`,
|
|
818
|
+
typeof v === "string" ? v.slice(0, maxLen) : String(v)
|
|
819
|
+
)
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
let statusCode = "STATUS_CODE_UNSET";
|
|
825
|
+
let statusMessage;
|
|
826
|
+
if (ev.status === "error") {
|
|
827
|
+
statusCode = "STATUS_CODE_ERROR";
|
|
828
|
+
statusMessage = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error").slice(0, maxLen) : "error";
|
|
829
|
+
} else if (ev.status === "ok") {
|
|
830
|
+
statusCode = "STATUS_CODE_OK";
|
|
831
|
+
}
|
|
832
|
+
const spanJson = {
|
|
833
|
+
traceId,
|
|
834
|
+
spanId,
|
|
835
|
+
name: ev.name,
|
|
836
|
+
kind: "SPAN_KIND_INTERNAL",
|
|
837
|
+
startTimeUnixNano: startNs,
|
|
838
|
+
attributes: attrs,
|
|
839
|
+
status: {
|
|
840
|
+
code: statusCode,
|
|
841
|
+
...statusMessage !== void 0 ? { message: statusMessage } : {}
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
if (parentSpanId !== void 0) {
|
|
845
|
+
spanJson.parentSpanId = parentSpanId;
|
|
846
|
+
}
|
|
847
|
+
if (endNs !== void 0) {
|
|
848
|
+
spanJson.endTimeUnixNano = endNs;
|
|
849
|
+
}
|
|
850
|
+
spans.push(spanJson);
|
|
851
|
+
}
|
|
852
|
+
const payload = {
|
|
853
|
+
resourceSpans: [
|
|
854
|
+
{
|
|
855
|
+
resource: {
|
|
856
|
+
attributes: [stringAttr("service.name", "agent-inspect")]
|
|
857
|
+
},
|
|
858
|
+
scopeSpans: [
|
|
859
|
+
{
|
|
860
|
+
scope: { name: "agent-inspect" },
|
|
861
|
+
spans
|
|
862
|
+
}
|
|
863
|
+
]
|
|
864
|
+
}
|
|
865
|
+
]
|
|
866
|
+
};
|
|
867
|
+
return {
|
|
868
|
+
format: "otlp-json",
|
|
869
|
+
content: JSON.stringify(payload, null, pretty ? 2 : void 0),
|
|
870
|
+
contentType: "application/json",
|
|
871
|
+
fileExtension: ".otlp.json",
|
|
872
|
+
warnings
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// packages/core/src/exporters/validation.ts
|
|
877
|
+
var EXPERIMENTAL = "Experimental compatibility export \u2014 verify against your target tooling before relying on it.";
|
|
878
|
+
function validateExportContent(format, content) {
|
|
879
|
+
const errors = [];
|
|
880
|
+
const warnings = [EXPERIMENTAL];
|
|
881
|
+
if (format === "markdown") {
|
|
882
|
+
if (!content.startsWith("# AgentInspect Run")) {
|
|
883
|
+
errors.push('Markdown export must start with "# AgentInspect Run"');
|
|
884
|
+
}
|
|
885
|
+
return { ok: errors.length === 0, format, errors, warnings };
|
|
886
|
+
}
|
|
887
|
+
if (format === "html") {
|
|
888
|
+
const lower = content.toLowerCase();
|
|
889
|
+
if (!lower.includes("<!doctype html")) {
|
|
890
|
+
errors.push("HTML export must include <!doctype html>");
|
|
891
|
+
}
|
|
892
|
+
if (/<\s*script\b/i.test(content)) {
|
|
893
|
+
errors.push("HTML export must not contain script tags");
|
|
894
|
+
}
|
|
895
|
+
if (/<\s*link\b[^>]*href\s*=/i.test(content)) {
|
|
896
|
+
warnings.push("HTML export contains link tags \u2014 ensure no external stylesheets.");
|
|
897
|
+
}
|
|
898
|
+
return { ok: errors.length === 0, format, errors, warnings };
|
|
899
|
+
}
|
|
900
|
+
if (format === "openinference") {
|
|
901
|
+
let parsed;
|
|
902
|
+
try {
|
|
903
|
+
parsed = JSON.parse(content);
|
|
904
|
+
} catch {
|
|
905
|
+
errors.push("OpenInference export is not valid JSON");
|
|
906
|
+
return { ok: false, format, errors, warnings };
|
|
907
|
+
}
|
|
908
|
+
if (!parsed || typeof parsed !== "object") {
|
|
909
|
+
errors.push("OpenInference export JSON must be an object");
|
|
910
|
+
return { ok: false, format, errors, warnings };
|
|
911
|
+
}
|
|
912
|
+
const o = parsed;
|
|
913
|
+
if (o.format !== "openinference") {
|
|
914
|
+
errors.push('OpenInference export must include format: "openinference"');
|
|
915
|
+
}
|
|
916
|
+
if (!Array.isArray(o.spans)) {
|
|
917
|
+
errors.push("OpenInference export must include a spans array");
|
|
918
|
+
}
|
|
919
|
+
warnings.push("OpenInference-compatible JSON is not guaranteed for every backend.");
|
|
920
|
+
return { ok: errors.length === 0, format, errors, warnings };
|
|
921
|
+
}
|
|
922
|
+
if (format === "otlp-json") {
|
|
923
|
+
let parsed;
|
|
924
|
+
try {
|
|
925
|
+
parsed = JSON.parse(content);
|
|
926
|
+
} catch {
|
|
927
|
+
errors.push("OTLP JSON export is not valid JSON");
|
|
928
|
+
return { ok: false, format, errors, warnings };
|
|
929
|
+
}
|
|
930
|
+
if (!parsed || typeof parsed !== "object") {
|
|
931
|
+
errors.push("OTLP JSON export must be an object");
|
|
932
|
+
return { ok: false, format, errors, warnings };
|
|
933
|
+
}
|
|
934
|
+
const o = parsed;
|
|
935
|
+
if (!Array.isArray(o.resourceSpans)) {
|
|
936
|
+
errors.push("OTLP JSON export must include resourceSpans array");
|
|
937
|
+
}
|
|
938
|
+
warnings.push(
|
|
939
|
+
"OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation."
|
|
940
|
+
);
|
|
941
|
+
return { ok: errors.length === 0, format, errors, warnings };
|
|
942
|
+
}
|
|
943
|
+
errors.push(`Unsupported export format`);
|
|
944
|
+
return { ok: false, format, errors, warnings };
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// packages/core/src/exporters/index.ts
|
|
948
|
+
function mergeExportDefaults(options) {
|
|
949
|
+
return {
|
|
950
|
+
format: options.format,
|
|
951
|
+
includeMetadata: options.includeMetadata ?? true,
|
|
952
|
+
includeAttributes: options.includeAttributes ?? false,
|
|
953
|
+
includeErrors: options.includeErrors ?? true,
|
|
954
|
+
pretty: options.pretty ?? true,
|
|
955
|
+
redacted: options.redacted ?? true,
|
|
956
|
+
maxAttributeLength: options.maxAttributeLength ?? 500,
|
|
957
|
+
redactionProfile: options.redactionProfile ?? "local"
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
function exportRunTree(tree, options) {
|
|
961
|
+
const opts = mergeExportDefaults(options);
|
|
962
|
+
const exportTree = opts.redactionProfile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: opts.redactionProfile });
|
|
963
|
+
switch (opts.format) {
|
|
964
|
+
case "markdown":
|
|
965
|
+
return exportMarkdown(exportTree, opts);
|
|
966
|
+
case "html":
|
|
967
|
+
return exportHtml(exportTree, opts);
|
|
968
|
+
case "openinference":
|
|
969
|
+
return exportOpenInference(exportTree, opts);
|
|
970
|
+
case "otlp-json":
|
|
971
|
+
return exportOtlpJson(exportTree, opts);
|
|
972
|
+
default: {
|
|
973
|
+
const _x = opts.format;
|
|
974
|
+
throw new Error(`Unsupported export format: ${String(_x)}`);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
function validateExport(result) {
|
|
979
|
+
const base = validateExportContent(result.format, result.content);
|
|
980
|
+
return {
|
|
981
|
+
ok: base.ok,
|
|
982
|
+
format: base.format,
|
|
983
|
+
errors: base.errors,
|
|
984
|
+
warnings: [...result.warnings, ...base.warnings]
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
export { EXPORT_PAYLOAD_VERSION, exportHtml, exportMarkdown, exportOpenInference, exportOtlpJson, exportRunTree, manualTraceEventsToRunTree, mergeExportDefaults, redactRunTreeForExport, redactTraceEventsForReport, validateExport, validateExportContent };
|
|
989
|
+
//# sourceMappingURL=chunk-Y56BPA3B.mjs.map
|
|
990
|
+
//# sourceMappingURL=chunk-Y56BPA3B.mjs.map
|